[tracing] Update common files for branch 8.3.x #15
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
| # WARNING: Do not edit this file directly. Instead, go to: | |
| # | |
| # https://github.com/micronaut-projects/micronaut-project-template/tree/master/.github/workflows | |
| # | |
| # and edit them there. Note that it will be sync'ed to all the Micronaut repos | |
| # | |
| # Sonar analysis for pull requests raised from forks. | |
| # | |
| # GitHub never exposes repository secrets to a `pull_request` event raised from a | |
| # fork, so the "Run static analysis" step in gradle.yml is skipped there and the | |
| # "SonarCloud Code Analysis" check is never reported. Where that check is required | |
| # by a ruleset, contributor pull requests stay blocked forever. | |
| # | |
| # This workflow lets someone with write access run the analysis on demand, either | |
| # from the Actions tab or by commenting "/sonar" on the pull request. The build | |
| # scripts of the pull request are executed with SONAR_TOKEN in the environment, so | |
| # the diff MUST be reviewed first: the human trigger IS the security boundary. | |
| # | |
| # Pass the commit that was reviewed - "/sonar <full 40 character sha>", or the `sha` | |
| # dispatch input - to close the window in which the contributor pushes again between | |
| # the review and the trigger. Without it the run analyses whatever the head is when | |
| # it starts. | |
| name: Sonar PR | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| pr: | |
| description: 'Pull request number. Review the diff first - this runs the pull request build with SONAR_TOKEN.' | |
| required: true | |
| type: string | |
| sha: | |
| description: 'The full 40 character SHA of the commit you reviewed. Optional; if given, the run is refused unless it is still the head.' | |
| required: false | |
| type: string | |
| issue_comment: | |
| types: [created] | |
| permissions: | |
| contents: read | |
| jobs: | |
| # Authorisation, command parsing and pull request resolution are kept in their | |
| # own job so that a refusal stops the run outright: a step cannot skip the rest | |
| # of its job, so gating inline would let a rejected trigger fall through to the | |
| # analysis. This job never checks out or executes pull request code, which is | |
| # why it is also the only one holding a write-capable GH_TOKEN. | |
| authorize: | |
| name: "authorize" | |
| # `startsWith(comment.body, '/sonar')` is deliberately coarse: a false | |
| # negative here is a silent no-op that looks like a broken workflow, whereas | |
| # a false positive only starts this cheap job, which then rejects the exact | |
| # command below. Anything stricter risks missing trailing whitespace or a | |
| # line ending in the comment body. | |
| if: >- | |
| github.repository != 'micronaut-projects/micronaut-project-template' && | |
| (github.event_name == 'workflow_dispatch' || | |
| (github.event.issue.pull_request != null && | |
| startsWith(github.event.comment.body, '/sonar'))) | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| # `gh pr view` needs pull request read; the acknowledgement needs write. | |
| # A comment on a pull request is an issue comment, so the reaction | |
| # endpoint needs `issues: write` rather than `pull-requests: write`. | |
| issues: write | |
| pull-requests: write | |
| outputs: | |
| proceed: ${{ steps.command.outputs.proceed }} | |
| head_sha: ${{ steps.pr.outputs.head_sha }} | |
| head_branch: ${{ steps.pr.outputs.head_branch }} | |
| base_branch: ${{ steps.pr.outputs.base_branch }} | |
| env: | |
| SONAR_TOKEN_AVAILABLE: ${{ secrets.SONAR_TOKEN != '' }} | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| PR_NUMBER: ${{ github.event_name == 'workflow_dispatch' && inputs.pr || github.event.issue.number }} | |
| steps: | |
| - name: "β Parse the /sonar command" | |
| id: command | |
| env: | |
| COMMENT_BODY: ${{ github.event.comment.body }} | |
| INPUT_SHA: ${{ inputs.sha }} | |
| run: | | |
| proceed=true | |
| requested_sha="$INPUT_SHA" | |
| if [ "$GITHUB_EVENT_NAME" = "issue_comment" ]; then | |
| line=$(printf '%s' "$COMMENT_BODY" | head -n 1 | tr -d '\r') | |
| # Word splitting does the trimming. `xargs` is avoided because it | |
| # aborts on an unbalanced quote in the comment, and `set -f` stops a | |
| # `*` in the comment being expanded against the workspace. | |
| set -f | |
| # shellcheck disable=SC2086 | |
| set -- $line | |
| set +f | |
| if [ "${1:-}" != "/sonar" ]; then | |
| echo "Ignoring comment: '${1:-}' is not the '/sonar' command." | |
| proceed=false | |
| else | |
| requested_sha="${2:-}" | |
| fi | |
| fi | |
| # The full 40 characters, not a prefix. A short prefix is cheap to | |
| # grind, so a contributor could push an unreviewed commit that still | |
| # matched it and the run would report itself as pinned. | |
| if [ "$proceed" = "true" ] && [ -n "$requested_sha" ]; then | |
| case "$requested_sha" in | |
| *[!0-9a-fA-F]*|"") | |
| echo "::error::'${requested_sha}' is not a commit SHA." | |
| exit 1 | |
| ;; | |
| esac | |
| if [ "${#requested_sha}" -ne 40 ]; then | |
| echo "::error::'${requested_sha}' is abbreviated. Pass the full 40 character SHA, which 'gh pr view ${PR_NUMBER} --json headRefOid' will print." | |
| exit 1 | |
| fi | |
| requested_sha=$(printf '%s' "$requested_sha" | tr 'A-F' 'a-f') | |
| fi | |
| echo "proceed=${proceed}" >> "$GITHUB_OUTPUT" | |
| echo "requested_sha=${requested_sha}" >> "$GITHUB_OUTPUT" | |
| - name: "π Verify the requester has write access" | |
| if: steps.command.outputs.proceed == 'true' | |
| run: | | |
| if [ "$SONAR_TOKEN_AVAILABLE" != "true" ]; then | |
| echo "::error::SONAR_TOKEN is not configured for this repository." | |
| exit 1 | |
| fi | |
| # A non-collaborator makes this endpoint answer 404, so the exit code | |
| # has to be inspected before the permission value: otherwise the step | |
| # dies on gh's own error and never reports why the run was refused. | |
| if ! permission=$(gh api "repos/${GITHUB_REPOSITORY}/collaborators/${GITHUB_ACTOR}/permission" -q '.permission' 2>"${RUNNER_TEMP}/perm.err"); then | |
| echo "::error::Unable to determine ${GITHUB_ACTOR}'s permission on ${GITHUB_REPOSITORY}; they are most likely not a collaborator. $(cat "${RUNNER_TEMP}/perm.err")" | |
| exit 1 | |
| fi | |
| echo "${GITHUB_ACTOR} has '${permission}' permission" | |
| case "$permission" in | |
| admin|write) ;; | |
| *) | |
| echo "::error::${GITHUB_ACTOR} has '${permission}' permission on ${GITHUB_REPOSITORY}; running Sonar requires write access." | |
| exit 1 | |
| ;; | |
| esac | |
| - name: "π Resolve the pull request" | |
| id: pr | |
| if: steps.command.outputs.proceed == 'true' | |
| env: | |
| REQUESTED_SHA: ${{ steps.command.outputs.requested_sha }} | |
| run: | | |
| gh pr view "$PR_NUMBER" --repo "$GITHUB_REPOSITORY" \ | |
| --json headRefOid,headRefName,baseRefName,isCrossRepository > "${RUNNER_TEMP}/pr.json" | |
| head_sha=$(jq -r '.headRefOid' "${RUNNER_TEMP}/pr.json") | |
| # Close the window between the maintainer reading the diff and this run | |
| # starting: if they named the commit they reviewed, refuse to analyse | |
| # anything else. | |
| if [ -n "$REQUESTED_SHA" ]; then | |
| if [ "$(printf '%s' "$head_sha" | tr 'A-F' 'a-f')" != "$REQUESTED_SHA" ]; then | |
| echo "::error::#${PR_NUMBER} has moved on to ${head_sha} since ${REQUESTED_SHA} was reviewed. Review the new commit and trigger again." | |
| exit 1 | |
| fi | |
| else | |
| echo "::warning::No reviewed commit was given, so ${head_sha} is analysed as-is. Pass '/sonar <sha>' to pin the run to the commit you reviewed." | |
| fi | |
| { | |
| echo "head_sha=${head_sha}" | |
| echo "head_branch=$(jq -r '.headRefName' "${RUNNER_TEMP}/pr.json")" | |
| echo "base_branch=$(jq -r '.baseRefName' "${RUNNER_TEMP}/pr.json")" | |
| } >> "$GITHUB_OUTPUT" | |
| { | |
| echo "### Sonar analysis of #${PR_NUMBER}" | |
| echo "" | |
| echo "| | |" | |
| echo "|---|---|" | |
| echo "| Head commit | \`${head_sha}\` |" | |
| echo "| Pinned to reviewed commit | ${REQUESTED_SHA:-no} |" | |
| echo "| Head branch | \`$(jq -r '.headRefName' "${RUNNER_TEMP}/pr.json")\` |" | |
| echo "| Base branch | \`$(jq -r '.baseRefName' "${RUNNER_TEMP}/pr.json")\` |" | |
| echo "| From a fork | $(jq -r '.isCrossRepository' "${RUNNER_TEMP}/pr.json") |" | |
| echo "| Triggered by | @${GITHUB_ACTOR} |" | |
| } >> "$GITHUB_STEP_SUMMARY" | |
| # Acknowledged from this job, not from `sonar`, so that the write-capable | |
| # GH_TOKEN is never present in an environment that runs pull request code. | |
| - name: "π¬ Acknowledge the comment" | |
| if: steps.command.outputs.proceed == 'true' && github.event_name == 'issue_comment' | |
| continue-on-error: true | |
| env: | |
| HEAD_SHA: ${{ steps.pr.outputs.head_sha }} | |
| COMMENT_ID: ${{ github.event.comment.id }} | |
| run: | | |
| gh api --method POST \ | |
| "repos/${GITHUB_REPOSITORY}/issues/comments/${COMMENT_ID}/reactions" \ | |
| -f content='eyes' | |
| gh pr comment "$PR_NUMBER" --repo "$GITHUB_REPOSITORY" --body \ | |
| "Running Sonar analysis on \`${HEAD_SHA}\`: ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}" | |
| sonar: | |
| name: "sonar" | |
| needs: authorize | |
| if: needs.authorize.outputs.proceed == 'true' | |
| runs-on: ubuntu-latest | |
| # Concurrency is scoped to this job rather than the workflow: at workflow | |
| # level, every unrelated comment on the pull request would join the group and | |
| # cancel an analysis that is already running. | |
| concurrency: | |
| group: sonar-pr-${{ github.event_name == 'workflow_dispatch' && inputs.pr || github.event.issue.number }} | |
| cancel-in-progress: true | |
| # This job checks out and executes contributor-controlled build scripts, so it | |
| # holds no write permission and no GH_TOKEN. SONAR_TOKEN is the only secret it | |
| # needs, and it is scoped to the analysis step alone. | |
| permissions: | |
| contents: read | |
| env: | |
| TESTCONTAINERS_RYUK_DISABLED: true | |
| PR_NUMBER: ${{ github.event_name == 'workflow_dispatch' && inputs.pr || github.event.issue.number }} | |
| steps: | |
| # https://github.com/actions/virtual-environments/issues/709 | |
| - name: Remove system JDKs | |
| run: | | |
| sudo rm -rf /usr/lib/jvm/* | |
| unset JAVA_HOME | |
| export PATH=$(echo "$PATH" | tr ':' '\n' | grep -v '/usr/lib/jvm' | paste -sd:) | |
| - name: "π Free disk space" | |
| run: | | |
| sudo rm -rf "/usr/local/share/boost" | |
| sudo rm -rf "$AGENT_TOOLSDIRECTORY" | |
| sudo rm -rf "/opt/ghc" | |
| sudo rm -rf "/usr/share/dotnet" | |
| sudo rm -rf "/usr/local/lib/android" | |
| sudo apt-get clean | |
| df -h | |
| # Checked out by commit rather than by refs/pull/N/head so that the analysed | |
| # code is exactly what the authorize job recorded, even if the contributor | |
| # pushes again while the build is in flight. | |
| - name: "π₯ Checkout the pull request" | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 | |
| with: | |
| ref: ${{ needs.authorize.outputs.head_sha }} | |
| fetch-depth: 0 | |
| persist-credentials: false | |
| # The only secret left in this job besides SONAR_TOKEN. It is a `with:` | |
| # input, so it reaches this action's step alone and not the Gradle steps, | |
| # and the job's token is `contents: read`. Kept because dropping it makes | |
| # release resolution flaky against the GitHub API rate limit. | |
| - name: "π§ Setup GraalVM CE" | |
| uses: graalvm/setup-graalvm@0426e2e191540e8514dff98dc52a5f5146a2a276 # v1 | |
| with: | |
| distribution: 'graalvm' | |
| java-version: '25' | |
| github-token: ${{ secrets.GITHUB_TOKEN }} | |
| - name: "π§ Setup Gradle" | |
| uses: gradle/actions/setup-gradle@3f5f9adaf7d9fecd50b5935e54106014257a94e6 # v6 | |
| - name: "β Optional setup step" | |
| run: | | |
| [ -f ./setup.sh ] && ./setup.sh || [ ! -f ./setup.sh ] | |
| # Unlike gradle.yml, no Develocity or GitHub credentials are passed here. | |
| # gradle.yml can afford them because on a fork pull request GitHub withholds | |
| # every secret and they arrive empty; this workflow runs in the base | |
| # repository, where they would be real and readable by contributor | |
| # controlled build scripts and tests. The remote cache credentials matter | |
| # most: a leak there poisons every later build in every repository. The | |
| # cost is losing build scans and remote cache reads, and fork pull requests | |
| # already build without any of these today. | |
| - name: "π Build with Gradle" | |
| run: | | |
| ./gradlew check jacocoReport --no-daemon --continue | |
| # Analysis is a second Gradle invocation rather than `check jacocoReport | |
| # sonar` in one, matching gradle.yml, for two reasons. `sonar` needs | |
| # `--no-parallel`, and applying that to the whole build would serialise the | |
| # entire test suite. And SONAR_TOKEN would otherwise be exported to every | |
| # test in the pull request under analysis, rather than only to the scanner. | |
| # The repeated configuration time is the lesser cost. | |
| # | |
| # The pull request coordinates have to be passed explicitly: this is not a | |
| # `pull_request` event, so the scanner's own CI detection would otherwise | |
| # analyse this as a branch build of the default branch. | |
| - name: "π Run static analysis" | |
| env: | |
| SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} | |
| HEAD_BRANCH: ${{ needs.authorize.outputs.head_branch }} | |
| BASE_BRANCH: ${{ needs.authorize.outputs.base_branch }} | |
| HEAD_SHA: ${{ needs.authorize.outputs.head_sha }} | |
| run: | | |
| ./gradlew sonar --no-parallel --continue \ | |
| -Dsonar.pullrequest.key="${PR_NUMBER}" \ | |
| -Dsonar.pullrequest.branch="${HEAD_BRANCH}" \ | |
| -Dsonar.pullrequest.base="${BASE_BRANCH}" \ | |
| -Dsonar.scm.revision="${HEAD_SHA}" | |
| - name: "β Optional cleanup step" | |
| if: always() | |
| run: | | |
| [ -f ./cleanup.sh ] && ./cleanup.sh || [ ! -f ./cleanup.sh ] |