Implement GOV.UK Frontend v5.0 on Get school experience and Manage school experience #4015
Workflow file for this run
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: Build and Deploy | |
on: | |
workflow_dispatch: | |
pull_request: | |
push: | |
branches: [ master ] | |
permissions: | |
contents: write | |
deployments: write | |
issues: write | |
packages: write | |
pull-requests: write | |
jobs: | |
build: | |
name: Build | |
runs-on: ubuntu-latest | |
outputs: | |
DOCKER_IMAGE: ${{ steps.docker.outputs.DOCKER_IMAGE }} | |
steps: | |
- name: Check out the repo | |
uses: actions/checkout@v4 | |
- name: set-up-environment | |
uses: DFE-Digital/github-actions/set-up-environment@master | |
- uses: Azure/login@v2 | |
with: | |
creds: ${{ secrets.GSE_REPO_AZ_CREDENTIALS }} | |
- name: Lint Dockerfile | |
uses: brpaz/hadolint-action@master | |
with: | |
dockerfile: "Dockerfile" | |
- name: Set up Docker Buildx | |
id: buildx | |
uses: docker/setup-buildx-action@master | |
- name: Get Short SHA | |
id: sha | |
run: echo "short=$(echo $GITHUB_SHA | cut -c -7)" >> $GITHUB_OUTPUT | |
- name: Set DOCKER_IMAGE environment variable | |
id: docker | |
run: | | |
if [ "${{github.ref}}" == "refs/heads/master" ] | |
then | |
echo "DOCKER_IMAGE=${{ env.DOCKER_REPOSITORY }}:sha-${{ steps.sha.outputs.short }}" >> $GITHUB_OUTPUT | |
echo "DOCKER_MASTER=${{ env.DOCKER_REPOSITORY }}:master" >> $GITHUB_OUTPUT | |
else | |
echo "DOCKER_IMAGE=${{ env.DOCKER_REPOSITORY }}:review-${{steps.sha.outputs.short }}" >> $GITHUB_OUTPUT | |
echo "DOCKER_MASTER=${{ env.DOCKER_REPOSITORY }}:PR-${{ github.event.number }}" >> $GITHUB_OUTPUT | |
fi | |
- name: Login to GitHub Container Registry | |
uses: docker/login-action@v3 | |
with: | |
registry: ghcr.io | |
username: ${{ github.repository_owner }} | |
password: ${{ secrets.GITHUB_TOKEN }} | |
- name: Build and push to GitHub Container Registry | |
uses: docker/build-push-action@v5 | |
with: | |
cache-from: ${{ env.DOCKER_REPOSITORY }}:master | |
tags: | | |
${{ steps.docker.outputs.DOCKER_IMAGE }} | |
${{ steps.docker.outputs.DOCKER_MASTER }} | |
push: true | |
build-args: | |
SHA=${{ steps.sha.outputs.short }} | |
- name: Fetch slack web hook | |
if: failure() && github.ref == 'refs/heads/master' | |
uses: azure/CLI@v2 | |
id: slack-web-hook | |
with: | |
inlineScript: | | |
SECRET_VALUE=$(az keyvault secret show --name "SLACK-WEBHOOK" --vault-name "${{ secrets.INF_KEY_VAULT}}" --query "value" -o tsv) | |
echo "::add-mask::$SECRET_VALUE" | |
echo "SLACK-WEBHOOK=$SECRET_VALUE" >> $GITHUB_OUTPUT | |
- name: Slack Notification | |
if: failure() && github.ref == 'refs/heads/master' | |
uses: rtCamp/action-slack-notify@master | |
env: | |
SLACK_COLOR: ${{env.SLACK_ERROR}} | |
SLACK_MESSAGE: 'There has been a failure building the application' | |
SLACK_TITLE: 'Failure Building Application' | |
SLACK_WEBHOOK: " ${{ steps.slack-web-hook.outputs.SLACK-WEBHOOK }} " | |
spec_tests: | |
name: Unit Tests | |
runs-on: ubuntu-latest | |
needs: [ build ] | |
steps: | |
- name: Check out the repo | |
uses: actions/checkout@v4 | |
- name: set-up-environment | |
uses: DFE-Digital/github-actions/set-up-environment@master | |
- uses: Azure/login@v2 | |
with: | |
creds: ${{ secrets.AZURE_CREDENTIALS }} | |
- name: Login to GitHub Container Registry | |
uses: docker/login-action@v3 | |
with: | |
registry: ghcr.io | |
username: ${{ github.repository_owner }} | |
password: ${{ secrets.GITHUB_TOKEN }} | |
- name: Bring up Docker compose Stack | |
run: docker-compose -f docker-compose-ci.yml up -d | |
env: | |
IMAGE: ${{needs.build.outputs.DOCKER_IMAGE}} | |
- name: Lint Ruby | |
run: docker run -t --rm -v ${PWD}/out:/app/out -e RAILS_ENV=test ${{needs.build.outputs.DOCKER_IMAGE}} rubocop | |
- name: Keep Rubocop output | |
if: always() | |
uses: actions/upload-artifact@v3 | |
with: | |
name: Rubocop_results | |
path: ${{ github.workspace }}/out/rubocop-result.json | |
- name: Run Specs | |
run: docker-compose -f docker-compose-ci.yml run --rm db-tasks rspec | |
env: | |
IMAGE: ${{needs.build.outputs.DOCKER_IMAGE}} | |
- name: Keep Unit Tests Results | |
if: always() | |
uses: actions/upload-artifact@v3 | |
with: | |
name: unit_tests | |
path: ${{ github.workspace }}/out/test-report.xml | |
- name: Keep Code Coverage Report | |
if: always() | |
uses: actions/upload-artifact@v3 | |
with: | |
name: Code_Coverage | |
path: ${{ github.workspace }}/coverage/coverage.json | |
security_tests: | |
name: Security Tests | |
runs-on: ubuntu-latest | |
needs: [ build ] | |
steps: | |
- name: Checkout | |
uses: actions/checkout@v4 | |
- name: set-up-environment | |
uses: DFE-Digital/github-actions/set-up-environment@master | |
- uses: Azure/login@v2 | |
with: | |
creds: ${{ secrets.GSE_REPO_AZ_CREDENTIALS }} | |
- name: Login to GitHub Container Registry | |
uses: docker/login-action@v3 | |
with: | |
registry: ghcr.io | |
username: ${{ github.repository_owner }} | |
password: ${{ secrets.GITHUB_TOKEN }} | |
- name: Fetch synk token from key vault | |
uses: azure/CLI@v2 | |
id: fetch-synk-token | |
with: | |
inlineScript: | | |
SECRET_VALUE=$(az keyvault secret show --name "SNYK-TOKEN" --vault-name "${{ secrets.INF_KEY_VAULT}}" --query "value" -o tsv) | |
echo "::add-mask::$SECRET_VALUE" | |
echo "SNYK-TOKEN=$SECRET_VALUE" >> $GITHUB_OUTPUT | |
- name: Run Snyk to check Docker image for vulnerabilities | |
uses: snyk/actions/docker@master | |
env: | |
SNYK_TOKEN: ${{ steps.fetch-synk-token.outputs.SNYK-TOKEN }} | |
with: | |
image: ${{needs.build.outputs.DOCKER_IMAGE}} | |
args: --severity-threshold=high --file=Dockerfile --exclude-app-vulns --policy-path=/.snyk | |
- name: Run Brakeman static security scanner | |
run: docker run -t --rm -e RAILS_ENV=test ${{needs.build.outputs.DOCKER_IMAGE}} brakeman | |
cucumber_tests: | |
name: Cucumber Tests | |
runs-on: ubuntu-latest | |
needs: [ build ] | |
strategy: | |
fail-fast: false | |
matrix: | |
node: [1, 2] | |
steps: | |
- name: Check out the repo | |
uses: actions/checkout@v4 | |
- name: set-up-environment | |
uses: DFE-Digital/github-actions/set-up-environment@master | |
- uses: Azure/login@v2 | |
with: | |
creds: ${{ secrets.AZURE_CREDENTIALS }} | |
- name: Login to GitHub Container Registry | |
uses: docker/login-action@v3 | |
with: | |
registry: ghcr.io | |
username: ${{ github.repository_owner }} | |
password: ${{ secrets.GITHUB_TOKEN }} | |
- name: Run Cucumber Tests | |
run: |- | |
docker-compose -f docker-compose-ci.yml run --rm \ | |
-e RAILS_ENV \ | |
-e DATABASE_CLEANER_ALLOW_REMOTE_DATABASE_URL \ | |
-e NODE \ | |
-e NODE_COUNT \ | |
db-tasks -p ${PROFILE} cucumber | |
env: | |
IMAGE: ${{needs.build.outputs.DOCKER_IMAGE}} | |
PROFILE: continuous_integration | |
DATABASE_CLEANER_ALLOW_REMOTE_DATABASE_URL: true | |
NODE: ${{ matrix.node }} | |
NODE_COUNT: 2 | |
- name: Keep Unit Tests Results | |
if: always() | |
uses: actions/upload-artifact@v3 | |
with: | |
name: cucumber_tests | |
path: ${{ github.workspace }}/out | |
selenium_cucumber_tests: | |
name: Chrome Cucumber Tests | |
runs-on: ubuntu-latest | |
needs: [ build ] | |
strategy: | |
fail-fast: false | |
matrix: | |
node: [1, 2] | |
steps: | |
- name: Check out the repo | |
uses: actions/checkout@v4 | |
- name: set-up-environment | |
uses: DFE-Digital/github-actions/set-up-environment@master | |
- uses: Azure/login@v2 | |
with: | |
creds: ${{ secrets.AZURE_CREDENTIALS }} | |
- name: Login to GitHub Container Registry | |
uses: docker/login-action@v3 | |
with: | |
registry: ghcr.io | |
username: ${{ github.repository_owner }} | |
password: ${{ secrets.GITHUB_TOKEN }} | |
- name: Run Cucumber Tests | |
run: |- | |
docker-compose -f docker-compose-ci.yml run --rm \ | |
-e RAILS_ENV \ | |
-e DATABASE_CLEANER_ALLOW_REMOTE_DATABASE_URL \ | |
-e NODE \ | |
-e NODE_COUNT \ | |
school-experience -p ${PROFILE} cucumber | |
env: | |
IMAGE: ${{needs.build.outputs.DOCKER_IMAGE}} | |
PROFILE: selenium | |
RAILS_ENV: test | |
DATABASE_CLEANER_ALLOW_REMOTE_DATABASE_URL: true | |
NODE: ${{ matrix.node }} | |
NODE_COUNT: 2 | |
- name: Keep Unit Tests Results | |
if: always() | |
uses: actions/upload-artifact@v3 | |
with: | |
name: selenium_cucumber_tests | |
path: ${{ github.workspace }}/out | |
sonarcloud: | |
name: SonarCloud | |
runs-on: ubuntu-latest | |
needs: [ selenium_cucumber_tests, cucumber_tests , security_tests, spec_tests ] | |
steps: | |
- uses: actions/checkout@v4 | |
with: | |
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis | |
- name: set-up-environment | |
uses: DFE-Digital/github-actions/set-up-environment@master | |
- uses: Azure/login@v2 | |
with: | |
creds: ${{ secrets.GSE_REPO_AZ_CREDENTIALS }} | |
- name: Download Test Artifacts | |
uses: actions/download-artifact@v3 | |
with: | |
path: ${{ github.workspace }}/out/ | |
- name: Fixup report file paths | |
run: sudo sed -i "s?/app/app?/github/workspace/app?" ${{ github.workspace }}/out/Code_Coverage/coverage.json | |
- name: Fetch Sonar token from key vault | |
uses: azure/CLI@v2 | |
id: fetch-sonar-token | |
with: | |
inlineScript: | | |
SECRET_VALUE=$(az keyvault secret show --name "SONAR-TOKEN" --vault-name "${{ secrets.INF_KEY_VAULT}}" --query "value" -o tsv) | |
echo "::add-mask::$SECRET_VALUE" | |
echo "SONAR-TOKEN=$SECRET_VALUE" >> $GITHUB_OUTPUT | |
- name: SonarCloud Scan | |
uses: SonarSource/sonarcloud-github-action@master | |
env: | |
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
SONAR_TOKEN: ${{ steps.fetch-sonar-token.outputs.SONAR-TOKEN }} | |
prepare: | |
name: Configure Matrix Deployments | |
needs: [ sonarcloud ] | |
runs-on: ubuntu-latest | |
outputs: | |
matrix_environments: ${{ env.MATRIX_ENVIRONMENTS }} | |
release_tag: ${{steps.tag_version.outputs.pr_number}} | |
steps: | |
- name: Set matrix environments (Push to master) | |
if: github.event_name == 'push' && github.ref == 'refs/heads/master' | |
run: | | |
echo "MATRIX_ENVIRONMENTS={\"environment\":[\"development\",\"staging\",\"production\"]}" >> $GITHUB_ENV | |
- name: Set matrix environments ( Review) | |
if: github.event_name == 'pull_request' && github.ref != 'refs/heads/master' | |
run: | | |
echo "MATRIX_ENVIRONMENTS={\"environment\":[\"review\"]}" >> $GITHUB_ENV | |
- name: Generate Tag from PR Number | |
if: github.event_name == 'push' && github.ref == 'refs/heads/master' | |
id: tag_version | |
uses: DFE-Digital/github-actions/GenerateReleaseFromSHA@master | |
with: | |
sha: ${{github.sha}} | |
- name: Create a GitHub Release | |
if: github.event_name == 'push' && github.ref == 'refs/heads/master' && steps.tag_version.outputs.pr_found == 1 | |
id: release | |
uses: actions/create-release@v1 | |
env: | |
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
with: | |
tag_name: ${{ steps.tag_version.outputs.pr_number }} | |
body: ${{ steps.tag_version.outputs.pr_number }} | |
release_name: Release ${{ steps.tag_version.outputs.pr_number }} | |
commitish: ${{ github.sha}} | |
prerelease: false | |
- name: Copy PR Info to Release | |
if: github.event_name == 'push' && github.ref == 'refs/heads/master' && steps.release.outputs.id | |
uses: DFE-Digital/github-actions/CopyPRtoRelease@master | |
with: | |
PR_NUMBER: ${{ steps.tag_version.outputs.pr_number }} | |
RELEASE_ID: ${{ steps.release.outputs.id }} | |
TOKEN: ${{secrets.GITHUB_TOKEN}} | |
deployments: | |
name: Deployments | |
strategy: | |
max-parallel: 1 | |
matrix: ${{fromJSON(needs.prepare.outputs.matrix_environments)}} | |
environment: | |
name: ${{matrix.environment}} | |
concurrency: ${{matrix.environment}}_${{github.event.number}} | |
needs: [prepare ] | |
runs-on: ubuntu-latest | |
steps: | |
- name: Check out the repo | |
uses: actions/checkout@v4 | |
- name: set-up-environment | |
uses: DFE-Digital/github-actions/set-up-environment@master | |
- uses: Azure/login@v2 | |
with: | |
creds: ${{ secrets.AZURE_CREDENTIALS }} | |
- name: Fetch SECURE USERNAME | |
uses: azure/CLI@v2 | |
id: fetch-username | |
with: | |
inlineScript: | | |
SECRET_VALUE=$(az keyvault secret show --name "SECURE-USERNAME" --vault-name "${{ secrets.APP_KEY_VAULT}}" --query "value" -o tsv) | |
echo "::add-mask::$SECRET_VALUE" | |
echo "SECURE_USERNAME=$SECRET_VALUE" >> $GITHUB_OUTPUT | |
- name: Fetch SECURE PASSWORD | |
uses: azure/CLI@v2 | |
id: fetch-password | |
with: | |
inlineScript: | | |
SECRET_VALUE=$(az keyvault secret show --name "SECURE-PASSWORD" --vault-name "${{ secrets.APP_KEY_VAULT}}" --query "value" -o tsv) | |
echo "::add-mask::$SECRET_VALUE" | |
echo "SECURE_PASSWORD=$SECRET_VALUE" >> $GITHUB_OUTPUT | |
- name: Trigger Deployment to ${{matrix.environment}} | |
id: deploy | |
uses: ./.github/workflows/actions/deploy | |
with: | |
environment: ${{matrix.environment}} | |
sha: ${{ github.sha }} | |
azure-credentials: ${{ secrets.AZURE_CREDENTIALS }} | |
pr: ${{github.event.number}} | |
secure-username: ${{ steps.fetch-username.outputs.SECURE_USERNAME}} | |
secure-password: ${{ steps.fetch-password.outputs.SECURE_PASSWORD}} | |
- name: Determine DfE Sign In Message | |
if: matrix.environment == 'Review' | |
uses: haya14busa/action-cond@v1 | |
id: dsiMessage | |
with: | |
cond: ${{ steps.deploy.outputs.dsi-hostname != '' }} | |
if_true: ':white_check_mark: DfE sign in route obtained: https://${{ steps.deploy.outputs.dsi-hostname }}' | |
if_false: ':warning: **DfE sign in route pool exhausted (close some open PRs!)**' | |
- name: Post sticky pull request comment | |
if: matrix.environment == 'Review' | |
uses: marocchino/sticky-pull-request-comment@v2 | |
with: | |
recreate: true | |
header: PR | |
message: | | |
Review app deployed to https://${{env.REVIEW_APPLICATION}}-${{github.event.number}}.${{env.REVIEW_DOMAIN}} | |
${{ steps.dsiMessage.outputs.value }} | |
- name: Add Review Label | |
if: matrix.environment == 'Review' && contains(github.event.pull_request.user.login, 'dependabot') == false | |
uses: actions-ecosystem/action-add-labels@v1 | |
with: | |
github_token: ${{ secrets.GITHUB_TOKEN }} | |
labels: Review | |
- name: Get Release Id from Tag | |
id: tag_id | |
uses: DFE-Digital/github-actions/DraftReleaseByTag@master | |
with: | |
TAG: ${{needs.prepare.outputs.release_tag}} | |
TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
- name: Publish Release | |
if: matrix.environment == 'Production' && steps.tag_id.outputs.release_id | |
uses: eregon/publish-release@v1 | |
env: | |
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
with: | |
release_id: ${{steps.tag_id.outputs.release_id}} | |
- name: Fetch slack token | |
uses: azure/CLI@v2 | |
id: fetch-slack-secret | |
with: | |
inlineScript: | | |
SECRET_VALUE=$(az keyvault secret show --name "SLACK-WEBHOOK" --vault-name "${{ secrets.INF_KEY_VAULT}}" --query "value" -o tsv) | |
echo "::add-mask::$SECRET_VALUE" | |
echo "SLACK-WEBHOOK=$SECRET_VALUE" >> $GITHUB_OUTPUT | |
- name: Slack Release Notification | |
if: matrix.environment == 'Production' && steps.tag_id.outputs.release_id | |
uses: rtCamp/action-slack-notify@master | |
env: | |
SLACK_COLOR: ${{env.SLACK_SUCCESS}} | |
SLACK_MESSAGE: ${{ fromJson(steps.tag_id.outputs.release_body) }} | |
SLACK_TITLE: "Release Published: ${{steps.tag_id.outputs.release_name}}" | |
SLACK_WEBHOOK: "${{steps.fetch-slack-secret.outputs.SLACK-WEBHOOK}}" | |
MSG_MINIMAL: true | |
- name: Slack Notification | |
if: failure() && matrix.environment == 'Production' | |
uses: rtCamp/action-slack-notify@master | |
env: | |
SLACK_COLOR: ${{env.SLACK_ERROR}} | |
SLACK_TITLE: Failure in Post-Development Deploy | |
SLACK_MESSAGE: Failure with initialising ${{matrix.environment}} deployment for ${{env.APPLICATION}} | |
SLACK_WEBHOOK: "${{steps.fetch-slack-secret.outputs.SLACK-WEBHOOK}}" | |
owasp: | |
name: 'OWASP Test' | |
runs-on: ubuntu-latest | |
needs: [deployments ] | |
if: github.event_name == 'push' && github.ref == 'refs/heads/master' | |
steps: | |
- name: Checkout | |
uses: actions/checkout@v4 | |
- name: set-up-environment | |
uses: DFE-Digital/github-actions/set-up-environment@master | |
- uses: Azure/login@v2 | |
with: | |
creds: ${{ secrets.GSE_REPO_AZ_CREDENTIALS }} | |
- name: Fetch SECURE USERNAME | |
uses: azure/CLI@v2 | |
id: fetch-username | |
with: | |
inlineScript: | | |
SECRET_VALUE=$(az keyvault secret show --name "SECURE-USERNAME" --vault-name "${{ secrets.APP_KEY_VAULT}}" --query "value" -o tsv) | |
echo "::add-mask::$SECRET_VALUE" | |
echo "SECURE_USERNAME=$SECRET_VALUE" >> $GITHUB_OUTPUT | |
- name: Fetch SECURE PASSWORD | |
uses: azure/CLI@v2 | |
id: fetch-password | |
with: | |
inlineScript: | | |
SECRET_VALUE=$(az keyvault secret show --name "SECURE-PASSWORD" --vault-name "${{ secrets.APP_KEY_VAULT}}" --query "value" -o tsv) | |
echo "::add-mask::$SECRET_VALUE" | |
echo "SECURE_PASSWORD=$SECRET_VALUE" >> $GITHUB_OUTPUT | |
- name: ZAP Scan | |
uses: zaproxy/[email protected] | |
with: | |
token: ${{ secrets.GITHUB_TOKEN }} | |
docker_name: 'owasp/zap2docker-stable' | |
target: 'https://${{ steps.fetch-username.outputs.SECURE_USERNAME}}:${{ steps.fetch-password.outputs.SECURE_PASSWORD }}@${{env.APPLICATION_NAME}}-development.${{env.REVIEW_DOMAIN}}' | |
rules_file_name: '.zap/rules.tsv' | |
cmd_options: '-a' | |
- name: Fetch secrets from key vault | |
uses: azure/CLI@v2 | |
id: fetch-slack-secret | |
with: | |
inlineScript: | | |
SECRET_VALUE=$(az keyvault secret show --name "SLACK-WEBHOOK" --vault-name "${{ secrets.INF_KEY_VAULT}}" --query "value" -o tsv) | |
echo "::add-mask::$SECRET_VALUE" | |
echo "SLACK-WEBHOOK=$SECRET_VALUE" >> $GITHUB_OUTPUT | |
- name: Slack Notification | |
if: failure() | |
uses: rtCamp/action-slack-notify@master | |
env: | |
SLACK_COLOR: ${{env.SLACK_FAILURE}} | |
SLACK_MESSAGE: 'Pipeline Failure carrying out OWASP Testing on https://${{env.APPLICATION_NAME}}-development.${{env.REVIEW_DOMAIN}}/' | |
SLACK_TITLE: 'Failure: OWSAP Testing has failed on Development' | |
SLACK_WEBHOOK: "${{ steps.fetch-slack-secret.outputs.SLACK-WEBHOOK}}" |