Matt Haff
Matt Haff
Apr 20, 2018 · 5 min read

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.

  1. Select or create a GCP project (manage resources page)
  2. Make sure that billing is enabled for your project (learn how)
  3. 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.

  1. In the Google Cloud Platform Console, go to the Credentials page.
  2. Click Create credentials > Service account key.
  3. In the next page select Compute Engine default service account in the Service account dropdown.
  4. 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:

  1. In the Cloud Platform Console, go to Cloud Shell.
  2. 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.

  1. Push code to your bitbucket repo
  2. Bitbucket runs npm install and npm run build
  3. Static files from npm run build are available at dist/ (your compiled code directory)
  4. Bitbucket installs the Google Cloud SDK
  5. Bitbucket runs gcloud app deploy
  6. Google Cloud App Deploy will upload all files from dist/ to Google Cloud Bucket
  7. Google Cloud App Deploy will update your Google Cloud App Engine services
  8. Your application is deployed to https://your-project-name.appspot.com
Hurray you made it!

Resources

These are the various documents and articles that I’ve pieced together

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.

Google Cloud Platform - Community

A collection of technical articles published or curated by Google Cloud Platform Developer Advocates. The views expressed are those of the authors and don't necessarily reflect those of Google.

Matt Haff

Written by

Matt Haff

Sr. UI Developer, Ally to all, I love all things Japanese, I’m a Husband, Father, Christian, Whovian.

Google Cloud Platform - Community

A collection of technical articles published or curated by Google Cloud Platform Developer Advocates. The views expressed are those of the authors and don't necessarily reflect those of Google.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade