<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Max</title>
    <description>The latest articles on DEV Community by Max (@ekeih).</description>
    <link>https://dev.to/ekeih</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F160794%2F9b37e484-8313-4f9d-bcd3-125c810e60b4.png</url>
      <title>DEV Community: Max</title>
      <link>https://dev.to/ekeih</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ekeih"/>
    <language>en</language>
    <item>
      <title>Autoscaling GitLab Runners on Hetzner Cloud</title>
      <dc:creator>Max</dc:creator>
      <pubDate>Sun, 06 Dec 2020 19:00:59 +0000</pubDate>
      <link>https://dev.to/ekeih/autoscaling-gitlab-runners-on-hetzner-cloud-89e</link>
      <guid>https://dev.to/ekeih/autoscaling-gitlab-runners-on-hetzner-cloud-89e</guid>
      <description>&lt;p&gt;I use a private instance of &lt;a href="https://gitlab.com"&gt;GitLab&lt;/a&gt; a lot and utilize the GitLab CI for most of my private projects. To speed up the CI jobs I wanted to have more GitLab runners available on demand. From our GitLab instance at work I already knew that GitLab supports autoscaling runners with &lt;em&gt;docker-machine&lt;/em&gt;. At work we use this in combination with our OpenStack cloud, but for my personal use I was looking for a way to deploy GitLab runners at &lt;a href="https://www.hetzner.de/cloud"&gt;Hetzner Cloud&lt;/a&gt;. Searching the internet for a while led me to this (German) &lt;a href="https://www.marc-willmann.de/gitlab-runner-autoscale-in-der-hetzner-cloud"&gt;blog post&lt;/a&gt; which sounded like a good approach. So I went ahead and started to implement it in a very similar way. Long story short... I am extremly happy with the result! I can get an infinite amount of runners in less then a minute when I need them and they are deleted if I don't need them anymore. This way I only pay when I use them... some would even say that this is pretty much the point of &lt;em&gt;the cloud&lt;/em&gt; and &lt;em&gt;on demand resources&lt;/em&gt; 😄&lt;/p&gt;

&lt;h2&gt;
  
  
  The Architecture
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://docs.gitlab.com/runner/"&gt;GitLab runners&lt;/a&gt; can be installed on all kinds of systems and then ask the GitLab server in regular intervals for new jobs they should execute. &lt;a href="https://docs.gitlab.com/runner/executors/docker_machine.html"&gt;Docker-machine&lt;/a&gt; takes this approach to the next level and the jobs are not executed by the runner directly, instead it creates additional machines on demand and instructs them to run the jobs. If the additional machines idle for a while docker-machine removes them to save resources and money. Originally docker-machine was developed by Docker, but they stopped working on the project. Lucky for us GitLab forked the project and maintains it with minimal patches to ensure that we can still create autoscaling runners with it.&lt;/p&gt;

&lt;p&gt;Docker-machine itself does not support Hetzner Cloud out of the box but someone implemented &lt;a href="https://github.com/JonasProgrammer/docker-machine-driver-hetzner"&gt;a driver for it&lt;/a&gt;. 🙂 We are going to use this &lt;a href="https://github.com/mawalu/hetzner-gitlab-runner"&gt;Docker image&lt;/a&gt; to run docker-machine with Hetzner Cloud support. The image is based on the official runner image and adds the driver to it, which is exactly what we need.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Runner
&lt;/h2&gt;

&lt;p&gt;How and where we setup the runner is up to us. It doesn't need much resources (remember it does not run the jobs itself) so I just run it on the same server with mit GitLab instance. An easy way to get started is to use &lt;a href="https://docs.docker.com/compose/"&gt;docker-compose&lt;/a&gt;. Feel free to use any other kind of orchestrator/automation/script to manage it.&lt;/p&gt;

