Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
264 changes: 69 additions & 195 deletions starter/pipeline/cloudrun-gitlab/.gitlab-ci.yml
Original file line number Diff line number Diff line change
@@ -1,208 +1,82 @@
# Define environments in GitLab UI with vars according to the branches for the pipeline
# ENVIRONMENT={{develpoment/stage/production}}
# GCP_PROJECT_ID={{PROJECT_NAME}}
# GCP_PROJECT_ID=node-app
# GCP_REGION={{europe-west3}}
# GCP_SECRETS_NAME={{PROJECT_NAME}}
# GCP_SECRETS_NAME=node-app
# GCP_SECRETS_VERSION=latest
# GCP_SA_KEY={{base64 encoded string with SA key to deploy Cloud Run}}
# GCP_CLOUD_RUN_SA_NAME={{name of the service account for Cloud Run}}

variables:
# Default configuration, check .export_variables job for calculated env variables based on branch config
# Node image for pipeline runner
NODE_BASE_IMAGE: node:24.5.0
# Where to store json secrets from Cloud provider
SECRETS_PATH: '/config/secrets.json'

## GCP configuration ##
# Where to temporary store Google service account
GCP_SA_KEY_JSON_PATH: /tmp/key.json

## Docker artifact registry configuration ##
# Project name for docker compose CI job runs
DOCKER_COMPOSE_PROJECT_NAME: $CI_PROJECT_NAME-job-$CI_JOB_ID
# Build docker version tag
DOCKER_IMAGE_TAG: $CI_COMMIT_SHORT_SHA

default:
image: ackee/gitlab-builder
before_script:
- echo "//${CI_SERVER_HOST}/api/v4/projects/${CI_PROJECT_ID}/packages/npm/:_authToken=${CI_JOB_TOKEN}" >> .npmrc
- echo "//${CI_SERVER_HOST}/api/v4/packages/npm/:_authToken=${CI_JOB_TOKEN}" >> .npmrc;

cache: &docker_cache
key: "$CI_COMMIT_REF_NAME"
paths:
- app_image*.tar
policy: pull
# GCP_CLOUD_RUN_ALLOCATED_MEMORY={{allocated memory for Cloud Run}}
# GCP_SQL_INSTANCE_NAME={{name of the SQL instance for Cloud Run}}

stages:
- build
- test
- push
- deploy

##
# HELPERS
##
# Before script extension that exports ci-branch-config environment variables and stores the GCP service
# account on the disk to be used by another jobs in the CI. This should be extended by every job that uses
# any of the branch configuration, docker image or CI SA
.export_variables:
before_script:
- export DOCKER_REGISTRY_URL="$GCP_REGION-docker.pkg.dev"
- export DOCKER_IMAGE_NAME="$DOCKER_REGISTRY_URL/$GCP_PROJECT_ID/${GCP_PROJECT_ID}-docker/$CI_PROJECT_NAME"
- export DOCKER_IMAGE_BUILDER_NAME="$DOCKER_IMAGE_NAME-builder"
- echo "$GCP_SA_KEY" | base64 -d > "$GCP_SA_KEY_JSON_PATH"


# Fetches built docker builder image from remote storage or cache
.fetch_build_image: &fetch_build_image
- |
if ! docker inspect --type=image "$DOCKER_IMAGE_BUILDER_NAME:$DOCKER_IMAGE_TAG" >/dev/null; then
docker load -i app_image_builder.tar
fi

##
# BUILD STAGE
##
# Builds the builder docker image that can be used for running jobs using the CI docker dependencies and
# configuration (tests, lint, audit, ...)
build image:
stage: build
interruptible: true
extends: .export_variables
script:
- |
echo "app_image*" >> .dockerignore
docker build --target builder \
-t "$DOCKER_IMAGE_BUILDER_NAME:$DOCKER_IMAGE_TAG" .
- docker save "$DOCKER_IMAGE_BUILDER_NAME:$DOCKER_IMAGE_TAG" > app_image_builder.tar
cache:
<<: *docker_cache
policy: push
when: on_success

