Skip to content

Commit cae708e

Browse files
committed
CIVendor option: Github action
1 parent b0b3d9e commit cae708e

File tree

10 files changed

+400
-5
lines changed

10 files changed

+400
-5
lines changed

Makefile

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,19 @@
66
GITHUB_ORG := $(shell echo ${REPOSITORY} | cut -d "/" -f 2)
77
GITHUB_REPO := $(shell echo ${REPOSITORY} | cut -d "/" -f 3)
88

9-
run:
9+
run: ci_setup
10+
@echo "\nDone"
11+
12+
ci_setup:
13+
# Conditionally setup GitHub Action OR CircleCI's environment for CI
14+
ifeq ($(CIVendor), github-actions)
15+
ci_setup: github_actions_setup
16+
endif
17+
ifeq ($(CIVendor), circleci)
18+
ci_setup: circle_ci_setup
19+
endif
20+
21+
circle_ci_setup:
1022
@echo "Set CIRCLECI environment variables\n"
1123
export AWS_ACCESS_KEY_ID=$(shell aws secretsmanager get-secret-value --region ${region} --secret-id=${PROJECT_NAME}-ci-user-aws-keys${randomSeed} | jq -r '.SecretString'| jq -r .access_key_id)
1224
export AWS_SECRET_ACCESS_KEY=$(shell aws secretsmanager get-secret-value --region ${region} --secret-id=${PROJECT_NAME}-ci-user-aws-keys${randomSeed} | jq -r '.SecretString'| jq -r .secret_key)
@@ -15,7 +27,9 @@ run:
1527
curl -X POST --header "Content-Type: application/json" -d '{"name":"AWS_SECRET_ACCESS_KEY", "value":"${AWS_SECRET_ACCESS_KEY}"}' https://circleci.com/api/v1.1/project/github/${GITHUB_ORG}/${GITHUB_REPO}/envvar?circle-token=${CIRCLECI_API_KEY}
1628
@echo "\nFollow CIRCLECI project"
1729
curl -X POST https://circleci.com/api/v1.1/project/github/${GITHUB_ORG}/${GITHUB_REPO}/follow?circle-token=${CIRCLECI_API_KEY}
18-
@echo "\nDone"
30+
31+
github_actions_setup:
32+
sh scripts/gha-setup.sh
1933

2034
summary:
2135
@echo "zero-deployable-node-backend:"