&lt;p&gt;A minimal &lt;code&gt;docker-compose.yml&lt;/code&gt; file is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;version: '2'
services:
  hetzner-runner:
    image: mawalu/hetzner-gitlab-runner:latest
    volumes:
      - "./hetzner_config:/etc/gitlab-runner"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's get this up by running &lt;code&gt;docker-compose up -d&lt;/code&gt;. Next thing we can do is to run &lt;code&gt;docker-compose run hetzner-runner register&lt;/code&gt; and answer a few interactive prompts, I decided to use Hetzners &lt;em&gt;cpx11&lt;/em&gt; flavor with 2 CPUs and 2GB of memory and the ubuntu-20.04 image. Adjust this depending on the requirements of your jobs. We can now exit our temporary container and should find a config file in &lt;code&gt;./hetzner_config/config.toml&lt;/code&gt; which we can edit to configure the runner further.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;concurrent = 50
check_interval = 0

[session_server]
  session_timeout = 1800

[[runners]]
  name = "Hetzner Autoscale"
  url = "https://git.example.com"
  token = "RUNNER TOKEN FROM GITLAB"
  executor = "docker+machine"
  limit = 10
  [runners.custom_build_dir]
  [runners.cache]
    Type = "s3"
    Shared = true
    [runners.cache.s3]
      ServerAddress = "OPTIONAL S3 PROVIDER"
      AccessKey = "OPTIONAL S3 CONFIG"
      SecretKey = "OPTIONAL S3 CONFIG"
      BucketName = "OPTIONAL S3 CONFIG"
  [runners.docker]
    tls_verify = false
    image = "ubuntu:20.04"
    privileged = true
    disable_entrypoint_overwrite = false
    oom_kill_disable = false
    disable_cache = false
    volumes = ["/cache", "/var/run/docker.sock:/var/run/docker.sock"]
    shm_size = 0
  [runners.machine]
    IdleCount = 0
    IdleTime = 1800
    MachineDriver = "hetzner"
    MachineName = "runner-%s"
    MachineOptions = ["hetzner-api-token=HETZNER API TOKEN", "hetzner-image=ubuntu-20.04", "hetzner-server-type=cpx11"]
  [[runners.machine.autoscaling]]
    Periods = ["* * * * * sat,sun *"]
    IdleTime = 21600
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All configuration options are documented in the &lt;a href="https://docs.gitlab.com/runner/configuration/autoscale.html"&gt;official docs&lt;/a&gt; but let's take a closer look at a few options.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;limit = 10:&lt;/strong&gt; The upper limit of machines created by autoscaling. We should always set this to a reasonable amount for our environment. In my case I don't want to create 100 machines by accident, so I keep it at a low ten. Though, this means if I have more than ten parallel jobs a few have to stay in a pending state for a while until a runner becomes available.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;runners.cache:&lt;/strong&gt; This is completely optional but if you have some S3 compatible object storage available you can configure it here. This speeds CI jobs up because the runners are able to share their cache. You can even use &lt;a href="https://min.io/"&gt;Minio&lt;/a&gt; to setup your own S3 compatible storage if you want to. If you don't have S3 storage available, don't worry, just remove this from the config.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;/var/run/docker.sock:&lt;/strong&gt; This is also optional! The Docker socket is usually required when we want to build Docker images with native Docker (&lt;code&gt;docker build ...&lt;/code&gt;). The downside of this is that we bypass all security features that we gain by using Docker in the first place. So we can only do this if we trust all people running code in these runners. If you don't need the Docker socket available inside your runners, remove this from the &lt;em&gt;volumes&lt;/em&gt; list. An alternative to mounting the socket and using &lt;code&gt;docker build&lt;/code&gt; is to use a Docker builder which runs in userspace and does not need the Docker daemon, for example I tinkered around with &lt;a href="https://github.com/GoogleContainerTools/kaniko"&gt;kaniko&lt;/a&gt; in the past. It works but in my experience it takes roughly twice as long as the Docker daemon to build a new image.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;runners.machine:&lt;/strong&gt; That's where it gets interesting. &lt;code&gt;IdleCount = 0&lt;/code&gt; tells docker-machine to not create any spare machines by default. This means if there are no jobs, there won't be any machines (also no costs). Though, if we create new jobs it will take a moment until new machines are created and become available, this leads to a slower pipeline run, but Hetzner Cloud machines boot up really fast — the machines are up and running in less then ten seconds and then afterwards it takes less then a minute for docker-machine to provision the runner on it. For faster results we could increase the &lt;code&gt;IdleCount&lt;/code&gt;, e.g. &lt;code&gt;IdleCound = 5&lt;/code&gt; to always have at least five runners available to run new jobs immediately (though we would have to pay five servers permanently). &lt;code&gt;IdleTime = 1800&lt;/code&gt; instructs docker-machine to keep idle runners around for 30 minutes before deleting them. This makes consecutive jobs faster because we don't have to wait for new machines to be provisioned.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;runners.machine.autoscaling:&lt;/strong&gt; We can define this multiple times to override the &lt;strong&gt;runners.machine&lt;/strong&gt; settings for specific time periods. In this example we tell docker-machine to keep idle runners for 6 hours on the weekend. For me this makes sense because I mostly use the runners for personal projects during the weekend. So when I start a few jobs at the weekend I will probably start more, so there is no need to delete idle runners just after 30 minutes. If you want to use this kind of setup in a work environment you probably want other autoscaling settings. For example, a bunch of &lt;code&gt;IdleCount&lt;/code&gt; machines and a high &lt;code&gt;IdleTime&lt;/code&gt; during the week from morning to evening, less during night and almost none at the weekend.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The costs
&lt;/h2&gt;