##
# TEST STAGE
##
# Performs npm ci-lint script in builder image.
# Make sure the ci-lint outputs json file "output/checkstyle-result.json" that reports the result of the linter
lint:
stage: test
interruptible: true
extends: .export_variables
script:
- if [ "$SKIP_LINT" == "true" ]; then warn "Lint skipped."; exit 0; fi
- *fetch_build_image
- cd "$CI_PROJECT_DIR/docker-compose"
- |
DOCKER_IMAGE_NAME="$DOCKER_IMAGE_BUILDER_NAME" docker-compose -p "$DOCKER_COMPOSE_PROJECT_NAME" \
-f docker-compose.yml -f docker-compose.ci.yml \
run --rm --no-deps app npm run ci-lint
needs:
- build image
artifacts:
reports:
codequality: output/checkstyle-result.json

# Npm audit run inside of builder image
npm audit:
stage: test
interruptible: true
extends: .export_variables
script:
- if [ "$SKIP_AUDIT" == "true" ]; then warn "Audit skipped."; exit 0; fi
- *fetch_build_image
- cd "$CI_PROJECT_DIR/docker-compose"
- |
DOCKER_IMAGE_NAME="$DOCKER_IMAGE_BUILDER_NAME" docker-compose -p "$DOCKER_COMPOSE_PROJECT_NAME" \
-f docker-compose.yml -f docker-compose.ci.yml \
run --rm --no-deps app npm audit --production --audit-level=high
needs:
- build image

# Test job running npm ci-test script. Make sure the ci-test script exports two files on disk:
# output/test.xml - jUnit reporter with test results
# output/cobertura-coverage.xml - Test coverage results
test:
stage: test
interruptible: true
extends: .export_variables
script:
- if [ "$SKIP_TESTS" == "true" ]; then warn "Tests skipped."; exit 0; fi
- *fetch_build_image
- cd "$CI_PROJECT_DIR/docker-compose"
- |
DOCKER_IMAGE_NAME="$DOCKER_IMAGE_BUILDER_NAME" \
docker-compose -p "$DOCKER_COMPOSE_PROJECT_NAME" \
-f docker-compose.yml -f docker-compose.ci.yml \
run --rm -e RUN_DOCKER_COMPOSE_DEPS=true -e DB_HOST=postgres app npm run ci-test
artifacts:
when: always
reports:
junit: output/test.xml
coverage_report:
coverage_format: cobertura
path: output/cobertura-coverage.xml
needs:
- build image
variables:
# Node image for pipeline runner
NODEJS_VERSION: "24.12.0"
# Where to store json secrets from Cloud provider
SECRETS_PATH: '/config/secrets.json'
# Port for Cloud Run
PORT: "3000"
# Docker image name for Cloud Run
IMAGE_NAME: $GCP_REGION-docker.pkg.dev/$GCP_PROJECT_ID/$GCP_PROJECT_ID-docker/$CI_PROJECT_NAME:$CI_COMMIT_SHA

##
# DEPLOY STAGE
##
# Build production image and push to registry
# Signs in with GCP SA key from pipeline, perform build of the image and pushes it to remote registry
# Make sure /ci-branch-config-name/{branch} exists when adding new branches to "only" field
build and push to registry:
stage: deploy
extends: .export_variables
script:
- docker login -u _json_key --password-stdin $DOCKER_REGISTRY_URL < "$GCP_SA_KEY_JSON_PATH"
- set -a && source ci-branch-config/${CI_COMMIT_REF_NAME}.env && set +a
- |
docker build \
-t "$DOCKER_IMAGE_NAME:$DOCKER_IMAGE_TAG" \
$(for var in $(cat ci-branch-config/${CI_COMMIT_REF_NAME}.env | sed 's/=.*//'); do echo "--build-arg $var=${!var} "; done) \
--build-arg "SECRETS_PATH=$SECRETS_PATH" \
.
- docker push "$DOCKER_IMAGE_NAME:$DOCKER_IMAGE_TAG"
needs:
- build image
- npm audit
- test
environment:
name: $CI_COMMIT_REF_NAME
only:
- development
- stage
- master

