A long time ago we started using GitLab. It was a greate step forward and definitly the right decision to switch from SVN to a distributed version control system. If you want to try GitLab you should definitly install it using GitLab Omnibus distribution.

Manual installation

But this long time ago there was no omnibus installation available so we installed GitLab with a manual git checkout. Their monthly release cycle brought some upgrade effort every month - there was an upgrade tool available which was marked as discontinued - so i started to write some anisble scripts to handle the upgrade process.

Dependencies to shell and workhorse

GitLab uses now several different modular packages as GitLab workhorse and GitLab shell which have some version dependencies, so it would be great if ansible could determine these versions from GitLab checkout itself.

Ansible gitlab role

I’ve created a gitlab role which upgrades all parts of GitLab with just one single command:

gitlab
├── defaults
│   └── main.yml
├── handlers
│   └── main.yml
└── tasks
    ├── gitlab-shell.yml
    ├── gitlab-workhorse.yml
    ├── gitlab.yml
    └── main.yml

3 directories, 6 files

Role explained

  1. First we have to detect the installed version and compare it to required one, which is set in gitlab/tasks/defaults.yml. If version is already installed, we’re finished.

    File: ansible-roles/gitlab/defaults/main.yml

    ---
        # -----------------------------------------------------------------------------
        # ATTENTION: Please strip leading `v` from version information
        # valid: 7.12.2
        # invalid: v7.12.2
        # -----------------------------------------------------------------------------
        gitlab_user: git
        gitlab_version: 8.4.0
        
  2. Now fetch GitLab remote repository and checkout required version. installed one. If it is different to current installed one we have multiple tasks to be executed in given order.

    File: ansible-roles/gitlab/tasks/gitlab.yml

    ---
        - name: copy new init script
          shell: cp /home/{{ gitlab_user }}/gitlab/lib/support/init.d/gitlab /etc/init.d/gitlab
        
        - name: replace socket to tcp listener in init script (apache only)
          replace: dest=/etc/init.d/gitlab
                   regexp='^(gitlab_workhorse_options=".+ -listenNetwork) unix (-listenAddr) \$socket_path/gitlab-workhorse\.socket (.+) -authSocket \$rails_socket (.+)$'
                   replace='\1 tcp \2 127.0.0.1:8181 \3 \4'
                   backup=no
        
        - name: install using bundler
          shell: chdir=/home/{{ gitlab_user }}/gitlab PATH=$PATH:/usr/pgsql-9.4/bin bundle install --deployment --without development test mysql
          sudo_user: "{{ gitlab_user }}"
        
        - name: run database migration
          shell: chdir=/home/{{ gitlab_user }}/gitlab bundle exec rake db:migrate RAILS_ENV=production
          sudo_user: "{{ gitlab_user }}"
        
        - name: recompile assets
          shell: chdir=/home/{{ gitlab_user }}/gitlab bundle exec rake assets:clean assets:precompile cache:clear RAILS_ENV=production
          sudo_user: "{{ gitlab_user }}"
        
  3. If the version differs from current installed version, check new required shell version and checkout new shell if required.

    File: ansible-roles/gitlab/tasks/gitlab-shell.yml

    ---
        - name: read required shell version
          command: chdir=/home/{{ gitlab_user }}/gitlab cat GITLAB_SHELL_VERSION
          register: gitlab_shell_required_version
        
        - name: fetch remote and checkout required shell version
          git: repo=https://github.com/gitlabhq/gitlab-shell.git
               dest=/home/{{ gitlab_user }}/gitlab-shell
               version=v{{ gitlab_shell_required_version.stdout }}
               force=yes
          sudo_user: "{{ gitlab_user }}"
          when: gitlab_shell_required_version.changed
        
  4. If the version differs from current installed version, check new required workhorse version and checkout new workhorse if required. After checkout compile workhorse.

    File: ansible-roles/gitlab/tasks/gitlab-workhorse.yml

    ---
        - name: read required workhorse version
          command: chdir=/home/{{ gitlab_user }}/gitlab cat GITLAB_WORKHORSE_VERSION
          register: gitlab_workhorse_required_version
        
        # No semantic versioning, see: https://gitlab.com/gitlab-org/gitlab-git-http-server/issues/14
        - name: fetch remote and checkout required workhorse version
          git: repo=https://gitlab.com/gitlab-org/gitlab-workhorse.git
               dest=/home/{{ gitlab_user }}/gitlab-workhorse
               version={{ gitlab_workhorse_required_version.stdout }}
               force=yes
          sudo_user: "{{ gitlab_user }}"
          when: gitlab_workhorse_required_version.changed
        
        - name: compile workhorse
          command: make chdir=/home/{{ gitlab_user }}/gitlab-workhorse
          sudo_user: "{{ gitlab_user }}"
          when: gitlab_workhorse_required_version.changed
        
  5. Restart gitlab and apache service if a version has changed.

    File: ansible-roles/gitlab/handlers/main.yml

    ---
        - name: restart gitlab
          service:
            name: gitlab
            state: restarted
            enabled: true
        
        - name: restart apache
          service:
            name: httpd
            state: restarted
            enabled: true
        

Final words

Of course these formulas are specific to your setup, that’s why i did not push them into a public repo. If you use nginx as webserver, these may be different and you could use unix sockets instead of tcp listeners.

But anyway - feel free to submit your comments and reuse parts you need.