&lt;p&gt;What does it cost do run GitLab runners on Hetzner Cloud? Unfortunately there is no easy answer. The most important factor is the machine flavor we choose, the range goes from 0.005€/h (1 CPU, 2GB memory) to 0.095€/h (16 CPUs, 32GB memory). I always find these hourly prices hard to grasp, so let's put it another way: If we run a runner of the smallest instance for a full month we will pay roughly 3€, with the biggest flavor we pay 60€. The &lt;em&gt;cpx11&lt;/em&gt; flavor of our example costs 0.007€/h, which means roughly 4€ per month. In my use cases none of the runners will run for a full month and during most weekdays none will be created whatsoever, so I don't expect more than a few bucks per month. If you set &lt;code&gt;IdleCount = 10&lt;/code&gt; for a full month you will see a difference on the invoice though. One thing to keep in mind while configuring the &lt;code&gt;IdleTime&lt;/code&gt;: Hetzner bills the machines hourly, so each started hour costs, no matter if the server runs 1 minute or 59 minutes. So a very small &lt;code&gt;IdleTime&lt;/code&gt; probably doesn't make sense because we would create and delete servers more then necessary which means unnecessary costs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;With this fairly easy setup we now have autoscaling on demand GitLab runners on Hetzner Cloud. We only pay what we use and it works super reliable. 🚀&lt;/p&gt;

</description>
      <category>gitlab</category>
      <category>cicd</category>
      <category>operations</category>
    </item>
    <item>
      <title>Managing your git repositories with ghq</title>
      <dc:creator>Max</dc:creator>
      <pubDate>Sun, 29 Sep 2019 00:14:37 +0000</pubDate>
      <link>https://dev.to/ekeih/managing-your-git-repositories-with-ghq-3ffa</link>
      <guid>https://dev.to/ekeih/managing-your-git-repositories-with-ghq-3ffa</guid>
      <description>&lt;p&gt;The more you work with &lt;a href="https://dev.to/search?q=git"&gt;git&lt;/a&gt; to manage your code (or other stuff), the messier your directory structure becomes and after a few years you will maybe end up with something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;├── code
│   ├── blog
│   └── website
├── repos
│   ├── coolproject
│   └── demo
├── repositories
│   └── website
└── src
    ├── foo
    └── project1337
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;At least for me this happened. Most of these directories were at least actual git repositories but some were not. Obviously this is a mess and if each of the directories contains a few dozens of repositories it is getting hard to find the right one.&lt;/p&gt;

