Build and Deploy with Evidence and Policy Check #35
This file contains hidden or 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 with Evidence and Policy Check | |
| on: | |
| [workflow_dispatch] | |
| permissions: | |
| id-token: write | |
| contents: read | |
| # Global variables | |
| env: | |
| # Name for the Docker image | |
| IMAGE_NAME: example-project-image | |
| # Name of the Docker Registry; i.e., the JFrog Cloud server base URL | |
| DOKCER_REGISTRY: tomjfrog.jfrog.io | |
| # Virtual repository for the Docker image build and deploy operations | |
| DOCKER_VIRTUAL: evdemo-docker-virtual | |
| # Local Docker repository member of the virtual repository. Required for Evidence attachment | |
| DOCKER_LOCAL: evdemo-docker-local | |
| # Name of the generic repo for the README file | |
| GENERIC_LOCAL: evdemo-generic-local | |
| # GENERIC_QA_LOCAL: evdemo-generic-local | |
| # Name of the signing key residing in the JFrog Artifactory | |
| PRIVATE_KEY_NAME: ${{ secrets.PRIVATE_KEY_NAME }} | |
| # Name of the Docker Metadata file | |
| DOCKER_METADATA: build-metadata | |
| # Name of the Build | |
| BUILD_NAME: evdemo-build | |
| # name of the Release bundle | |
| BUNDLE_NAME: evdemo-bundle-two | |
| # Public Key Alias Name of the "Public Key" uploaded to the JFrog Platform under "Keys Management" | |
| # Used to verify the signature on attached evidence | |
| KEY_ALIAS: evidence-key | |
| # Project Name | |
| JF_PROJECT: ${{ vars.JF_PROJECT }} | |
| # Sonar Cloud Token | |
| SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} | |
| # Sonar Cloud Organization | |
| SONAR_ORGANIZATION: tomjfrog | |
| # Sonar Cloud Project Key | |
| SONAR_PROJECT_KEY: tomjfrog_Evidence-Examples | |
| # SonarQube Scan Result File Name | |
| SONAR_RESULT_FILE: sonar-results.json | |
| jobs: | |
| Docker-Build-With-Evidence: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Install JFrog CLI | |
| uses: jfrog/setup-jfrog-cli@v4 | |
| id: cli | |
| env: | |
| JF_URL: ${{ vars.JF_URL }} | |
| # JF_PROJECT: ${{ vars.JF_URL }} | |
| with: | |
| oidc-provider-name: github-oidc-integration | |
| oidc-audience: jfrog-github | |
| disable-auto-build-publish: true | |
| - uses: actions/checkout@v4 | |
| - name: Log in to Artifactory Docker Registry | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ${{ vars.ARTIFACTORY_URL }} | |
| username: ${{ steps.cli.outputs.oidc-user }} | |
| password: ${{ steps.cli.outputs.oidc-token }} | |
| - name: Set up QEMU | |
| id: setup-qemu | |
| uses: docker/setup-qemu-action@v3 | |
| - name: Set up Docker Buildx | |
| id: setup-buildx | |
| uses: docker/setup-buildx-action@v3 | |
| with: | |
| platforms: linux/amd64,linux/arm64 | |
| install: true | |
| - name: Setup Signing Key | |
| id: setup-signing-key | |
| run: | | |
| echo "${{ secrets.PRIVATE_KEY_EXPORT }}" > "${{ secrets.PRIVATE_KEY_NAME }}" | |
| chmod 600 ${{ secrets.PRIVATE_KEY_NAME }} | |
| openssl rsa -in ${{ secrets.PRIVATE_KEY_NAME }} -check | |
| - name: Install SonarQube Scanner | |
| run: | | |
| curl -sL -sSLo sonar-scanner.zip https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-6.2.1.4610.zip | |
| unzip sonar-scanner.zip | |
| export PATH=$PATH:$PWD/sonar-scanner-6.2.1.4610/bin | |
| pwd | |
| ls -l $PWD/sonar-scanner-6.2.1.4610/bin/ | |
| echo "$PWD/sonar-scanner-6.2.1.4610/bin" | |
| - name: Set up JDK 17 | |
| uses: actions/setup-java@v4 | |
| with: | |
| java-version: '21' # Specify the desired Java version here | |
| distribution: 'temurin' # You can also use 'temurin', 'zulu', etc. | |
| - name: Build Docker image | |
| run: | | |
| URL=$(echo ${{ vars.ARTIFACTORY_URL }} | sed 's|^https://||') | |
| REPO_URL=${URL}'/${{ env.DOCKER_VIRTUAL}}' | |
| docker build \ | |
| --build-arg REPO_URL=${REPO_URL} \ | |
| -f Dockerfile . \ | |
| --tag ${REPO_URL}/${{ env.IMAGE_NAME }}:${{ github.run_number }} \ | |
| --output=type=image \ | |
| --platform linux/amd64 \ | |
| --metadata-file=${{ env.DOCKER_METADATA }} \ | |
| --push | |
| - name: Create Docker Build Info | |
| run: | | |
| jf rt build-docker-create ${{ vars.DOCKER_VIRTUAL}} \ | |
| --image-file ${{ env.DOCKER_METADATA }} \ | |
| --build-name ${{ vars.BUILD_NAME }} \ | |
| --build-number ${{ github.run_number }} | |
| - name: Run SonarScanner | |
| id: run-sonar-scanner | |
| env: | |
| SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} | |
| run: | | |
| $PWD/sonar-scanner-6.2.1.4610/bin/sonar-scanner \ | |
| -Dsonar.projectKey=${{ env.SONAR_PROJECT_KEY }} \ | |
| -Dsonar.organization=${{ env.SONAR_ORGANIZATION }} \ | |
| -Dsonar.host.url=https://sonarcloud.io \ | |
| -Dsonar.java.jdkHome=$JAVA_HOME \ | |
| -Dsonar.verbose=true \ | |
| -Dsonar.token=${{ env.SONAR_TOKEN }} | |
| # create evidence from sonar-scan analysis | |
| set +e | |
| # --FailOnAnalysisFailure causes a failure on gateway-failed sonar analysis | |
| cat $PWD/.scannerwork/report-task.txt | |
| EXIT_CODE=$? | |
| set -e | |
| # write the exit code to the github output so that it can be used in the evidence creation step | |
| # echo "------${{ env.SONAR_RESULT_FILE }}------" | |
| # cat ${{ env.SONAR_RESULT_FILE }} | |
| echo "------EXIT------" | |
| echo "create-sonar-evidence=$EXIT_CODE" | |
| echo "create-sonar-evidence=$EXIT_CODE" >> $GITHUB_OUTPUT | |
| - name: Fetch Sonar CE Task Result | |
| env: | |
| SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} | |
| run: | | |
| set -euo pipefail | |
| INPUT_FILE=$PWD/.scannerwork/report-task.txt | |
| OUTPUT_FILE=${{ env.SONAR_RESULT_FILE }} | |
| # Extract the ceTaskUrl from the report file | |
| CE_TASK_URL=$(grep '^ceTaskUrl=' "$INPUT_FILE" | cut -d= -f2-) | |
| if [[ -z "$CE_TASK_URL" ]]; then | |
| echo "Error: ceTaskUrl not found in $INPUT_FILE" >&2 | |
| exit 1 | |
| fi | |
| # Fetch the CE task details and save as JSON | |
| curl -sS \ | |
| -u "${SONAR_TOKEN}:" \ | |
| -H "Accept: application/json" \ | |
| "$CE_TASK_URL" \ | |
| -o "$OUTPUT_FILE" | |
| echo "✅ Fetched Sonar task details into $OUTPUT_FILE" | |
| - name: Create and Attach Evidence on Docker Image | |
| run: | | |
| echo '{ "actor": "${{ github.actor }}", "date": "'$(date -u +"%Y-%m-%dT%H:%M:%SZ")'" }' > sign.json | |
| jf evd create \ | |
| --package-name ${{ env.IMAGE_NAME }} \ | |
| --package-version ${{ github.run_number }} \ | |
| --package-repo-name ${{ env.DOCKER_LOCAL }} \ | |
| --key "${{ env.PRIVATE_KEY_NAME }}" \ | |
| --key-alias ${{ env.KEY_ALIAS }} \ | |
| --predicate ./sign.json \ | |
| --predicate-type https://jfrog.com/evidence/foo/v1 | |
| echo '🔎 Evidence attached: `signature` 🔏 ' | |
| - name: Upload README file | |
| run: | | |
| jf rt upload ./README.md ${{ env.GENERIC_LOCAL }}/readme/${{ github.run_number }}/ \ | |
| --build-name ${{ vars.BUILD_NAME }} \ | |
| --build-number ${{ github.run_number }} | |
| - name: Attach Evidence on README file | |
| run: | | |
| jf evd create \ | |
| --subject-repo-path ${{ env.GENERIC_LOCAL }}/readme/${{ github.run_number }}/README.md \ | |
| --key "${{ env.PRIVATE_KEY_NAME }}" \ | |
| --key-alias ${{ env.KEY_ALIAS }} \ | |
| --predicate ./sign.json \ | |
| --predicate-type https://jfrog.com/evidence/signature/v1 \ | |
| --project ${{ env.JF_PROJECT }} \ | |
| - name: Decorate Build Info with Git Context | |
| run: jf rt build-add-git ${{ vars.BUILD_NAME }} ${{ github.run_number }} | |
| - name: Decorate Build Info with Environment Variables | |
| run: jf rt build-collect-env ${{ vars.BUILD_NAME }} ${{ github.run_number }} | |
| - name: Publish Build Info | |
| run: jfrog rt build-publish ${{ vars.BUILD_NAME }} ${{ github.run_number }} | |
| - name: Create and Attach Build Actor & Timestamp Evidence | |
| run: | | |
| echo '{ "actor": "${{ github.actor }}", "date": "'$(date -u +"%Y-%m-%dT%H:%M:%SZ")'" }' > sign.json | |
| jf evd create \ | |
| --build-name ${{ vars.BUILD_NAME }} \ | |
| --build-number ${{ github.run_number }} \ | |
| --predicate ./sign.json --predicate-type https://jfrog.com/evidence/build-signature/v1 \ | |
| --key "${{ secrets.PRIVATE_KEY_NAME }}" \ | |
| --key-alias ${{ env.KEY_ALIAS }} \ | |
| --project ${{ env.JF_PROJECT }} | |
| echo '🔎 Evidence attached: `build-signature` 🔏 ' >> $GITHUB_STEP_SUMMARY | |
| - name: Attach SonarQube Scan Result Evidence To Build Info | |
| if: ${{ steps.run-sonar-scanner.outputs.create-sonar-evidence == 0 }} | |
| run: | | |
| # Attach evidence onto build using JFrog CLI | |
| jf evd create \ | |
| --build-name ${{ vars.BUILD_NAME }} \ | |
| --build-number "${{ github.run_number }}" \ | |
| --predicate ${{ env.SONAR_RESULT_FILE }} \ | |
| --predicate-type https://jfrog.com/evidence/sonar-scan/v1 \ | |
| --key "${{ secrets.PRIVATE_KEY_NAME }}" \ | |
| --key-alias ${{ env.KEY_ALIAS }} \ | |
| --project ${{ env.JF_PROJECT }} | |
| - name: Create Release Bundle | |
| run: | | |
| echo '{ "files": [ {"build": "'"${{ vars.BUILD_NAME }}/${{ github.run_number }}"'", "project": "'"${{ env.JF_PROJECT }}"'" } ] }' > bundle-spec.json | |
| jf release-bundle-create ${{ env.BUNDLE_NAME }} ${{ github.run_number }} \ | |
| --signing-key ${{ secrets.GPG_KEY_NAME }} \ | |
| --spec bundle-spec.json \ | |
| --sync=true \ | |
| --project ${{ env.JF_PROJECT }} | |
| NAME_LINK=${{ vars.ARTIFACTORY_URL }}'/ui/artifactory/lifecycle/?bundleName='${{ env.BUNDLE_NAME }}'&bundleToFlash='${{ env.BUNDLE_NAME }}'&repositoryKey=release-bundles-v2&activeKanbanTab=promotion' | |
| VER_LINK=${{ vars.ARTIFACTORY_URL }}'/ui/artifactory/lifecycle/?bundleName='${{ env.BUNDLE_NAME }}'&bundleToFlash='${{ env.BUNDLE_NAME }}'&releaseBundleVersion='${{ github.run_number }}'&repositoryKey=release-bundles-v2&activeVersionTab=Version%20Timeline&activeKanbanTab=promotion' | |
| echo '📦 Release bundle ['${{ env.BUNDLE_NAME }}']('${NAME_LINK}'):['${{ github.run_number }}']('${VER_LINK}') created' >> $GITHUB_STEP_SUMMARY | |
| Promote-To-QA-And-Test: | |
| needs: Docker-Build-With-Evidence | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Install JFrog CLI | |
| uses: jfrog/setup-jfrog-cli@v4 | |
| id: cli | |
| env: | |
| JF_URL: ${{ vars.JF_URL }} | |
| JF_PROJECT: ${{ vars.PROJECT_NAME }} | |
| with: | |
| oidc-provider-name: github-oidc-integration | |
| oidc-audience: jfrog-github | |
| disable-auto-build-publish: true | |
| - name: Promote to QA | |
| run: | | |
| jf release-bundle-promote ${{ env.BUNDLE_NAME }} ${{ github.run_number }} QA \ | |
| --signing-key ${{ secrets.GPG_KEY_NAME }} \ | |
| --sync=true \ | |
| --project ${{ env.JF_PROJECT }} | |
| echo "🚀 Succesfully promote to \`QA\` environemnt" >> $GITHUB_STEP_SUMMARY | |
| - name: Setup Signing Key | |
| id: setup-signing-key | |
| run: | | |
| echo "${{ secrets.PRIVATE_KEY_EXPORT }}" > "${{ secrets.PRIVATE_KEY_NAME }}" | |
| chmod 600 ${{ secrets.PRIVATE_KEY_NAME }} | |
| openssl rsa -in ${{ secrets.PRIVATE_KEY_NAME }} -check | |
| - name: Attach Evidence on Release Bundle | |
| run: | | |
| echo '{ "actor": "${{ github.actor }}", "date": "'$(date -u +"%Y-%m-%dT%H:%M:%SZ")'", "test": "CI test", "result": "success" }' > test_evidence.json | |
| JF_LINK=${{ vars.ARTIFACTORY_URL }}'/ui/artifactory/lifecycle/?bundleName='${{ env.BUNDLE_NAME }}'&bundleToFlash='${{ env.BUNDLE_NAME }}'&releaseBundleVersion='${{ github.run_number }}'&repositoryKey=release-bundles-v2&activeVersionTab=Version%20Timeline&activeKanbanTab=promotion' | |
| echo 'Test on Release bundle ['${{ env.BUNDLE_NAME }}':'${{ github.run_number }}']('${JF_LINK}') success' >> $GITHUB_STEP_SUMMARY | |
| jf evd create \ | |
| --release-bundle ${{ env.BUNDLE_NAME }} \ | |
| --release-bundle-version ${{ github.run_number }} \ | |
| --predicate ./test_evidence.json \ | |
| --predicate-type https://jfrog.com/evidence/testing-results/v1 \ | |
| --key ${{ env.PRIVATE_KEY_NAME }} \ | |
| --key-alias ${{ env.KEY_ALIAS }} \ | |
| --project ${{ env.JF_PROJECT }} | |
| echo '🔎 Evidence attached: integration-test 🧪 ' >> $GITHUB_STEP_SUMMARY | |
| Policy-Check-And-Promote-To-Prod: | |
| needs: Promote-To-QA-And-Test | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Install JFrog CLI | |
| uses: jfrog/setup-jfrog-cli@v4 | |
| id: cli | |
| env: | |
| JF_URL: ${{ vars.JF_URL }} | |
| JF_PROJECT: ${{ vars.PROJECT_NAME }} | |
| with: | |
| oidc-provider-name: github-oidc-integration | |
| oidc-audience: jfrog-github | |
| disable-auto-build-publish: true | |
| - name: Setup Signing Key | |
| id: setup-signing-key | |
| run: | | |
| echo "${{ secrets.PRIVATE_KEY_EXPORT }}" > "${{ secrets.PRIVATE_KEY_NAME }}" | |
| chmod 600 ${{ secrets.PRIVATE_KEY_NAME }} | |
| openssl rsa -in ${{ secrets.PRIVATE_KEY_NAME }} -check | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Install OPA | |
| run: | | |
| curl -L -o opa https://openpolicyagent.org/downloads/latest/opa_linux_amd64 | |
| chmod +x opa | |
| sudo mv opa /usr/local/bin/ | |
| - name: Call GraphQL To Fetch Entire Evidence Graph | |
| run: | | |
| ./scripts/graphql.sh ${{ vars.JF_URL }} ${{ steps.cli.outputs.oidc-token }} evdemo-release-bundles-v2 ${{ env.BUNDLE_NAME }} ${{ github.run_number }} ./evidence-graph.json | |
| cat ./evidence-graph.json | |
| - name: Run policy | |
| id: run_policy | |
| run: | | |
| opa eval --input ./evidence-graph.json --data ./policy/policy.rego "data.policy.output" | jq '.result[0].expressions[0].value' > policy.json | |
| result=$(jq .approved ./policy.json) | |
| echo "RESULT=$result" >> $GITHUB_ENV | |
| - name: Promote to Production (expected to fail due to missing policy requirement)) | |
| run: | | |
| if [ "${{ env.RESULT }}" == "true" ]; then | |
| jf evd create \ | |
| --key "${{ env.PRIVATE_KEY_NAME }}" \ \ | |
| --key-alias ${{ env.KEY_ALIAS }} \ | |
| --release-bundle ${{ env.BUNDLE_NAME }} \ | |
| --release-bundle-version ${{ github.run_number }} \ | |
| --predicate ./policy.json \ | |
| --predicate-type https://jfrog.com/evidence/approval/v1 \ | |
| --project ${{ env.JF_PROJECT }} | |
| jf release-bundle-promote ${{ env.BUNDLE_NAME }} ${{ github.run_number }} PROD \ | |
| --signing-key ${{ secrets.GPG_KEY_NAME }} \ | |
| --sync=true \ | |
| --project ${{ env.JF_PROJECT }} | |
| echo "🚀 Succesfully promote to \`PROD\` environemnt" >> $GITHUB_STEP_SUMMARY | |
| else | |
| echo "Fail promotion policy check" >> $GITHUB_STEP_SUMMARY | |
| exit 1 | |
| fi |