依存にプライベートレポジトリを含むGAE/Go（go111）をCloud Buildで自動でプロイする

# はじめに

`go111` の登場で、Cloud Build を使えば設定ファイル1つで自動デプロイ出来るようになったはずでした。

```yaml:cloudbuild.yaml
steps:
- name: 'gcr.io/cloud-builders/gcloud'
  args: ['app', 'deploy', '--project=$PROJECT_ID', 'gae/app/app.yaml']
```

monorepo なら private repository でも上記の設定でデプロイ出来ます。
しかし、manyrepo をやろうと本体と別の private repository を参照するとデプロイ出来なくなります。

我々が Cloud Build のトリガーに設定するプロセス（以下「Cloud Build(Trigger)」）とは別に、`gcloud app deploy` によって実行される Cloud Build のプロセス（以下「Cloud Build(GAE)」）があり、後者は `go.mod` を見つけると依存を自動解決します。

非常に素敵なのですが、sshの鍵だったりトークンだったり何も設定できないため、プライベートレポジトリが `go.mod` に含まれると必ず失敗します。

![oybFJinHqBLJSCyiyIXDKh1IA2Wjpk22ye3YIiuv-QKf2awbcJaf6Y55cUdfgKMQIa1rvQb96VdvsLmu5A1hTNTq1MjBJmOB9XOKAAIa5kJaLt9XKOPJSxv2RdwAWfwU7W00.png](https://qiita-image-store.s3.amazonaws.com/0/919/fe648461-84f6-f92e-619f-2e34d90cd271.png)

# 前段でgo mod vendorし、go.modをdeployしない

![oybFJinHqBLJSCyiyIXDKh1IA2Wjpk22ye3YIiuv-QKf2awbcJaf6Y55cUdfgKMQIa1rvQb96VdvsLmu5E3Jcfwla9kVeb2McfUINqIi0UwEhX3DoM31n882XPJKWfpyIW00.png](https://qiita-image-store.s3.amazonaws.com/0/919/1227f25b-12dc-94fe-0e7b-751328f86a90.png)

Cloud Build(Trigger) 側で `go mod vendor` し、更に `.gcloudignore` に `go.mod` を指定して `gcloud app deploy` すれば解決します。
vendor ディレクトリごと GCS にアップロードするので、`.gcloudignore` に `vendor` を含めてはいけません。

## まとめ

1. go111 のプロジェクトを `gcloud app deploy` すると Cloud Build による deploy が実行される
2. この際に実行される Cloud Build に対しては何も設定出来ない
3. `go.mod` を含む場合、自動で go get し、そこにプライベートレポジトリがあると失敗する
4. `.gcloudignore` で `go.mod` を除外指定していれば自動で go get されない
5. そのかわり、`vendor` を `.gcloudignore` に指定せず、 `go mod vendor` 後に `gcloud app deploy` する
6. `vendor` ディレクトリごと Upload され、`go get` も行われないので deploy は成功する
7. 一連の作業を Cloud Build で自動化する為の `cloudbuild.yaml` がこちら

```yaml:cloudbuild.yaml
steps:
- name: 'gcr.io/cloud-builders/go:alpine'
  entrypoint: sh
  args:
    - -c
    - |
      mv `find /workspace -maxdepth 1 ! -name go` ./
      git config --global url."https://$$GITHUB_TOKEN:x-oauth-basic@github.com/".insteadOf "https://github.com/"
      go mod vendor
      find /workspace
  dir: 'go/src/github.com/YOUR_ORG/YOUR_PROJECT'
  env:
    - 'GOPATH=/workspace/go'
    - 'GO111MODULE=on'
  secretEnv: ['GITHUB_TOKEN']
- name: 'gcr.io/cloud-builders/gcloud'
  args:
    - 'app'
    - 'deploy'
    - '--project=$PROJECT_ID'
    - 'app/app.yaml'
  dir: 'go/src/github.com/YOUR_ORG/YOUR_PROJECT'
  env:
    - 'GOPATH=/workspace/go'
    - 'GO111MODULE=off'
secrets:
- kmsKeyName: 'projects/YOUR_GCP_PROJECT_ID/locations/global/keyRings/github-tokens/cryptoKeys/github-deploy-token'
  secretEnv:
    GITHUB_TOKEN: 'YOUR_ENCRYPTED_GITHUB_TOKEN'
```

`dir` や `mv` などで `GOPATH` に則ったディレクトリ構成にしています。
`kmsKeyName` に変数埋め込みは利用できないのでべた書きしてください、ご注意を。

.gcloudignore はこんな感じに

```text:.gcloudignore
.gcloudignore
.git
.gitignore
go.mod
go.sum
```

# YOUR_ENCRYPTED_GITHUB_TOKEN の作り方

https://github.com/settings/tokens でトークンを生成し、以下の様に暗号、前述 yaml に貼り付ける

```sh
gcloud kms keyrings create github-tokens --location=global --project=YOUR_GCP_PROJECT_ID
gcloud kms keys create github-deploy-token --location=global --project=YOUR_GCP_PROJECT_ID --keyring=github-tokens --purpose=encryption
echo -n $GITHUB_TOKEN | gcloud kms encrypt \
  --plaintext-file=- \
  --ciphertext-file=- \
  --location=global \
  --project=YOUR_GCP_PROJECT_ID \
  --keyring=github-tokens \
  --key=github-deploy-token | base64
```