&lt;p&gt;So I was looking for a better way to organize my repositories and a friend told me about &lt;a href="https://github.com/motemen/ghq"&gt;ghq&lt;/a&gt;. ghq makes it easy to manage all your git repositories and enforces an organized directory structure at the same time. If you work with &lt;a href="https://golang.org"&gt;go&lt;/a&gt; you will already know the directory structure as it is exactly the same.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vJ70wriM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/github-logo-ba8488d21cd8ee1fee097b8410db9deaa41d0ca30b004c0c63de0a479114156f.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/x-motemen"&gt;
        x-motemen
      &lt;/a&gt; / &lt;a href="https://github.com/x-motemen/ghq"&gt;
        ghq
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Remote repository management made easy
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="adoc"&gt;
&lt;h1&gt;
ghq(1) &lt;span&gt;&lt;a href="https://github.com/x-motemen/ghq/actions?workflow=test"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--TsMArp3r--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://github.com/x-motemen/ghq/workflows/test/badge.svg%3Fbranch%3Dmaster" alt="Build Status"&gt;&lt;/a&gt;&lt;/span&gt; &lt;span&gt;&lt;a href="https://coveralls.io/r/motemen/ghq?branch=master" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/a48c5e0cf24b232e926540cd41ee5b76f87a692a990bd1f20e5ca8fb1f84daed/68747470733a2f2f636f766572616c6c732e696f2f7265706f732f6d6f74656d656e2f6768712f62616467652e7376673f6272616e63683d6d6173746572" alt="Coverage"&gt;&lt;/a&gt;&lt;/span&gt;
&lt;/h1&gt;
&lt;div&gt;
&lt;h2 id="user-content-name"&gt;
NAME&lt;/h2&gt;
&lt;div&gt;
&lt;div&gt;
&lt;p&gt;ghq - Manage remote repository clones&lt;/p&gt;
&lt;/div&gt;


&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;

&lt;div&gt;
&lt;h2 id="user-content-description"&gt;
DESCRIPTION&lt;/h2&gt;
&lt;div&gt;
&lt;div&gt;
&lt;p&gt;'ghq' provides a way to organize remote repository clones, like go get does. When you clone a remote repository by ghq get, ghq makes a directory under a specific root directory (by default ~/ghq) using the remote repository URL’s host and path.&lt;/p&gt;
&lt;/div&gt;

&lt;div&gt;
&lt;div&gt;
&lt;pre&gt;$ ghq get https://github.com/x-motemen/ghq
# Runs `git clone https://github.com/x-motemen/ghq ~/ghq/github.com/x-motemen/ghq`&lt;/pre&gt;
&lt;/div&gt;


&lt;/div&gt;

&lt;div&gt;
&lt;p&gt;You can also list local repositories (ghq list).&lt;/p&gt;
&lt;/div&gt;


&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;

&lt;div&gt;
&lt;h2 id="user-content-synopsis"&gt;
SYNOPSIS&lt;/h2&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre&gt;ghq get [-u] [-p] [--shallow] [--vcs &amp;lt;vcs&amp;gt;] [--look] [--silent] [--branch] [--no-recursive] &amp;lt;repository URL&amp;gt;|&amp;lt;host&amp;gt;/&amp;lt;user&amp;gt;/&amp;lt;project&amp;gt;|&amp;lt;user&amp;gt;/&amp;lt;project&amp;gt;|&amp;lt;project&amp;amp;gt
ghq list [-p] [-e] [&amp;lt;query&amp;gt;]
ghq create [--vcs &amp;lt;vcs&amp;gt;] &amp;lt;repository URL&amp;gt;|&amp;lt;host&amp;gt;/&amp;lt;user&amp;gt;/&amp;lt;project&amp;gt;|&amp;lt;user&amp;gt;/&amp;lt;project&amp;gt;|&amp;lt;project&amp;amp;gt
ghq root [--all]&lt;/pre&gt;
&lt;/div&gt;


