diff --git a/.github/actions/checksum-validate/README.md b/.github/actions/checksum-validate/README.md new file mode 100644 index 00000000000..b228bfcab77 --- /dev/null +++ b/.github/actions/checksum-validate/README.md @@ -0,0 +1,94 @@ +# Checksum Validate Action + +[![Test Action](https://github.com/JosiahSiegel/checksum-validate-action/actions/workflows/test_action.yml/badge.svg)](https://github.com/JosiahSiegel/checksum-validate-action/actions/workflows/test_action.yml) + +## Synopsis + +1. Generate a checksum from either a string or shell command (use command substitution: `$()`). +2. Validate if checksum is identical to input (even across multiple jobs), using a `key` to link the validation attempt with the correct generated checksum. + * Validation is possible across jobs since the checksum is uploaded as a workflow artifact + +## Usage + +```yml +jobs: + generate-checksums: + name: Generate checksum + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4.1.1 + + - name: Generate checksum of string + uses: ./.github/actions/checksum-validate@ebdf8c12c00912d18de93c483b935d51582f9236 + with: + key: test string + input: hello world + + - name: Generate checksum of command output + uses: ./.github/actions/checksum-validate@ebdf8c12c00912d18de93c483b935d51582f9236 + with: + key: test command + input: $(cat action.yml) + + validate-checksums: + name: Validate checksum + needs: + - generate-checksums + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4.1.1 + + - name: Validate checksum of valid string + id: valid-string + uses: ./.github/actions/checksum-validate@ebdf8c12c00912d18de93c483b935d51582f9236 + with: + key: test string + validate: true + fail-invalid: true + input: hello world + + - name: Validate checksum of valid command output + id: valid-command + uses: ./.github/actions/checksum-validate@ebdf8c12c00912d18de93c483b935d51582f9236 + with: + key: test command + validate: true + fail-invalid: true + input: $(cat action.yml) + + - name: Get outputs + run: | + echo ${{ steps.valid-string.outputs.valid }} + echo ${{ steps.valid-command.outputs.valid }} +``` + +## Workflow summary + +### ✅ test string checksum valid ✅ + +### ❌ test string checksum INVALID ❌ + +## Inputs + +```yml +inputs: + validate: + description: Check if checksums match + default: false + key: + description: String to keep unique checksums separate + required: true + fail-invalid: + description: Fail step if invalid checksum + default: false + input: + description: String or command for checksum + required: true +``` + +## Outputs +```yml +outputs: + valid: + description: True if checksums match +``` diff --git a/.github/actions/checksum-validate/action.yml b/.github/actions/checksum-validate/action.yml new file mode 100644 index 00000000000..1ad3023476e --- /dev/null +++ b/.github/actions/checksum-validate/action.yml @@ -0,0 +1,111 @@ +# action.yml +name: Checksum Validate Action +description: Generate and validate checksums +branding: + icon: 'lock' + color: 'orange' +inputs: + validate: + description: Check if checksums match + default: false + key: + description: String to keep unique checksums separate + required: true + fail-invalid: + description: Fail step if invalid checksum + default: false + input: + description: String or command for checksum + required: true +outputs: + valid: + description: True if checksums match + value: ${{ steps.validate_checksum.outputs.valid }} + +runs: + using: "composite" + steps: + + # CHECKSUM START + - name: Generate SHA + uses: nick-fields/retry@v3.0.0 + with: + max_attempts: 5 + retry_on: any + timeout_seconds: 10 + retry_wait_seconds: 15 + command: | + function fail { + printf '%s\n' "$1" >&2 + exit "${2-1}" + } + input_cmd="${{ inputs.input }}" || fail + sha="$(echo "$input_cmd" | sha256sum)" + echo "sha=$sha" >> $GITHUB_ENV + echo "success=true" >> $GITHUB_ENV + + - name: Get input SHA + if: env.success + id: input_sha + shell: bash + run: echo "sha=${{ env.sha }}" >> $GITHUB_OUTPUT + + - name: Get input SHA + if: env.success != 'true' + shell: bash + run: | + echo "failed to generate sha" + exit 1 + # CHECKSUM END + + # UPLOAD FILE START + - name: Create checksum file + if: inputs.validate != 'true' + shell: bash + run: | + echo "${{ steps.input_sha.outputs.sha }}" > "${{ github.sha }}-${{ inputs.key }}.txt" + + - name: Upload checksum file + if: inputs.validate != 'true' + uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 + with: + name: "${{ github.sha }}-${{ inputs.key }}.txt" + path: "${{ github.sha }}-${{ inputs.key }}.txt" + retention-days: 5 + # UPLOAD FILE END + + # VALIDATE FILE START + - name: Download checksum file + if: inputs.validate == 'true' + uses: actions/download-artifact@eaceaf801fd36c7dee90939fad912460b18a1ffe + with: + name: "${{ github.sha }}-${{ inputs.key }}.txt" + + - name: Validate pre and post checksums + if: inputs.validate == 'true' + id: validate_checksum + shell: bash + run: | + echo "${{ steps.input_sha.outputs.sha }}" > "${{ github.sha }}-${{ inputs.key }}-2.txt" + DIFF=$(diff -q "${{ github.sha }}-${{ inputs.key }}-2.txt" "${{ github.sha }}-${{ inputs.key }}.txt") || true + codevalid=true + if [ "$DIFF" != "" ] + then + codevalid=false + fi + echo "valid=$codevalid" >> $GITHUB_OUTPUT + + - name: Create summary + if: inputs.validate == 'true' + run: | + # Use ternary operator to assign emoji based on validity + emoji=${{ steps.validate_checksum.outputs.valid == 'true' && '✅' || '❌' }} + valid=${{ steps.validate_checksum.outputs.valid == 'true' && 'valid' || 'INVALID' }} + echo "### $emoji ${{ inputs.key }} checksum $valid $emoji" >> $GITHUB_STEP_SUMMARY + shell: bash + # VALIDATE FILE END + + - name: Fail if invalid checksum + if: inputs.validate == 'true' && steps.validate_checksum.outputs.valid == 'false' && inputs.fail-invalid == 'true' + run: exit 1 + shell: bash diff --git a/.github/actions/deploy-backend/action.yml b/.github/actions/deploy-backend/action.yml index 60c558eae6d..1d9beecaa4f 100644 --- a/.github/actions/deploy-backend/action.yml +++ b/.github/actions/deploy-backend/action.yml @@ -332,10 +332,7 @@ runs: - name: Validate function app checksum if: inputs.checksum-validation == 'true' - - uses: JosiahSiegel/checksum-validate-action@ebdf8c12c00912d18de93c483b935d51582f9236 - ## DevSecOps - Aquia (Replace) uses: ./.github/actions/checksum-validate-action - + uses: ./.github/actions/checksum-validate with: key: backend validate: true diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 498f787b167..6365ac26954 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -125,6 +125,11 @@ updates: schedule: interval: "daily" + - package-ecosystem: "github-actions" + directory: "/.github/actions/checksum-validate" + schedule: + interval: "daily" + # Frontend - package-ecosystem: "npm" directory: "/frontend-react" diff --git a/.github/workflows/release_to_azure.yml b/.github/workflows/release_to_azure.yml index 481834e4db4..6e01c192131 100644 --- a/.github/workflows/release_to_azure.yml +++ b/.github/workflows/release_to_azure.yml @@ -147,10 +147,7 @@ jobs: env: checksum_validation: ${{ vars.CHECKSUM_VALIDATION }} if: needs.pre_job.outputs.has_router_change == 'true' && env.checksum_validation == 'true' - - uses: JosiahSiegel/checksum-validate-action@ebdf8c12c00912d18de93c483b935d51582f9236 - ## DevSecOps - Aquia (Replace) - uses: ./.github/actions/checksum-validate-action - + uses: ./.github/actions/checksum-validate with: key: backend input: $(az functionapp config appsettings list -g prime-data-hub-${{ needs.pre_job.outputs.env_name }} -n pdh${{ needs.pre_job.outputs.env_name }}-functionapp -o tsv | sort)