Skip to content

MSE Copy Updates

MSE Copy Updates #4005

Workflow file for this run

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@v1
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@v1
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@v1
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@v1
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@v1
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@v1
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@v1
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@v1
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@v1
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@v1
with:
creds: ${{ secrets.AZURE_CREDENTIALS }}
- name: Fetch SECURE USERNAME
uses: azure/CLI@v1
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@v1
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@v1
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@v1
with:
creds: ${{ secrets.GSE_REPO_AZ_CREDENTIALS }}
- name: Fetch SECURE USERNAME
uses: azure/CLI@v1
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@v1
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@v1
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}}"