&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;

&lt;div&gt;
&lt;h2 id="user-content-commands"&gt;
COMMANDS&lt;/h2&gt;
&lt;div&gt;
&lt;div&gt;
&lt;dl&gt;
&lt;dt&gt;get&lt;/dt&gt;
&lt;dd&gt;
&lt;p&gt;Clone a remote repository under ghq root directory (see
&lt;a href="https://raw.githubusercontent.com/x-motemen/ghq/master/README.adoc/#directory-structures"&gt;DIRECTORY STRUCTURES&lt;/a&gt; below). If the repository is
already cloned to local, nothing will happen unless '-u' ('--update')
flag is supplied, in which case the local repository is updated ('git pull --ff-only' eg.)
When you use '-p' option, the repository is cloned via…&lt;/p&gt;
&lt;/dd&gt;
&lt;/dl&gt;
&lt;/div&gt;


&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;br&gt;
  &lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/x-motemen/ghq"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


&lt;h2&gt;
  
  
  Install ghq
&lt;/h2&gt;

&lt;p&gt;Installing ghq is &lt;a href="https://github.com/motemen/ghq#installation"&gt;easy&lt;/a&gt;. If you use a Linux distribution you can check if ghq is available with your package manger. Otherwise you have a few other options. You can download a current release from &lt;a href="https://github.com/motemen/ghq/releases"&gt;GitHub&lt;/a&gt; or if you have &lt;code&gt;go&lt;/code&gt; installed you can run &lt;code&gt;go get github.com/motemen/ghq&lt;/code&gt;. In case you use macOS you can run &lt;code&gt;brew install ghq&lt;/code&gt;. To verify your installation just run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ghq root
/home/max/.ghq
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Change the ghq root directory
&lt;/h2&gt;

&lt;p&gt;By default ghq organizes all git repositories in &lt;code&gt;~/.ghq&lt;/code&gt; but this can be changed in your &lt;code&gt;~/.gitconfig&lt;/code&gt; file. For example, I do not like the idea of putting all my repositories in a hidden directory like &lt;code&gt;.ghq&lt;/code&gt;. Instead I use &lt;code&gt;~/repos&lt;/code&gt;, so the config entry in my &lt;code&gt;~/.gitconfig&lt;/code&gt; file looks like this:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[ghq]
  root = ~/repos
  root = ~/go/src
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;By defining &lt;code&gt;root=...&lt;/code&gt; multiple times ghq uses all directories to look for repositories. This is particularly helpful if you work with &lt;em&gt;go&lt;/em&gt; as it organizes repositories in the same way but puts them in &lt;code&gt;~/go/src&lt;/code&gt; by default. If you run &lt;code&gt;ghq list&lt;/code&gt; to display all your repositories ghq looks in &lt;code&gt;~/repos&lt;/code&gt; and &lt;code&gt;~/go/src&lt;/code&gt; as well.&lt;/p&gt;
&lt;h2&gt;
  
  
  Clone a new repository
&lt;/h2&gt;

&lt;p&gt;Instead of running &lt;code&gt;git clone https://github.com/githubtraining/hellogitworld.git&lt;/code&gt; to checkout a new repository you now have to run &lt;code&gt;ghq get https://github.com/githubtraining/hellogitworld.git&lt;/code&gt; and ghq will use the repository URL to create a proper directory structure:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;repos/
└── github.com
    └── githubtraining
        └── hellogitworld
            ├── build.gradle
            ├── fix.txt
            ├── pom.xml
            ├── README.txt
            └ ...

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;If you clone more repositories from different servers and from different users ghq will create directories to keep everything organized. For example an excerpt of my &lt;code&gt;~/repos&lt;/code&gt; directory looks like this:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;├── aur.archlinux.org
│   └── heluxup
│       └── PKGBUILD
├── git.centralserver.de
│   └── max
│       ├── bachelor-thesis
│       ├── dotfiles
│       ├── fotoallerlei
│       ├── k8s
│       ├── telegram-stickers
│       └── wiki
└ github.com
    ├── ekeih
    │   ├── dwdpollen
    │   ├── FrundenBot
    │   ├── hello-github-actions
    │   ├── heluxup
    │   ├── hetzner-deployment
    │   ├── i3-renameworkspace
    │   ├── icinga2telegram
    │   ├── InfiniteWisdomBot
    │   ├── tado
    │   ├── tado-influxdb
    │   ├── taustakuva
    │   └── webhook
    ├── fluxcd
    │   └── helm-operator
    └── grandchild
        └── arch-cleaner
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;ghq look&lt;/code&gt; and what's bad about it
&lt;/h2&gt;

