10mins read time
It’s been a while since I posted and I thought I’d cover something I recently struggled with during Hacktoberfest.
This article covers how to integrate create-react-app, Github and CircleCI. Enabling an automated pipeline to help you test and deploy through development and production.
First of all, a little about the tools we are using. If you work with modern web stacks then you will be aware and maybe familiar of what create-react-app is and what it solves. It’s a tool created by our friends at Facebook to get a website up and running in a couple of commands, super useful and here’s a link – https://github.com/facebook/create-react-app .
Next we have CircleCI. This is what it says on the tin, a continuous integration tool. It has a free tier for OSS projects which generally makes it ideal for most use cases. I have a tendency to use it nowadays and enjoy the simplicity of defining workflows within the project with a lot of flexibility and integration with different VCS’ and environments. Again here’s a link – https://circleci.com/
I think everyone should know what Github is.
Creating your create-react-app project
So let’s get going by creating a new app and setup your OSS project. First you need to create your CRA project.
NPM <=5.1 users will need to run
npm i -g create-react-app && create-react-app my-app
Everyone else can use
npx create-react-app my-app
You should now be able to run
cd my-app && npm start. This will build and run you project on a local development server at http://localhost:3000/, this is so simple.
Adding CircleCI config
A few initial steps which you need to do to make the project truly OSS are as follows:
Add the integration/web hook from CircleCI to your source control, Github is the default for this article.
You need to enable ‘Build forked pull requests’ otherwise any contributors won’t be able to have the workflow test their PR’s.
Setup a ‘user key’ to allow your CI the permissions it needs to deploy.
Setup any necessary variables for your workflow to run smoothly, such as emails and usernames that you may want to obfuscate.
We need to set up the integration and web hook between your CI and Github. First, sign into your CircleCI account and make sure you have added your Github account to CircleCI integration (If this isn’t provided as a first action, then you will find this in User Settings > Account Integrations). This will allow the CI to discover all the repositories available. Next, go to ‘Add projects’ and ‘set up project’ for the repo that you are working on. This will create the webhook into your Github account for that project only. You will most likely have to authorise CircleCI as a third party application.
The next page should provide you with the next steps needed to get going initially(use the defaults), including a sample CircleCI config for the project, and how to get the build running. This is a great place to start, make sure to add and commit this to your project. Later on I will provide you with my own when we want to get deploying.
Nice and easy so far. Now you need to navigate to your Github repo and then to Settings > Branches > Branch Protection Rules > Add rule. Here we are simply going to define the merge checks needed for pull requests to be merged. All we want to do is set up a rule for master, later on you can go crazy and define additional rules if you want for other branches. Tick the following:
Require status checks to pass before merging
Require branches to be up to date before merging
At this point, any PR’s which are created in Github and their destination is Master will kick off a build on the CI. Before they can be merged, all ‘ci/circleci’ checks will need to pass.
Forked Pull Requests
Now this is one of the tricky steps which took a few Google’s to get to the solution. The reason we need to do this step is because in Github, other contributors will typically fork your project when they want to work on it. When they are ready to commit work to your project they will push a branch to their forked repo and then create a PR from their fork to your master project. This is where the disconnect happens, as this type of PR won’t trigger builds, what we need to do is enable the CI to allow this.
Go to your CircleCI dashboard, Settings > Projects > Cog Icon(for the followed repo) > Advanced Settings > Tick ‘Build forked pull requests’
Voila! PR’s from forked repos to yours will now trigger builds. Maybe you can find a friend to test this.
In order for your project to later be deployed, the CI needs the correct permissions to act on your behalf. For this we need to set up a ‘User key’, essentially an SSH key which has full privileges.
Go to your CircleCI dashboard, Settings > Projects > Cog Icon(for the followed repo) > Checkout SSH Keys
Here there is more information on why we are doing this, but there should also be a link to add a User Key for this project. Go ahead and do this.
There are two variables we will use later on within the CircleCI config, Github email and Github name.
Once again, go to your CircleCI dashboard, Settings > Projects > Cog Icon(for the followed repo) > Environment Variables
I set up mine with the variable names `GH_EMAIL` and `GH_NAME` to give context on what and where they are for.
Pipeline and workflow
Out the box, CRA gives you so much. The starter package scripts are one thing, and the Jest testing is another. There will be a simple test file ready to be run with `npm test`. Give it a go to see it in action. As you probably know, the CircleCI config has a line which runs `yarn test`. This does exactly the same thing.
The test script is the initial check we do for each PR we wish to merge, but we can do so much more. Where we want to get to is automating most of the heavy lifting. If we add any additional tests, linting, coverage, we want CircleCI to be doing this via the triggers. This allows us to run through the PR’s, see a green button and click ‘Merge’!!
Now, I like to make use of workflows. This allows you to break down the pipeline into a series of checks. If you think of it like a bunch of logic gates, then first we checkout our project, if successful we test the project, if successful we build it, and so on and so forth. If your pipeline fails at any point, then this will make it easier to identify which part broke in the chain. Each step should require the previous step in order to continue, that way each step has it’s own dependencies. You will end up with something like this in your CircleCI workflow…
In addition to this, we can also specify special outcomes for specific branches. This is particularly useful when we only want to deploy when something is merged to master.
Real world examples
I have simplified some of my config to show how to set up a simple workflow that will test, build and deploy to Github pages.
Firstly we need to define our steps, the following is an example step…
Let’s walk through what we have here. We set the version to 2, this allows us to use the latest workflow and processes available. Jobs allows us to define each of the individual ‘jobs’ of the pipeline, in our example, we are creating a ‘build and test’ job.
Within the job we define the container image, latest node image, and the steps necessary for the job. Those being to checkout the branch, restore cache(if available, this speeds up the pipeline), install project dependencies, save cache(when done the first time it will be used by restore cache for future builds) and finally to run the tests.
As mentioned before, we create more jobs to define the workflow of the pipeline and allow us to see easily what passes and fails.
Now, we take these jobs and define our workflow…
Workflows is one of the benefits of version 2. It is much cleaner and easier to define, with a lot of key methods to help streamline the pipeline. This time in jobs, we define which jobs we want to run in our workflow. In this example I have defined an additional job called ‘deploy’.
As you may notice we have two new fields, ‘filters’ and ‘requires’. Filters allows us to define the specific branches we run our steps on, whether to ‘ignore’ or ‘only’ run on a specific branch(es). Requires tells the step that it can only run if the build meets the requirements, in this example ‘deploy’ needs ‘build_and_test’ to succeed first.
Easy as that, you’re on your way to making CircleCI doing all the heavy lifting.
One of the final pieces of the puzzle is getting the deploy script to actually deploy without errors.
If you use the script as is, it creates a
gh-pagesbranch in order to deploy from your build folder. The build folder doesn’t contain a CircleCI config and so it will fail. Complaining that no configuration was found.
Here you will see some common commands. The magic here happens in the final run command. Okay it’s not so much magic, if you recall from before we created two environment variables. We use those here to set the git config email and name to avoid permission issues during the build as I mentioned earlier.
Some extra goodness happens after the build, where we essentially create a directory for the CircleCI config and then copy the copy of the project to the build folder. Why we do this is so that we can avoid any build failures being reported in our CI.
You may have noticed earlier in our workflow we also stated that we wanted to ignore `gh-pages` branches from tests. This will be accounted for in our CI, it will work out that the branch matches and ignore testing this branch.
That’s all that’s needed, you should now have a project built with CRA and integrated with Github and CircleCI. Any pull requests which are raised into your repository will trigger the tests on the CI and, once they pass, will be reported back to Github to allow you to merge into your project. This is a typical merge strategy, but there are plenty of ways to improve which you can investigate yourself.