Skip to content

DRAFT DO NOT REVIEW 426 2 candidate withdrawal reasons list refinement and mandatory #18288

DRAFT DO NOT REVIEW 426 2 candidate withdrawal reasons list refinement and mandatory

DRAFT DO NOT REVIEW 426 2 candidate withdrawal reasons list refinement and mandatory #18288

# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json
name: Build and Deploy
concurrency: build_and_deploy_${{ github.ref_name }}
on:
push:
branches:
- main
pull_request:
branches:
- main
types: [opened, reopened, synchronize, labeled]
permissions:
contents: write
deployments: write
packages: write
pull-requests: write
jobs:
build:
environment:
name: review_aks
name: Build
runs-on: ubuntu-latest
env:
DOCKER_IMAGE: ghcr.io/dfe-digital/apply-teacher-training
GEMS_NODE_MODULES_IMAGE: ghcr.io/dfe-digital/apply-teacher-training-gems-node-modules
outputs:
IMAGE_TAG: ${{ env.IMAGE_TAG }}
GIT_BRANCH: ${{ env.GIT_BRANCH }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set docker image tag (push)
if: github.event_name == 'push'
run: |
GIT_REF=${{ github.ref }}
echo "GIT_BRANCH=${GIT_REF##*/}" >> $GITHUB_ENV # GIT_BRANCH will be main for refs/heads/main
echo "IMAGE_TAG=${{ github.sha }}" >> $GITHUB_ENV
- name: Set docker image tag (pull_request)
if: github.event_name == 'pull_request'
run: |
# This is the actual PR branch
GIT_REF=${{ github.head_ref }}
echo "GIT_BRANCH=${GIT_REF##*/}" >> $GITHUB_ENV
echo "IMAGE_TAG=${{ github.event.pull_request.head.sha }}" >> $GITHUB_ENV
# TODO Need to check this for AKS review apps
- name: Set KV environment variables
run: |
# tag build to the review env for vars and secrets
tf_vars_file=terraform/aks/workspace_variables/review_aks.tfvars.json
echo "KEY_VAULT_NAME=$(jq -r '.key_vault_name' ${tf_vars_file})" >> $GITHUB_ENV
echo "KEY_VAULT_INFRA_SECRET_NAME=$(jq -r '.key_vault_infra_secret_name' ${tf_vars_file})" >> $GITHUB_ENV
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- uses: azure/login@v2
with:
creds: ${{ secrets.AZURE_CREDENTIALS }}
- uses: DFE-Digital/keyvault-yaml-secret@v1
id: get-secret
with:
keyvault: ${{ env.KEY_VAULT_NAME }}
secret: ${{ env.KEY_VAULT_INFRA_SECRET_NAME }}
key: SNYK_TOKEN
- name: Build gems-node-modules Docker Image
uses: docker/build-push-action@v6
with:
target: gems-node-modules
tags: |
${{ env.GEMS_NODE_MODULES_IMAGE }}:${{ env.IMAGE_TAG }}
${{ env.GEMS_NODE_MODULES_IMAGE }}:${{ env.GIT_BRANCH }}
push: true
cache-to: type=inline
cache-from: |
type=registry,ref=${{ env.GEMS_NODE_MODULES_IMAGE }}:${{ env.GIT_BRANCH }}
type=registry,ref=${{ env.GEMS_NODE_MODULES_IMAGE }}:main
- name: Build Docker Image
uses: docker/build-push-action@v6
with:
tags: |
${{ env.DOCKER_IMAGE }}:${{ env.IMAGE_TAG }}
${{ env.DOCKER_IMAGE }}:${{ env.GIT_BRANCH }}
push: false
load: true
cache-to: type=inline
cache-from: |
type=registry,ref=${{ env.DOCKER_IMAGE }}:main
type=registry,ref=${{ env.DOCKER_IMAGE }}:${{ env.IMAGE_TAG }}
type=registry,ref=${{ env.DOCKER_IMAGE }}:${{ env.GIT_BRANCH }}
type=registry,ref=${{ env.GEMS_NODE_MODULES_IMAGE }}:${{ env.GIT_BRANCH }}
type=registry,ref=${{ env.GEMS_NODE_MODULES_IMAGE }}:main
build-args: |
SHA=${{ env.IMAGE_TAG }}
- name: Run Snyk to check Docker image for vulnerabilities
uses: snyk/actions/docker@master
env:
SNYK_TOKEN: ${{ steps.get-secret.outputs.snyk_token }}
with:
image: ${{ env.DOCKER_IMAGE }}:${{ env.IMAGE_TAG }}
args: --file=Dockerfile --severity-threshold=high --exclude-app-vulns
- name: Push ${{ env.DOCKER_IMAGE }} images
run: docker image push --all-tags ${{ env.DOCKER_IMAGE }}
- name: Notify Slack channel on job failure
if: failure() && github.event_name == 'push'
uses: rtCamp/action-slack-notify@v2
env:
SLACK_USERNAME: CI Deployment
SLACK_TITLE: Build failure
SLACK_MESSAGE: Build failure on branch ${{env.GIT_BRANCH}}
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
SLACK_COLOR: failure
SLACK_FOOTER: Sent from build job in build workflow
lint:
name: Lint
needs: [build]
runs-on: ubuntu-latest
defaults:
run:
working-directory: /app
strategy:
fail-fast: false
matrix:
tests: [rubocop, erblint, brakeman, yarn_lint]
include:
- tests: rubocop
command: bundle exec rubocop --format clang --parallel
- tests: erblint
command: bundle exec rake erblint
- tests: brakeman
command: bundle exec rake brakeman
- tests: yarn_lint
command: |
yarn install
yarn run lint && yarn run stylelint app/frontend/styles && \
yarn run test
container:
image: ghcr.io/dfe-digital/apply-teacher-training-gems-node-modules:${{ needs.build.outputs.IMAGE_TAG }}
options: -a STDOUT -a STDERR -t
credentials:
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
steps:
- name: ${{ matrix.tests }}
run: ${{ env.COMMAND }}
env:
COMMAND: ${{ matrix.command }}
- name: Notify Slack channel on job failure
if: failure() && github.event_name == 'push'
uses: rtCamp/action-slack-notify@v2
env:
SLACK_USERNAME: CI Deployment
SLACK_TITLE: Lint failure
SLACK_MESSAGE: ${{ matrix.tests }} lint failure on branch ${{ needs.build.outputs.GIT_BRANCH }}
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
SLACK_COLOR: failure
SLACK_FOOTER: Sent from lint job in build workflow
test:
name: Tests
needs: [build]
runs-on: ubuntu-latest
outputs:
IMAGE_TAG: ${{ needs.build.outputs.IMAGE_TAG }}
flakey_tests: ${{ steps.set_flakey_test_results_var.outputs.flakey_tests }}
strategy:
fail-fast: false
matrix:
tests:
[
unit_shared,
unit_candidate-provider,
unit_support-referee-api,
integration_shared,
integration_provider,
integration_candidate,
]
feature-flags: [on, off]
# Use these offsets as the end of cycle approaches
# offset-date: [real_world, after_apply_deadline, before_apply_reopens, after_apply_reopens]
# Use these offsets through mid-cycle
offset-date: [real_world]
include:
- tests: unit_shared
include-pattern: spec/.*_spec.rb
exclude-pattern: spec/(system|smoke|.*/(candidate_interface|provider_interface|support_interface|referee_interface|.*_api|api_.*|.*_api_.*))/.*_spec.rb
- tests: unit_candidate-provider
include-pattern: spec/.*/(candidate_interface|provider_interface)/.*_spec.rb
exclude-pattern: spec/(system|smoke)/.*_spec.rb
- tests: unit_support-referee-api
include-pattern: spec/.*/(support_interface|referee_interface|.*_api|api_.*|.*_api_.*)/.*_spec.rb
exclude-pattern: spec/(system|smoke)/.*_spec.rb
- tests: integration_shared
include-pattern: spec/system/.*_spec.rb
exclude-pattern: spec/system/(provider_interface|candidate_interface)/.*_spec.rb
- tests: integration_provider
include-pattern: spec/system/provider_interface/.*_spec.rb
- tests: integration_candidate
include-pattern: spec/system/candidate_interface/.*_spec.rb
services:
redis:
image: redis:alpine
ports:
- 6379:6379
postgres:
image: postgres:14
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: postgres
ports:
- 5432:5432
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
defaults:
run:
working-directory: /app
container:
image: ghcr.io/dfe-digital/apply-teacher-training:${{ needs.build.outputs.IMAGE_TAG }}
credentials:
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
env:
RAILS_ENV: test
DB_HOSTNAME: postgres
DB_USERNAME: postgres
DB_PASSWORD: postgres
REDIS_URL: redis://redis:6379/0
DB_PORT: 5432
ZENDESK_MESSAGING_KEY: ${{ secrets.ZENDESK_MESSAGING_KEY_CI }}
steps:
- name: Setup Parallel Database
run: bundle exec rake parallel:setup
- name: Install chromedriver
run: apk add chromium chromium-chromedriver
- name: ${{ matrix.tests }} tests with feature flags ${{ matrix.feature-flags }}
run: bundle exec --verbose parallel_rspec --pattern "${{ env.INCLUDE_PATTERN }}" --exclude-pattern "${{ env.EXCLUDE_PATTERN }}"
env:
INCLUDE_PATTERN: ${{ matrix.include-pattern }}
EXCLUDE_PATTERN: ${{ matrix.exclude-pattern || ' ' }}
TEST_MATRIX_NODE_NAME: ${{ matrix.tests }}
DEFAULT_FEATURE_FLAG_STATE: ${{ matrix.feature-flags }}
TEST_DATE_AND_TIME: ${{ matrix.offset-date }}
- name: Read flakey test results
id: set_flakey_test_results_var
run: |
file_text=$(cat /app/tmp/rspec-retry-flakey-specs.log)
if [ ! -z "$file_text" ]
then
echo "Flakey specs found"
echo "file_text: $file_text"
# Use GitHub SHA as uuidgen is not available
echo "flakey_tests<<$GITHUB_SHA" >> $GITHUB_OUTPUT
cat /app/tmp/rspec-retry-flakey-specs.log >> $GITHUB_OUTPUT
echo "$GITHUB_SHA" >> $GITHUB_OUTPUT
else
echo "No flakey tests logged"
fi
- name: Notify Slack channel on job failure
if: failure() && github.event_name == 'push'
uses: rtCamp/action-slack-notify@v2
env:
SLACK_USERNAME: CI Deployment
SLACK_TITLE: Test failure
SLACK_MESSAGE: ${{ matrix.tests }} (feature-flags ${{ matrix.feature-flags }}) test failure on branch ${{ needs.build.outputs.GIT_BRANCH }}
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
SLACK_COLOR: failure
SLACK_FOOTER: Sent from test job in build workflow
report-flakey-specs:
name: Report on flakey specs in pull request
needs: [test]
runs-on: ubuntu-latest
steps:
- name: Checkout source
uses: actions/checkout@v4
- name: Comment on flakey specs in pull request
uses: actions/github-script@v7
if: github.event_name == 'pull_request' && needs.test.outputs.flakey_tests
env:
FLAKEY_TEST_DATA: |
${{ needs.test.outputs.flakey_tests }}
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const script = require('/home/runner/work/apply-for-teacher-training/apply-for-teacher-training/.github/scripts/comment_on_flakey_specs.js')
script({github, context})
deploy-v2-review-app:
name: Deployment To Review v2
concurrency: deploy_v2_review_${{ github.event.pull_request.number }}
if: ${{ github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'deploy_v2') }}
environment:
name: review_aks
needs: [build]
runs-on: ubuntu-latest
steps:
- name: Start review_aks-${{ github.event.pull_request.number }} Deployment
uses: bobheadxi/deployments@v1
id: deployment
with:
env: review_aks-${{ github.event.pull_request.number }}
ref: ${{ github.head_ref }}
step: start
token: ${{ secrets.GITHUB_TOKEN }}
- name: Checkout
uses: actions/checkout@v4
- name: Deploy App to Review v2
id: deploy_v2_review
uses: ./.github/actions/deploy_v2/
with:
azure-credentials: ${{ secrets.AZURE_CREDENTIALS }}
environment: review_aks
pr-number: ${{ github.event.pull_request.number }}
sha: ${{ needs.build.outputs.IMAGE_TAG }}
slack-webhook: ${{ secrets.SLACK_WEBHOOK }}
- name: Update review_aks-${{ github.event.pull_request.number }} status
if: always()
uses: bobheadxi/deployments@v1
with:
env: review_aks-${{ github.event.pull_request.number }}
ref: ${{ github.head_ref }}
step: finish
token: ${{ secrets.GITHUB_TOKEN }}
status: ${{ job.status }}
deployment_id: ${{ steps.deployment.outputs.deployment_id }}
env_url: ${{ steps.deploy_v2_review.outputs.deploy-url }}
merge-dependabot:
name: Merge dependabot
if: ${{ github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'dependencies') }}
needs: [lint, test, deploy-v2-review-app, report-flakey-specs]
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Merge minor dependency updates
uses: fastify/github-action-merge-dependabot@v3
with:
github-token: ${{ secrets.ACTIONS_API_ACCESS_TOKEN }}
target: minor
exclude: "govuk-components,govuk_design_system_formbuilder,govuk-frontend"
merge-method: merge
deploy-aks-before-production:
name: Parallel deployment before production v2
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
environment:
name: ${{ matrix.environment }}
url: ${{ steps.deploy_app_before_production_v2.outputs.deploy-url }}
needs: [lint, test]
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
environment: [qa_aks, staging_aks]
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Deploy App to ${{ matrix.environment }} v2
id: deploy_app_before_production_v2
uses: ./.github/actions/deploy_v2/
with:
azure-credentials: ${{ secrets.AZURE_CREDENTIALS }}
environment: ${{ matrix.environment }}
sha: ${{ github.sha }}
slack-webhook: ${{ secrets.SLACK_WEBHOOK }}
deploy-aks-production:
name: Production deployment v2
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
environment:
name: production_aks
url: ${{ steps.deploy_app_v2.outputs.deploy-url }}
needs: [deploy-aks-before-production]
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Deploy App to production v2
id: deploy_app_v2
uses: ./.github/actions/deploy_v2/
with:
azure-credentials: ${{ secrets.AZURE_CREDENTIALS }}
environment: production_aks
sha: ${{ github.sha }}
slack-webhook: ${{ secrets.SLACK_WEBHOOK }}
# TODO Update needs to deploy-aks-production when production is deployed
deploy-aks-after-production:
name: Sandbox deployment v2
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
environment:
name: sandbox_aks
url: ${{ steps.deploy_app_after_production_v2.outputs.deploy-url }}
needs: [deploy-aks-production]
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Deploy App to sandbox v2
id: deploy_app_after_production_v2
uses: ./.github/actions/deploy_v2/
with:
azure-credentials: ${{ secrets.AZURE_CREDENTIALS }}
environment: sandbox_aks
sha: ${{ github.sha }}
slack-webhook: ${{ secrets.SLACK_WEBHOOK }}