&lt;p&gt;Personally, I find this way of organization already a huge benefit of ghq but the major advantage is the way ghq helps to directly jump to the right repository:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ pwd
/home/max
$ ghq look hello
$ pwd
/home/max/repos/github.com/githubtraining/hellogitworld
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This way it is possible to switch between different repositories fast and without the need to know the full paths of them. Unfortunately, this has a downside... the current working directory of a process is part of its environment and a process environment can not be modified by its child processes. If you want to read some more technical background about this I recommend this &lt;a href="https://stackoverflow.com/questions/53984853/change-parent-shell-directory-from-go/53984896#53984896"&gt;Stack Overflow answer&lt;/a&gt; which explains it in more detail. In the end it means that ghq can not really change the current working directory of your shell... instead it starts a new subshell in the new directory. Technically this works but I find it very annoying that each &lt;code&gt;ghq look ...&lt;/code&gt; adds an additional nested shell process end exiting the shell means to return to the previous shell.&lt;/p&gt;


&lt;div class="ltag__stackexchange--container"&gt;
  &lt;div class="ltag__stackexchange--title-container"&gt;
    
      &lt;div class="ltag__stackexchange--title"&gt;
        &lt;h1&gt;
          &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--pTF_nE4a--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/stackoverflow-logo-b42691ae545e4810b105ee957979a853a696085e67e43ee14c5699cf3e890fb4.svg" alt=""&gt;
            &lt;a href="https://stackoverflow.com/questions/53984853/change-parent-shell-directory-from-go/53984896#53984896" rel="noopener noreferrer"&gt;
              &lt;span class="title-flare"&gt;answer&lt;/span&gt; re:  Change parent shell directory from Go
            &lt;/a&gt;
        &lt;/h1&gt;
        &lt;div class="ltag__stackexchange--post-metadata"&gt;
          &lt;span&gt;Dec 31 '18&lt;/span&gt;
        &lt;/div&gt;
      &lt;/div&gt;
      &lt;a class="ltag__stackexchange--score-container" href="https://stackoverflow.com/questions/53984853/change-parent-shell-directory-from-go/53984896#53984896" rel="noopener noreferrer"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5MiFESHx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/stackexchange-arrow-up-eff2e2849e67d156181d258e38802c0b57fa011f74164a7f97675ca3b6ab756b.svg" alt=""&gt;
        &lt;div class="ltag__stackexchange--score-number"&gt;
          5
        &lt;/div&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Rk_a5QFN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/stackexchange-arrow-down-4349fac0dd932d284fab7e4dd9846f19a3710558efde0d2dfd05897f3eeb9aba.svg" alt=""&gt;
      &lt;/a&gt;
    
  &lt;/div&gt;
  &lt;div class="ltag__stackexchange--body"&gt;
    
&lt;blockquote&gt;
  &lt;p&gt;How can I write a Go program that will behave as 'cd ' does?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;This is &lt;em&gt;impossible&lt;/em&gt;&lt;/strong&gt; on POSIX systems (even by using any other programming languages).&lt;/p&gt;

