In my previous article I showed how to use Cloudflare workers to add custom HTTP headers onto your site to make it compliant with security expectations.
In this instalment I'm going to add onto that a CI/CD pipeline to test and deploy future changes to that worker.
CI/CD?
CI/CD stands for 'continuous integration' & 'continuous deployment'. It is a methodology that says every time a developer commits code we should build it, test it and if the tests pass (the pipeline is 'green') we should deploy it to production (with confidence).
Typically a CI/CD pipeline would consist of the following stages:
- Unit Tests
- Build & Package
- Deploy to Test environment
- Functional Tests
- Non-functional Tests e.g. performance
- Deploy to Production
- Smoketest
Not every step is needed every time, but there are some common steps you'll do most times including Unit Tests, Deployments and Functional Tests.
Our pipeline will be simple:
- Deploy the worker to a workers.dev subdomain
- Test that the headers are coming as expected
- Deploy the worker to the production route
- Check the site is loading (returning a HTTP 2xx code)
Environments
In the previous article we had a single environment (the production site), but here we are adding a second. This means modifying your wrangler.toml file as follows:
name = "name-of-worker"
type = "javascript"
account_id = "..."
workers_dev = true
[env.production]
route = "..."
zone_id = "..."
Note if you have previously deployed to production without calling it production your first deployment after adding the 'env.production' block will fail with a duplicate route error. You will need to manually delete that route in order for subsequent deployments to be successful.
Of course your account_id, route and zone_id will be populated with meaningful values for your setup. This will give you two environments a 'dev' or 'test' environment and production.
You deploy to dev like this:
wrangler publish
You deploy to production like this:
wrangler publish --env production
Testing
Now we know how to deploy, we need to add some tests to make sure that the deployment to workers.dev has been successful. We can do this test by making a HTTP GET to the worker and checking the headers that are set on the response.
I've published my code for this here https://rjk.xyz/RYRX it uses the axios library so make sure it is installed in your project
npm install --save axios
Looking at the code you'll notice it is a bit hacky, this is because Cloudflare returns an origin error when this request is made (as there is no origin to return a response). However this can still work for us, as the error object still shows us the headers set on the response and that is all we need to make sure the worker is working properly. All you need to do is edit the expectedHeaders
array to match your needs:
const expectedHeaders = [
{
name: "Strict-Transport-Security",
value: "max-age=63072000"
},
{
name: "X-Frame-Options",
value: "DENY"
},
{
name: "X-Content-Type-Options",
value: "nosniff"
},
{
name: "X-XSS-Protection",
value: "1; mode=block"
},
{
name: "Referrer-Policy",
value: "strict-origin-when-cross-origin"
}
]
The script will exit(0) on success or exit(1) on failure, printing the reason why it failed.
There is a feature in the pipeline
wrangler dev
which will allow you to run a local worker and test against 'localhost'. Once that becomes available I'll update this script. There are more details on this upcoming feature here https://github.com/cloudflare/wrangler/milestone/18
Putting it in a pipeline
I'm using GitHub actions to pull all this together. My workflow file looks like this
name: Build and Deploy Worker
on: [push]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [12.x]
steps:
- uses: actions/checkout@v1
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- name: install wrangler
run: npm install -g @cloudflare/wrangler
- name: deploy to dev
run: CF_ACCOUNT_ID=${{ secrets.CF_ACCOUNT_ID }} CF_API_TOKEN=${{ secrets.CF_API_TOKEN }} wrangler publish
- name: install and run tests
run: |
cd tests/
npm ci
node index.js
- name: production deployment
run: CF_ACCOUNT_ID=${{ secrets.CF_ACCOUNT_ID }} CF_API_TOKEN=${{ secrets.CF_API_TOKEN }} wrangler publish --env production
To make it work you will need your tests in a folder called tests/
. If those tests fail (exit with anything other than code=0) then the whole workflow will fail and the production deployment will not happen.
Outcome
Now whenever I commit code to my cloudflare worker repository it is deployed to the workers.dev subdomain, tested and if those tests pass it is deployed to production.
-- Richard, Jan 2020