From 5b4794a21623471552e17dc5ea84d21f3af15611 Mon Sep 17 00:00:00 2001 From: Mason Watson <32622851+msnwatson@users.noreply.github.com> Date: Fri, 19 Apr 2024 14:33:44 -0700 Subject: [PATCH] Secrel Decouple (#2882) * first pass at changing secrel to be more decoupled * work on syntax error * put secrel inputs step in separate script * change conditions * checkout repo in secrel-inputs step * add back double bracing * add space in braces * move script back * add gate step * change run conditions * add debug echo statement * change form of run condition * change form of run condition * change form of run condition * change form of run condition * change form of run condition * change form of run condition * change form of run condition * change form of run condition * change form of run condition * change form of run condition * change form of run condition * change form of run condition * change form of run condition * change form of run condition * change form of run condition * change form of run condition * change form of run condition * update list * update text * update text * try stop quote stripping * try stop quote stripping * try stop quote stripping --- .github/workflows/secrel.yml | 190 +++++++++++++++++--------------- scripts/secrel-secrel-inputs.sh | 42 +++++++ 2 files changed, 144 insertions(+), 88 deletions(-) create mode 100755 scripts/secrel-secrel-inputs.sh diff --git a/.github/workflows/secrel.yml b/.github/workflows/secrel.yml index aa99bae89d..3fb3672cb5 100644 --- a/.github/workflows/secrel.yml +++ b/.github/workflows/secrel.yml @@ -6,6 +6,7 @@ on: push: branches: [ main, qa, develop, domain-* ] + # Run SecRel on Dependabot PRs, automatically pull_request: branches: [ develop ] @@ -22,6 +23,37 @@ on: required: true type: boolean default: true + publish_images: + description: "Publish all images from the head of the selected branch?" + required: true + type: boolean + default: false + image_name: + description: "Which images do you want to run SecRel on? This does not affect image publishing." + required: true + type: choice + default: all + options: + - all + - cc-app + - xample-workflows + - svc-bgs-api + - svc-bie-kafka + - svc-bip-api + - svc-lighthouse-api + - api-gateway + - ee-max-cfi-app + - ee-ep-merge-app + - dev-tools + - db-init + - app + - redis + - rabbitmq + image_tag: + description: "Image tag to run SecRel on. If 'all' is selected, then 'latest' will always be used. This does not define the image tag during publishing" + required: true + type: string + default: latest env: # Id for the #benefits-vro-devops Slack channel @@ -33,33 +65,43 @@ jobs: # only run for the internal repo, where the images are published if: github.repository == 'department-of-veterans-affairs/abd-vro-internal' outputs: - continue: ${{ steps.check-state.outputs.continue }} + run-secrel: ${{ steps.check-run-conds.outputs.run_secrel }} + publish-images: ${{ steps.check-run-conds.outputs.publish_images }} runs-on: ubuntu-latest steps: - - name: "Check trigger event" - id: check-state + - name: "Decide downstream actions" + id: check-run-conds run: | # Gate Check if [ "${{ github.event_name }}" == "pull_request" ]; then if echo "$GITHUB_HEAD_REF" | grep '^dependabot/'; then - CONTINUE=true + RUN_SECREL=true + PUBLISH_IMAGES=true echo "Running SecRel against a Dependabot PR" | tee -a "$GITHUB_STEP_SUMMARY" else - CONTINUE=false + RUN_SECREL=false echo "For PRs, SecRel runs against only Dependabot PR" | tee -a "$GITHUB_STEP_SUMMARY" fi + elif [ "${{ github.event_name }}" == "push" ]; then + PUBLISH_IMAGES=true + elif [ "${{ github.event_name }}" == "workflow_dispatch" ]; then + RUN_SECREL=${{ inputs.run_secrel }} + PUBLISH_IMAGES=${{ inputs.publish_images }} fi - echo "continue=${CONTINUE:-true}" >> "$GITHUB_OUTPUT" + { + echo "run_secrel=${RUN_SECREL:-true}" + echo "publish_images=${PUBLISH_IMAGES:-false}" + } >> "$GITHUB_OUTPUT" + # This step is always expected to output a non-empty list of images published publish-to-ghcr: name: Publish to GHCR needs: gate-check - if: needs.gate-check.outputs.continue == 'true' + if: needs.gate-check.outputs.publish-images == 'true' outputs: vro-images: ${{ steps.publish-images.outputs.images_list }} slack-response-ts: ${{ fromJson(steps.notify-slack.outputs.slack-result).response.message.ts }} - run-secrel: ${{ steps.image-props.outputs.run_secrel }} runs-on: ubuntu-latest steps: - name: "Determine image tag" @@ -68,7 +110,6 @@ jobs: # Set defaults IMG_TAG=${GITHUB_SHA:0:7} RUN_GRADLE_TESTS="true" - RUN_SECREL="true" # Override some defaults depending on the branch/ref_name echo "ref_name: ${{ github.ref_name }}" @@ -76,13 +117,11 @@ jobs: # If workflow was manually dispatched, override settings if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then RUN_GRADLE_TESTS=${{ inputs.run_tests }} - RUN_SECREL=${{ inputs.run_secrel }} fi { echo "image_tag=${IMG_TAG}" echo "run_tests=${RUN_GRADLE_TESTS}" - echo "run_secrel=${RUN_SECREL}" } >> "$GITHUB_OUTPUT" - name: "DEBUG" @@ -127,89 +166,64 @@ jobs: slack-optional-thread_ts: ${{ fromJson(steps.notify-slack.outputs.slack-result).response.message.ts }} slack-text: ":panda_builder: Images published!" - debug-job: - needs: publish-to-ghcr + secrel-inputs: + if: ${{ !failure() }} + needs: [publish-to-ghcr, gate-check] + outputs: + vro-images: ${{ steps.vro-images.outputs.image_list }} runs-on: ubuntu-latest steps: - - name: show vars + - name: "Checkout source code" + uses: actions/checkout@v4 + with: + # Checkout using a PAT so that we can do `git push` later in publish-images + token: ${{ secrets.ACCESS_TOKEN_PUSH_TO_DEVELOP }} + - name: "Determine VRO image inputs" + id: vro-images + env: + IMAGE_ARR: ${{needs.publish-to-ghcr.outputs.vro-images}} run: | - echo "ref_name: ${{github.ref_name}}" - echo "vro-images: ${{ needs.publish-to-ghcr.outputs.vro-images }}" + source scripts/image_vars.src + IMAGE_LIST="[]" - secrel: - name: SecRel Pipeline - needs: publish-to-ghcr - if: needs.publish-to-ghcr.outputs.run-secrel == 'true' - uses: department-of-veterans-affairs/lighthouse-tornado-secrel-pipeline/.github/workflows/pipeline.yml@v5 - with: - config-file: .github/secrel/config.yml - images: ${{ needs.publish-to-ghcr.outputs.vro-images }} - secrets: inherit + # This case captures manual dispatches where publishing is disabled + if [ "${{ needs.publish-to-ghcr.result }}" == 'skipped' ]; then - notify-secrel-error: - needs: secrel - if: always() && needs.secrel.result == 'failure' - runs-on: ubuntu-latest - steps: - - name: "Slack: SecRel Failure" - uses: archive/github-actions-slack@v2.9.0 - # only run for the internal repo - if: ${{ github.repository == 'department-of-veterans-affairs/abd-vro-internal' }} - with: - slack-bot-user-oauth-access-token: ${{ secrets.SLACK_BOT_USER_OAUTH_ACCESS_TOKEN }} - slack-channel: ${{ env.SLACK_CHANNEL }} - slack-text: ":redlight: <${{github.server_url}}/${{github.repository}}/actions/runs/${{github.run_id}}|\ - SecRel scan (#${{ github.run_number }}) failed>! \n\ - ${{github.ref_type}} `${{github.ref_name}}` (`${{github.sha}}`), \n\ - caused by `${{github.event_name}}` triggered by `${{github.triggering_actor}}` ..." + IMAGE_LIST="[" - slack-final: - needs: [ gate-check, publish-to-ghcr, secrel] - runs-on: ubuntu-latest - if: always() - steps: - - name: "Set status message" - run: | - URL=${{github.server_url}}/${{github.repository}}/actions/runs/${{github.run_id}} - { - if [ "${{needs.publish-to-ghcr.result}}" == 'failure' ]; then - echo "WORKFLOW_STATE_TEXT=:panda_blank: <$URL|Publishing> failed!" - echo 'WORKFLOW_STATE_EMOJI=x' - elif [ "${{needs.secrel.result}}" == 'failure' ]; then - echo "WORKFLOW_STATE_TEXT=:panda_cop: <$URL|SecRel> failed!" - echo 'WORKFLOW_STATE_EMOJI=x' - elif [ "${{needs.secrel.result}}" == 'success' ]; then - echo "WORKFLOW_STATE_TEXT=:panda-yay: SecRel passed and images signed!" - echo 'WORKFLOW_STATE_EMOJI=panda-yay' - elif [ "${{needs.publish-to-ghcr.result}}" == 'skipped' ]; then - echo "WORKFLOW_STATE_TEXT=:black_square_for_stop: <$URL|Publishing> skipped" - echo 'WORKFLOW_STATE_EMOJI=black_square_for_stop' - elif [ "${{needs.publish-to-ghcr.outputs.run-secrel}}" == "true" ] && [ "${{needs.secrel.result}}" != 'success' ]; then - echo "WORKFLOW_STATE_TEXT=:black_square_for_stop: <$URL|SecRel> ${{needs.secrel.result}}" - echo 'WORKFLOW_STATE_EMOJI=black_square_for_stop' - elif [ "${{needs.publish-to-ghcr.outputs.run-secrel}}" == "false" ] && [ "${{needs.publish-to-ghcr.result}}" == 'success' ]; then - echo "WORKFLOW_STATE_TEXT=:heavy_check_mark: Run completed (without SecRel scans or signing images)" - echo 'WORKFLOW_STATE_EMOJI=heavy_check_mark' + if [ "${{inputs.image_name}}" == 'all' ]; then + for PREFIX in "${VAR_PREFIXES_ARR[@]}"; do + IMG_NAME="$(getVarValue "${PREFIX}" _IMG)" + GHCR_PATH="ghcr.io/${{github.repository}}/${IMG_NAME}" + IMAGE_LIST+="\"${GHCR_PATH}:latest\"," + done + + # Remove the trailing comma + IMAGE_LIST=${IMAGE_LIST%?} else - echo "WORKFLOW_STATE_TEXT=publish: ${{needs.publish-to-ghcr.result}}; secrel: ${{needs.secrel.result}}" - echo 'WORKFLOW_STATE_EMOJI=shrug' + IMG_TAG="${{inputs.image_tag}}" + IMG_NAME="$(getVarValue "$(bashVarPrefix "${{inputs.image_name}}")" _IMG)" + GHCR_PATH="ghcr.io/${{github.repository}}/${IMG_NAME}" + IMAGE_LIST+="\"${GHCR_PATH}:${IMG_TAG}\"" fi - } >> "$GITHUB_ENV" - - name: "Slack thread: Post final status" - if: always() && needs.gate-check.outputs.continue == 'true' - uses: archive/github-actions-slack@v2.9.0 - with: - slack-bot-user-oauth-access-token: ${{ secrets.SLACK_BOT_USER_OAUTH_ACCESS_TOKEN }} - slack-channel: ${{ env.SLACK_CHANNEL }} - slack-optional-thread_ts: ${{ needs.publish-to-ghcr.outputs.slack-response-ts }} - slack-text: ${{ env.WORKFLOW_STATE_TEXT }} - - name: "Slack emoji: React success on top-level Slack notification" - if: always() && needs.gate-check.outputs.continue == 'true' - uses: archive/github-actions-slack@v2.9.0 - with: - slack-function: send-reaction - slack-bot-user-oauth-access-token: ${{ secrets.SLACK_BOT_USER_OAUTH_ACCESS_TOKEN }} - slack-channel: ${{ env.SLACK_CHANNEL }} - slack-message-timestamp: ${{ needs.publish-to-ghcr.outputs.slack-response-ts }} - slack-emoji-name: ${{ env.WORKFLOW_STATE_EMOJI }} + IMAGE_LIST+="]" + + echo "image_list=${IMAGE_LIST}" >> "$GITHUB_OUTPUT" + + # This case captures when the workflow is run automatically on PR merge along + # with manual dispatches where publishing is enabled + elif [ "${{needs.publish-to-ghcr.result}}" == 'success' ]; then + echo "image_list=$IMAGE_ARR" >> "$GITHUB_OUTPUT" + fi + + + secrel: + name: SecRel Pipeline + needs: [secrel-inputs, gate-check] + if: ${{always() && needs.gate-check.outputs.run-secrel == 'true'}} + secrets: inherit + uses: department-of-veterans-affairs/lighthouse-tornado-secrel-pipeline/.github/workflows/pipeline.yml@v5 + with: + config-file: .github/secrel/config.yml + images: ${{ needs.secrel-inputs.outputs.vro-images }} diff --git a/scripts/secrel-secrel-inputs.sh b/scripts/secrel-secrel-inputs.sh new file mode 100755 index 0000000000..c7ae0f2f5f --- /dev/null +++ b/scripts/secrel-secrel-inputs.sh @@ -0,0 +1,42 @@ +#!/usr/bin/env bash + +# This script is only used as the secrel-inputs step in the Secrel GH workflow + +source scripts/image_vars.src +IMAGE_LIST="[]" + +# This case captures manual dispatches where publishing is disabled +if [ "${{ needs.publish-to-ghcr.result }}" == 'skipped' ]; then + + IMAGE_LIST="[" + + if [ "${{inputs.image_name}}" == 'all' ]; then + for PREFIX in "${VAR_PREFIXES_ARR[@]}"; do + IMG_NAME="$(getVarValue "${PREFIX}" _IMG)" + GHCR_PATH="ghcr.io/${{github.repository}}/${IMG_NAME}" + IMAGE_LIST+="\"${GHCR_PATH}:latest\"," + done + + # Remove the trailing comma + IMAGE_LIST=${IMAGE_LIST%?} + else + IMG_TAG="${{inputs.image_tag}}" + IMG_NAME="$(getVarValue "$(bashVarPrefix "${{inputs.image_name}}")" _IMG)" + GHCR_PATH="ghcr.io/${{github.repository}}/${IMG_NAME}" + IMAGE_LIST+="\"${GHCR_PATH}:${IMG_TAG}\"" + fi + + IMAGE_LIST+="]" + +# This case captures when the workflow is run automatically on PR merge along +# with manual dispatches where publishing is disabled +elif [ "${{needs.publish-to-ghcr.result}}" == 'success' ]; then + IMAGE_LIST=${{needs.publish-to-ghcr.outputs.vro-images}} +fi + +# Throw an error if the IMAGE_LIST variable is an empty array +if [ "${IMAGE_LIST}" == "[]" ]; then + exit 1 +fi + +echo "image_list=${IMAGE_LIST}" >> "$GITHUB_OUTPUT"