Everything is working!
I’ve finally got a successful build and deploy with Circle CI.
There are a few things needed:
- Generate a GitHub repo deploy key with Circle CI
- Add your SSH private key to Circle CI
- Add your
.vault_pass
contents to an environment variable
- Add tags to deploy hooks so they can be ignored
- Configure Circle CI machines with
circle.yml
file
Generate a GitHub repo deploy key with Circle CI
Circle CI can create a deploy key automatically under Project Settings → Checkout SSH Keys
Add your SSH private key to Circle CI
When you connect to your server normally with Trellis, you have an SSH key pair which checks your private key on your local machine with the public key in the authorized_keys
file on the server.
Since the Circle CI build machine is now taking the place of your local machine, you need to give it your local private key. You could probably generate a new key specifically for this purpose but permissions between both servers & GitHub can be overly complex.
Add your .vault_pass
contents to an environment variable
Ansible Vault needs the password to decrypt the files but the .vault_pass
file is kept outside of version control. So we need to create one at build time by adding a custom environment variable and echoing it to a file once Circle CI has spun up.
echo "${ANSIBLE_VAULT_PASSWORD}" > .vault_pass
This is done in the circle.yml
file below after we change into the Trellis directory.
Add tags to deploy hooks so they can be ignored
There is some duplication that goes on during this configuration. Before testing my code, The build is run using Gulp + NPM. We don’t want to build it, run tests, then build it again on the CI. But we do want to be able to build it locally in my Vagrant VM.
To overcome this we add tags to the tasks in the before-build.yml
playbook and simply tell Ansible to skip tasks with the tag when calling the deploy playbook.
This is my build-before.yml
playbook.
- name: Run npm install
command: npm install
connection: local
args:
chdir: "{{ project.local_path }}/web/app"
tags: skip-using-ci
- name: Rebuild Node Sass
command: npm rebuild node-sass
connection: local
args:
chdir: "{{ project.local_path }}/web/app"
tags: skip-using-ci
- name: Run gulp
command: npm run production
connection: local
args:
chdir: "{{ project.local_path }}/web/app"
tags: skip-using-ci
- name: Copy project local files
synchronize:
src: "{{ project.local_path }}/web/app/themes"
dest: "{{ deploy_helper.new_release_path }}/web/app/themes"
group: no
owner: no
rsync_opts: --chmod=Du=rwx,--chmod=Dg=rx,--chmod=Do=rx,--chmod=Fu=rw,--chmod=Fg=r,--chmod=Fo=r
As you can see, some of them have the tag skip-using-ci
. When we call the deploy playbook we can add --skip-tags "skip-using-ci"
to, well, skip them.
Configure Circle CI machines
Using the circle.yml
file you need to configure the environment under which everything will run.
# Set the folder under which commands will be executed
general:
build_dir: site
# Configure the machine
machine:
timezone:
Europe/London
node:
version: 6.0.0
python:
version: 2.7.10
dependencies:
pre:
# Allow ansible playbooks to be run
- pip install ansible
# Turn an environment variable into a vault file that ansible can read
- cd ../trellis && echo "${ANSIBLE_VAULT_PASSWORD}" > .vault_pass
# This _should_ be inferred by Circle CI as we specify a node version but for some reason, it never ran for me. This tells it explicitly to run.
override:
- npm install
test:
# Again, this _should_ be inferred by Circle CI but wasn't.
override:
- npm run test
# Deployment Scenarios
deployment:
staging:
branch: staging
commands:
# This is just an ansible playbook command with some options to specify where the host file is located and that we want to skip tasks with the tag `skip-using-ci`
- cd ../trellis && ansible-playbook deploy.yml -e "site=example.com env=staging" -i hosts/staging --skip-tags "skip-using-ci" -vvv
I hope this helps. It will probably work with Travis as well. Not too sure about Jenkins.