scripts/gha-setup.sh

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
#!/bin/bash
2+
3+
# In order to set project env-vars, we must encrypt secrets
4+
# Using gh client allows us to set the secret without installing another
5+
# binary just to encrypt the secrets
6+
7+
# Login GH client
8+
# GITHUB_ACCESS_TOKEN is injected when zero apply runs
9+
gh auth login --with-token <<EOF
10+
$GITHUB_ACCESS_TOKEN
11+
EOF
12+
13+
gh auth status
14+
15+
AWS_KEY_PAIR=$(aws secretsmanager get-secret-value --region ${REGION} --secret-id=${PROJECT_NAME}-ci-user-aws-keys${RANDOM_SEED})
16+
AWS_ACCESS_KEY_ID=$(echo ${AWS_KEY_PAIR} | jq -r '.SecretString'| jq -r .access_key_id)
17+
AWS_SECRET_ACCESS_KEY=$(echo ${AWS_KEY_PAIR} | jq -r '.SecretString'| jq -r .secret_key)
18+
19+
## IMPORTANT: Set secret operates on the nearest .git repo even if you specify a different repository
20+
pushd $PROJECT_DIR && \
21+
gh secret set AWS_ACCESS_KEY_ID --repos="$GITHUB_REPO" --body="$AWS_ACCESS_KEY_ID" && \
22+
gh secret set AWS_SECRET_ACCESS_KEY --repos="$GITHUB_REPO" --body="$AWS_SECRET_ACCESS_KEY" && \
23+
popd
24+
25+
## Branch Protect for PRs
26+
## By default we setup Pull-request checks of [lint, unit-test] in `.github/workflows/pull-request.yml`
27+
## And we will enforce both the checks pass before PR can be merged into default branch
28+
DEFAULT_BRANCH=master
29+
curl -XPUT "https://api.github.com/repos/$GITHUB_ORG/$GITHUB_REPO/branches/$DEFAULT_BRANCH/protection" \
30+
--header "Authorization: token $GITHUB_ACCESS_TOKEN" \
31+
--header 'Content-Type: application/json' \
32+
--data '{
33+
"required_status_checks": {
34+
"strict": false,
35+
"contexts": ["unit-test"]
36+
},
37+
"enforce_admins": false,
38+
"required_pull_request_reviews": null,
39+
"restrictions": null
40+
}'
41+
42+
echo "Github actions environment variables setup successfully."
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
name: db-migration
2+
description: create kubernetes job to run migration
3+
inputs:
4+
namespace:
5+
description: Kubernetes namespace to run migration on
6+
required: true
7+
repository-name:
8+
description: name of repository, used to create migration job name
9+
required: true
10+
runs:
11+
using: "composite"
12+
steps:
13+
- name: Migration
14+
shell: bash
15+
run: |
16+
NAMESPACE=${{ inputs.namespace }}
17+
MIGRATION_NAME=${{ inputs.repository-name }}-migration
18+
SQL_DIR="${GITHUB_WORKSPACE}/database/migration"
19+
20+
kubectl -n $NAMESPACE delete configmap $MIGRATION_NAME || echo "no migration configmap existing for deletion"
21+
kubectl -n $NAMESPACE delete job $MIGRATION_NAME || echo "no migration job existing for deletion"
22+
23+
if [ -f ${SQL_DIR}/*.sql ] ; then
24+
pushd kubernetes/migration
25+
kubectl -n $NAMESPACE delete configmap $MIGRATION_NAME || echo "no migration configmap existing for deletion"
26+
kubectl -n $NAMESPACE create configmap $MIGRATION_NAME --from-file ${SQL_DIR}/*.sql
27+
28+
kubectl -n $NAMESPACE create -f job.yml
29+
if ! kubectl -n $NAMESPACE wait --for=condition=complete --timeout=180s job/$MIGRATION_NAME ; then
30+
echo "$MIGRATION_NAME run failed:"
31+
kubectl -n $NAMESPACE describe job $MIGRATION_NAME
32+
exit 1
33+
fi
34+
popd
35+
else
36+
kubectl -n $NAMESPACE create configmap $MIGRATION_NAME
37+
fi
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
name: deploy
2+
description: Deploy docker image by image-tag to kubernetes
3+
inputs:
4+
namespace:
5+
description: Kubernetes namespace to deploy
6+
required: true
7+
repository-name:
8+
description: Name of ECR repository containing the image
9+
required: true
10+
image-tag:
11+
description: image-tag to deploy
12+
required: true
13+
docker-host:
14+
description: Docker host consisting of the deploy image
15+
required: true
16+
region:
17+
description: Name of AWS region
18+
default: us-west-2
19+
required: false
20+
environment-overlay:
21+
description: Overlay folder to pick from for Kustomize apply
22+
default: staging
23+
required: false
24+
runs:
25+
using: "composite"
26+
steps:
27+
- name: Migration
28+
shell: bash
29+
run: |
30+
DEPLOYMENT=${{ inputs.repository-name }}
31+
NAMESPACE=${{ inputs.namespace }}
32+
33+
cd kubernetes/overlays/${{ inputs.environment-overlay }}
34+
IMAGE=${{ inputs.docker-host }}/${{ inputs.repository-name }}
35+
kustomize edit set image fake-image=${IMAGE}:${{ inputs.image-tag }}
36+
kustomize build . | kubectl apply -f - -n $NAMESPACE
37+
if ! kubectl -n $NAMESPACE rollout status deployment/$DEPLOYMENT -w --timeout=180s ; then
38+
echo "$DEPLOYMENT rollout check failed:"
39+
echo "$DEPLOYMENT deployment:"
40+
kubectl -n $NAMESPACE describe deployment $DEPLOYMENT
41+
echo "$DEPLOYMENT replicaset:"
42+
kubectl -n $NAMESPACE describe rs -l app=$DEPLOYMENT
43+
echo "$DEPLOYMENT pods:"
44+
kubectl -n $NAMESPACE describe pod -l app=$DEPLOYMENT
45+
exit 1
46+
fi
47+
# Update the last-deployed tag to be used in dev environments
48+
MANIFEST=$(aws ecr batch-get-image --region ${{ inputs.region }} --repository-name ${{ inputs.repository-name }} --image-ids imageTag=${{ inputs.image-tag }} --query 'images[].imageManifest' --output text)
49+
aws ecr put-image --region ${{ inputs.region }} --repository-name ${{ inputs.repository-name }} --image-tag last-deployed --image-manifest "$MANIFEST"
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
name: setup-kustomize
2+
description: setup kustomize on an image
3+
inputs:
4+
# this step has pre-requisite of AWS-cli and kubectl being installed
5+
cluster-name:
6+
description: Name of cluster using `aws eks` tool
7+
required: true
8+
cluster-authentication-role-arn:
9+
description: role-arn used with AWS-iam-authenticator for kubernetes cluster
10+
required: true
11+
region:
12+
description: Name of AWS region
13+
default: us-west-2
14+
required: false
15+
runs:
16+
using: "composite"
17+
steps:
18+
# Deploy to s3
19+
- name: Install aws cli
20+
shell: bash
21+
run: python -m pip install --upgrade pip awscli
22+
- name: Install kustomize
23+
shell: bash
24+
run: |
25+
KUSTOMIZE_VERSION=3.5.4
26+
curl -L -o ./kustomize.tar.gz "https://github.com/kubernetes-sigs/kustomize/releases/download/kustomize%2Fv${KUSTOMIZE_VERSION}/kustomize_v${KUSTOMIZE_VERSION}_linux_amd64.tar.gz"
27+
sudo tar xvzf ./kustomize.tar.gz -C /usr/local/bin/
28+
sudo chmod +x /usr/local/bin/kustomize
29+
kustomize version
30+
- name: Setup kubernetes configuration
31+
shell: bash
32+
run: |
33+
aws eks update-kubeconfig --name ${{ inputs.cluster-name }} \
34+
--region ${{ inputs.region }} \
35+
--role-arn ${{ inputs.cluster-authentication-role-arn }}
36+
cat ~/.kube/config

templates/.github/workflows/ci.yml

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
# CI pipeline
2+
# This runs against all the commits landed on master
3+
# It will do the following things:
4+
# - run sanity tests against the checked-out code
5+
# - build an image and upload the docker image to ECR
6+
# - deploy to staging environment
7+
# - run smoke test against staging enviroment
8+
# - deploy to production
9+
name: CI Pipeline
10+
on:
11+
push:
12+
branches: [master, main]
13+
env:
14+
# Environment variables shared across jobs and doesn't change per environment
15+
region: <% index .Params `region` %>
16+
accountId: "<% index .Params `accountId` %>"
17+
repo: <% .Name %>
18+
namespace: <% .Name %>
19+
jobs:
20+
unit-test:
21+
runs-on: ubuntu-latest
22+
env:
23+
CI: true
24+
build_env: production
25+
steps:
26+
- uses: actions/checkout@v2
27+
- uses: actions/setup-node@v2
28+
with:
29+
node-version: "14"
30+
- run: npm install
31+
- run: npm test
32+
33+
build:
34+
needs: unit-test
35+
runs-on: ubuntu-latest
36+
steps:
37+
- uses: actions/checkout@v2
38+
- name: Set IMAGE_TAG as env
39+
run: |
40+
IMAGE_TAG=$(git rev-parse --short=7 ${{ github.sha }})
41+
echo "IMAGE_TAG=${IMAGE_TAG}" >> $GITHUB_ENV
42+
- name: Configure AWS credentials
43+
uses: aws-actions/configure-aws-credentials@v1
44+
with:
45+
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
46+
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
47+
aws-region: ${{ env.region }}
48+
- name: Login to Amazon ECR
49+
id: login-ecr
50+
uses: aws-actions/amazon-ecr-login@v1
51+
- name: Build and push
52+
id: docker_build
53+
uses: docker/build-push-action@v2
54+
with:
55+
push: true
56+
tags: ${{ env.accountId }}.dkr.ecr.${{ env.region }}.amazonaws.com/${{ env.repo }}:${{ env.IMAGE_TAG }}
57+
58+
staging-deploy:
59+
needs: build
60+
runs-on: ubuntu-latest
61+
env:
62+
environment-overlay: staging
63+
cluster-name: "<% .Name %>-stage-<% index .Params `region` %>"
64+
cluster-authentication-role-arn: "arn:aws:iam::<% index .Params `accountId` %>:role/<% .Name %>-kubernetes-deployer-stage"
65+
steps:
66+
- uses: actions/checkout@v2
67+
- name: Set IMAGE_TAG as env
68+
run: |
69+
IMAGE_TAG=$(git rev-parse --short=7 ${{ github.sha }})
70+
echo "IMAGE_TAG=${IMAGE_TAG}" >> $GITHUB_ENV
71+
- name: Install kubectl
72+
uses: azure/setup-kubectl@v1
73+
- name: Configure AWS credentials
74+
uses: aws-actions/configure-aws-credentials@v1
75+
with:
76+
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
77+
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
78+
aws-region: ${{ env.region }}
79+
- name: Setup binaries(aws-cli/kustomize/iam-authenticator)
80+
uses: ./.github/actions/setup-aws-kustomize
81+
with:
82+
cluster-authentication-role-arn: ${{ env.cluster-authentication-role-arn }}
83+
cluster-name: ${{ env.cluster-name }}
84+
region: ${{ env.region }}
85+
- name: Check Namespace
86+
run: |
87+
DEPLOYMENT=${{ env.repo }}
88+
NAMESPACE=${{ env.namespace }}
89+
kubectl create namespace $NAMESPACE || echo "Namespace already exists"
90+
- name: Database migration
91+
uses: ./.github/actions/db-migration
92+
with:
93+
namespace: ${{ env.namespace }}
94+
repository-name: ${{ env.repo }}
95+
- name: Deploy image
96+
uses: ./.github/actions/deploy
97+
with:
98+
namespace: ${{ env.namespace }}
99+
repository-name: ${{ env.repo }}
100+
image-tag: ${{ env.IMAGE_TAG }}
101+
docker-host: ${{ env.accountId }}.dkr.ecr.${{ env.region }}.amazonaws.com
102+
region: ${{ env.region }}
103+
environment-overlay: ${{ env.environment-overlay }}
104+
integration-test:
105+
needs: [staging-deploy]
106+
runs-on: ubuntu-latest
107+
name: integration-test
108+
steps:
109+
## Example of smoke test against staging env before deploying to production
110+
## To be enhanced to more sophisicated checks
111+
- run: echo "TEST_RESPONSE_COD=$(curl -o /dev/null -s -w \"%{http_code}\" https://<% index .Params `stagingBackendSubdomain` %><% index .Params `stagingHostRoot` %>)" >> $GITHUB_ENV
112+
- if: env.TEST_RESPONSE_COD >= 400
113+
run: exit 1
114+
production-deploy:
115+
needs: [build, integration-test]
116+
runs-on: ubuntu-latest
117+
name: production-deploy
118+
env:
119+
environment-overlay: production
120+
cluster-name: "<% .Name %>-prod-<% index .Params `region` %>"
121+
cluster-authentication-role-arn: "arn:aws:iam::<% index .Params `accountId` %>:role/<% .Name %>-kubernetes-deployer-prod"
122+
steps:
123+
- uses: actions/checkout@v2
124+
- name: Set IMAGE_TAG as env
125+
run: |
126+
IMAGE_TAG=$(git rev-parse --short=7 ${{ github.sha }})
127+
echo "IMAGE_TAG=${IMAGE_TAG}" >> $GITHUB_ENV
128+
- name: Install kubectl
129+
uses: azure/setup-kubectl@v1
130+
- name: Configure AWS credentials
131+
uses: aws-actions/configure-aws-credentials@v1
132+
with:
133+
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
134+
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
135+
aws-region: ${{ env.region }}
136+
- name: Setup binaries(aws-cli/kustomize/iam-authenticator)
137+
uses: ./.github/actions/setup-aws-kustomize
138+
with:
139+
cluster-authentication-role-arn: ${{ env.cluster-authentication-role-arn }}
140+
cluster-name: ${{ env.cluster-name }}
141+
region: ${{ env.region }}
142+
- name: Check Namespace
143+
run: |
144+
DEPLOYMENT=${{ env.repo }}
145+
NAMESPACE=${{ env.namespace }}
146+
kubectl create namespace $NAMESPACE || echo "Namespace already exists"
147+
- name: Database migration
148+
uses: ./.github/actions/db-migration
149+
with:
150+
namespace: ${{ env.namespace }}
151+
repository-name: ${{ env.repo }}
152+
- name: Deploy image
153+
uses: ./.github/actions/deploy
154+
with:
155+
namespace: ${{ env.namespace }}
156+
repository-name: ${{ env.repo }}
157+
image-tag: ${{ env.IMAGE_TAG }}
158+
docker-host: ${{ env.accountId }}.dkr.ecr.${{ env.region }}.amazonaws.com
159+
region: ${{ env.region }}
160+
environment-overlay: ${{ env.environment-overlay }}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Pull request
2+
# they are sanity checks that runs on every commit in a PR
3+
# By default your repository setup will require a pre-configured list [`unit-test`] of status for merging,
4+
# You can modify this behavior in the Github's Branch protection settings
5+
name: Pull request
6+
on:
7+
pull_request:
8+
branches: ['*']
9+
jobs:
10+
unit-test:
11+
runs-on: ubuntu-latest
12+
env:
13+
CI: true
14+
build_env: production
15+
steps:
16+
- uses: actions/checkout@v2
17+
- uses: actions/setup-node@v2
18+
with:
19+
node-version: "14"
20+
- run: npm install
21+
- run: npm test

0 commit comments

Comments
 (0)