&lt;p&gt;Because each &lt;a href="https://en.wikipedia.org/wiki/Process_(computing)" rel="nofollow noreferrer"&gt;process&lt;/a&gt;, including the parent shell process, has its &lt;em&gt;own&lt;/em&gt; current &lt;a href="https://en.wikipedia.org/wiki/Working_directory" rel="nofollow noreferrer"&gt;working directory&lt;/a&gt;. Hence &lt;code&gt;cd&lt;/code&gt; has to be a &lt;a href="https://en.wikipedia.org/wiki/Shell_builtin" rel="nofollow noreferrer"&gt;shell&lt;/a&gt;…&lt;/p&gt;
    
  &lt;/div&gt;
  &lt;div class="ltag__stackexchange--btn--container"&gt;
    
      &lt;a href="https://stackoverflow.com/questions/53984853/change-parent-shell-directory-from-go/53984896#53984896" rel="noopener noreferrer"&gt;Open Full Answer&lt;/a&gt;
    
  &lt;/div&gt;
&lt;/div&gt;



&lt;p&gt;The developer of ghq suggested to use a shell function to wrap ghq to solve this issue in a &lt;a href="https://github.com/motemen/ghq/issues/29#issuecomment-52584401"&gt;GitHub issue&lt;/a&gt;. This approach may seem a bit weird but it actually works great and is probably the only solution to really change the current working directory by running &lt;code&gt;cd&lt;/code&gt; which is a shell builtin (shell builtins run directly in the shell process and not in a new one, therefore they can modify the environment of the shell process).&lt;/p&gt;


&lt;div class="ltag_github-liquid-tag"&gt;
  &lt;h1&gt;
    &lt;a href="https://github.com/x-motemen/ghq/issues/29#issuecomment-52584401"&gt;
      &lt;img class="github-logo" alt="GitHub logo" src="https://res.cloudinary.com/practicaldev/image/fetch/s--vJ70wriM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/github-logo-ba8488d21cd8ee1fee097b8410db9deaa41d0ca30b004c0c63de0a479114156f.svg"&gt;
      &lt;span class="issue-title"&gt;
        Comment for
      &lt;/span&gt;
      &lt;span class="issue-number"&gt;#29&lt;/span&gt;
    &lt;/a&gt;
  &lt;/h1&gt;
  &lt;div class="github-thread"&gt;
    &lt;div class="timeline-comment-header"&gt;
      &lt;a href="https://github.com/motemen"&gt;
        &lt;img class="github-liquid-tag-img" src="https://res.cloudinary.com/practicaldev/image/fetch/s--bF9KFXrT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://avatars2.githubusercontent.com/u/8465%3Fv%3D4" alt="motemen avatar"&gt;
      &lt;/a&gt;
      &lt;div class="timeline-comment-header-text"&gt;
        &lt;strong&gt;
          &lt;a href="https://github.com/motemen"&gt;motemen&lt;/a&gt;
        &lt;/strong&gt; commented on &lt;a href="https://github.com/x-motemen/ghq/issues/29#issuecomment-52584401"&gt;&lt;time&gt;Aug 19, 2014&lt;/time&gt;&lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag-github-body"&gt;
      &lt;p&gt;I'd like to, but I don't think ghq can change the shell's working directory since it is just a command, not a shell function. Any ideas to implement that?&lt;/p&gt;
&lt;p&gt;One solution would be providing a shell function like:&lt;/p&gt;
&lt;div class="highlight highlight-source-shell js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-en"&gt;ghq&lt;/span&gt; () {
    &lt;span class="pl-k"&gt;if&lt;/span&gt; [ &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;span class="pl-smi"&gt;$1&lt;/span&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt; &lt;span class="pl-k"&gt;=&lt;/span&gt; look &lt;span class="pl-k"&gt;-a&lt;/span&gt; &lt;span class="pl-k"&gt;-n&lt;/span&gt; &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;span class="pl-smi"&gt;$2&lt;/span&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt; ]&lt;span class="pl-k"&gt;;&lt;/span&gt; &lt;span class="pl-k"&gt;then&lt;/span&gt;
        &lt;span class="pl-c1"&gt;cd&lt;/span&gt; &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;$(&lt;/span&gt;command ghq list -e -p &lt;span class="pl-smi"&gt;$2&lt;/span&gt;&lt;span class="pl-pds"&gt;)&lt;/span&gt;&lt;/span&gt;
        &lt;span class="pl-k"&gt;return&lt;/span&gt;
    &lt;span class="pl-k"&gt;fi&lt;/span&gt;

    &lt;span class="pl-c1"&gt;command&lt;/span&gt; ghq &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;span class="pl-smi"&gt;$@&lt;/span&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;
}&lt;/pre&gt;

