3333 runs-on : ${{ matrix.os }}
3434 timeout-minutes : 45
3535
36+ concurrency :
37+ cancel-in-progress : ${{ github.event_name == 'pull_request' || false }}
38+ group : pr-checks-unit-tests-${{ github.ref }}-${{ github.event_name }}-${{ matrix.os }}-node${{ matrix['node-version'] }}
39+
3640 steps :
3741 - name : Prepare git (Windows)
3842 if : runner.os == 'Windows'
@@ -71,22 +75,21 @@ jobs:
7175 sarif_file : eslint.sarif
7276 category : eslint
7377
74- # Verifying the PR checks are up-to-date requires Node 24. The PR checks are not dependent
75- # on the main codebase and therefore do not need to be run as part of the same matrix that
76- # we use for the `unit-tests` job.
77- verify-pr-checks :
78- name : Verify PR checks
78+ # These checks do not need to be run as part of the same matrix that we use for the `unit-tests`
79+ # job.
80+ other-checks :
81+ name : Other checks
7982 if : github.triggering_actor != 'dependabot[bot]'
8083 permissions :
8184 contents : read
82- runs-on : ubuntu-slim
85+ runs-on : ubuntu-latest
8386 timeout-minutes : 10
8487
85- steps :
86- - name : Prepare git (Windows)
87- if : runner.os == 'Windows'
88- run : git config --global core.autocrlf false
88+ concurrency :
89+ cancel-in-progress : ${{ github.event_name == 'pull_request' || false }}
90+ group : pr-checks-pr-checks-${{ github.ref }}-${{ github.event_name }}
8991
92+ steps :
9093 - name : Checkout repository
9194 uses : actions/checkout@v6
9295
@@ -97,57 +100,134 @@ jobs:
97100 cache : ' npm'
98101
99102 - name : Install dependencies
103+ id : install-deps
100104 run : npm ci
101105
102106 - name : Verify PR checks up to date
103- if : always()
107+ if : ${{ !cancelled() && steps.install-deps.outcome == 'success' }}
104108 run : .github/workflows/script/verify-pr-checks.sh
105109
106110 - name : Run pr-checks tests
107- if : always()
111+ if : ${{ !cancelled() && steps.install-deps.outcome == 'success' }}
108112 working-directory : pr-checks
109113 run : npx tsx --test
110114
111- check-node-version :
112- if : github.triggering_actor != 'dependabot[bot]'
113- name : Check Action Node versions
114- runs-on : ubuntu-latest
115- timeout-minutes : 45
116- env :
117- BASE_REF : ${{ github.base_ref }}
118-
119- permissions :
120- contents : read
121-
122- steps :
123- - uses : actions/checkout@v6
124- - id : head-version
125- name : Verify all Actions use the same Node version
115+ - name : Verify all Actions use the same Node version
116+ id : head-version
126117 run : |
127- NODE_VERSION=$(find . -name "action.yml" -exec yq -e '.runs.using' {} \; | grep node | sort | uniq )
118+ 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 | .[]' )
128119 echo "NODE_VERSION: ${NODE_VERSION}"
129120 if [[ $(echo "$NODE_VERSION" | wc -l) -gt 1 ]]; then
130121 echo "::error::More than one node version used in 'action.yml' files."
131122 exit 1
132123 fi
133124 echo "node_version=${NODE_VERSION}" >> $GITHUB_OUTPUT
134125
135- - id : checkout-base
136- name : ' Backport: Check out base ref'
126+ - name : Fetch base commit
127+ id : fetch-base
128+ # Forks and Dependabot PRs don't have permission to write comments, so skip the repo size
129+ # check in those cases.
130+ if : >-
131+ github.event_name == 'pull_request' &&
132+ github.event.pull_request.head.repo.full_name == github.repository &&
133+ github.event.pull_request.user.login != 'dependabot[bot]'
134+ env :
135+ BASE_SHA : ${{ github.event.pull_request.base.sha }}
136+ HEAD_SHA : ${{ github.event.pull_request.head.sha }}
137+ GH_TOKEN : ${{ secrets.GITHUB_TOKEN }}
138+ run : |
139+ # Compare against the merge base so the size delta reflects only the commits actually
140+ # added by this PR, ignoring any changes that have landed on the base branch since the
141+ # PR branched off.
142+ merge_base=$(gh api "repos/$GITHUB_REPOSITORY/compare/$BASE_SHA...$HEAD_SHA" --jq '.merge_base_commit.sha')
143+ echo "merge_base=$merge_base" >> "$GITHUB_OUTPUT"
144+ git fetch --no-tags --depth=1 origin "$merge_base" "$HEAD_SHA"
145+
146+ - name : Check repo size
147+ if : steps.fetch-base.outcome == 'success'
148+ working-directory : pr-checks
149+ env :
150+ BASE_REF : ${{ github.event.pull_request.base.ref }}
151+ BASE_SHA : ${{ steps.fetch-base.outputs.merge_base }}
152+ HEAD_SHA : ${{ github.event.pull_request.head.sha }}
153+ RUN_URL : ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
154+ run : npx tsx check-repo-size.ts --output-dir "$RUNNER_TEMP/repo-size"
155+
156+ - name : Upload repo size comment
157+ if : steps.fetch-base.outcome == 'success'
158+ uses : actions/upload-artifact@v7
159+ with :
160+ name : repo-size-comment
161+ path : ${{ runner.temp }}/repo-size/
162+ if-no-files-found : error
163+
164+ - name : ' Backport: Check out base ref'
165+ id : checkout-base
137166 if : ${{ startsWith(github.head_ref, 'backport-') }}
138167 uses : actions/checkout@v6
139168 with :
140- ref : ${{ env.BASE_REF }}
169+ ref : ${{ github.base_ref }}
141170
142171 - name : ' Backport: Verify Node versions unchanged'
143172 if : steps.checkout-base.outcome == 'success'
144173 env :
145174 HEAD_VERSION : ${{ steps.head-version.outputs.node_version }}
146175 run : |
147- BASE_VERSION=$(find . -name "action.yml" -exec yq -e '.runs.using' {} \; | grep node | sort | uniq )
176+ 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 | .[]' )
148177 echo "HEAD_VERSION: ${HEAD_VERSION}"
149178 echo "BASE_VERSION: ${BASE_VERSION}"
150179 if [[ "$BASE_VERSION" != "$HEAD_VERSION" ]]; then
151180 echo "::error::Cannot change the Node version of an Action in a backport PR."
152181 exit 1
153182 fi
183+
184+ post-repo-size-comment :
185+ name : Post repo size comment
186+ needs : other-checks
187+ # Keep write permissions isolated from the job that checks out and tests PR code. This job only
188+ # posts the candidate comment body produced by the read-only `pr-checks` job.
189+ if : >-
190+ github.event_name == 'pull_request' &&
191+ github.event.pull_request.head.repo.full_name == github.repository &&
192+ github.event.pull_request.user.login != 'dependabot[bot]' &&
193+ needs.other-checks.result == 'success'
194+ permissions :
195+ contents : read
196+ pull-requests : write
197+ runs-on : ubuntu-slim
198+ timeout-minutes : 10
199+
200+ concurrency :
201+ cancel-in-progress : true
202+ group : check-repo-size-${{ github.event.pull_request.number }}
203+
204+ steps :
205+ - name : Download repo size comment
206+ uses : actions/download-artifact@v8
207+ with :
208+ name : repo-size-comment
209+ path : repo-size-comment
210+
211+ - name : Post repo size comment
212+ env :
213+ COMMENT_MARKER : " <!-- repo-size-diff-bot -->"
214+ GH_TOKEN : ${{ secrets.GITHUB_TOKEN }}
215+ PR_NUMBER : ${{ github.event.pull_request.number }}
216+ run : |
217+ significant=$(jq -r '.significant' repo-size-comment/metadata.json)
218+ comment_id=$(
219+ gh api "repos/$GITHUB_REPOSITORY/issues/$PR_NUMBER/comments" \
220+ --paginate \
221+ --jq ".[] | select(.body | contains(\"$COMMENT_MARKER\")) | .id" \
222+ | head -n 1
223+ )
224+
225+ if [[ -n "$comment_id" ]]; then
226+ echo "Updating existing comment $comment_id."
227+ gh api --method PATCH "repos/$GITHUB_REPOSITORY/issues/comments/$comment_id" --field body=@repo-size-comment/body.md
228+ elif [[ "$significant" == "true" ]]; then
229+ echo "Creating new repo size comment."
230+ gh api --method POST "repos/$GITHUB_REPOSITORY/issues/$PR_NUMBER/comments" --field body=@repo-size-comment/body.md
231+ else
232+ echo "Skipping repo size comment because the delta is below the threshold and no sticky comment exists."
233+ fi
0 commit comments