Skip to content

Build & Unit Test

Build & Unit Test #846

name: Deploy to AWS
run-name: ${{ github.event_name == 'workflow_dispatch' && format('Deploy to {0}', github.event.inputs.environment) || (github.ref == 'refs/heads/main' && 'Deploy to Test-UAT-Prod' || 'Build & Unit Test') }}
permissions:
packages: write
contents: write
on:
push:
paths-ignore:
[
"**/README.md",
".github/workflows/increment-version.yml",
"version",
"python/**",
]
workflow_dispatch:
inputs:
environment:
description: Deploy to which environnment
type: choice
required: true
options:
- dev
- test
- uat
- prod
run_performance_tests:
required: false
default: false
type: boolean
description: Run performance tests
run_e2e_tests_assessment:
required: false
default: false
type: boolean
description: Run e2e tests (assessment)
run_e2e_tests_application:
required: false
default: true
type: boolean
description: Run e2e tests (application)
workflow_call:
inputs:
runner-cache-ref:
description: cache ref for the runner app
required: true
type: string
designer-cache-ref:
description: cache ref for the designer app
required: true
type: string
env:
DOCKER_REGISTRY: ghcr.io
IMAGE_NAME: "funding-service-design-form-runner-adapter"
IMAGE_NAME_DESIGNER: "funding-service-design-form-designer-adapter"
IMAGE_REPO_PATH: "ghcr.io/${{github.repository_owner}}"
jobs:
setup:
uses: communitiesuk/funding-service-design-workflows/.github/workflows/determine-jobs.yml@main
with:
environment: ${{ inputs.environment }}
docker-runner-build:
runs-on: ubuntu-latest
needs: [ setup ]
steps:
- name: Checkout repo
uses: actions/checkout@v4
- name: "Set version in env"
id: set-version
run: |
source version
echo "VERSION=$VERSION" >> $GITHUB_ENV
- name: Docker metadata
id: metadata
uses: docker/metadata-action@v4
with:
images: ${{env.IMAGE_REPO_PATH}}/${{env.IMAGE_NAME}}
tags: |
type=sha,format=long
type=raw,value=latest,enable=${{ github.ref == format('refs/heads/{0}', 'main') }}
type=raw,value=${{env.VERSION}},enable=${{ github.ref == format('refs/heads/{0}', 'main') }}
type=raw,value=latest
type=ref,event=branch
- name: Log in to the Container registry
uses: docker/login-action@v1
with:
registry: ${{ env.DOCKER_REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- uses: actions/checkout@v4
- name: Use Node.js
uses: actions/setup-node@v3
with:
node-version: "20.x"
- name: Get yarn cache directory path
id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn config get cacheFolder)"
- name: Get into the directory
id: change-dir
run: yarn config set enableImmutableInstalls false
- name: Create .env for runner workspace
run: |
touch ./runner/.env
echo LAST_TAG_GH=runner >> ./runner/.env
echo LAST_COMMIT_GH=runner >> ./runner/.env
cat ./runner/.env
- name: Run Unit test before docker build
run: |
echo "-- Git submodule initialize into local --"
git submodule update --init
echo "-- Pulling git submodules into local --"
git pull --recurse-submodules
echo "-- Installing digital-form-builder-adapter locally --"
node update-package.js
yarn install
echo "-- Building digital-form-builder-adapter locally --"
yarn setup
echo "-- Building digital-form-builder locally --"
# shellcheck disable=SC2164
cd digital-form-builder
yarn
echo "-- Building digital-form-builder model locally --"
yarn model build
echo "-- Building digital-form-builder queue-model locally --"
yarn queue-model build
echo "-- Running unit tests --"
yarn runner test-cov
- name: Set up Docker Buildx property
run: echo "DOCKER_BUILDKIT=1" >> $GITHUB_ENV
- name: Cache Docker layers
uses: actions/cache@v4
with:
path: ~/.buildx/cache
key: ${{ runner.os }}-buildx-runner-${{ hashFiles('./runner/Dockerfile') }}-${{ hashFiles('./runner/package.json') }}
restore-keys: |
${{ runner.os }}-buildx-runner-${{ hashFiles('./runner/Dockerfile') }}-${{ hashFiles('./runner/package.json') }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
with:
driver: docker-container
- name: Build and push docker image
uses: docker/build-push-action@v4
with:
context: .
tags: ${{ steps.metadata.outputs.tags}}
labels: ${{ steps.metadata.outputs.labels }}
push: true
file: ./runner/Dockerfile
build-args: |
LAST_TAG='${{env.VERSION}}'
LAST_COMMIT='${{ github.sha }}'
cache-from: type=gha,scope=buildx
cache-to: type=gha,mode=max,scope=buildx
docker-designer-build:
runs-on: ubuntu-latest
needs: [ setup ]
steps:
- name: Checkout repo
uses: actions/checkout@v4
- name: "Set version in env"
id: set-version
run: |
source version
echo "VERSION=$VERSION" >> $GITHUB_ENV
- name: Docker metadata
id: metadata
uses: docker/metadata-action@v4
with:
images: ${{env.IMAGE_REPO_PATH}}/${{env.IMAGE_NAME_DESIGNER}}
tags: |
type=sha,format=long
type=raw,value=latest,enable=${{ github.ref == format('refs/heads/{0}', 'main') }}
type=raw,value=${{env.VERSION}},enable=${{ github.ref == format('refs/heads/{0}', 'main') }}
type=raw,value=latest
type=ref,event=branch
- name: Log in to the Container registry
uses: docker/login-action@v1
with:
registry: ${{ env.DOCKER_REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Create .env for designer workspace
run: |
touch ./designer/.env
echo LAST_TAG_GH=designer >> ./designer/.env
echo LAST_COMMIT_GH=designer >> ./designer/.env
cat ./designer/.env
- name: Cache Docker layers
uses: actions/cache@v4
with:
path: ~/.buildx/cache
key: ${{ runner.os }}-buildx-runner-${{ hashFiles('./designer/Dockerfile') }}-${{ hashFiles('./designer/package.json') }}
restore-keys: |
${{ runner.os }}-buildx-designer-${{ hashFiles('./designer/Dockerfile') }}-${{ hashFiles('./designer/package.json') }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
with:
driver: docker-container
- name: Build and push docker image
uses: docker/build-push-action@v4
with:
context: .
tags: ${{ steps.metadata.outputs.tags}}
labels: ${{ steps.metadata.outputs.labels }}
push: true
file: ./designer/Dockerfile
build-args: |
LAST_TAG='${{env.VERSION}}'
LAST_COMMIT='${{ github.sha }}'
cache-from: type=gha,scope=buildx
cache-to: type=gha,mode=max,scope=buildx
e2e-test:
needs: [ setup, docker-runner-build, docker-designer-build ]
runs-on: ubuntu-latest
name: run e2e
outputs:
tag: ${{ steps.hashFile.outputs.tag }}
hash: ${{ steps.hashFile.outputs.hash }}
steps:
- uses: actions/checkout@v4
- name: Use Node.js
uses: actions/setup-node@v3
with:
node-version: "20.x"
- name: Get into the directory
id: change-dir
run: yarn config set enableImmutableInstalls false
- uses: actions/cache@v3
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
with:
path: |
node_modules # Cache the installed node modules
**/node_modules # Include subfolders if using workspaces
key: ${{ runner.os }}-yarn-e2e-${{ hashFiles('**/package.json') }}
restore-keys: |
${{ runner.os }}-yarn-e2e
${{ runner.os }}-yarn
- name: Setup e2e dependencies
run: |
echo "-- Git submodule initialize into local --"
git submodule update --init
echo "-- Pulling git submodules into local --"
git pull --recurse-submodules
echo "-- Installing digital-form-builder-adapter locally --"
node update-package.js
yarn install
echo "-- Install e2e dependencies --"
yarn e2e-test install
echo "-- Building digital-form-builder locally --"
cd digital-form-builder
yarn
- name: Injecting image into docker compose e2e
run: |
yq -i '.services.designer.image = "ghcr.io/communitiesuk/funding-service-design-form-designer-adapter:sha-${{ github.sha }}"' docker-compose.e2e.yml
yq -i '.services.runner.image = "ghcr.io/communitiesuk/funding-service-design-form-runner-adapter:sha-${{ github.sha }}"' docker-compose.e2e.yml
- name: Start containers
run: docker compose -f docker-compose.e2e.yml up -d
- name: Create S3 bucket
run: docker exec localstack /bin/sh -c "awslocal s3api create-bucket --bucket fsd-bucket --create-bucket-configuration LocationConstraint=eu-west-2 --region eu-west-2"
- name: Create S3 cors
run: |
docker exec localstack /bin/sh -c "awslocal s3api put-bucket-cors --bucket fsd-bucket --cors-configuration '{\"CORSRules\": [{\"AllowedOrigins\": [\"*\"],\"AllowedMethods\": [\"GET\", \"PUT\", \"POST\", \"DELETE\", \"HEAD\"],\"AllowedHeaders\": [\"*\"]}]}'"
- name: Run e2e tests
id: e2e
run: yarn e2e-test cypress run
continue-on-error: true
- name: Create Folders
run: mkdir -p ./e2e-test/cypress/screenshots/logs
- name: Get Logs from localstack
run: docker logs localstack > ./e2e-test/cypress/screenshots/logs/localstack.log
- name: Get Logs from runner
run: docker logs runner > ./e2e-test/cypress/screenshots/logs/runner.log
- name: Get all the uploaded files in the bucket
run: docker exec localstack /bin/sh -c "awslocal s3api list-objects --bucket fsd-bucket"
- name: Upload E2E Test Report Application
if: success() || failure()
uses: actions/[email protected]
with:
name: e2e-test-report-application
path: ./e2e-test/cypress/screenshots
retention-days: 5
- name: Check for errors & exit the job
run: |
if [ "${{ steps.e2e.outcome }}" == "failure" ]; then
echo "Test failures please fix before deploying the service"
exit 1
fi
dev_deploy:
needs: [ setup, docker-designer-build, docker-runner-build ]
if: ${{ always() && contains(fromJSON(needs.setup.outputs.jobs_to_run), 'dev') && (! contains(needs.*.result, 'failure') ) && (! contains(needs.*.result, 'cancelled') ) }}
permissions:
id-token: write # This is required for requesting the JWT
contents: read # This is required for actions/checkout
strategy:
matrix:
include:
- appname: "form-runner-adapter"
image_location: ghcr.io/communitiesuk/funding-service-design-form-runner-adapter:sha-${{ github.sha }}
- appname: "form-designer-adapter"
image_location: ghcr.io/communitiesuk/funding-service-design-form-designer-adapter:sha-${{ github.sha }}
uses: communitiesuk/funding-service-design-workflows/.github/workflows/standard-deploy.yml@main
secrets:
AWS_ACCOUNT: ${{ secrets.AWS_ACCOUNT }}
with:
environment: dev
app_name: ${{ matrix.appname }}
run_db_migrations: false
image_location: ${{ matrix.image_location }}
notify_slack: false
post_dev_deploy_tests:
needs: [ setup, docker-designer-build, docker-runner-build, dev_deploy ]
permissions:
id-token: write # This is required for requesting the JWT
contents: read # This is required for actions/checkout
secrets:
FSD_GH_APP_ID: ${{ secrets.FSD_GH_APP_ID }}
FSD_GH_APP_KEY: ${{ secrets.FSD_GH_APP_KEY }}
FS_BASIC_AUTH_USERNAME: ${{ secrets.FS_BASIC_AUTH_USERNAME }}
FS_BASIC_AUTH_PASSWORD: ${{ secrets.FS_BASIC_AUTH_PASSWORD }}
AWS_ACCOUNT: ${{ secrets.AWS_ACCOUNT }}
uses: communitiesuk/funding-service-design-workflows/.github/workflows/post-deploy.yml@main
with:
run_performance_tests: ${{ inputs.run_performance_tests || true }}
run_e2e_tests_assessment: ${{ inputs.run_e2e_tests_assessment || false }}
run_e2e_tests_application: ${{ inputs.run_e2e_tests_application || false }}
run_static_security_python: false
run_zap_scan: true
app_name: forms
environment: dev
notify_slack: false
test_deploy:
needs: [ setup, docker-designer-build, docker-runner-build, dev_deploy, post_dev_deploy_tests ]
if: ${{ always() && contains(fromJSON(needs.setup.outputs.jobs_to_run), 'test') && (! contains(needs.*.result, 'failure') ) && (! contains(needs.*.result, 'cancelled') ) }}
permissions:
id-token: write # This is required for requesting the JWT
contents: read # This is required for actions/checkout
strategy:
matrix:
include:
- appname: "form-runner-adapter"
image_location: ghcr.io/communitiesuk/funding-service-design-form-runner-adapter:sha-${{ github.sha }}
- appname: "form-designer-adapter"
image_location: ghcr.io/communitiesuk/funding-service-design-form-designer-adapter:sha-${{ github.sha }}
uses: communitiesuk/funding-service-design-workflows/.github/workflows/standard-deploy.yml@main
secrets:
AWS_ACCOUNT: ${{ secrets.AWS_ACCOUNT }}
SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
SLACK_NOTIFICATION_CHANNEL_ID: ${{ secrets.SLACK_NOTIFICATION_CHANNEL_ID }}
with:
environment: test
app_name: ${{ matrix.appname }}
run_db_migrations: false
image_location: ${{ matrix.image_location }}
notify_slack: true
post_test_deploy_tests:
needs: [ setup, docker-designer-build, docker-runner-build, dev_deploy, test_deploy ]
permissions:
id-token: write # This is required for requesting the JWT
contents: read # This is required for actions/checkout
if: ${{ always() && contains(fromJSON(needs.setup.outputs.jobs_to_run), 'test') && (! contains(needs.*.result, 'failure') ) && (! contains(needs.*.result, 'cancelled') ) }}
secrets:
FSD_GH_APP_ID: ${{ secrets.FSD_GH_APP_ID }}
FSD_GH_APP_KEY: ${{ secrets.FSD_GH_APP_KEY }}
FS_BASIC_AUTH_USERNAME: ${{ secrets.FS_BASIC_AUTH_USERNAME }}
FS_BASIC_AUTH_PASSWORD: ${{ secrets.FS_BASIC_AUTH_PASSWORD }}
AWS_ACCOUNT: ${{ secrets.AWS_ACCOUNT }}
SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
SLACK_NOTIFICATION_CHANNEL_ID: ${{ secrets.SLACK_NOTIFICATION_CHANNEL_ID }}
uses: communitiesuk/funding-service-design-workflows/.github/workflows/post-deploy.yml@main
with:
run_performance_tests: ${{ inputs.run_performance_tests || true }}
run_e2e_tests_assessment: ${{ inputs.run_e2e_tests_assessment || false }}
run_e2e_tests_application: ${{ inputs.run_e2e_tests_application || true }}
run_static_security_python: false
run_zap_scan: true
app_name: forms
environment: test
notify_slack: true
uat_deploy:
needs: [ setup, docker-designer-build, docker-runner-build, dev_deploy, test_deploy, post_test_deploy_tests ]
if: ${{ always() && contains(fromJSON(needs.setup.outputs.jobs_to_run), 'uat') && (! contains(needs.*.result, 'failure') ) && (! contains(needs.*.result, 'cancelled') ) }}
permissions:
id-token: write # This is required for requesting the JWT
contents: read # This is required for actions/checkout
uses: communitiesuk/funding-service-design-workflows/.github/workflows/standard-deploy.yml@main
secrets:
AWS_ACCOUNT: ${{ secrets.AWS_ACCOUNT }}
SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
SLACK_NOTIFICATION_CHANNEL_ID: ${{ secrets.SLACK_NOTIFICATION_CHANNEL_ID }}
with:
environment: uat
app_name: form-runner-adapter
run_db_migrations: false
image_location: ghcr.io/communitiesuk/funding-service-design-form-runner-adapter:sha-${{ github.sha }}
notify_slack: true
post_uat_deploy_tests:
needs: [ setup, docker-designer-build, docker-runner-build, dev_deploy, test_deploy, uat_deploy ]
permissions:
id-token: write # This is required for requesting the JWT
contents: read # This is required for actions/checkout
if: ${{ always() && contains(fromJSON(needs.setup.outputs.jobs_to_run), 'uat') && (! contains(needs.*.result, 'failure') ) && (! contains(needs.*.result, 'cancelled') ) }}
secrets:
FSD_GH_APP_ID: ${{ secrets.FSD_GH_APP_ID }}
FSD_GH_APP_KEY: ${{ secrets.FSD_GH_APP_KEY }}
FS_BASIC_AUTH_USERNAME: ${{ secrets.FS_BASIC_AUTH_USERNAME }}
FS_BASIC_AUTH_PASSWORD: ${{ secrets.FS_BASIC_AUTH_PASSWORD }}
AWS_ACCOUNT: ${{ secrets.AWS_ACCOUNT }}
SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
SLACK_NOTIFICATION_CHANNEL_ID: ${{ secrets.SLACK_NOTIFICATION_CHANNEL_ID }}
uses: communitiesuk/funding-service-design-workflows/.github/workflows/post-deploy.yml@main
with:
run_performance_tests: ${{ inputs.run_performance_tests || true }}
run_e2e_tests_assessment: ${{ inputs.run_e2e_tests_assessment || false }}
run_e2e_tests_application: ${{ inputs.run_e2e_tests_application || true }}
run_static_security_python: false
run_zap_scan: true
app_name: forms
environment: uat
notify_slack: true
prod_deploy:
needs: [ setup, docker-designer-build, docker-runner-build, dev_deploy, test_deploy, uat_deploy, post_uat_deploy_tests ]
if: ${{ always() && contains(fromJSON(needs.setup.outputs.jobs_to_run), 'prod') && (! contains(needs.*.result, 'failure') ) && (! contains(needs.*.result, 'cancelled') ) }}
permissions:
id-token: write # This is required for requesting the JWT
contents: read # This is required for actions/checkout
uses: communitiesuk/funding-service-design-workflows/.github/workflows/standard-deploy.yml@main
secrets:
AWS_ACCOUNT: ${{ secrets.AWS_ACCOUNT }}
SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
SLACK_NOTIFICATION_CHANNEL_ID: ${{ secrets.SLACK_NOTIFICATION_CHANNEL_ID }}
with:
environment: prod
app_name: form-runner-adapter
run_db_migrations: false
image_location: ghcr.io/communitiesuk/funding-service-design-form-runner-adapter:sha-${{ github.sha }}
notify_slack: true