Skip to content

CICD

CICD #2

Workflow file for this run

name: CICD
on:
workflow_dispatch:
push:
branches:
- master
pull_request:
branches:
- master
release:
types:
- released
env:
REGISTRY: ghcr.io/${{ github.repository_owner }}/bazaar
jobs:
lint:
name: Lint
permissions:
checks: write
contents: write
runs-on: ubuntu-latest
concurrency:
group: ${{ github.workflow }}-lint-${{ matrix.project }}-${{ github.ref }}
cancel-in-progress: true
strategy:
fail-fast: false
matrix:
project:
- api
- web
include:
- project: api
dir: ./src/api
lang: cs
config:
auto_fix: false
continue_on_error: false
check_name: "Dotnet Format (${dir})"
dotnet_format: true
dotnet_format_dir: ./src/api
env: {}
- project: web
dir: ./src/web
lang: js
config:
auto_fix: false
continue_on_error: false
check_name: "${linter} (${dir})"
# ESLint
eslint: true
eslint_dir: ./src/web
eslint_extensions: js,ts,tsx
eslint_args: --ignore-path ../../.gitignore --config .eslintrc.cjs
# Prettier
prettier: true
prettier_dir: ./src/web
prettier_extensions: js,ts,tsx
prettier_args: --ignore-path ../../.gitignore --config prettier.config.cjs
env:
SKIP_ENV_VALIDATION: true
# outputs: # https://github.com/actions/runner/pull/2477
# ${{ matrix.project }}-changes: ${{ steps.filter.outputs.changes }}
steps:
- uses: actions/checkout@v4
- uses: dorny/paths-filter@v3
id: filter
with:
base: master
filters: |-
changes:
- '${{ matrix.dir }}/**'
- uses: actions/setup-node@v4
if: (steps.filter.outputs.changes == 'true' || github.event_name == 'workflow_dispatch') && matrix.lang == 'js'
with:
node-version: 18
cache: yarn
cache-dependency-path: ${{ matrix.dir }}/yarn.lock
- uses: actions/cache@v4
if: (steps.filter.outputs.changes == 'true' || github.event_name == 'workflow_dispatch') && matrix.lang == 'js'
with:
path: ${{ matrix.dir }}/node_modules
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
- run: yarn install --frozen-lockfile
if: (steps.filter.outputs.changes == 'true' || github.event_name == 'workflow_dispatch') && matrix.lang == 'js'
working-directory: ${{ matrix.dir }}
- uses: actions/setup-dotnet@v4
if: (steps.filter.outputs.changes == 'true' || github.event_name == 'workflow_dispatch') && matrix.lang == 'cs'
with:
dotnet-version: 8.x
- uses: wearerequired/lint-action@v2
if: steps.filter.outputs.changes == 'true' || github.event_name == 'workflow_dispatch'
with: ${{ matrix.config }}
env: ${{ matrix.env }}
build:
name: Build and push
permissions:
contents: read
packages: write
runs-on: ubuntu-latest
concurrency:
group: ${{ github.workflow }}-build-${{ matrix.image }}-${{ github.ref }}
cancel-in-progress: true
needs:
- lint
outputs:
image_version: ${{ steps.meta.outputs.version }}
strategy:
matrix:
image:
- yoma-api
- yoma-web
include:
- image: yoma-api
context: ./src/api
helm: ./helm/yoma-api
file: ./src/api/Dockerfile
buildArgs: ""
- image: yoma-web
context: ./src/web
helm: ./helm/yoma-web
file: ./src/web/Dockerfile
buildArgs: |-
NEXT_PUBLIC_ENVIRONMENT=production
steps:
- uses: actions/checkout@v4
- uses: dorny/paths-filter@v3 # https://github.com/actions/runner/pull/2477
id: filter
with:
base: master
filters: |-
changes:
- '${{ matrix.context }}/**'
- '${{ matrix.helm }}/**'
- name: Should build?
id: should-run
run: |-
if [ "${{ github.event_name }}" = "release" ] || \
[ "${{ steps.filter.outputs.changes }}" = "true" ] || \
[ "${{ github.event_name }}" = "workflow_dispatch" ]; then
echo run=true >> $GITHUB_OUTPUT
else
echo run=false >> $GITHUB_OUTPUT
fi
- uses: docker/setup-buildx-action@v3
if: steps.should-run.outputs.run == 'true'
- uses: docker/login-action@v3
if: steps.should-run.outputs.run == 'true'
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ github.token }}
- uses: docker/metadata-action@v5
id: meta
with:
images: ${{ env.REGISTRY }}/${{ matrix.image }}
tags: |
type=raw,value=latest,enable=${{ github.ref == 'refs/heads/master' }}
type=sha,prefix=pr-${{ github.event.pull_request.number }}-,priority=601,enable=${{ github.event_name == 'pull_request' }}
type=sha,prefix={{branch}}-,priority=601,enable=${{ github.event_name != 'pull_request' && github.event_name != 'release' }}
type=ref,event=branch,priority=600
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
- uses: docker/build-push-action@v5
if: steps.should-run.outputs.run == 'true'
with:
platforms: linux/amd64 # linux/arm64/v8 is a little too slow right now
context: ${{ matrix.context }}
file: ${{ matrix.file }}
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: |
type=gha,scope=build-${{ matrix.image }}
type=registry,ref=${{ env.REGISTRY }}/${{ matrix.image }}:latest
cache-to: type=gha,mode=max,scope=build-${{ matrix.image }}
build-args: ${{ matrix.buildArgs }}
test-e2e:
name: Test (e2e)
permissions:
id-token: write
contents: read
packages: read
needs: build
runs-on: ubuntu-latest
env:
PUBLIC_S3_ACCESS_KEY: ${{ secrets.PUBLIC_S3_ACCESS_KEY }}
PUBLIC_S3_SECRET_KEY: ${{ secrets.PUBLIC_S3_SECRET_KEY }}
PRIVATE_S3_ACCESS_KEY: ${{ secrets.PRIVATE_S3_ACCESS_KEY }}
PRIVATE_S3_SECRET_KEY: ${{ secrets.PRIVATE_S3_SECRET_KEY }}
concurrency:
group: ${{ github.workflow }}-e2e-${{ github.ref }}
cancel-in-progress: true
steps:
- uses: actions/checkout@v4
- uses: dorny/paths-filter@v3 # https://github.com/actions/runner/pull/2477
id: filter
with:
base: master
filters: |-
api:
- './src/api/**'
web:
- './src/web/**'
keycloak:
- './src/keycloak/**'
cypress:
- './cypress/**'
- name: Should e2e run?
id: should-run
run: |-
if [ "${{ github.event_name }}" = "release" ] || \
[ "${{ steps.filter.outputs.api }}" = "true" ] || \
[ "${{ steps.filter.outputs.web }}" = "true" ] || \
[ "${{ steps.filter.outputs.keycloak }}" = "true" ] || \
[ "${{ steps.filter.outputs.cypress }}" = "true" ] || \
[ "${{ github.event_name }}" = "workflow_dispatch" ]; then
echo run=true >> $GITHUB_OUTPUT
else
echo run=false >> $GITHUB_OUTPUT
fi
- uses: docker/login-action@v3
if: steps.should-run.outputs.run == 'true'
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ github.token }}
- name: Create env file
if: steps.should-run.outputs.run == 'true'
run: |
echo "AWSS3__Buckets__Public__AccessKey=${{ secrets.PUBLIC_S3_ACCESS_KEY }}" >> src/api/env.secrets
echo "AWSS3__Buckets__Public__SecretKey=${{ secrets.PUBLIC_S3_SECRET_KEY }}" >> src/api/env.secrets
echo "AWSS3__Buckets__Public__BucketName=yoma-v3-public-storage" >> src/api/env.secrets
echo "AWSS3__Buckets__Private__AccessKey=${{ secrets.PRIVATE_S3_ACCESS_KEY }}" >> src/api/env.secrets
echo "AWSS3__Buckets__Private__SecretKey=${{ secrets.PRIVATE_S3_SECRET_KEY }}" >> src/api/env.secrets
echo "AWSS3__Buckets__Private__BucketName=yoma-v3-private-storage" >> src/api/env.secrets
echo "AriesCloudAPI__BaseUri=${{ secrets.ARIESCLOUDAPI_BASEURI }}" >> src/api/env.secrets
echo "AriesCloudAPI__GroupId=${{ secrets.ARIESCLOUDAPI_GROUPID }}" >> src/api/env.secrets
echo "AriesCloudAPI__ProtocolVersion=v2" >> src/api/env.secrets
echo "AriesCloudAPI__GovernanceAdmin__ClientId=${{ secrets.ARIESCLOUDAPI_GOVERNANCEADMIN_CLIENTID }}" >> src/api/env.secrets
echo "AriesCloudAPI__GovernanceAdmin__ClientSecret=${{ secrets.ARIESCLOUDAPI_GOVERNANCEADMIN_CLIENTSECRET }}" >> src/api/env.secrets
echo "AriesCloudAPI__Customer__ClientId=${{ secrets.ARIESCLOUDAPI_CUSTOMER_CLIENTID }}" >> src/api/env.secrets
echo "AriesCloudAPI__Customer__ClientSecret=${{ secrets.ARIESCLOUDAPI_CUSTOMER_CLIENTSECRET }}" >> src/api/env.secrets
echo "Zlto__Username=${{ secrets.ZLTO_USERNAME }}" >> src/api/env.secrets
echo "Zlto__Password=${{ secrets.ZLTO_PASSWORD }}" >> src/api/env.secrets
echo "AppSettings__SSIIssuerNameYomaOrganization=${{ secrets.APPSETTINGS_SSIISSUERNAMEYOMAORGANIZATION }}" >> src/api/env.secrets
echo "AppSettings__SSISchemaFullNameYoID=${{ secrets.APPSETTINGS_SSISCHEMAFULLNAMEYOID }}" >> src/api/env.secrets
- name: Pre-pull images
if: steps.should-run.outputs.run == 'true'
run: |-
docker compose -f ./src/api/docker-compose.yml pull
docker compose -f ./src/web/docker-compose.yml pull
env:
API_TAG: ${{ steps.filter.outputs.api == 'true' && needs.build.outputs.image_version || 'latest' }}
YOUTH_TAG: ${{ steps.filter.outputs.web == 'true' && needs.build.outputs.image_version || 'latest' }}
continue-on-error: true
- name: Start services
if: steps.should-run.outputs.run == 'true'
run: |-
docker compose -f ./src/api/docker-compose.yml up -d
docker compose -f ./src/web/docker-compose.yml up -d
env:
API_TAG: ${{ steps.filter.outputs.api == 'true' && needs.build.outputs.image_version || 'latest' }}
YOUTH_TAG: ${{ steps.filter.outputs.web == 'true' && needs.build.outputs.image_version || 'latest' }}
- uses: actions/setup-node@v4
if: steps.should-run.outputs.run == 'true'
with:
node-version: 18
cache: yarn
- uses: cypress-io/github-action@v6
if: steps.should-run.outputs.run == 'true'
- name: Upload Cypress Screenshots
uses: actions/upload-artifact@v4
if: always()
with:
name: cypress-screenshots
path: cypress/screenshots
if-no-files-found: ignore
- name: Upload Cypress Recording
uses: actions/upload-artifact@v4
if: always()
with:
name: cypress-videos
path: cypress/videos
if-no-files-found: ignore
- name: Keycloak Config logs
if: always() && steps.should-run.outputs.run == 'true'
run: docker logs keycloak-config
- name: Keycloak logs
if: always() && steps.should-run.outputs.run == 'true'
run: docker logs keycloak
- name: Yoma API logs
if: always() && steps.should-run.outputs.run == 'true'
run: docker logs yoma-api
- name: Yoma Web logs
if: always() && steps.should-run.outputs.run == 'true'
run: docker logs yoma-web
- run: docker compose down -v
deploy:
name: Deploy
permissions:
id-token: write
contents: read
packages: read
needs: [test-e2e, build]
runs-on: ubuntu-latest
env:
TAILSCALE_VERSION: 1.62.1
HELMFILE_VERSION: v0.163.1
HELM_VERSION: v3.14.3
TAG: ${{ needs.build.outputs.image_version }}
concurrency:
group: ${{ github.workflow }}-deploy
cancel-in-progress: false
environment:
name: ${{
github.event_name == 'release' && 'prod' ||
(github.event_name == 'push' && github.event.repository.default_branch == github.ref_name) && 'stage' ||
'dev'
}}
url: ${{ vars.PUBLIC_URL }}
if: ${{ !always() }}
steps:
- uses: actions/checkout@v4
- uses: dorny/paths-filter@v3 # https://github.com/actions/runner/pull/2477
id: filter
with:
base: master
filters: |-
api:
- './src/api/**'
- './helm/yoma-api/**'
web:
- './src/web/**'
- './helm/yoma-web/**'
keycloak:
- './src/keycloak/**'
- './helm/keycloak/**'
- name: Should deploy?
id: should-run
run: |-
if [ "${{ github.event_name }}" = "release" ] || \
[ "${{ steps.filter.outputs.api }}" = "true" ] || \
[ "${{ steps.filter.outputs.web }}" = "true" ] || \
[ "${{ steps.filter.outputs.keycloak }}" = "true" ] || \
[ "${{ github.event_name }}" = "workflow_dispatch" ]; then
echo run=true >> $GITHUB_OUTPUT
else
echo run=false >> $GITHUB_OUTPUT
fi
- uses: unfor19/install-aws-cli-action@v1
if: steps.should-run.outputs.run == 'true'
- name: SOPS Binary Installer
if: steps.should-run.outputs.run == 'true'
uses: mdgreenwald/[email protected]
- uses: aws-actions/configure-aws-credentials@v4
if: steps.should-run.outputs.run == 'true'
with:
aws-region: ${{ secrets.AWS_REGION }}
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
role-session-name: github-cicd
- run: aws eks update-kubeconfig --name ${{ secrets.CLUSTER }}
if: steps.should-run.outputs.run == 'true'
- uses: tailscale/github-action@main
if: steps.should-run.outputs.run == 'true'
with:
authkey: ${{ secrets.TAILSCALE_AUTHKEY }}
version: ${{ env.TAILSCALE_VERSION }}
# Diff on PR draft, otherwise Apply
- name: Helmfile Apply/Diff Keycloak
if: (
github.event_name == 'release' ||
steps.filter.outputs.keycloak == 'true' ||
github.event_name == 'workflow_dispatch'
) && steps.should-run.outputs.run == 'true'
uses: helmfile/[email protected]
with:
helmfile-args: |
${{ (github.event_name == 'pull_request' && github.event.pull_request.draft) && 'diff' || 'apply' }} \
--environment ${{ vars.ENVIRONMENT }} \
--selector app=keycloak \
--set config-cli.init.ref=${{ github.event_name == 'release' && github.ref_name || github.sha }} \
--set keycloak.themes.ref=${{ github.event_name == 'release' && github.ref_name || github.sha }} \
--set postInstallHook.ref=${{ github.event_name == 'release' && github.ref_name || github.sha }}
helmfile-version: ${{ env.HELMFILE_VERSION }}
helm-version: ${{ env.HELM_VERSION }}
helm-plugins: |
https://github.com/databus23/helm-diff,
https://github.com/jkroepke/helm-secrets,
https://github.com/aslafy-z/helm-git
- name: Helmfile Apply/Diff API
if: (
github.event_name == 'release' ||
steps.filter.outputs.api == 'true' ||
github.event_name == 'workflow_dispatch'
) && steps.should-run.outputs.run == 'true'
uses: helmfile/[email protected]
with:
helmfile-args: |
${{ (github.event_name == 'pull_request' && github.event.pull_request.draft) && 'diff' || 'apply' }} \
--environment ${{ vars.ENVIRONMENT }} \
--selector app=yoma-api \
--set postInstallHook.ref=${{ github.event_name == 'release' && github.ref_name || github.sha }}
helmfile-version: ${{ env.HELMFILE_VERSION }}
helm-version: ${{ env.HELM_VERSION }}
helm-plugins: |
https://github.com/databus23/helm-diff,
https://github.com/jkroepke/helm-secrets,
https://github.com/aslafy-z/helm-git
- name: Helmfile Apply/Diff Web
if: (
github.event_name == 'release' ||
steps.filter.outputs.web == 'true' ||
github.event_name == 'workflow_dispatch'
) && steps.should-run.outputs.run == 'true'
uses: helmfile/[email protected]
with:
helmfile-args: |
${{ (github.event_name == 'pull_request' && github.event.pull_request.draft) && 'diff' || 'apply' }} \
--environment ${{ vars.ENVIRONMENT }} \
--selector app=yoma-web
helmfile-version: ${{ env.HELMFILE_VERSION }}
helm-version: ${{ env.HELM_VERSION }}
helm-plugins: |
https://github.com/databus23/helm-diff,
https://github.com/jkroepke/helm-secrets,
https://github.com/aslafy-z/helm-git