Fix/make sure start date before end date #122036
Workflow file for this run
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: Code Checks | |
| on: | |
| push: | |
| branches: [master] | |
| pull_request: | |
| types: [opened, reopened, synchronize] | |
| permissions: | |
| id-token: write | |
| contents: read | |
| checks: write | |
| jobs: | |
| compare_sha: | |
| runs-on: ubuntu-latest | |
| name: Compare sha | |
| steps: | |
| - name: Compare commit ids | |
| run: | | |
| echo "github.sha: ${{ github.sha }}" | |
| echo "github.event.push.head_commit.id: ${{ github.event.push.head_commit.id }}" | |
| echo "github.event.pull_request.merge_commit_sha: ${{ github.event.pull_request.merge_commit_sha }}" | |
| echo "github.event.head_commit.id: ${{ github.event.head_commit.id }}" | |
| linting_and_security: | |
| name: Linting and Security | |
| env: | |
| BUNDLE_ENTERPRISE__CONTRIBSYS__COM: ${{ secrets.BUNDLE_ENTERPRISE__CONTRIBSYS__COM }} | |
| permissions: write-all | |
| runs-on: ubuntu-32-cores-latest | |
| steps: | |
| - name: Checkout PR | |
| if: ${{ github.event_name != 'pull_request' }} | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 | |
| - name: Checkout PR HEAD ref | |
| if: ${{ github.event_name == 'pull_request' }} | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 | |
| with: | |
| ref: ${{ github.event.pull_request.head.ref }} | |
| repository: ${{ github.event.pull_request.head.repo.full_name }} | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| fetch-depth: 1 | |
| - name: Compute changed files for boundary check | |
| if: ${{ github.event_name == 'pull_request' }} | |
| run: | | |
| git fetch --no-tags --depth=1 origin "${{ github.event.pull_request.base.sha }}" | |
| git diff --name-only --diff-filter=ACMR "${{ github.event.pull_request.base.sha }}" HEAD > /tmp/changed-files.txt | |
| - name: Check runtime does not depend on spec code | |
| run: | | |
| if [[ "${{ github.event_name }}" == "pull_request" ]]; then | |
| CHANGED_FILES_FILE=/tmp/changed-files.txt bash script/ci/check_runtime_spec_dependencies.sh | |
| else | |
| bash script/ci/check_runtime_spec_dependencies.sh | |
| fi | |
| - uses: ruby/setup-ruby@3ff19f5e2baf30647122352b96108b1fbe250c64 # v1 | |
| with: | |
| bundler-cache: true | |
| - name: Check for Gemfile changes | |
| id: gemfile_changes | |
| if: github.event_name == 'pull_request' | |
| uses: tj-actions/changed-files@v47.0.5 | |
| with: | |
| files: | | |
| Gemfile | |
| Gemfile.lock | |
| modules/*/Gemfile | |
| - name: Run bundle-audit (checks gems for CVE issues) | |
| id: bundle_audit | |
| if: | | |
| ( | |
| github.ref == 'refs/heads/master' || | |
| (github.event_name == 'pull_request' && steps.gemfile_changes.outputs.any_changed == 'true') | |
| ) | |
| continue-on-error: true | |
| run: bundle exec bundle-audit check --update | |
| - name: Configure AWS Credentials | |
| if: steps.bundle_audit.outcome == 'failure' | |
| uses: aws-actions/configure-aws-credentials@8df5847569e6427dd6c4fb1cf565c83acfa8afa7 # v6 | |
| with: | |
| role-to-assume: ${{ vars.AWS_ASSUME_ROLE }} | |
| aws-region: us-gov-west-1 | |
| - uses: department-of-veterans-affairs/action-inject-ssm-secrets@d8e6de3bde4dd728c9d732baef58b3c854b8c4bb # latest | |
| if: steps.bundle_audit.outcome == 'failure' | |
| with: | |
| ssm_parameter: /devops/VA_VSP_BOT_GITHUB_TOKEN | |
| env_variable_name: VA_VSP_BOT_GITHUB_TOKEN | |
| - name: Checkout VSP actions | |
| if: steps.bundle_audit.outcome == 'failure' | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 | |
| with: | |
| repository: department-of-veterans-affairs/vsp-github-actions | |
| ref: refs/heads/main | |
| token: ${{ env.VA_VSP_BOT_GITHUB_TOKEN }} | |
| persist-credentials: false | |
| path: ./.github/actions/vsp-github-actions | |
| - uses: department-of-veterans-affairs/action-inject-ssm-secrets@d8e6de3bde4dd728c9d732baef58b3c854b8c4bb # latest | |
| if: steps.bundle_audit.outcome == 'failure' | |
| with: | |
| ssm_parameter: /devops/github_actions_slack_socket_token | |
| env_variable_name: SLACK_APP_TOKEN | |
| - uses: department-of-veterans-affairs/action-inject-ssm-secrets@d8e6de3bde4dd728c9d732baef58b3c854b8c4bb # latest | |
| if: steps.bundle_audit.outcome == 'failure' | |
| with: | |
| ssm_parameter: /devops/github_actions_slack_bot_user_token | |
| env_variable_name: SLACK_BOT_TOKEN | |
| - name: Slack notify - bundle-audit CVE failure | |
| if: | | |
| steps.bundle_audit.outcome == 'failure' && | |
| ( | |
| github.ref == 'refs/heads/master' || | |
| (github.event_name == 'pull_request' && steps.gemfile_changes.outputs.any_changed == 'true') | |
| ) | |
| uses: ./.github/actions/vsp-github-actions/slack-socket | |
| with: | |
| slack_app_token: ${{ env.SLACK_APP_TOKEN }} | |
| slack_bot_token: ${{ env.SLACK_BOT_TOKEN }} | |
| message: "Critical: Vets-API bundle-audit detected CVE vulnerabilities" | |
| blocks: | | |
| [ | |
| { "type": "divider" }, | |
| { | |
| "type": "header", | |
| "text": { | |
| "type": "plain_text", | |
| "text": ":rotating_light: CRITICAL: CVE Vulnerability Detected", | |
| "emoji": true | |
| } | |
| }, | |
| { | |
| "type": "section", | |
| "text": { | |
| "type": "mrkdwn", | |
| "text": "*bundle-audit found gems with known CVE vulnerabilities in vets-api*" | |
| } | |
| }, | |
| { | |
| "type": "section", | |
| "fields": [ | |
| { | |
| "type": "mrkdwn", | |
| "text": ":x: *Status*: Build Failed" | |
| }, | |
| { | |
| "type": "mrkdwn", | |
| "text": ":git: *Branch*: `${{ github.ref_name }}`" | |
| } | |
| ] | |
| }, | |
| { | |
| "type": "section", | |
| "text": { | |
| "type": "mrkdwn", | |
| "text": ":bust_in_silhouette: *Author:* ${{ github.actor }}" | |
| } | |
| }, | |
| { | |
| "type": "actions", | |
| "elements": [ | |
| { | |
| "type": "button", | |
| "text": { | |
| "type": "plain_text", | |
| "text": ":mag_right: View Run", | |
| "emoji": true | |
| }, | |
| "url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}", | |
| "style": "danger" | |
| } | |
| ] | |
| }, | |
| { "type": "divider" } | |
| ] | |
| channel_id: "C0APA867M0V" | |
| - name: Fail job if bundle-audit found CVEs | |
| if: steps.bundle_audit.outcome == 'failure' | |
| run: exit 1 | |
| - name: Run Rubocop | |
| run: bundle exec rubocop --parallel --format github | |
| - name: Run Brakeman | |
| run: bundle exec brakeman --ensure-latest --confidence-level=2 --format github | |
| - name: Run Reek | |
| uses: reviewdog/action-reek@43d3a426ebcfd42aca089a877b60f8e6170c51e0 # v1.20.0 | |
| with: | |
| reek_version: gemfile | |
| reporter: github-pr-review | |
| reek_flags: --smell DuplicateMethodCall | |
| # Note: this is the default anyway but explicitly set to remind us we can change to hard-fail on reek warnings with `any` | |
| # For now we just want to run in advisory mode to surface architectural smells as comments during review | |
| # ...but _without_ blocking merges for now | |
| fail_level: none | |
| # Always add the "lint-failure" label on failure and remove it on success | |
| - name: Add Lint Failure label | |
| if: ${{ failure() && github.event_name == 'pull_request' }} | |
| uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 | |
| with: | |
| github-token: ${{ secrets.GITHUB_TOKEN }} | |
| script: | | |
| const prNumber = context.payload.pull_request.number | |
| await github.rest.issues.addLabels({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: prNumber, | |
| labels: ['lint-failure'], | |
| }) | |
| - name: Remove Lint Failure label | |
| if: ${{ success() && github.event_name == 'pull_request' }} | |
| uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 | |
| with: | |
| github-token: ${{ secrets.GITHUB_TOKEN }} | |
| script: | | |
| const prNumber = context.payload.pull_request.number | |
| try { | |
| await github.rest.issues.removeLabel({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: prNumber, | |
| name: 'lint-failure', | |
| }) | |
| } catch (e) { | |
| if (e.status !== 404) throw e | |
| } | |
| build: | |
| name: Build and Cache Docker Image | |
| runs-on: ubuntu-16-cores-latest | |
| env: | |
| BUNDLE_ENTERPRISE__CONTRIBSYS__COM: ${{ secrets.BUNDLE_ENTERPRISE__CONTRIBSYS__COM }} | |
| DOCKER_BUILDKIT: 1 | |
| steps: | |
| - name: Checkout PR | |
| if: ${{ github.event_name != 'pull_request' }} | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 | |
| - name: Checkout PR HEAD ref | |
| if: ${{ github.event_name == 'pull_request' }} | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 | |
| with: | |
| ref: ${{ github.event.pull_request.head.ref }} | |
| repository: ${{ github.event.pull_request.head.repo.full_name }} | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| fetch-depth: 1 | |
| - name: Login to DockerHub | |
| uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4 | |
| with: | |
| username: ${{ secrets.DOCKERHUB_USER }} | |
| password: ${{ secrets.DOCKERHUB_PASSWORD }} | |
| - name: Configure AWS Credentials | |
| uses: aws-actions/configure-aws-credentials@8df5847569e6427dd6c4fb1cf565c83acfa8afa7 # v6 | |
| with: | |
| role-to-assume: ${{ vars.AWS_ASSUME_ROLE }} | |
| aws-region: us-gov-west-1 | |
| - name: Login to Amazon ECR | |
| id: ecr-login | |
| uses: aws-actions/amazon-ecr-login@062b18b96a7aff071d4dc91bc00c4c1a7945b076 # v2.0.1 | |
| with: | |
| mask-password: true | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4 | |
| - name: Build and Cache Docker Image | |
| uses: docker/build-push-action@d08e5c354a6adb9ed34480a06d141179aa583294 # v7 | |
| env: | |
| DOCKER_BUILD_SUMMARY: false | |
| with: | |
| build-args: | | |
| BUNDLE_ENTERPRISE__CONTRIBSYS__COM=${{ env.BUNDLE_ENTERPRISE__CONTRIBSYS__COM }} | |
| USER_ID=1001 | |
| context: . | |
| push: false | |
| load: false | |
| tags: vets-api | |
| outputs: type=docker,dest=/tmp/vets-api.tar | |
| cache-from: type=gha | |
| cache-to: type=gha,mode=max | |
| - name: Compress Docker image | |
| run: zstd -T0 /tmp/vets-api.tar -o /tmp/vets-api.tar.zst | |
| - name: Save Docker image to cache | |
| uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 | |
| with: | |
| path: /tmp/vets-api.tar.zst | |
| key: docker-image-${{ github.sha }} | |
| tests: | |
| name: Test (Group ${{ matrix.test_group }}) | |
| runs-on: ubuntu-16-cores-latest | |
| permissions: write-all | |
| needs: build | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| test_group: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24] | |
| env: | |
| BUNDLE_ENTERPRISE__CONTRIBSYS__COM: ${{ secrets.BUNDLE_ENTERPRISE__CONTRIBSYS__COM }} | |
| ECR_REGISTRY_WITH_SLASH: "${{ secrets.ECR_REGISTRY }}/" | |
| CI: true | |
| RAILS_ENV: test | |
| TERM: xterm-256color | |
| DOCKER_BUILDKIT: 1 | |
| COMPOSE_DOCKER_CLI_BUILD: 1 | |
| COMPOSE_BASH_CMD: docker compose -f docker-compose.test.yml run web bash -c | |
| steps: | |
| - name: Checkout PR | |
| if: ${{ github.event_name != 'pull_request' }} | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 | |
| - name: Checkout PR HEAD ref | |
| if: ${{ github.event_name == 'pull_request' }} | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 | |
| with: | |
| ref: ${{ github.event.pull_request.head.ref }} | |
| repository: ${{ github.event.pull_request.head.repo.full_name }} | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| fetch-depth: 1 | |
| - name: Login to DockerHub | |
| uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4 | |
| with: | |
| username: ${{ secrets.DOCKERHUB_USER }} | |
| password: ${{ secrets.DOCKERHUB_PASSWORD }} | |
| - name: Configure AWS Credentials | |
| uses: aws-actions/configure-aws-credentials@8df5847569e6427dd6c4fb1cf565c83acfa8afa7 # v6 | |
| with: | |
| role-to-assume: ${{ vars.AWS_ASSUME_ROLE }} | |
| aws-region: us-gov-west-1 | |
| - name: Login to Amazon ECR | |
| id: ecr-login | |
| uses: aws-actions/amazon-ecr-login@062b18b96a7aff071d4dc91bc00c4c1a7945b076 # v2.0.1 | |
| with: | |
| mask-password: true | |
| - name: Restore Docker image from cache | |
| uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 | |
| with: | |
| path: /tmp/vets-api.tar.zst | |
| key: docker-image-${{ github.sha }} | |
| fail-on-cache-miss: true | |
| - name: Load Docker image | |
| run: zstd -d /tmp/vets-api.tar.zst --stdout | docker load | |
| # Shard balancing: if a runtime log exists (cached from a previous run), | |
| # parallel_test uses --group-by runtime to distribute specs by actual | |
| # measured execution time. Otherwise it falls back to --group-by filesize. | |
| # The runtime log is generated in publish_results from JUnit XML timing | |
| # data and cached for subsequent runs. | |
| # | |
| # Cache key priority: | |
| # 1. This branch's most recent run | |
| # 2. master branch (so new PRs get balanced shards immediately) | |
| # 3. Any previous run (catch-all fallback) | |
| # If no cache exists at all (e.g. first-ever run), parallel_test uses | |
| # filesize grouping automatically. | |
| - name: Restore runtime log from cache | |
| uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 | |
| with: | |
| path: tmp/parallel_runtime_rspec.log | |
| key: parallel-runtime-log-${{ github.ref }}-${{ github.run_id }} | |
| restore-keys: | | |
| parallel-runtime-log-${{ github.ref }}- | |
| parallel-runtime-log-refs/heads/master- | |
| parallel-runtime-log- | |
| - name: Determine shard grouping strategy | |
| id: grouping | |
| run: | | |
| if [ -s tmp/parallel_runtime_rspec.log ]; then | |
| echo "strategy=runtime" >> $GITHUB_OUTPUT | |
| echo "Runtime log found, using runtime-based shard balancing" | |
| else | |
| echo "strategy=filesize" >> $GITHUB_OUTPUT | |
| echo "No runtime log found, falling back to filesize grouping" | |
| fi | |
| - name: Setup Database | |
| uses: nick-fields/retry@ad984534de44a9489a53aefd81eb77f87c70dc60 # v4.0.0 | |
| with: | |
| timeout_minutes: 10 | |
| retry_wait_seconds: 3 # Seconds | |
| max_attempts: 3 | |
| command: > | |
| ${{ env.COMPOSE_BASH_CMD }} | |
| "bundle exec rails db:test:prepare" | |
| - name: Setup system tmp dir | |
| run: > | |
| ${{ env.COMPOSE_BASH_CMD }} | |
| "mkdir -p tmp/systmp" | |
| - name: Run Specs | |
| timeout-minutes: 15 | |
| env: | |
| TEST_ENV_NUMBER: ${{ matrix.test_group }} | |
| run: > | |
| ${{ env.COMPOSE_BASH_CMD }} | |
| "TEST_ENV_NUMBER=${{ matrix.test_group }} TMPDIR=tmp/systmp bundle exec parallel_test spec modules --group-by ${{ steps.grouping.outputs.strategy }} --type rspec --only-group ${{ matrix.test_group }} -n 24 -o '--profile 20'" | |
| - name: Prepare SimpleCov shard payload | |
| if: always() | |
| run: | | |
| mkdir -p simplecov-resultset-${{ matrix.test_group }} | |
| if [ -f coverage/.resultset.json ]; then | |
| mv coverage/.resultset.json simplecov-resultset-${{ matrix.test_group }}/.resultset.json | |
| fi | |
| - name: Upload SimpleCov raw resultset | |
| if: always() | |
| uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 | |
| with: | |
| name: simplecov-resultset-${{ matrix.test_group }} | |
| path: simplecov-resultset-${{ matrix.test_group }}/.resultset.json | |
| include-hidden-files: true | |
| if-no-files-found: warn | |
| - name: Upload Test Results (JUnit XMLs) | |
| if: always() | |
| uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 | |
| with: | |
| name: Test Results Group ${{ matrix.test_group }} | |
| path: log/*.xml | |
| if-no-files-found: ignore | |
| - name: Upload Test Artifacts (log) | |
| uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 | |
| if: always() | |
| with: | |
| name: Test Artifacts (log directory) Group ${{ matrix.test_group }} | |
| path: | | |
| log | |
| !log/*.xml | |
| if-no-files-found: ignore | |
| - name: Upload Test Artifacts (rails tmp) | |
| if: failure() | |
| uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 | |
| with: | |
| name: Test Artifacts (rails tmp directory) Group ${{ matrix.test_group }} | |
| path: tmp | |
| if-no-files-found: ignore | |
| retention-days: 14 | |
| publish_results: | |
| name: Publish Test Results and Coverage | |
| if: always() | |
| needs: [build, tests] | |
| permissions: write-all | |
| runs-on: ubuntu-16-cores-latest | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 | |
| - uses: ruby/setup-ruby@3ff19f5e2baf30647122352b96108b1fbe250c64 # v1 | |
| env: | |
| BUNDLE_ENTERPRISE__CONTRIBSYS__COM: ${{ secrets.BUNDLE_ENTERPRISE__CONTRIBSYS__COM }} | |
| with: | |
| bundler-cache: ${{ needs.tests.result == 'success' }} | |
| - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8 | |
| - name: Publish Test Results to GitHub | |
| if: always() | |
| uses: EnricoMi/publish-unit-test-result-action@c950f6fb443cb5af20a377fd0dfaa78838901040 # v2 | |
| with: | |
| check_name: Test Results | |
| comment_mode: off | |
| files: Test Results Group*/*.xml | |
| github_token: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Add Test Failure label | |
| if: ${{ needs.tests.result == 'failure' && github.event_name == 'pull_request' }} | |
| uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 | |
| with: | |
| github-token: ${{ secrets.GITHUB_TOKEN }} | |
| script: | | |
| const prNumber = context.payload.pull_request.number | |
| await github.rest.issues.addLabels({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: prNumber, | |
| labels: ['test-failure'], | |
| }) | |
| - name: Fail job if tests failed | |
| if: ${{ needs.tests.result == 'failure' }} | |
| run: exit 1 | |
| - name: Remove Test Failure label | |
| if: ${{ needs.tests.result == 'success' && github.event_name == 'pull_request' }} | |
| uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 | |
| with: | |
| github-token: ${{ secrets.GITHUB_TOKEN }} | |
| script: | | |
| const prNumber = context.payload.pull_request.number | |
| try { | |
| await github.rest.issues.removeLabel({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: prNumber, | |
| name: 'test-failure', | |
| }) | |
| } catch (e) { | |
| if (e.status !== 404) throw e | |
| } | |
| # Convert JUnit XML test results into a parallel_test runtime log so that | |
| # future runs can use --group-by runtime (runtime-based shard balancing). | |
| # Each shard uploads JUnit XMLs as artifacts; the download-artifact step | |
| # above already fetched them into "Test Results Group N/" directories. | |
| # The generated log is cached (below) for the next workflow run. | |
| - name: Generate parallel_test runtime log from JUnit XML | |
| if: always() | |
| run: | | |
| mkdir -p tmp | |
| ruby script/junit_to_runtime_log.rb tmp/parallel_runtime_rspec.log "Test Results Group*/*.xml" | |
| - name: Save runtime log to cache | |
| if: always() | |
| uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 | |
| with: | |
| path: tmp/parallel_runtime_rspec.log | |
| key: parallel-runtime-log-${{ github.ref }}-${{ github.run_id }} | |
| - name: Download coverage artifacts | |
| if: needs.tests.result == 'success' | |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8 | |
| with: | |
| pattern: simplecov-resultset-* | |
| path: . | |
| - name: Collate coverage and generate report | |
| if: needs.tests.result == 'success' | |
| env: | |
| BUNDLE_ENTERPRISE__CONTRIBSYS__COM: ${{ secrets.BUNDLE_ENTERPRISE__CONTRIBSYS__COM }} | |
| run: bundle exec ruby script/coverage_collate.rb | |
| - name: Upload Combined Coverage HTML | |
| if: needs.tests.result == 'success' | |
| uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 | |
| with: | |
| name: Coverage Report | |
| path: coverage | |
| if-no-files-found: ignore | |
| - name: Fix up coverage report to work with coverage-check-action | |
| id: coverage | |
| if: needs.tests.result == 'success' | |
| run: | | |
| echo "=== Fixing coverage report format ===" | |
| sed -i 's/"line"/"covered_percent"/g' 'coverage/.last_run.json' | |
| echo "=== Verifying modified format ===" | |
| pct=$(cat coverage/.last_run.json | jq '.result.covered_percent') | |
| echo "percent=$pct" >> $GITHUB_OUTPUT | |
| echo "Extracted coverage: $pct" | |
| - name: Publish Coverage Report | |
| if: needs.tests.result == 'success' | |
| uses: devmasx/coverage-check-action@4a754f8957c6824e0a0d44bf9168fdbdf47e7e2f # v1.2.0 | |
| with: | |
| type: simplecov | |
| result_path: coverage/.last_run.json | |
| min_coverage: 90 | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Configure AWS Credentials | |
| if: needs.tests.result == 'success' && github.event_name != 'pull_request' | |
| uses: aws-actions/configure-aws-credentials@8df5847569e6427dd6c4fb1cf565c83acfa8afa7 # v6 | |
| with: | |
| role-to-assume: ${{ vars.AWS_ASSUME_ROLE }} | |
| aws-region: us-gov-west-1 | |
| - uses: department-of-veterans-affairs/action-inject-ssm-secrets@d8e6de3bde4dd728c9d732baef58b3c854b8c4bb # latest | |
| if: needs.tests.result == 'success' && github.event_name != 'pull_request' | |
| with: | |
| ssm_parameter: /devops/VA_VSP_BOT_GITHUB_TOKEN | |
| env_variable_name: VA_VSP_BOT_GITHUB_TOKEN | |
| - name: Checkout VSP actions | |
| if: needs.tests.result == 'success' && github.event_name != 'pull_request' | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 | |
| with: | |
| repository: department-of-veterans-affairs/vsp-github-actions | |
| ref: refs/heads/main | |
| token: ${{ env.VA_VSP_BOT_GITHUB_TOKEN }} | |
| persist-credentials: false | |
| path: ./.github/actions/vsp-github-actions | |
| - uses: department-of-veterans-affairs/action-inject-ssm-secrets@d8e6de3bde4dd728c9d732baef58b3c854b8c4bb # latest | |
| if: needs.tests.result == 'success' && github.event_name != 'pull_request' | |
| with: | |
| ssm_parameter: /devops/github_actions_slack_socket_token | |
| env_variable_name: SLACK_APP_TOKEN | |
| - uses: department-of-veterans-affairs/action-inject-ssm-secrets@d8e6de3bde4dd728c9d732baef58b3c854b8c4bb # latest | |
| if: needs.tests.result == 'success' && github.event_name != 'pull_request' | |
| with: | |
| ssm_parameter: /devops/github_actions_slack_bot_user_token | |
| env_variable_name: SLACK_BOT_TOKEN | |
| - name: Slack notify - critical coverage alert | |
| if: needs.tests.result == 'success' && github.event_name != 'pull_request' && fromJSON(steps.coverage.outputs.percent) < 90 | |
| uses: ./.github/actions/vsp-github-actions/slack-socket | |
| with: | |
| slack_app_token: ${{ env.SLACK_APP_TOKEN }} | |
| slack_bot_token: ${{ env.SLACK_BOT_TOKEN }} | |
| message: "Critical: Vets-API Testing Coverage Below 90% - Deployment Blocked" | |
| blocks: | | |
| [ | |
| { "type": "divider" }, | |
| { | |
| "type": "header", | |
| "text": { | |
| "type": "plain_text", | |
| "text": ":rotating_light: CRITICAL: Test Coverage Too Low", | |
| "emoji": true | |
| } | |
| }, | |
| { | |
| "type": "section", | |
| "text": { | |
| "type": "mrkdwn", | |
| "text": "*Vets-API deployment is BLOCKED until coverage reaches 90%*" | |
| } | |
| }, | |
| { | |
| "type": "section", | |
| "fields": [ | |
| { | |
| "type": "mrkdwn", | |
| "text": ":chart_with_downwards_trend: *Current Coverage*: ${{ steps.coverage.outputs.percent }}%" | |
| }, | |
| { | |
| "type": "mrkdwn", | |
| "text": ":dart: *Required Minimum*: 90%" | |
| }, | |
| { | |
| "type": "mrkdwn", | |
| "text": ":x: *Status*: Deployment Blocked" | |
| } | |
| ] | |
| }, | |
| { | |
| "type": "section", | |
| "text": { | |
| "type": "mrkdwn", | |
| "text": ":bust_in_silhouette: *Author:* ${{ github.actor }}" | |
| } | |
| }, | |
| { | |
| "type": "actions", | |
| "elements": [ | |
| { | |
| "type": "button", | |
| "text": { | |
| "type": "plain_text", | |
| "text": ":mag_right: View Test Run", | |
| "emoji": true | |
| }, | |
| "url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}", | |
| "style": "danger" | |
| } | |
| ] | |
| }, | |
| { | |
| "type": "context", | |
| "elements": [ | |
| { | |
| "type": "mrkdwn", | |
| "text": ":bulb: *View detailed coverage:* <https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}|Open test run> → Scroll to *Artifacts* → Download *Coverage Report* → Open *index.html*" | |
| } | |
| ] | |
| }, | |
| { "type": "divider" } | |
| ] | |
| channel_id: "C0APA867M0V" | |
| - name: Slack notify - warning low coverage alert | |
| if: needs.tests.result == 'success' && github.event_name != 'pull_request' && fromJSON(steps.coverage.outputs.percent) >= 90 && fromJSON(steps.coverage.outputs.percent) < 93 | |
| uses: ./.github/actions/vsp-github-actions/slack-socket | |
| with: | |
| slack_app_token: ${{ env.SLACK_APP_TOKEN }} | |
| slack_bot_token: ${{ env.SLACK_BOT_TOKEN }} | |
| message: "Warning: Vets-API Testing Coverage Below 93%" | |
| blocks: | | |
| [ | |
| { "type": "divider" }, | |
| { | |
| "type": "header", | |
| "text": { | |
| "type": "plain_text", | |
| "text": ":warning: Warning: Vets-API Coverage Below Target", | |
| "emoji": true | |
| } | |
| }, | |
| { | |
| "type": "section", | |
| "fields": [ | |
| { | |
| "type": "mrkdwn", | |
| "text": ":chart_with_downwards_trend: *Current Coverage*: ${{ steps.coverage.outputs.percent }}%" | |
| }, | |
| { | |
| "type": "mrkdwn", | |
| "text": ":dart: *Required Minimum*: 90%" | |
| }, | |
| { | |
| "type": "mrkdwn", | |
| "text": ":large_green_circle: *Status*: Deployment Allowed" | |
| } | |
| ] | |
| }, | |
| { | |
| "type": "section", | |
| "text": { | |
| "type": "mrkdwn", | |
| "text": ":bust_in_silhouette: *Author:* ${{ github.actor }}" | |
| } | |
| }, | |
| { | |
| "type": "actions", | |
| "elements": [ | |
| { | |
| "type": "button", | |
| "text": { | |
| "type": "plain_text", | |
| "text": ":mag_right: View Test Run", | |
| "emoji": true | |
| }, | |
| "url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}", | |
| "style": "danger" | |
| } | |
| ] | |
| }, | |
| { | |
| "type": "context", | |
| "elements": [ | |
| { | |
| "type": "mrkdwn", | |
| "text": ":bulb: *View detailed coverage:* <https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}|Open test run> → Scroll to *Artifacts* → Download *Coverage Report* → Open *index.html*" | |
| } | |
| ] | |
| }, | |
| { "type": "divider" } | |
| ] | |
| channel_id: "C0APA867M0V" | |
| - name: Comment Coverage on PR | |
| if: needs.tests.result == 'success' && github.event_name == 'pull_request' && fromJSON(steps.coverage.outputs.percent) < 90 | |
| uses: marocchino/sticky-pull-request-comment@70d2764d1a7d5d9560b100cbea0077fc8f633987 # v3 | |
| with: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| header: coverage | |
| message: | | |
| ## :x: Critical: Test Coverage Too Low | |
| **Coverage:** ${{ steps.coverage.outputs.percent }}% | |
| Your test coverage is **below the required minimum of 90%**. | |
| Please add additional tests before this PR can be considered for merge. | |
| Once coverage is improved, push another commit or rerun the workflow to update this report. | |
| **View detailed coverage:** [Open test run](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}) → Scroll to **Artifacts** → Download **Coverage Report** → Open **index.html** | |
| - name: Comment Coverage on PR | |
| if: needs.tests.result == 'success' && github.event_name == 'pull_request' && fromJSON(steps.coverage.outputs.percent) >= 90 && fromJSON(steps.coverage.outputs.percent) < 93 | |
| uses: marocchino/sticky-pull-request-comment@70d2764d1a7d5d9560b100cbea0077fc8f633987 # v3 | |
| with: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| header: coverage | |
| message: | | |
| ## :warning: Warning: Low Coverage Report | |
| **Coverage:** ${{ steps.coverage.outputs.percent }}% | |
| Please add or expand tests to improve coverage before merging. | |
| **View detailed coverage:** [Open test run](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}) → Scroll to **Artifacts** → Download **Coverage Report** → Open **index.html** | |
| - name: Remove coverage comment when coverage is healthy | |
| if: needs.tests.result == 'success' && github.event_name == 'pull_request' && fromJSON(steps.coverage.outputs.percent) >= 93 | |
| uses: marocchino/sticky-pull-request-comment@70d2764d1a7d5d9560b100cbea0077fc8f633987 # v3 | |
| with: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| header: coverage | |
| delete: true |