name: PR Checks on: push: pull_request: # Run checks on reopened draft PRs to support triggering PR checks on draft PRs that were opened # by other workflows. types: [opened, synchronize, reopened, ready_for_review] merge_group: types: [checks_requested] workflow_dispatch: defaults: run: shell: bash jobs: unit-tests: name: Unit Tests if: github.triggering_actor != 'dependabot[bot]' strategy: fail-fast: false matrix: os: [ubuntu-latest, macos-latest, windows-latest] node-version: [20, 24] permissions: contents: read security-events: write # needed to upload ESLint results runs-on: ${{ matrix.os }} timeout-minutes: 45 concurrency: cancel-in-progress: ${{ github.event_name == 'pull_request' || false }} group: pr-checks-unit-tests-${{ github.ref }}-${{ github.event_name }}-${{ matrix.os }}-node${{ matrix['node-version'] }} steps: - name: Prepare git (Windows) if: runner.os == 'Windows' run: git config --global core.autocrlf false - uses: actions/checkout@v6 - name: Set up Node.js uses: actions/setup-node@v6 with: node-version: ${{ matrix.node-version }} cache: 'npm' - name: Install dependencies run: | # Use the system Bash shell to ensure we can run commands like `npm ci` # that are not available in the default shell on Windows. npm config set script-shell bash npm ci - name: Verify compiled JS up to date run: .github/workflows/script/check-js.sh - name: Run unit tests if: always() run: npm test - name: Lint if: always() && matrix.os != 'windows-latest' run: npm run lint-ci - name: Upload sarif uses: github/codeql-action/upload-sarif@v4 if: matrix.os == 'ubuntu-latest' && matrix.node-version == 24 with: sarif_file: eslint.sarif category: eslint # These checks do not need to be run as part of the same matrix that we use for the `unit-tests` # job. other-checks: name: Other checks if: github.triggering_actor != 'dependabot[bot]' permissions: contents: read runs-on: ubuntu-latest timeout-minutes: 10 concurrency: cancel-in-progress: ${{ github.event_name == 'pull_request' || false }} group: pr-checks-pr-checks-${{ github.ref }}-${{ github.event_name }} steps: - name: Checkout repository uses: actions/checkout@v6 - name: Set up Node.js uses: actions/setup-node@v6 with: node-version: 24 cache: 'npm' - name: Install dependencies id: install-deps run: npm ci - name: Verify PR checks up to date if: ${{ !cancelled() && steps.install-deps.outcome == 'success' }} run: .github/workflows/script/verify-pr-checks.sh - name: Run pr-checks tests if: ${{ !cancelled() && steps.install-deps.outcome == 'success' }} working-directory: pr-checks run: npx tsx --test - name: Verify all Actions use the same Node version id: head-version run: | NODE_VERSION=$(find . -path "*/node_modules" -prune -o -name "action.yml" -exec yq -o=json '.runs.using' {} \; | jq -rs '[.[] | select(. != null and startswith("node"))] | unique | .[]') echo "NODE_VERSION: ${NODE_VERSION}" if [[ $(echo "$NODE_VERSION" | wc -l) -gt 1 ]]; then echo "::error::More than one node version used in 'action.yml' files." exit 1 fi echo "node_version=${NODE_VERSION}" >> $GITHUB_OUTPUT - name: Fetch base commit id: fetch-base # Forks and Dependabot PRs don't have permission to write comments, so skip the repo size # check in those cases. if: >- github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository && github.event.pull_request.user.login != 'dependabot[bot]' env: BASE_SHA: ${{ github.event.pull_request.base.sha }} HEAD_SHA: ${{ github.event.pull_request.head.sha }} GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | # Compare against the merge base so the size delta reflects only the commits actually # added by this PR, ignoring any changes that have landed on the base branch since the # PR branched off. merge_base=$(gh api "repos/$GITHUB_REPOSITORY/compare/$BASE_SHA...$HEAD_SHA" --jq '.merge_base_commit.sha') echo "merge_base=$merge_base" >> "$GITHUB_OUTPUT" git fetch --no-tags --depth=1 origin "$merge_base" "$HEAD_SHA" - name: Check repo size if: steps.fetch-base.outcome == 'success' working-directory: pr-checks env: BASE_REF: ${{ github.event.pull_request.base.ref }} BASE_SHA: ${{ steps.fetch-base.outputs.merge_base }} HEAD_SHA: ${{ github.event.pull_request.head.sha }} RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} run: npx tsx check-repo-size.ts --output-dir "$RUNNER_TEMP/repo-size" - name: Upload repo size comment if: steps.fetch-base.outcome == 'success' uses: actions/upload-artifact@v7 with: name: repo-size-comment path: ${{ runner.temp }}/repo-size/ if-no-files-found: error - name: 'Backport: Check out base ref' id: checkout-base if: ${{ startsWith(github.head_ref, 'backport-') }} uses: actions/checkout@v6 with: ref: ${{ github.base_ref }} - name: 'Backport: Verify Node versions unchanged' if: steps.checkout-base.outcome == 'success' env: HEAD_VERSION: ${{ steps.head-version.outputs.node_version }} run: | BASE_VERSION=$(find . -path "*/node_modules" -prune -o -name "action.yml" -exec yq -o=json '.runs.using' {} \; | jq -rs '[.[] | select(. != null and startswith("node"))] | unique | .[]') echo "HEAD_VERSION: ${HEAD_VERSION}" echo "BASE_VERSION: ${BASE_VERSION}" if [[ "$BASE_VERSION" != "$HEAD_VERSION" ]]; then echo "::error::Cannot change the Node version of an Action in a backport PR." exit 1 fi post-repo-size-comment: name: Post repo size comment needs: other-checks # Keep write permissions isolated from the job that checks out and tests PR code. This job only # posts the candidate comment body produced by the read-only `pr-checks` job. if: >- github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository && github.event.pull_request.user.login != 'dependabot[bot]' && needs.other-checks.result == 'success' permissions: contents: read pull-requests: write runs-on: ubuntu-slim timeout-minutes: 10 concurrency: cancel-in-progress: true group: check-repo-size-${{ github.event.pull_request.number }} steps: - name: Download repo size comment uses: actions/download-artifact@v8 with: name: repo-size-comment path: repo-size-comment - name: Post repo size comment env: COMMENT_MARKER: "" GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} PR_NUMBER: ${{ github.event.pull_request.number }} run: | significant=$(jq -r '.significant' repo-size-comment/metadata.json) comment_id=$( gh api "repos/$GITHUB_REPOSITORY/issues/$PR_NUMBER/comments" \ --paginate \ --jq ".[] | select(.body | contains(\"$COMMENT_MARKER\")) | .id" \ | head -n 1 ) if [[ -n "$comment_id" ]]; then echo "Updating existing comment $comment_id." gh api --method PATCH "repos/$GITHUB_REPOSITORY/issues/comments/$comment_id" --field body=@repo-size-comment/body.md elif [[ "$significant" == "true" ]]; then echo "Creating new repo size comment." gh api --method POST "repos/$GITHUB_REPOSITORY/issues/$PR_NUMBER/comments" --field body=@repo-size-comment/body.md else echo "Skipping repo size comment because the delta is below the threshold and no sticky comment exists." fi