How to Continuously Deploy a Create-React-App Using GitHub Actions and Zeit Now
When we worked on our hackathon project jetztein.de, we wanted an easy way to immediately see the current state of our application. Ideally a new version should get deployed every time we push changes to the repository. This is commonly referred to as Continuous Deployment (CD). The GitLab blog has a nice introduction to the topic (and also how their tooling can help you implement CI/CD). They write:
Continuous Deployment is a software development practice in which every code change goes through the entire pipeline and is put into production automatically, resulting in many production deployments every day. It does everything that Continuous Delivery does, but the process is fully automated, there’s no human intervention at all.
Kicking off the process should only require this one easy step: running
git push or hitting the Merge-button in the GitHub UI would automatically trigger the process to build and then deploy the application which would finally become accessible at some URL.
This sounds promising — less things to worry about and more time for the exciting stuff! 👍
We already had a GitHub repository with our app and an account with the zero-configuration app platform Zeit Now. So we had all the parts but were missing the tool to connect them. Enter GitHub Actions.
Unfortunately our GitHub Organization didn’t have the Actions feature enabled at the time so we had to do this process manually. But since we now do have access, we wanted to revisit the topic and see if it makes sense to consider this for future projects.
You’ll need a couple things if you want to try it out yourself (but you can also just follow along):
- An application you would like to deploy: We are working with a Create-React-App but any kind of app with a supported “builder” in Now will do.
- A GitHub repository with the app: Our hackathon repository is private but this will work with both public and private ones as long as you have…
- Access to the GitHub Actions feature: The feature is in beta at the time of writing. You may request access to it. You can confirm that you have it by checking the navigation of your GitHub repository: you should see a tab called Actions.
- A Now account: You’ll need to create a personal access token in your account settings which we’ll use as
ZEIT_TOKENto trigger commands from GitHub Actions.
Please note: We won’t be using the existing GitHub integration offered by Now in the following example. Instead we make use of the new GitHub Actions feature which gives us much greater power and flexibility. This also doesn’t require linking your entire GitHub Organization to Now.
There are three easy steps to Continuous Deployment (CD) with GitHub Actions and Now:
- Adding a
now.jsonto the repository
- Adding an NPM script called
- Adding a GitHub Actions workflow to the repository
Let’s get started! 🚀
1. Adding a
now.json to the Repository
Now uses a configuration file, commonly called
now.json. If the file is not present, the CLI will try its best to auto-discover some configuration values based on the given environment. We prefer configuration in code to make the behavior consistent and explicit.
The Now docs have an example for Create-React-App which we adapted for our needs:
Let’s go through these options one-by-one to understand them a bit better:
Please consult the official configuration reference for further details.
2. Adding an NPM Script Called
now-build to the
static-build builder runs a command to bundle the application as static files into an output folder. Now expects an NPM script called
now-build which it will try to run automatically. Furthermore the builder expects the static files to be in a folder called
dist. We'll need to move the generated files there:
3. Adding a GitHub Actions Workflow to the Repository
A GitHub Actions workflow can be described as a rooted directed graph. A failure at each step down the workflow will abort the execution and mark the action as failed ❌.
The workflow specification lives as a text-file with the
.workflow extension in the well-known
.github directory in the root of your repository.
GitHub provides both a visual editor UI as well as their classic text-editor. Here are both in comparison for the same workflow we’ll be using:
Which one do you prefer? 🤔
We think both have their place. The visual editor UI appeals to the idea of flow-based programming. Adding and connecting nodes (the actions) has a certain satisfaction you’ll have to experience yourself. The text version on the other hand makes it trivial to use with version control (
diff) and is easily readable as well as parseable.
Let’s look at the specific nodes: Which actions do we need to implement our CD process?
We want to:
nowto deploy the application. This is the default effect of the Zeit Now action.
- Continue only if the pushed branch was
master. This is achieved with the GitHub Actions filter and passing
branch masteras arguments.
now aliasto have the alias point to the new deployment. We'll need to pass
aliasas an argument to the Zeit Now action for this.
Note: A common gotcha during our research was to miss the scope of Now resources. The
ZEIT_TOKEN belongs to a specific user account but the alias domain most likely to an organization. We'll need to explicitly pass the relevant team scope to the CLI, like
now --team peerigon to avoid permission errors. The same applies to secrets. You won't need this in a single-user setup.
Every time you push to your GitHub repository now, GitHub Actions will wake up automatically and do its job. It will run the workflow as we defined it in the previous step:
- By running the command
nowthe application will be deployed at a random (sub-)domain.
- If the pushed branch was
master, the workflow continues and aliases the immutable deployment to the specified domain. Otherwise it stops here.
If all steps pass, the status icon of the repository will show the green checkmark ✅. All good!
The initial setup for this automation probably took you less than 15 minutes — time you’ll easily save over the next weeks as you continue working on your project. It also helps to document and share the knowledge about deploying the application. Anyone in your team is now empowered to deploy with ease! There are no steps to remember, it’s all automatic.
Thanks to the flexibility of GitHub Actions this workflow could be easily extended, e.g. to run the tests in a separate container or to post the immutable deployment URL on your internal Slack channel and so on. The ecosystem of available actions is constantly growing.
You may also define your own actions using a
Dockerfile right within your repository. This makes GitHub Actions the awesome piping for modern applications - the possibilities are endless.
The ability to switch between the visual node editor and the raw text format is really nice and should make it easy to review future pull requests for more elaborate workflows.
But there are some things to consider before hopping aboard:
- The GitHub Actions feature is still flagged as beta and should be considered as such. We experienced some hanging jobs (usually resolved by either waiting or pushing again). The documentation is also lacking at this point but some actions have additional example repositories (like
actions/example-zeit-now) which demonstrate the intended use. The workflow UI dialogs could use some love and should provide auto-completion or contextual help. Testing out workflows usually requires pushing, making changes and then pushing again. We expect this to improve as the feature matures.
- As with every decision related to technology, there is a certain tax involved with this setup as well. How far in the direction of a proprietary technology (like the GitHub Actions workflow format) or service (like Now) do we want to go? This is both a fundamental as well as a specific question you’ll probably have to evaluate on a project-per-project basis. In any case, we are happy to have this method in our toolbox.
Thanks for following along and happy continuous shipping! ⛵️⛵️⛵️
Bonus: Install Private NPM Dependencies with
git+ssh on Now
We also wanted to setup an internal application which has an NPM dependency on a private GitHub repository using this method. This turned out to be a bit more difficult than we would have liked. We documented our findings in a detailed Gist. Check it out if you face similar (permission-related) challenges.