@@ -8,6 +8,7 @@ permissions:
88 actions : read
99 contents : read
1010 packages : write
11+ pull-requests : read
1112
1213concurrency :
1314 group : |
2526 runs-on : ${{ vars.RUNNER_CHECK_SKIP || 'ubuntu-24.04-arm' }}
2627 outputs :
2728 skip : ${{ steps.skip-check.outputs.skip }}
29+ run-build-tests : ${{ steps.classify-changes.outputs.run-build-tests }}
2830 runner-amd64 : ${{ steps.select-runner.outputs.runner_amd64 }}
2931 runner-arm64 : ${{ steps.select-runner.outputs.runner_arm64 }}
3032 use-blacksmith : ${{ steps.select-runner.outputs.use_blacksmith }}
@@ -39,18 +41,102 @@ jobs:
3941 run : |
4042 if [[ "${{ github.event_name }}" == "push" && "${{ vars.SKIP_ON_PUSH }}" != "" ]]; then
4143 echo "Skipping build on push due to SKIP_ON_PUSH environment variable"
42- echo "skip=true" >> $GITHUB_OUTPUT
44+ echo "skip=true" >> " $GITHUB_OUTPUT"
4345 elif [[ "${{ github.event_name }}" == "pull_request_target" && "${{ vars.SKIP_ON_PR }}" != "" ]]; then
4446 echo "Skipping build on pull request due to SKIP_ON_PR environment variable"
45- echo "skip=true" >> $GITHUB_OUTPUT
47+ echo "skip=true" >> " $GITHUB_OUTPUT"
4648 else
47- echo "skip=false" >> $GITHUB_OUTPUT
49+ echo "skip=false" >> " $GITHUB_OUTPUT"
4850 fi
4951
5052 - name : Checkout code
5153 if : ${{ steps.skip-check.outputs.skip == 'false' }}
5254 uses : actions/checkout@v6
5355
56+ - name : Collect changed paths
57+ id : changed-paths
58+ if : ${{ steps.skip-check.outputs.skip == 'false' }}
59+ uses : actions/github-script@v8
60+ env :
61+ CHANGED_PATHS_FILE : ${{ runner.temp }}/changed-paths.json
62+ with :
63+ script : |
64+ const fs = require('fs');
65+ let complete = true;
66+ let files = [];
67+
68+ try {
69+ if (context.eventName === 'pull_request_target') {
70+ files = await github.paginate(github.rest.pulls.listFiles, {
71+ owner: context.repo.owner,
72+ repo: context.repo.repo,
73+ pull_number: context.payload.pull_request.number,
74+ per_page: 100,
75+ });
76+ if (files.length >= 3000) {
77+ complete = false;
78+ core.warning('Pull request file list reached the GitHub API limit');
79+ }
80+ } else if (context.eventName === 'push') {
81+ const after = context.payload.after;
82+ if (context.ref.startsWith('refs/tags/')) {
83+ complete = false;
84+ } else {
85+ // Comparing with the last successful run preserves coverage when a
86+ // later push cancels an in-flight run before its build matrix finishes.
87+ const previousRuns = await github.rest.actions.listWorkflowRuns({
88+ owner: context.repo.owner,
89+ repo: context.repo.repo,
90+ workflow_id: 'build.yml',
91+ branch: context.ref.replace('refs/heads/', ''),
92+ event: 'push',
93+ status: 'success',
94+ per_page: 1,
95+ });
96+ const previousSha = previousRuns.data.workflow_runs[0]?.head_sha;
97+ if (!previousSha) {
98+ complete = false;
99+ } else {
100+ const comparison = await github.rest.repos.compareCommitsWithBasehead({
101+ owner: context.repo.owner,
102+ repo: context.repo.repo,
103+ basehead: `${previousSha}...${after}`,
104+ });
105+ files = comparison.data.files || [];
106+ if (files.length >= 300) {
107+ complete = false;
108+ core.warning('Push file list reached the GitHub API limit');
109+ }
110+ }
111+ }
112+ } else {
113+ complete = false;
114+ }
115+ } catch (error) {
116+ complete = false;
117+ core.warning(`Unable to collect a complete changed path list: ${error.message}`);
118+ }
119+
120+ const paths = [...new Set(files.flatMap(file =>
121+ [file.filename, file.previous_filename].filter(Boolean)
122+ ))].sort();
123+ fs.writeFileSync(process.env.CHANGED_PATHS_FILE, JSON.stringify(paths));
124+ core.setOutput('complete', complete.toString());
125+
126+ - name : Classify changed paths
127+ id : classify-changes
128+ if : ${{ steps.skip-check.outputs.skip == 'false' }}
129+ env :
130+ CHANGED_PATHS_COMPLETE : ${{ steps.changed-paths.outputs.complete }}
131+ CHANGED_PATHS_FILE : ${{ runner.temp }}/changed-paths.json
132+ run : |
133+ if ! python3 .github/workflows/classify_ci_changes.py \
134+ --complete "$CHANGED_PATHS_COMPLETE" "$CHANGED_PATHS_FILE"; then
135+ echo "Path classification failed; running the full build and test matrix"
136+ echo "run-build-tests=true" >> "$GITHUB_OUTPUT"
137+ echo "decision-reason=path classification failed" >> "$GITHUB_OUTPUT"
138+ fi
139+
54140 - name : Select runners
55141 id : select-runner
56142 if : ${{ steps.skip-check.outputs.skip == 'false' }}
65151
66152 - name : Get base image digest
67153 id : base-image
68- if : ${{ steps.skip-check.outputs.skip == 'false' }}
154+ if : |
155+ steps.skip-check.outputs.skip == 'false' &&
156+ steps.classify-changes.outputs.run-build-tests == 'true'
69157 run : |
70158 # Fetch the canonical manifest digest for ubuntu:noble so the
71159 # depends cache key changes when Canonical pushes base image
@@ -86,15 +174,19 @@ jobs:
86174 cache-sources :
87175 name : Cache depends sources
88176 needs : [check-skip]
89- if : ${{ needs.check-skip.outputs.skip == 'false' }}
177+ if : |
178+ needs.check-skip.outputs.skip == 'false' &&
179+ needs.check-skip.outputs.run-build-tests == 'true'
90180 uses : ./.github/workflows/cache-depends-sources.yml
91181 with :
92182 runs-on : ${{ needs.check-skip.outputs['runner-arm64'] }}
93183
94184 container :
95185 name : Build container
96186 needs : [check-skip]
97- if : ${{ needs.check-skip.outputs.skip == 'false' }}
187+ if : |
188+ needs.check-skip.outputs.skip == 'false' &&
189+ needs.check-skip.outputs.run-build-tests == 'true'
98190 uses : ./.github/workflows/build-container.yml
99191 with :
100192 context : ./contrib/containers/ci
0 commit comments