# Deploy to Google Cloud Run based on image stored on remote registry
deploy cloud run:
stage: deploy
image: google/cloud-sdk:slim
extends: .export_variables
script:
- gcloud auth activate-service-account --key-file "$GCP_SA_KEY_JSON_PATH"
- gcloud config set project $GCP_PROJECT_ID
- gcloud auth configure-docker
- |
gcloud run deploy wake-arena-project-api \
--image "$DOCKER_IMAGE_NAME:$DOCKER_IMAGE_TAG" \
--project "$GCP_PROJECT_ID" \
--platform managed \
--port 3000 \
--region "$GCP_REGION" \
--allow-unauthenticated \
--memory "$GCP_CLOUD_RUN_ALLOCATED_MEMORY" \
--service-account="$GCP_CLOUD_RUN_SA_NAME" \
--set-secrets=$SECRETS_PATH="$GCP_SECRETS_NAME:$GCP_SECRETS_VERSION" \
--set-cloudsql-instances="$GCP_PROJECT_ID:$GCP_REGION:$GCP_SQL_INSTANCE_NAME"
only:
- development
- stage
- master
environment:
name: $CI_COMMIT_REF_NAME
needs:
- build and push to registry
include:
# BUILD APP
- component: $CI_SERVER_FQDN/Backend/gitlab-components/build@v1.0.0
inputs:
job-stage: build
# BUILD DOCKER IMAGE
- component: $CI_SERVER_FQDN/Backend/gitlab-components/docker-build@v1.0.0
inputs:
job-stage: build
allowed-branches: "master|stage|development"
image-tag: $IMAGE_NAME
# CODE QUALITY
- component: $CI_SERVER_FQDN/Backend/gitlab-components/codequality@v1.0.0
inputs:
job-stage: test
# CODE STYLE
- component: $CI_SERVER_FQDN/Backend/gitlab-components/codestyle@v1.0.0
inputs:
job-stage: test
# AUDIT
- component: $CI_SERVER_FQDN/Backend/gitlab-components/audit@v1.0.0
inputs:
job-stage: test
audit-command: npm audit --production --audit-level=high
# TEST
- component: $CI_SERVER_FQDN/Backend/gitlab-components/test@v1.0.0
inputs:
job-stage: test
test-command: DB_CONNECTION_STRING="postgres://node-app_docker:node-app_docker@postgres:5432/postgres" npm run ci-test
postgres-user: node-app_docker
postgres-password: node-app_docker
postgres-db: postgres
# PUSH DOCKER IMAGE
- component: $CI_SERVER_FQDN/Backend/gitlab-components/artifact-registry-push@v1.0.0
inputs:
job-stage: push
allowed-branches: "master|stage|development"
image-name: $IMAGE_NAME
registry: $GCP_REGION-docker.pkg.dev
# DEPLOY TO CLOUD RUN
- component: $CI_SERVER_FQDN/Backend/gitlab-components/cloudrun-deploy@v1.0.0
inputs:
job-stage: deploy
allowed-branches: "master|stage|development"
deploy-command: >-
gcloud run deploy $CI_PROJECT_NAME
--image $IMAGE_NAME
--project "$GCP_PROJECT_ID"
--platform managed
--port $PORT
--region "$GCP_REGION"
--allow-unauthenticated
--memory "$GCP_CLOUD_RUN_ALLOCATED_MEMORY"
--service-account="$GCP_CLOUD_RUN_SA_NAME"
--set-secrets=$SECRETS_PATH="$GCP_SECRETS_NAME:$GCP_SECRETS_VERSION"