Automatically Deploying to Google Appengine from Github using Cloud Build

When it comes to CI/CD pipeline, Jenkins is one of the popular tools that a lot of software companies use. With google cloud build one can build and deploy their app right from source repository like GitHub to AppEngine. It's a pay as you go service. However the first 120 build-minutes per day is free, so one can very easily try for their side projects where the deployment frequency is not more than a few times a day. However its always a good idea to limit your billing account and have alerts on usage in case it goes over the free tier due to some build stuck and running indefinitely. Here we will take an example of a go based appengine standard project.

First, to have Cloud Build be able to deploy to appengine we need to give the service account it's going to use the roles that are required.

Find the project id

NUM=$(gcloud projects describe $PROJECT \
--format="value(projectNumber)") && echo ${NUM}

Derive the service account name from the project id and assigned the roles

gcloud projects add-iam-policy-binding ${PROJECT} \
--member=serviceAccount:${NUM}@cloudbuild.gserviceaccount.com \
--role=roles/appengine.deployer

gcloud projects add-iam-policy-binding ${PROJECT} \
--member=serviceAccount:${NUM}@cloudbuild.gserviceaccount.com \
--role=roles/appengine.serviceAdmin

Then, we need to have a cloudbuild.yaml file in the root directory of the project.


substitutions:
_APP: speechtest
_BUCKET: cloud-build-speechtest
_NOPROMOTE: --promote

steps:
- name: 'gcr.io/cloud-builders/go'
args: ['get', 'google.golang.org/appengine']
env: ['GOPATH=go']
- name: 'gcr.io/cloud-builders/go'
args: ['get', 'golang.org/x/net/context']
env: ['GOPATH=go']
- name: 'gcr.io/cloud-builders/gcloud'
args: ['app', 'deploy', '${_NOPROMOTE}' ,'go/${_APP}']
env: ['GOPATH=go']
artifacts:
objects:
location: 'gs://${_BUCKET}/'
paths: ['go/${_APP}/home.go']
 
 
The `substitutions` can be passed from the cloud build configuration later, here we just give the default values if it's not passed.

The directory structure of your code in GitHub should be under the GOPATH i.e. 'go/_APP/......', There are probably better ways to do it without really having to change your directory structure in GitHub just because of cloud build integration.

Example repository - https://github.com/neilghosh/speechtest

In the steps, we are installing the dependencies which are outside of the GO SDK and firing "gcloud app deploy" command to deploy to app engine.

Here our idea is to deploy to appengine every time we commit in the GitHub repository. We want to switch the traffic to 100% in the new version if it was triggered from a commit from the master branch and deploy as yet another version without making it default if it was committed in any other branch for testing purpose. Remember we can always test our app by pointing to the version-specific URL of appengine i.e.

  version-dot-service-dot-project.appspot.com


 To achieve this we create a trigger in the Cloud Build section of Google Cloud Platform console. We create two of them, one responds to master commits and one which responds to other branches. We need to pass a different value to the substitution '_NOPROMOTE' which will eventually decide whether to make the newly deployed version to default or not.



We can manually trigger the build as follows

gcloud builds submit --config cloudbuild.yaml . --substitutions=_NOPROMOTE="--no-promote"

We can also test our triggers by pushing a commit to GitHub. It should trigger a build and show up in history.


There is a Google Cloud Build app in GitHub market for easy integration. But as of now, there was no way to control when to trigger a build as it would just kick off a build on every commit. (without even having to create a trigger in cloud build console.)

One can also show the build status in the GitHub Pull requests by tapping into the Pub/Sub events that cloud build automatically sends to a topic called "cloud-builds". You can use the code from the following

https://github.com/pixiteapps/android-cloud-build/tree/master/github-status

You just have to get a githib access token so that the cloud function code can use it to update the status in the pull requests.