GitLab Review Apps with Capistrano and Nginx

Wait, What are Review Apps?

I very recently set up GitLab’s Review Apps for this site, meaning that I can very easily spin up a copy of my site for visual review.

For example, the example/review-apps branch is deployed under the review/example/review-apps environment to http://example-review-apps.review.jvt.me/:

`example/review-apps` environment

This means that each branch I push to will spin up a new instance of my site under the review.jvt.me subdomain.

Being a static site, this hasn’t got a lot of overhead, especially as each Review App is going to have minimal traffic, due to it only being used by me in review. However, for a larger static site, or even a fully fledged web application, it can be understood why you may not want to be having each and every branch being built and deployed. This can be changed by setting it to be a manual task, rather than on each and every push.

Changes for Capistrano

For my site, I’m using Capistrano as the deployment tool, which means that when I want to perform a deploy, I can run something like cap production deploy. I’d ideally want to follow the same structure, and have a new stage so I can run cap review deploy.

This is easy to do, by creating the file config/deploy/review.rb:

# config/review/deploy.rb
server 'review.jvt.me', user: 'review_jvt_me', roles: %w{app}
set :deploy_to, "/srv/www/review.jvt.me/review.jvt.me/review/#{ENV['CI_COMMIT_REF_SLUG']}"

Next, we want to have the ability to tear down the environment once we’re finished with it. This can be done by creating our own Rake task file, in lib/capistrano/tasks/review.rake, and allows us to run cap review stop.

# lib/capistrano/tasks/review.rake
task :stop do
  on  roles(:app) do
    within(release_path) do
      execute "rm -rf #{fetch:deploy_to}"
    end
  end
end

Changes for GitLab CI

GitLab has the full steps required for setting up Review Apps in the Review Apps documentation. The first step required is to add a new entry in the deploy stage, which deploys into a review/... environment:

review_deploy:
  image: $CONTAINER_DEPLOY_IMAGE
  stage: deploy
  script:
    - eval $(ssh-agent -s)
    # ...
    - ssh-add key
    - cap review deploy
  environment:
    name: review/$CI_COMMIT_REF_SLUG
    url: http://$CI_COMMIT_REF_SLUG.review.jvt.me
    on_stop: review_stop
  only:
    - branches
  except:
    - master

Note the on_stop attribute, which is the name of the stage that is to be called once we tear down a Review App.

This does half of the work - we’ve spun up our Review App, but we haven’t yet specified how to tear it down. For instance, if you’re deploying a site with lots of images, or an application that may require resources such as CPU or memory to run, you may not always want to have the Review App always there.

Additionally, if you’re deploying a sufficiently large Git repo, or a site with a number of images, the disk space on your server may start to run out.

Therefore, to run a stop action, we use the name of the stage from the environment.on_stop attribute:

review_stop:
  image: $CONTAINER_DEPLOY_IMAGE
  stage: deploy
  script:
    - eval $(ssh-agent -s)
    # ...
    - ssh-add key
    - cap review stop
  environment:
    name: review/$CI_COMMIT_REF_SLUG
    action: stop
  when: manual
  only:
    - branches
  except:
    - master

Note that the only significant changes to the above are that we now:

Changes for Nginx

While investigating the easiest way of setting up Nginx to work with this, I stumbled upon the regular expression names functionality in Nginx, which allows you to define regular expressions for a DNS name. This was perfect, allowing me to add the following to my config:

# /etc/nginx/sites-enabled/review.jvt.me
server {
    listen 80;
    server_name ~^(www\.)?(?<sname>.+?).review.jvt.me$;
    root /srv/www/review.jvt.me/review.jvt.me/review/$sname/current/site;

    index index.html index.htm index.php;
    error_page 404 /404.html;

    charset utf-8;

    access_log /var/log/nginx/review.jvt.me-access.log;
    error_log  /var/log/nginx/review.jvt.me-error.log debug;
}

Points for Improvements

I’ve not yet got this configured yet as I fully want, and have been collecting future improvements and other useful thoughts in the ~review-apps label in my site’s issue tracker. I’m sure I’ll be tweaking it over the coming weeks as I find out what I like and want to have done with it.

*****

Written by Jamie Tanna on 18 July 2017, and last updated on 18 July 2017.

Tags

Categories