How to deploy a React application to Google Cloud using Bitbucket Pipelines
Over the course of the past couple of weeks I have been piecing together multiple articles, blog posts and documentation. In an effort to give back to the community, I’m sharing what I learned.
Note: I’ll be piecing together parts of various documentation. I realize that some of the documents may have some differences, my goal is to get you start to finish in the easiest way possible.
Example Variables
Google Cloud Project = your-project-name
Node Version = 9.11.1
Source code directory (don’t deploy) = app/
Compiled directory (deploy) = dist/
Setup Google Cloud
First off you’ll want to setup a Google Cloud account if you haven’t already.
- Select or create a GCP project (manage resources page)
- Make sure that billing is enabled for your project (learn how)
- Enable the App Engine Admin API (enable APIs)
Creating authorization credentials for Bitbucket
Create an App Engine service account and API key. Bitbucket needs this information to deploy to App Engine.
- In the Google Cloud Platform Console, go to the Credentials page.
- Click Create credentials > Service account key.
- In the next page select Compute Engine default service account in the Service account dropdown.
- Click the Create button. A copy of the JSON file downloads to your computer. (This is your JSON credential file)
Create an App Engine application
The pipeline script expects your project to have an App Engine application. To create an App Engine application:
- In the Cloud Platform Console, go to Cloud Shell.
- Run
gcloud app create
to create your default App Engine application
Configure App Engine
Create a new file and save it as app.yaml
and paste the following code into it. Be sure to save this file in the root of your project (same place as package.json
).
By default when Google Cloud deploys it will upload every file in your project. If you are like me and don’t want to wait hours for all of your uncompiled source code to upload (including node_modules
) then you’ll want to modify skip_files
in the config below. Replace dist
with your compiled code directory and app
with your source files directory.
Specifying which files to skip will over replace the default settings so I have also included them below my node_modules/
so as not to accidentally upload anything extra.
runtime: python27
api_version: 1
threadsafe: true
handlers:
- url: /(.*\.(html|css|js|png|jpg|woff|json))
static_files: dist/\1
upload: dist/(.*\.(html|css|js|png|jpg|woff|json))
- url: /.*
static_files: dist/index.html
upload: dist/index.html
- url: /
static_dir: build
skip_files:
- app/
- node_modules/
- ^\.git/.*
- ^(.*/)?#.*#$
- ^(.*/)?.*~$
- ^(.*/)?.*\.py[co]$
- ^(.*/)?.*/RCS/.*$
- ^(.*/)?\..*$
- ^(.*/)?.*\.bak$
Note: the file will not execute properly if you are using tabs. .yml
files must use spaces only.
Setup Bitbucket
Create a repo if you haven’t already, then you’ll want to enable Pipelines.
Select JavaScript as your option and update the config to this…
image: node:9.11.1options:
max-time: 10
pipelines:
default:
— step:
name: Build Application
caches:
— node
script:
— npm install
— npm run build
artifacts:
— dist/** # change this to your build directory
— step:
name: Deploy to GCloud
deployment: staging
script:
# Set a couple variables for readability
— SDK_VERSION=197.0.0
— SDK_FILENAME=google-cloud-sdk-${SDK_VERSION}-linux-x86_64.tar.gz
# Install Google Cloud SDK
— curl -o /tmp/google-cloud-sdk.tar.gz https://dl.google.com/dl/cloudsdk/channels/rapid/downloads/${SDK_FILENAME}
— tar -xvf /tmp/google-cloud-sdk.tar.gz -C /tmp/
— /tmp/google-cloud-sdk/install.sh -q
— source /tmp/google-cloud-sdk/path.bash.inc
— gcloud -v
# Authenticating with the service account key file
— echo $GCLOUD_API_KEYFILE | base64 — decode — ignore-garbage > ./gcloud-api-key.json
— gcloud auth activate-service-account — key-file gcloud-api-key.json
# Linking to the Google Cloud project
— gcloud config set project $GCLOUD_PROJECT
# Deploying the application
— gcloud app deploy
Note: the file will not execute properly if you are using tabs. .yml
files must use spaces only.
Configure the environment variables required by the pipeline script
Open up your terminal and browse to the location of your JSON credential file from earlier. Then run the command below to encode your file in base64 format. Copy the output of the command to your clipboard.
base64 <your-credentials-file.json>
Go to your repository settings in Bitbucket and navigate to Pipelines > Environment variables. Create a new variable named GCLOUD_API_KEYFILE
and paste the encoded service account credentials in it.
Add another variable called GCLOUD_PROJECT
and set the value to the key of your Google Cloud project that you created in the first step your-project-name
Note: you don’t need to check secured for any of these fields
Configuring Node
Google Cloud can’t read from devDependencies
so you’ll want to toss any necessary packages into dependencies
instead, for me that ended up being everything however I’ll be making my way back through it and cleaning up where possible.
Add a section to your package.json
to specify which version of node your project is running on.
"engines": {
"node": "9.11.1"
}
Conclusion
If you have everything setup properly then this is how your deployment should flow.
- Push code to your bitbucket repo
- Bitbucket runs
npm install
andnpm run build
- Static files from
npm run build
are available atdist/
(your compiled code directory) - Bitbucket installs the Google Cloud SDK
- Bitbucket runs
gcloud app deploy
- Google Cloud App Deploy will upload all files from
dist/
to Google Cloud Bucket - Google Cloud App Deploy will update your Google Cloud App Engine services
- Your application is deployed to
https://your-project-name.appspot.com
Resources
These are the various documents and articles that I’ve pieced together
- https://confluence.atlassian.com/bitbucket/deploy-to-google-cloud-900820342.html
- https://medium.com/google-cloud/how-to-deploy-a-static-react-site-to-google-cloud-platform-55ff0bd0f509
- https://cloud.google.com/solutions/continuous-delivery-bitbucket-app-engine
- https://cloud.google.com/appengine/docs/standard/python/getting-started/deploying-the-application
Bug: Deployment Timeout Issue
There is a known issue which Google is aware of that causes the deployment to hang for no apparent reason. I suggest adding an option to your bitbucket-pipelines.yml
which I included above to have max-time: 10
this will cause the pipeline to fail if it takes more than 10 minutes. Be sure to adjust this based on how long your deployment takes, mine should take 2–3 minutes. If it fails then you will need to manually re-run the pipeline, sometimes it takes a few tries.