Skip to content

Commit

Permalink
Merge pull request #112 from pwei1018/main
Browse files Browse the repository at this point in the history
new frontend CD flow.
  • Loading branch information
pwei1018 authored Apr 15, 2024
2 parents 7d0a59a + a53d802 commit e33f1c5
Show file tree
Hide file tree
Showing 11 changed files with 600 additions and 0 deletions.
16 changes: 16 additions & 0 deletions .github/actions/frontend-deploy/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
name: "Frontend application deployment files"
description: ""

inputs:
working-directory:
required: false
default: "."

runs:
using: "composite"
steps:
# Copy files
- name: Copy build/deployment files
shell: bash
run: |
cp ${{ github.action_path }}/files/* ${{ inputs.working-directory }}
6 changes: 6 additions & 0 deletions .github/actions/frontend-deploy/files/Dockerfile-build
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Use a large Node.js base image to build the application and name it "build"
FROM node:21-alpine

WORKDIR /app

COPY . /app
144 changes: 144 additions & 0 deletions .github/actions/frontend-deploy/files/cloudbuild.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
steps:

# build image if not exists in artifact registry
- name: "gcr.io/google.com/cloudsdktool/cloud-sdk"
script: |
#!/usr/bin/env bash
# Build/Push Image to Artifactory
if [[ -z `gcloud artifacts docker images describe ${_REGION}-docker.pkg.dev/${_DEPLOYMENT_PROJECT}/firebase-repo/${_APP_NAME}:${_SHORT_SHA} --verbosity=none` ]]
then
docker build \
-f Dockerfile-build \
-t ${_REGION}-docker.pkg.dev/${_DEPLOYMENT_PROJECT}/firebase-repo/${_APP_NAME}:${_SHORT_SHA} \
--cache-from ${_REGION}-docker.pkg.dev/${_DEPLOYMENT_PROJECT}/firebase-repo/${_APP_NAME}:latest\
.
docker push ${_REGION}-docker.pkg.dev/${_DEPLOYMENT_PROJECT}/firebase-repo/${_APP_NAME}:${_SHORT_SHA}
gcloud artifacts docker tags add \
${_REGION}-docker.pkg.dev/${_DEPLOYMENT_PROJECT}/firebase-repo/${_APP_NAME}:${_SHORT_SHA} \
${_REGION}-docker.pkg.dev/${_DEPLOYMENT_PROJECT}/firebase-repo/${_APP_NAME}:latest
else
echo "image tag exists"
fi
# copy content of the build version
docker create --name ${_APP_NAME} northamerica-northeast1-docker.pkg.dev/c4hnrd-tools/firebase-repo/${_APP_NAME}:${_SHORT_SHA}
docker cp ${_APP_NAME}:/app/. /workspace/app
# Prepare build environments
- name: "northamerica-northeast1-docker.pkg.dev/c4hnrd-tools/cicd-repo/gcp-sre"
secretEnv: ["OP_CONNECT_HOST", "OP_CONNECT_TOKEN"]
dir: "app"
script: |
#!/usr/bin/env bash
# Set firebase config
APP_HOST_NAME=$(op read -n op://CD/${_DEPLOYMENT_ENVIRONMENT}/${_APP_NAME}/FIREBASE_HOST_NAME)
firebase=$(jq '.hosting.site='\"$APP_HOST_NAME\"'' firebase-${_DEPLOYMENT_ENVIRONMENT}.json)
echo -E "${firebase}" > firebase-${_DEPLOYMENT_ENVIRONMENT}.json
echo $(op read -n op://CD/${_DEPLOYMENT_ENVIRONMENT}/${_APP_NAME}/DEPLOY_PROJECT_ID) > /workspace/project_id.txt
echo $(op read -n op://CD/${_DEPLOYMENT_ENVIRONMENT}/${_APP_NAME}/BUILD_FOLDER) > /workspace/build_folder.txt
BUILD_FOLDER=$(cat /workspace/build_folder.txt)
if [ -z "${BUILD_FOLDER}" ]; then
BUILD_FOLDER=.
fi
# Prepare .env by vaults
export APP_ENV=${_DEPLOYMENT_ENVIRONMENT}
op inject -i ./devops/vaults.env -o ${BUILD_FOLDER}/.env -f
# Load firebase Admin SDK from secret manager
if grep -q "GOOGLE_APPLICATION_CREDENTIALS=" "${BUILD_FOLDER}/.env"; then
gcloud secrets versions access ${_DEPLOYMENT_ENVIRONMENT} --secret=FIREBASE_ADMINSDK \
--project=${_DEPLOYMENT_PROJECT} \
--format="get(payload.data)" | tr "_-" "/+" | base64 -d > ${BUILD_FOLDER}/project-firebase-adminsdk.json
fi
# Load e2e settings from secret manager
if grep -q "E2E_SETTINGS=" "${BUILD_FOLDER}/.env"; then
gcloud secrets versions access ${_DEPLOYMENT_ENVIRONMENT} --secret=${_APP_NAME}_E2E_SETTINGS \
--project=${_DEPLOYMENT_PROJECT} \
--format="get(payload.data)" | tr "_-" "/+" | base64 -d > ${BUILD_FOLDER}/e2e.json
fi
# Build application
- name: "northamerica-northeast1-docker.pkg.dev/c4hnrd-tools/firebase-repo/${_APP_NAME}:${_SHORT_SHA}"
dir: "app"
script: |
#!/usr/bin/env sh
BUILD_FOLDER=$(cat /workspace/build_folder.txt)
pnpm_project=$((ls pnpm_*.yaml >> /dev/null 2>&1 && echo "EXIST") || echo "NOT_EXIST")
if [ "$pnpm_project" = "EXIST" ]; then
npm install --global pnpm
pnpm install --frozen-lockfile
# Nuxt's package
if [ -n "${BUILD_FOLDER}" ]; then
echo executing build:$BUILD_FOLDER
pnpm build:$BUILD_FOLDER
# move build content back to root app folder
mv $BUILD_FOLDER/dist /workspace/app
else
pnpm build
fi
else
npm install
npm build
fi
ls -la
# Deploy application to firebase
- name: gcr.io/${_DEPLOYMENT_PROJECT}/firebase
dir: "app"
script: |
#!/usr/bin/env sh

RUNNING_PROJECT=$(cat /workspace/project_id.txt)

firebase deploy --project=${RUNNING_PROJECT} --config=firebase-${_DEPLOYMENT_ENVIRONMENT}.json --only hosting

# E2E testing
- name: "northamerica-northeast1-docker.pkg.dev/c4hnrd-tools/firebase-repo/${_APP_NAME}:${_SHORT_SHA}"
dir: "app"
script: |
#!/usr/bin/env sh
# pnpm run test:e2e:ui
# Tag image after deployment done
- name: "gcr.io/google.com/cloudsdktool/cloud-sdk"
script: |
#!/usr/bin/env sh
# tag image
gcloud artifacts docker tags add \
${_REGION}-docker.pkg.dev/${_DEPLOYMENT_PROJECT}/firebase-repo/${_APP_NAME}:${_SHORT_SHA} \
${_REGION}-docker.pkg.dev/${_DEPLOYMENT_PROJECT}/firebase-repo/${_APP_NAME}:${_DEPLOYMENT_ENVIRONMENT}
availableSecrets:
secretManager:
- versionName: projects/331250273634/secrets/OP_CONNECT_HOST/versions/latest
env: "OP_CONNECT_HOST"
- versionName: projects/331250273634/secrets/OP_CONNECT_TOKEN/versions/latest
env: "OP_CONNECT_TOKEN"

options:
automapSubstitutions: true
substitutionOption: "ALLOW_LOOSE"
substitutions:
_APP_NAME: ${_APP_NAME}
_DEPLOYMENT_ENVIRONMENT: ${_DEPLOYMENT_ENVIRONMENT} #dev/test/sandbox/prod
_DEPLOYMENT_PROJECT: "c4hnrd-tools"
_REGION: "northamerica-northeast1"

logsBucket: "gs://github-actions-cloudbuild/history"

timeout: 3600s
66 changes: 66 additions & 0 deletions .github/actions/frontend-deploy/files/firebase-dev.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
{
"hosting":
{
"site": "",
"public": "dist",
"ignore": ["**/devops/**", "**/.*", "**/node_modules/**"],
"rewrites": [
{
"source": "**",
"destination": "/index.html"
}
],
"headers" : [
{
"source": "**",
"headers" : [
{ "key" : "Access-Control-Allow-Origin", "value" : "*" },
{ "key" : "X-Frame-Options", "value" : "DENY" },
{ "key" : "X-Content-Type-Options", "value" : "nosniff" },
{ "key" : "X-XSS-Protection", "value" : "1; mode=block" },
{
"key": "Content-Security-Policy",
"value": "default-src 'self'; frame-src 'self' *.gov.bc.ca *.hotjar.com *.googleapis.com https://*.nr-data.net https://*.newrelic.com https://*.cac1.pure.cloud; script-src 'self' 'unsafe-eval' 'unsafe-inline' *.googletagmanager.com *.gov.bc.ca *.hotjar.com *.googleapis.com https://*.nr-data.net https://*.newrelic.com https://*.cac1.pure.cloud; style-src 'self' 'unsafe-inline' *.cloudflare.com *.googleapis.com *.jsdelivr.net; font-src 'self' *.gov.bc.ca *.hotjar.com *.cloudflare.com *.googleapis.com *.gstatic.com *.jsdelivr.net; img-src 'self' data: *.hotjar.com *.postescanada-canadapost.ca https://*.cac1.pure.cloud; connect-src 'self' blob: *.zenhub.com *.gov.bc.ca *.launchdarkly.com *.run.app *.hotjar.com *.postescanada-canadapost.ca *.sentry.io *.apigee.net wss://*.hotjar.com *.hotjar.io https://*.nr-data.net https://shyrka-prod-cac1.s3.ca-central-1.amazonaws.com https://*.newrelic.com https://*.cac1.pure.cloud wss://*.cac1.pure.cloud *.googleapis.com *.google-analytics.com; manifest-src 'self'; media-src 'self' https://*.cac1.pure.cloud; object-src 'self' https://*.cac1.pure.cloud; child-src 'self' blob: *.gov.bc.ca https://*.cac1.pure.cloud; worker-src blob:;"
},
{ "key": "Cache-Control", "value": "private, no-cache, no-store, must-revalidate"},
{ "key": "Pragma", "value": "no-cache"},
{ "key": "Referrer-Policy", "value": "no-referrer" },
{ "key": "Feature-Policy", "value": "microphone 'self'" },
{ "key": "Strict-Transport-Security", "value": "max-age=31536000;" }
]
},
{
"source": "**/*.@(ico|jpg|jpeg|gif|png|svg|eot|otf|ttf|ttc|woff|woff2)",
"headers": [
{
"key": "Cache-Control", "value": "public,max-age=31536000"
}
]
},
{
"source": "**/*.@(css|js)",
"headers": [
{
"key": "Cache-Control", "value": "public,max-age=31536000"
}
]
},
{
"source": "**/*.@(service-worker.js)",
"headers": [
{
"key": "Cache-Control", "value": "public,no-cache"
}
]
},
{
"source": "**/*.@(html|json)",
"headers": [
{
"key": "Cache-Control", "value": "no-cache, no-store, must-revalidate"
}
]
}
]
}
}
66 changes: 66 additions & 0 deletions .github/actions/frontend-deploy/files/firebase-prod.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
{
"hosting":
{
"site": "",
"public": "dist",
"ignore": ["firebase.json", "**/.*", "**/node_modules/**"],
"rewrites": [
{
"source": "**",
"destination": "/index.html"
}
],
"headers" : [
{
"source": "**",
"headers" : [
{ "key" : "Access-Control-Allow-Origin", "value" : "*" },
{ "key" : "X-Frame-Options", "value" : "DENY" },
{ "key" : "X-Content-Type-Options", "value" : "nosniff" },
{ "key" : "X-XSS-Protection", "value" : "1; mode=block" },
{
"key": "Content-Security-Policy",
"value": "default-src 'self'; frame-src 'self' *.gov.bc.ca *.hotjar.com *.googleapis.com https://*.nr-data.net https://*.newrelic.com https://*.cac1.pure.cloud; script-src 'self' 'unsafe-eval' 'unsafe-inline' *.googletagmanager.com *.gov.bc.ca *.hotjar.com *.googleapis.com https://*.nr-data.net https://*.newrelic.com https://*.cac1.pure.cloud; style-src 'self' 'unsafe-inline' *.cloudflare.com *.googleapis.com *.jsdelivr.net; font-src 'self' *.gov.bc.ca *.hotjar.com *.cloudflare.com *.googleapis.com *.gstatic.com *.jsdelivr.net; img-src 'self' data: *.hotjar.com *.postescanada-canadapost.ca https://*.cac1.pure.cloud; connect-src 'self' blob: *.zenhub.com *.gov.bc.ca *.launchdarkly.com *.hotjar.com *.postescanada-canadapost.ca *.sentry.io *.apigee.net wss://*.hotjar.com *.hotjar.io https://*.nr-data.net https://shyrka-prod-cac1.s3.ca-central-1.amazonaws.com https://*.newrelic.com https://*.cac1.pure.cloud wss://*.cac1.pure.cloud *.googleapis.com *.google-analytics.com; manifest-src 'self'; media-src 'self' https://*.cac1.pure.cloud; object-src 'self' https://*.cac1.pure.cloud; child-src 'self' blob: *.gov.bc.ca https://*.cac1.pure.cloud; worker-src blob:;"
},
{ "key": "Cache-Control", "value": "private, no-cache, no-store, must-revalidate"},
{ "key": "Pragma", "value": "no-cache"},
{ "key": "Referrer-Policy", "value": "no-referrer" },
{ "key": "Feature-Policy", "value": "microphone 'self'" },
{ "key": "Strict-Transport-Security", "value": "max-age=31536000;" }
]
},
{
"source": "**/*.@(ico|jpg|jpeg|gif|png|svg|eot|otf|ttf|ttc|woff|woff2)",
"headers": [
{
"key": "Cache-Control", "value": "public,max-age=31536000"
}
]
},
{
"source": "**/*.@(css|js)",
"headers": [
{
"key": "Cache-Control", "value": "public,max-age=31536000"
}
]
},
{
"source": "**/*.@(service-worker.js)",
"headers": [
{
"key": "Cache-Control", "value": "public,no-cache"
}
]
},
{
"source": "**/*.@(html|json)",
"headers": [
{
"key": "Cache-Control", "value": "private, no-cache, no-store, must-revalidate"
}
]
}
]
}
}
66 changes: 66 additions & 0 deletions .github/actions/frontend-deploy/files/firebase-test.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
{
"hosting":
{
"site": "",
"public": "dist",
"ignore": ["firebase.json", "**/.*", "**/node_modules/**"],
"rewrites": [
{
"source": "**",
"destination": "/index.html"
}
],
"headers" : [
{
"source": "**",
"headers" : [
{ "key" : "Access-Control-Allow-Origin", "value" : "*" },
{ "key" : "X-Frame-Options", "value" : "DENY" },
{ "key" : "X-Content-Type-Options", "value" : "nosniff" },
{ "key" : "X-XSS-Protection", "value" : "1; mode=block" },
{
"key": "Content-Security-Policy",
"value": "default-src 'self'; frame-src 'self' *.gov.bc.ca *.hotjar.com *.googleapis.com https://*.nr-data.net https://*.newrelic.com https://*.cac1.pure.cloud; script-src 'self' 'unsafe-eval' 'unsafe-inline' *.googletagmanager.com *.gov.bc.ca *.hotjar.com *.googleapis.com https://*.nr-data.net https://*.newrelic.com https://*.cac1.pure.cloud; style-src 'self' 'unsafe-inline' *.cloudflare.com *.googleapis.com *.jsdelivr.net; font-src 'self' *.gov.bc.ca *.hotjar.com *.cloudflare.com *.googleapis.com *.gstatic.com *.jsdelivr.net; img-src 'self' data: *.hotjar.com *.postescanada-canadapost.ca https://*.cac1.pure.cloud; connect-src 'self' blob: *.zenhub.com *.run.app *.gov.bc.ca *.launchdarkly.com *.hotjar.com *.postescanada-canadapost.ca *.sentry.io *.apigee.net wss://*.hotjar.com *.hotjar.io https://*.nr-data.net https://shyrka-prod-cac1.s3.ca-central-1.amazonaws.com https://*.newrelic.com https://*.cac1.pure.cloud wss://*.cac1.pure.cloud *.googleapis.com *.google-analytics.com; manifest-src 'self'; media-src 'self' https://*.cac1.pure.cloud; object-src 'self' https://*.cac1.pure.cloud; child-src 'self' blob: *.gov.bc.ca https://*.cac1.pure.cloud; worker-src blob:;"
},
{ "key": "Cache-Control", "value": "private, no-cache, no-store, must-revalidate"},
{ "key": "Pragma", "value": "no-cache"},
{ "key": "Referrer-Policy", "value": "no-referrer" },
{ "key": "Feature-Policy", "value": "microphone 'self'" },
{ "key": "Strict-Transport-Security", "value": "max-age=31536000;" }
]
},
{
"source": "**/*.@(ico|jpg|jpeg|gif|png|svg|eot|otf|ttf|ttc|woff|woff2)",
"headers": [
{
"key": "Cache-Control", "value": "public,max-age=31536000"
}
]
},
{
"source": "**/*.@(css|js)",
"headers": [
{
"key": "Cache-Control", "value": "public,max-age=31536000"
}
]
},
{
"source": "**/*.@(service-worker.js)",
"headers": [
{
"key": "Cache-Control", "value": "public,no-cache"
}
]
},
{
"source": "**/*.@(html|json)",
"headers": [
{
"key": "Cache-Control", "value": "private, no-cache, no-store, must-revalidate"
}
]
}
]
}
}
29 changes: 29 additions & 0 deletions .github/workflows/codecov-ci.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: Codecov CI

on:
workflow_call:
inputs:
app_name:
required: true
type: string
working_directory:
type: string
default: "."
codecov_flag:
type: string

jobs:
codecov:
strategy:
fail-fast: true
matrix:
os: [ "ubuntu-latest" ]

runs-on: ${{ matrix.os }}

defaults:
run:
shell: bash
working-directory: ${{ inputs.working_directory }}

steps:
Loading

0 comments on commit e33f1c5

Please sign in to comment.