Skip to content

Update CI workflow name: #539

Update CI workflow name:

Update CI workflow name: #539

Workflow file for this run

name: Main
on:
push:
env:
KOSLI_DRY_RUN: ${{ vars.KOSLI_DRY_RUN }} # false
KOSLI_HOST: ${{ vars.KOSLI_HOST }} # https://app.kosli.com
KOSLI_ORG: ${{ vars.KOSLI_ORG }} # cyber-dojo
KOSLI_FLOW: ${{ vars.KOSLI_FLOW }} # differ-ci
KOSLI_API_TOKEN: ${{ secrets.KOSLI_API_TOKEN }}
KOSLI_TRAIL: ${{ github.sha }}
SERVICE_NAME: ${{ github.event.repository.name }} # differ
AWS_ACCOUNT_ID: ${{ vars.AWS_ACCOUNT_ID }}
AWS_ECR_ID: ${{ vars.AWS_ECR_ID }}
AWS_REGION: ${{ vars.AWS_REGION }}
IMAGE_TAR_FILENAME: /tmp/${{ github.event.repository.name }}:${{ github.sha }}.tar
SONARCLOUD_TOKEN: ${{ secrets.SONARCLOUD_TOKEN }}
SONARCLOUD_PROJECT_KEY: ${{ github.repository_owner }}_${{ github.event.repository.name }} #cyber-dojo_differ
jobs:
setup:
runs-on: ubuntu-latest
outputs:
aws_account_id: ${{ steps.vars.outputs.aws_account_id }}
ecr_registry: ${{ steps.vars.outputs.ecr_registry }}
aws_region: ${{ steps.vars.outputs.aws_region }}
gh_actions_iam_role_name: ${{ steps.vars.outputs.gh_actions_iam_role_name }}
service_name: ${{ steps.vars.outputs.service_name }}
image_tag: ${{ steps.vars.outputs.image_tag }}
image_name: ${{ steps.vars.outputs.image_name }}
image_name_previous: ${{ steps.vars.outputs.image_name_previous }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 2
- name: Prepare outputs for workflow jobs
id: vars
run: |
IMAGE_TAG=${GITHUB_SHA:0:7}
IMAGE_TAG_PREVIOUS=$(git rev-parse --short=7 HEAD^)
ECR_REGISTRY="${AWS_ECR_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com"
IMAGE_NAME="${ECR_REGISTRY}/${{ env.SERVICE_NAME }}:${IMAGE_TAG}"
IMAGE_NAME_PREVIOUS="${ECR_REGISTRY}/${{ env.SERVICE_NAME }}:${IMAGE_TAG_PREVIOUS}"
echo "aws_account_id=${AWS_ACCOUNT_ID}" >> ${GITHUB_OUTPUT}
echo "ecr_registry=${ECR_REGISTRY}" >> ${GITHUB_OUTPUT}
echo "aws_region=${AWS_REGION}" >> ${GITHUB_OUTPUT}
echo "gh_actions_iam_role_name=gh_actions_services" >> ${GITHUB_OUTPUT}
echo "service_name=${{ env.SERVICE_NAME }}" >> ${GITHUB_OUTPUT}
echo "image_tag=${IMAGE_TAG}" >> ${GITHUB_OUTPUT}
echo "image_name=${IMAGE_NAME}" >> ${GITHUB_OUTPUT}
echo "image_name_previous=${IMAGE_NAME_PREVIOUS}" >> ${GITHUB_OUTPUT}
- name: Setup Kosli CLI
if: ${{ github.ref == 'refs/heads/main' }}
uses: kosli-dev/setup-cli-action@v2
with:
version: ${{ vars.KOSLI_CLI_VERSION }}
- name: Begin Kosli Trail
if: ${{ github.ref == 'refs/heads/main' }}
run:
kosli begin trail "${{ env.KOSLI_TRAIL }}"
--flow="${{ env.KOSLI_FLOW }}"
--description="Diff files from two traffic-lights"
--template-file=.kosli.yml
pull-request:
if: ${{ github.ref == 'refs/heads/main' }}
runs-on: ubuntu-latest
needs: []
permissions:
id-token: write
contents: read
pull-requests: read
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 1
- name: Setup Kosli CLI
uses: kosli-dev/setup-cli-action@v2
with:
version: ${{ vars.KOSLI_CLI_VERSION }}
- name: Attest pull-request evidence to Kosli
run:
kosli attest pullrequest github
--github-token=${{ secrets.GITHUB_TOKEN }}
--name=pull-request
rubocop-lint:
runs-on: ubuntu-latest
needs: []
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 1
- name: Run Rubocop linter on source
id: lint
run:
make rubocop_lint
- name: Setup Kosli CLI
if: ${{ github.ref == 'refs/heads/main' && (success() || failure()) }}
uses: kosli-dev/setup-cli-action@v2
with:
version: ${{ vars.KOSLI_CLI_VERSION }}
- name: Attest evidence to Kosli
if: ${{ github.ref == 'refs/heads/main' && (success() || failure()) }}
run: |
KOSLI_COMPLIANT=$([ "${{ steps.lint.outcome }}" == 'success' ] && echo true || echo false)
kosli attest generic \
--name=differ.rubocop-lint
snyk-code-scan:
runs-on: ubuntu-latest
needs: []
env:
SARIF_FILENAME: snyk.code.scan.json
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 1
- name: Setup Snyk
uses: snyk/actions/setup@master
- name: Run Snyk code scan
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
run:
snyk code test
--policy-path=.snyk
--sarif
--sarif-file-output="${SARIF_FILENAME}"
.
- name: Setup Kosli CLI
if: ${{ github.ref == 'refs/heads/main' && (success() || failure()) }}
uses: kosli-dev/setup-cli-action@v2
with:
version: ${{ vars.KOSLI_CLI_VERSION }}
- name: Attest evidence to Kosli
if: ${{ github.ref == 'refs/heads/main' && (success() || failure()) }}
run:
kosli attest snyk
--name=differ.snyk-code-scan
--scan-results="${SARIF_FILENAME}"
build-image:
runs-on: ubuntu-latest
needs: [setup]
env:
IMAGE_NAME: ${{ needs.setup.outputs.image_name }}
permissions:
id-token: write
contents: write
outputs:
artifact_digest: ${{ steps.variables.outputs.artifact_digest }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 1
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
aws-region: ${{ needs.setup.outputs.aws_region }}
role-duration-seconds: 900
role-session-name: ${{ github.event.repository.name }}
role-to-assume: arn:aws:iam::${{ needs.setup.outputs.aws_account_id }}:role/${{ needs.setup.outputs.gh_actions_iam_role_name }}
mask-aws-account-id: 'no'
- name: Login to Amazon ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v2
- name: Build and push Docker image to ECR
id: docker_build
uses: docker/build-push-action@v6
with:
context: .
push: true
tags: ${{ env.IMAGE_NAME }}
cache-from: type=registry,ref=${{ needs.setup.outputs.image_name_previous }}
cache-to: type=inline,mode=max
build-args:
COMMIT_SHA=${{ github.sha }}
- name: Make Artifact fingerprint available to following jobs
id: variables
run: |
FINGERPRINT=$(echo ${{ steps.docker_build.outputs.digest }} | sed 's/.*://')
echo "artifact_digest=${FINGERPRINT}" >> ${GITHUB_OUTPUT}
- name: Tar Docker image
run: |
docker pull ${{ env.IMAGE_NAME }}
docker image save ${{ env.IMAGE_NAME }} --output ${{ env.IMAGE_TAR_FILENAME }}
- name: Cache Docker image
uses: actions/cache@v4
with:
path: ${{ env.IMAGE_TAR_FILENAME }}
key: ${{ env.IMAGE_NAME }}
- name: Setup Kosli CLI
if: ${{ github.ref == 'refs/heads/main' }}
uses: kosli-dev/setup-cli-action@v2
with:
version: ${{ vars.KOSLI_CLI_VERSION }}
- name: Attest image provenance to Kosli
if: ${{ github.ref == 'refs/heads/main' }}
run:
kosli attest artifact "${{ env.IMAGE_NAME }}"
--artifact-type=docker
--name=differ
unit-tests:
runs-on: ubuntu-latest
needs: [setup, build-image]
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 1
- name: Retrieve Docker image from cache
uses: actions/cache@v4
with:
path: ${{ env.IMAGE_TAR_FILENAME }}
key: ${{ needs.setup.outputs.image_name }}
- name: Load Docker image
run:
docker image load --input ${{ env.IMAGE_TAR_FILENAME }}
- name: Run unit tests
run:
make test_server
- name: Get unit test coverage
id: coverage
run:
make coverage_server
- name: Setup Kosli CLI
if: ${{ github.ref == 'refs/heads/main' && (success() || failure()) }}
uses: kosli-dev/setup-cli-action@v2
with:
version: ${{ vars.KOSLI_CLI_VERSION }}
- name: Attest junit test evidence to Kosli
if: ${{ github.ref == 'refs/heads/main' && (success() || failure()) }}
env:
KOSLI_FINGERPRINT: ${{ needs.build-image.outputs.artifact_digest }}
run:
kosli attest junit
--name=differ.unit-test
--results-dir=./reports/server/junit
- name: Attest coverage evidence to Kosli
if: ${{ github.ref == 'refs/heads/main' && (success() || failure()) }}
env:
KOSLI_FINGERPRINT: ${{ needs.build-image.outputs.artifact_digest }}
run: |
KOSLI_COMPLIANT=$([ "${{ steps.coverage.outcome }}" == 'success' ] && echo true || echo false)
kosli attest generic \
--description="unit-test branch-coverage and metrics" \
--name=differ.unit-test-coverage \
--user-data=./reports/server/coverage.json
integration-tests:
runs-on: ubuntu-latest
needs: [setup, build-image]
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 1
- name: Retrieve Docker image from cache
uses: actions/cache@v4
with:
path: ${{ env.IMAGE_TAR_FILENAME }}
key: ${{ needs.setup.outputs.image_name }}
- name: Load Docker image
run:
docker image load --input ${{ env.IMAGE_TAR_FILENAME }}
- name: Run integration tests
run:
make test_client
- name: Get integration test coverage
id: coverage
run:
make coverage_client
- name: Setup Kosli CLI
if: ${{ github.ref == 'refs/heads/main' && (success() || failure()) }}
uses: kosli-dev/setup-cli-action@v2
with:
version: ${{ vars.KOSLI_CLI_VERSION }}
- name: Attest junit test evidence to Kosli
if: ${{ github.ref == 'refs/heads/main' && (success() || failure()) }}
env:
KOSLI_FINGERPRINT: ${{ needs.build-image.outputs.artifact_digest }}
run:
kosli attest junit
--name=differ.integration-test
--results-dir=./reports/client/junit
- name: Attest coverage evidence to Kosli
if: ${{ github.ref == 'refs/heads/main' && (success() || failure()) }}
env:
KOSLI_FINGERPRINT: ${{ needs.build-image.outputs.artifact_digest }}
run: |
KOSLI_COMPLIANT=$([ "${{ steps.coverage.outcome }}" == 'success' ] && echo true || echo false)
kosli attest generic \
--description="integration-test branch-coverage and metrics" \
--name=differ.integration-test-coverage \
--user-data=./reports/client/coverage.json
snyk-container-scan:
runs-on: ubuntu-latest
needs: [setup, build-image]
env:
SARIF_FILENAME: snyk.container.scan.json
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 1
- name: Retrieve Docker image from cache
uses: actions/cache@v4
with:
path: ${{ env.IMAGE_TAR_FILENAME }}
key: ${{ needs.setup.outputs.image_name }}
- name: Load Docker image
run:
docker image load --input ${{ env.IMAGE_TAR_FILENAME }}
- name: Setup Snyk
uses: snyk/actions/setup@master
- name: Run Snyk container scan
env:
IMAGE_NAME: ${{ needs.setup.outputs.image_name }}
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
run:
snyk container test ${IMAGE_NAME}
--file=Dockerfile
--policy-path=.snyk
--sarif
--sarif-file-output="${SARIF_FILENAME}"
- name: Setup Kosli CLI
if: ${{ github.ref == 'refs/heads/main' && (success() || failure()) }}
uses: kosli-dev/setup-cli-action@v2
with:
version: ${{ vars.KOSLI_CLI_VERSION }}
- name: Attest evidence to Kosli
if: ${{ github.ref == 'refs/heads/main' && (success() || failure()) }}
env:
KOSLI_FINGERPRINT: ${{ needs.build-image.outputs.artifact_digest }}
run:
kosli attest snyk
--name=differ.snyk-container-scan
--scan-results="${SARIF_FILENAME}"
sonarcloud-scan:
runs-on: ubuntu-latest
needs: [build-image]
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Run SonarCloud scan - report results to Kosli (in Kosli webhook)
env:
SONAR_TOKEN: ${{ env.SONARCLOUD_TOKEN }}
uses: sonarsource/sonarcloud-github-action@master
with:
args: >
-Dsonar.analysis.kosli_flow=${{ env.KOSLI_FLOW }}
-Dsonar.analysis.kosli_trail=${{ env.KOSLI_TRAIL }}
-Dsonar.analysis.kosli_artifact_fingerprint=${{ needs.build-image.outputs.artifact_digest }}
-Dsonar.analysis.kosli_attestation=sonarcloud-scan
sdlc-control-gate:
if: ${{ github.ref == 'refs/heads/main' }}
runs-on: ubuntu-latest
needs: [setup, build-image, pull-request, rubocop-lint, unit-tests, integration-tests, snyk-container-scan, snyk-code-scan, sonarcloud-scan]
steps:
- name: Setup Kosli CLI
uses: kosli-dev/setup-cli-action@v2
with:
version: ${{ vars.KOSLI_CLI_VERSION }}
- name: Kosli SDLC gate to short-circuit the workflow
env:
IMAGE_NAME: ${{ needs.setup.outputs.image_name }}
KOSLI_FINGERPRINT: ${{ needs.build-image.outputs.artifact_digest }}
run:
kosli assert artifact ${IMAGE_NAME}
approve-deployment-to-beta:
runs-on: ubuntu-latest
needs: [setup, build-image, sdlc-control-gate]
environment:
name: staging
url: https://beta.cyber-dojo.org
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Kosli CLI
uses: kosli-dev/setup-cli-action@v2
with:
version: ${{ vars.KOSLI_CLI_VERSION }}
- name: Attest approval of deployment to Kosli
env:
IMAGE_NAME: ${{ needs.setup.outputs.image_name }}
KOSLI_FINGERPRINT: ${{ needs.build-image.outputs.artifact_digest }}
KOSLI_ENVIRONMENT: aws-beta
run:
kosli report approval ${IMAGE_NAME}
--approver="${{ github.actor }}"
deploy-to-beta:
needs: [setup, approve-deployment-to-beta]
uses: ./.github/workflows/sub_deploy_to_beta.yml
with:
IMAGE_TAG: ${{ needs.setup.outputs.image_tag }}
attest-beta-artifacts:
needs: [deploy-to-beta]
uses: ./.github/workflows/attest_tf_beta_artifacts.yml
secrets:
KOSLI_API_TOKEN: ${{ secrets.KOSLI_API_TOKEN }}
approve-deployment-to-prod:
runs-on: ubuntu-latest
needs: [setup, build-image, attest-beta-artifacts]
environment:
name: production
url: https://cyber-dojo.org
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Kosli CLI
uses: kosli-dev/setup-cli-action@v2
with:
version: ${{ vars.KOSLI_CLI_VERSION }}
- name: Attest approval of deployment to Kosli
env:
IMAGE_NAME: ${{ needs.setup.outputs.image_name }}
KOSLI_FINGERPRINT: ${{ needs.build-image.outputs.artifact_digest }}
KOSLI_ENVIRONMENT: aws-prod
run:
kosli report approval ${IMAGE_NAME}
--approver="${{ github.actor }}"
deploy-to-prod:
needs: [setup, approve-deployment-to-prod]
uses: ./.github/workflows/sub_deploy_to_prod.yml
with:
IMAGE_TAG: ${{ needs.setup.outputs.image_tag }}
attest-prod-artifacts:
needs: [deploy-to-prod]
uses: ./.github/workflows/attest_tf_prod_artifacts.yml
secrets:
KOSLI_API_TOKEN: ${{ secrets.KOSLI_API_TOKEN }}
# The cyberdojo/versioner refresh-env.sh script
# https://github.com/cyber-dojo/versioner/blob/master/sh/refresh-env.sh
# relies on being able to:
# - get the :latest image
# - extract the SHA env-var embedded inside it
# - use the 1st 7 chars of the SHA as a latest-equivalent tag
push-latest:
runs-on: ubuntu-latest
needs: [setup, attest-prod-artifacts]
permissions:
id-token: write
contents: write
steps:
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
aws-region: ${{ needs.setup.outputs.aws_region }}
role-duration-seconds: 900
role-session-name: ${{ github.event.repository.name }}
role-to-assume: arn:aws:iam::${{ needs.setup.outputs.aws_account_id }}:role/${{ needs.setup.outputs.gh_actions_iam_role_name }}
mask-aws-account-id: 'no'
- name: Login to Amazon ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v2
- uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USER }}
password: ${{ secrets.DOCKER_PASS }}
- name: Tag image to :latest and push to Dockerhub Registry
env:
IMAGE_NAME: ${{ needs.setup.outputs.image_name }}
IMAGE_TAG: ${{ needs.setup.outputs.image_tag }}
run: |
docker pull "${IMAGE_NAME}"
docker tag "${IMAGE_NAME}" cyberdojo/${{ env.SERVICE_NAME }}:${IMAGE_TAG}
docker tag "${IMAGE_NAME}" cyberdojo/${{ env.SERVICE_NAME }}:latest
docker push cyberdojo/${{ env.SERVICE_NAME }}:${IMAGE_TAG}
docker push cyberdojo/${{ env.SERVICE_NAME }}:latest