&lt;/div&gt;

    &lt;/div&gt;
    &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/x-motemen/ghq/issues/29#issuecomment-52584401"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;I extended the function a bit and added it to my &lt;code&gt;~/.bashrc&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ghq () {
  if [ "$1" = look -a -n "$2" ]; then
    local repos=($(command ghq list -p "$2"))
    case ${#repos[@]} in
      0)
        echo 'No repo found.'
        return 1
        ;;
      1)
        cd "${repos[0]}"
        return
        ;;
      *)
        local PS3="Select repo: "
        select reponame in ${repos[@]}; do
          cd "${reponame}"
          return
        done
    esac
  elif [ "$1" = get -a -n "$2" ]; then
    command ghq "$@"
    cd $(command ghq list -e -p "$2")
    return
  fi
  command ghq "$@"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Remember to open a new shell after modifying your &lt;code&gt;~/.bashrc&lt;/code&gt; or run &lt;code&gt;source ~/.bashrc&lt;/code&gt; to load the changes. So what does it do? Each time you run &lt;code&gt;ghq&lt;/code&gt; your shell does not run the real ghq binary (e.g. in &lt;code&gt;/usr/bin/ghq&lt;/code&gt;) directly, instead it runs the &lt;code&gt;ghq&lt;/code&gt; shell function with the same name.&lt;/p&gt;

&lt;p&gt;Line 2-19 wrap the &lt;code&gt;ghq look ...&lt;/code&gt; call which means that when you call &lt;code&gt;ghq look foobar&lt;/code&gt; the function searches for a repository with &lt;em&gt;foobar&lt;/em&gt; in its name and if it finds exactly one it runs &lt;code&gt;cd ...&lt;/code&gt; to switch to the directory of it. In case multiple repositories match &lt;em&gt;foobar&lt;/em&gt; it offers a selection and then switches to the selected repository.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ghq look foobar
No repo found.
$ ghq look fotoallerlei
$ pwd
/home/max/repos/git.centralserver.de/max/fotoallerlei
$ ghq look heluxup
1) /home/max/repos/aur.archlinux.org/heluxup
2) /home/max/repos/github.com/ekeih/heluxup
Select repo: 2
$ pwd
/home/max/repos/github.com/ekeih/heluxup
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Line 20-25 wrap the &lt;code&gt;ghq get ...&lt;/code&gt; call to switch to the new repository after it is cloned successfully. If ghq is called neither with &lt;code&gt;look&lt;/code&gt; nor with &lt;code&gt;get&lt;/code&gt; the original ghq binary is called directly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrap-Up
&lt;/h2&gt;

&lt;p&gt;After moving my existing repositories to the directory structure of ghq I started to use it every day. It took me a few days to remember to use &lt;code&gt;ghq get ...&lt;/code&gt; instead of &lt;code&gt;git clone ...&lt;/code&gt; and &lt;code&gt;ghq look ...&lt;/code&gt; instead of &lt;code&gt;cd ~/repos/...&lt;/code&gt;, but I like this new workflow very much and can recommend it to everyone who uses multiple git repositories. The shell function to wrap the ghq call to avoid nested shell processes makes it a lot easier to use, so please take a second and add it to your &lt;code&gt;~/.bashrc&lt;/code&gt; if you start to use ghq.&lt;/p&gt;

</description>
      <category>git</category>
      <category>productivity</category>
      <category>bash</category>
      <category>linux</category>
    </item>
  </channel>
</rss>
