name: CI on: pull_request: # The default types for pull_request are [ opened, synchronize, reopened ]. # This is insufficient for our needs, since we're skipping stuff on PRs in # draft mode. By adding the ready_for_review type, when a draft pr is marked # ready, we run everything, including the stuff we'd have skipped up until now. types: [opened, synchronize, reopened, ready_for_review] push: branches: - main - release/** workflow_dispatch: concurrency: group: ${{ github.head_ref || github.run_id }}-ci cancel-in-progress: true jobs: setup: name: Setup runs-on: ubuntu-latest outputs: compute-tiny: ${{ steps.setup-outputs.outputs.compute-tiny }} compute-standard: ${{ steps.setup-outputs.outputs.compute-standard }} compute-larger: ${{ steps.setup-outputs.outputs.compute-larger }} compute-huge: ${{ steps.setup-outputs.outputs.compute-huge }} enterprise: ${{ steps.setup-outputs.outputs.enterprise }} go-build-tags: ${{ steps.setup-outputs.outputs.go-build-tags }} steps: - id: setup-outputs name: Setup outputs run: | github_repository="${{ github.repository }}" if [ "${github_repository##*/}" == "vault-enterprise" ] ; then # shellcheck disable=SC2129 echo 'compute-tiny=["self-hosted","ondemand","linux","type=m5.large"]' >> "$GITHUB_OUTPUT" echo 'compute-standard=["self-hosted","ondemand","linux","type=m5.xlarge"]' >> "$GITHUB_OUTPUT" echo 'compute-larger=["self-hosted","ondemand","linux","type=m5.2xlarge"]' >> "$GITHUB_OUTPUT" echo 'compute-huge=["self-hosted","ondemand","linux","type=m5.4xlarge"]' >> "$GITHUB_OUTPUT" echo 'enterprise=1' >> "$GITHUB_OUTPUT" echo 'go-build-tags=ent,enterprise' >> "$GITHUB_OUTPUT" else # shellcheck disable=SC2129 echo 'compute-tiny="ubuntu-latest"' >> "$GITHUB_OUTPUT" # 2 cores, 7 GB RAM, 14 GB SSD echo 'compute-standard="custom-linux-small-vault-latest"' >> "$GITHUB_OUTPUT" # 8 cores, 32 GB RAM, 300 GB SSD echo 'compute-larger="custom-linux-medium-vault-latest"' >> "$GITHUB_OUTPUT" # 16 cores, 64 GB RAM, 600 GB SSD echo 'compute-huge="custom-linux-xl-vault-latest"' >> "$GITHUB_OUTPUT" # 32-cores, 128 GB RAM, 1200 GB SSD echo 'enterprise=' >> "$GITHUB_OUTPUT" echo 'go-build-tags=' >> "$GITHUB_OUTPUT" fi setup-go-cache: name: Go Caches needs: - setup uses: ./.github/workflows/setup-go-cache.yml with: runs-on: ${{ needs.setup.outputs.compute-standard }} secrets: inherit diff-oss-ci: name: Diff OSS needs: - setup if: ${{ needs.setup.outputs.enterprise != '' && github.base_ref != '' }} runs-on: ${{ fromJSON(needs.setup.outputs.compute-tiny) }} steps: - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 with: fetch-depth: 0 - id: determine-branch run: | branch="${{ github.base_ref }}" if [[ $branch = release/* ]] ; then branch=${branch%%+ent} # Add OSS remote git config --global user.email "github-team-secret-vault-core@hashicorp.com" git config --global user.name "hc-github-team-secret-vault-core" git remote add oss https://github.com/hashicorp/vault.git git fetch oss "$branch" branch="oss/$branch" else branch="origin/$branch" fi echo "BRANCH=$branch" >> "$GITHUB_OUTPUT" - id: diff run: | ./.github/scripts/oss-diff.sh ${{ steps.determine-branch.outputs.BRANCH }} HEAD test-go: name: Run Go tests needs: - setup - setup-go-cache # Don't run this job for PR branches starting with: # 'ui/', 'backport/ui/', 'docs/', or 'backport/docs/' # OR # the 'docs' label is present if: | !startsWith(github.head_ref, 'ui/') && !startsWith(github.head_ref, 'backport/ui/') && !startsWith(github.head_ref, 'docs/') && !startsWith(github.head_ref, 'backport/docs/') && !contains(github.event.pull_request.labels.*.name, 'docs') uses: ./.github/workflows/test-go.yml with: # The regular Go tests use an extra runner to execute the # binary-dependent tests. We isolate them there so that the # other tests aren't slowed down waiting for a binary build. total-runners: 17 go-arch: amd64 go-build-tags: '${{ needs.setup.outputs.go-build-tags }},deadlock' runs-on: ${{ needs.setup.outputs.compute-larger }} enterprise: ${{ needs.setup.outputs.enterprise }} secrets: inherit test-go-race: name: Run Go tests with data race detection needs: - setup - setup-go-cache # Don't run this job for PR branches starting with: # 'ui/', 'backport/ui/', 'docs/', or 'backport/docs/' # OR # the 'docs' label is present if: | github.event.pull_request.draft == false && !startsWith(github.head_ref, 'ui/') && !startsWith(github.head_ref, 'backport/ui/') && !startsWith(github.head_ref, 'docs/') && !startsWith(github.head_ref, 'backport/docs/') && !contains(github.event.pull_request.labels.*.name, 'docs') uses: ./.github/workflows/test-go.yml with: total-runners: 16 env-vars: | { "VAULT_CI_GO_TEST_RACE": 1 } extra-flags: '-race' go-arch: amd64 go-build-tags: ${{ needs.setup.outputs.go-build-tags }} runs-on: ${{ needs.setup.outputs.compute-huge }} enterprise: ${{ needs.setup.outputs.enterprise }} name: "-race" secrets: inherit test-go-fips: name: Run Go tests with FIPS configuration # Only run this job for the enterprise repo if the PR branch doesn't start with: # 'ui/', 'backport/ui/', 'docs/', or 'backport/docs/' # OR # the 'docs' label is not present if: | github.event.pull_request.draft == false && needs.setup.outputs.enterprise == 1 && !startsWith(github.head_ref, 'ui/') && !startsWith(github.head_ref, 'backport/ui/') && !startsWith(github.head_ref, 'docs/') && !startsWith(github.head_ref, 'backport/docs/') && !contains(github.event.pull_request.labels.*.name, 'docs') needs: - setup - setup-go-cache uses: ./.github/workflows/test-go.yml with: total-runners: 16 env-vars: | { "GOEXPERIMENT": "boringcrypto" } go-arch: amd64 go-build-tags: '${{ needs.setup.outputs.go-build-tags }},deadlock,cgo,fips,fips_140_2' runs-on: ${{ needs.setup.outputs.compute-larger }} enterprise: ${{ needs.setup.outputs.enterprise }} name: "-fips" secrets: inherit test-ui: name: Test UI # The test-ui job is only run on: # - pushes to main and branches starting with "release/" # - PRs where the branch starts with "ui/", "backport/ui/", "merge", or when base branch starts with "release/" # - PRs with the "ui" label on github if: | github.ref_name == 'main' || startsWith(github.ref_name, 'release/') || startsWith(github.head_ref, 'ui/') || startsWith(github.head_ref, 'backport/ui/') || startsWith(github.head_ref, 'merge') || contains(github.event.pull_request.labels.*.name, 'ui') needs: - setup permissions: id-token: write contents: read runs-on: ${{ fromJSON(needs.setup.outputs.compute-larger) }} steps: - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 - uses: actions/setup-go@fac708d6674e30b6ba41289acaab6d4b75aa0753 # v4.0.1 with: go-version-file: ./.go-version cache: true # Setup node.js without caching to allow running npm install -g yarn (next step) - uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0 with: node-version-file: './ui/package.json' - id: install-yarn run: | npm install -g yarn # Setup node.js with caching using the yarn.lock file - uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0 with: node-version-file: './ui/package.json' cache: yarn cache-dependency-path: ui/yarn.lock - id: install-browser uses: browser-actions/setup-chrome@c485fa3bab6be59dce18dbc18ef6ab7cbc8ff5f1 # v1.2.0 - id: ui-dependencies name: ui-dependencies working-directory: ./ui run: | yarn install --frozen-lockfile npm rebuild node-sass - id: vault-auth name: Authenticate to Vault if: github.repository == 'hashicorp/vault-enterprise' run: vault-auth - id: secrets name: Fetch secrets if: github.repository == 'hashicorp/vault-enterprise' uses: hashicorp/vault-action@130d1f5f4fe645bb6c83e4225c04d64cfb62de6e with: url: ${{ steps.vault-auth.outputs.addr }} caCertificate: ${{ steps.vault-auth.outputs.ca_certificate }} token: ${{ steps.vault-auth.outputs.token }} secrets: | kv/data/github/hashicorp/vault-enterprise/github-token token | PRIVATE_REPO_GITHUB_TOKEN; kv/data/github/hashicorp/vault-enterprise/license license_1 | VAULT_LICENSE; - id: setup-git name: Setup Git if: github.repository == 'hashicorp/vault-enterprise' run: | git config --global url."https://${{ steps.secrets.outputs.PRIVATE_REPO_GITHUB_TOKEN }}@github.com".insteadOf https://github.com - id: build-go-dev name: build-go-dev run: | rm -rf ./pkg mkdir ./pkg make ci-bootstrap dev - id: test-ui name: test-ui env: VAULT_LICENSE: ${{ steps.secrets.outputs.VAULT_LICENSE }} run: | export PATH="${PWD}/bin:${PATH}" if [ "${{ github.repository }}" == 'hashicorp/vault' ] ; then export VAULT_LICENSE="${{ secrets.VAULT_LICENSE }}" fi # Run Ember tests cd ui mkdir -p test-results/qunit yarn test:oss - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: test-results-ui path: ui/test-results if: success() || failure() - uses: test-summary/action@62bc5c68de2a6a0d02039763b8c754569df99e3f # TSCCR: no entry for repository "test-summary/action" with: paths: "ui/test-results/qunit/results.xml" show: "fail" if: always() tests-completed: needs: - setup - setup-go-cache - test-go - test-ui if: always() runs-on: ${{ fromJSON(needs.setup.outputs.compute-tiny) }} steps: - run: | tr -d '\n' <<< '${{ toJSON(needs.*.result) }}' | grep -q -v -E '(failure|cancelled)' notify-tests-completed-failures-oss: if: ${{ always() && github.repository == 'hashicorp/vault' && needs.tests-completed.result == 'failure' && (github.ref_name == 'main' || startsWith(github.ref_name, 'release/')) }} runs-on: ubuntu-latest permissions: id-token: write contents: read strategy: fail-fast: false needs: - tests-completed steps: - name: send-notification uses: slackapi/slack-github-action@e28cf165c92ffef168d23c5c9000cffc8a25e117 # v1.24.0 # We intentionally aren't using the following here since it's from an internal repo # uses: hashicorp/cloud-gha-slack-notifier@730a033037b8e603adf99ebd3085f0fdfe75e2f4 #v1 env: SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }} with: channel-id: "C05AABYEA9Y" # sent to #feed-vault-ci-official payload: | {"text":"OSS test failures on ${{ github.ref_name }}","blocks":[{"type":"header","text":{"type":"plain_text","text":":rotating_light: OSS test failures :rotating_light:","emoji":true}},{"type":"divider"},{"type":"section","text":{"type":"mrkdwn","text":"test(s) failed on ${{ github.ref_name }}"},"accessory":{"type":"button","text":{"type":"plain_text","text":"View Failing Workflow","emoji":true},"url":"${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"}}]} notify-tests-completed-failures-ent: if: ${{ always() && github.repository == 'hashicorp/vault-enterprise' && needs.tests-completed.result == 'failure' && (github.ref_name == 'main' || startsWith(github.ref_name, 'release/')) }} runs-on: ['self-hosted', 'linux', 'small'] permissions: id-token: write contents: read strategy: fail-fast: false needs: - tests-completed steps: - id: vault-auth name: Vault Authenticate run: vault-auth - id: secrets name: Fetch Vault Secrets uses: hashicorp/vault-action@130d1f5f4fe645bb6c83e4225c04d64cfb62de6e with: url: ${{ steps.vault-auth.outputs.addr }} caCertificate: ${{ steps.vault-auth.outputs.ca_certificate }} token: ${{ steps.vault-auth.outputs.token }} secrets: | kv/data/github/${{ github.repository }}/github_actions_notifications_bot token | SLACK_BOT_TOKEN; - name: send-notification uses: hashicorp/cloud-gha-slack-notifier@730a033037b8e603adf99ebd3085f0fdfe75e2f4 #v1 with: channel-id: "C05AABYEA9Y" # sent to #feed-vault-ci-official slack-bot-token: ${{ steps.secrets.outputs.SLACK_BOT_TOKEN }} payload: | {"text":"Enterprise test failures on ${{ github.ref_name }}","blocks":[{"type":"header","text":{"type":"plain_text","text":":rotating_light: Enterprise test failures :rotating_light:","emoji":true}},{"type":"divider"},{"type":"section","text":{"type":"mrkdwn","text":"test(s) failed on ${{ github.ref_name }}"},"accessory":{"type":"button","text":{"type":"plain_text","text":"View Failing Workflow","emoji":true},"url":"${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"}}]} test-summary: name: Go test failures runs-on: ubuntu-latest if: success() || failure() || needs.tests-completed.result == 'skipped' needs: - test-go - test-go-fips - test-go-race - tests-completed steps: - name: Download failure summary uses: actions/download-artifact@v3 with: name: failure-summary - name: Prepare failure summary run: | # We will store the jq query results in a temp file temp_file_name=temp-$(date +%s) # The 'jq' command below filters and formats JSON data from input files to generate a failure summary report. # The query is a bit of a nightmare, though, so let's have a closer look at it. # # The command takes input from files matching the pattern "failure-summary-*.json". # The input files should contain streams of JSON objects(with no commas in between), # one per line, representing test results. # Each object should have the "Action" and "Package" keys. # # We invoke the command with two flags: # - '-r' specifies that the output should be in raw format, # without any JSON formatting. (I.e. no quotes). # - '-n' tells 'jq' not to read any input from the command line. # It is used when input is provided through the 'inputs' function or other methods. # # 'inputs': # Read JSON objects from the input files specified after the 'jq' command. # We assume that the input files contain one JSON object per line. # # 'select(.Action == "fail") | select(.Test != null)': # Filter JSON array to contain only objects where the value of "Action" is "fail" # and the value of "Test" key is not null. # # The remaining part of the query constructs a formatted string for each filtered JSON object`: # - '\(.Package)' and '\(.Test)' insert the values of the "Package" and "Test" keys into the string, # respectively. # - 'input_filename' is a special variable in 'jq' that represents the name of the input file being processed. # - 'split("-")' splits the input filename on the hyphen ("-") character and returns an array of the # resulting parts. # - '.sub(".json"; "")' removes the ".json" extension from the string. # - The third part of the filename is extracted using '.split("-") | .[2]'. # - If the fourth part of the filename exists, it contains the test type. # Otherwise, the default value "normal" is used. # - The '.sub(".json"; "")' removes the ".json" extension from the string. # # The filtered and formatted data is outputted as rows of a Markdown table, like this: # | pkg1 | test1 | 1 | normal | # | pkg2 | test2 | 4 | race | # | pkg3 | test3 | 6 | fips | jq -r -n 'inputs | select(.Action == "fail") | select(.Test != null) | "| \(.Package) | \(.Test) | \(input_filename | split("-") | .[2] | sub(".json"; "")) | \(input_filename | split("-") | .[3] // "normal" | sub(".json";"") )"' failure-summary-*.json | sort >> "$temp_file_name" # if there are test failures, present them in a format of a GH Markdown table if [ -s "$temp_file_name" ]; then # shellcheck disable=SC2129 # Here we create the headings for the summary table echo "### Go test failures" >> "$GITHUB_STEP_SUMMARY" echo "| Package | Test | Runner Index | Test Type |" >> "$GITHUB_STEP_SUMMARY" echo "| ------- | ---- | ------------ | --------- |" >> "$GITHUB_STEP_SUMMARY" cat "$temp_file_name" >> "$GITHUB_STEP_SUMMARY" else echo "### All Go tests passed! :white_check_mark:" >> "$GITHUB_STEP_SUMMARY" fi