Skip to content

Commit 4917fa1

Browse files
justin808claude
andcommitted
Address code review feedback for docs-only guard
Improvements to .github/actions/ensure-master-docs-safety/action.yml: - Remove redundant null check (already handled by step condition) - Add comprehensive documentation for 7-day lookback window - Optimize API pagination with early exit strategy - Reduce per_page from 100 to 30 for efficiency - Improve deduplication to use run_number instead of created_at - Add clickable markdown links to failing workflows in error messages - Document handling of 'skipped' and 'neutral' workflow conclusions Workflow improvements: - Add clear inline comments explaining conditional logic in gem-tests.yml, integration-tests.yml, and lint-js-and-ruby.yml - Clarify "Skip only if: master push AND docs-only changes" pattern Code quality: - Remove duplicate dependencies in knip.ts (@babel/runtime, mini-css-extract-plugin) - Improve comment explaining runtime-injected dependencies All changes verified with RuboCop and Prettier checks passing. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
1 parent dc9ff1a commit 4917fa1

File tree

5 files changed

+62
-32
lines changed

5 files changed

+62
-32
lines changed

.github/actions/ensure-master-docs-safety/action.yml

Lines changed: 52 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -18,36 +18,57 @@ runs:
1818
with:
1919
script: |
2020
const previousSha = process.env.PREVIOUS_SHA;
21-
if (!previousSha) {
22-
core.info('No previous SHA found, skipping safety check.');
23-
return;
24-
}
2521
26-
// Query workflow runs from the last 7 days to avoid excessive API calls
27-
// For very old commits, we'll skip the check rather than scan the entire history
22+
// Query workflow runs from the last 7 days to avoid excessive API calls.
23+
// Why 7 days? This balances API efficiency with practical needs:
24+
// - Most master commits trigger CI within hours, not days
25+
// - Commits older than 7 days are likely stale; better to run full CI anyway
26+
// - Reduces pagination load on high-velocity repos
27+
// For commits outside this window, we skip the check and allow the docs-only skip.
2828
const createdAfter = new Date(Date.now() - 1000 * 60 * 60 * 24 * 7).toISOString();
29-
const workflowRuns = await github.paginate(github.rest.actions.listWorkflowRunsForRepo, {
30-
owner: context.repo.owner,
31-
repo: context.repo.repo,
32-
branch: 'master',
33-
event: 'push',
34-
per_page: 100,
35-
created: `>${createdAfter}`
36-
});
3729
38-
const relevantRuns = workflowRuns.filter((run) => run.head_sha === previousSha);
30+
// Optimize pagination: use lower per_page and collect only what we need
31+
const workflowRuns = [];
32+
let foundAllRelevant = false;
33+
34+
for await (const response of github.paginate.iterator(
35+
github.rest.actions.listWorkflowRunsForRepo,
36+
{
37+
owner: context.repo.owner,
38+
repo: context.repo.repo,
39+
branch: 'master',
40+
event: 'push',
41+
per_page: 30,
42+
created: `>${createdAfter}`
43+
}
44+
)) {
45+
const pageRuns = response.data;
46+
const relevantInPage = pageRuns.filter((run) => run.head_sha === previousSha);
47+
48+
if (relevantInPage.length > 0) {
49+
workflowRuns.push(...relevantInPage);
50+
}
51+
52+
// Early exit: if we found relevant runs and now seeing different SHAs,
53+
// we've likely collected all runs for the previous commit
54+
if (workflowRuns.length > 0 && relevantInPage.length === 0) {
55+
foundAllRelevant = true;
56+
break;
57+
}
58+
}
3959
40-
if (relevantRuns.length === 0) {
41-
core.info(`No workflow runs found for ${previousSha}. Nothing to enforce.`);
60+
if (workflowRuns.length === 0) {
61+
core.info(`No workflow runs found for ${previousSha} in the last 7 days. Allowing docs-only skip.`);
4262
return;
4363
}
4464
45-
// Deduplicate workflow runs by keeping only the latest run for each workflow_id
46-
// This handles cases where workflows are re-run manually
65+
// Deduplicate workflow runs by keeping only the latest run for each workflow_id.
66+
// This handles cases where workflows are re-run manually.
67+
// Use run_number as tiebreaker since created_at might be identical for rapid reruns.
4768
const latestByWorkflow = new Map();
48-
for (const run of relevantRuns) {
69+
for (const run of workflowRuns) {
4970
const existing = latestByWorkflow.get(run.workflow_id);
50-
if (!existing || new Date(run.created_at) > new Date(existing.created_at)) {
71+
if (!existing || run.run_number > existing.run_number) {
5172
latestByWorkflow.set(run.workflow_id, run);
5273
}
5374
}
@@ -61,7 +82,7 @@ runs:
6182
6283
if (incompleteRuns.length > 0) {
6384
const details = incompleteRuns
64-
.map((run) => `- ${run.name} (run #${run.run_number}) is still ${run.status}`)
85+
.map((run) => `- [${run.name} #${run.run_number}](${run.html_url}) is still ${run.status}`)
6586
.join('\n');
6687
core.setFailed(
6788
[
@@ -74,9 +95,14 @@ runs:
7495
return;
7596
}
7697
77-
// Check for workflows that failed on the previous commit
78-
// We treat cancelled runs as failures to be conservative, since they might indicate
79-
// timeouts or other infrastructure issues that should be resolved before skipping CI
98+
// Check for workflows that failed on the previous commit.
99+
// We treat these conclusions as failures:
100+
// - 'failure': Obvious failure case
101+
// - 'timed_out': Infrastructure or performance issue that should be investigated
102+
// - 'cancelled': Conservative - might indicate timeout or manual intervention needed
103+
// - 'action_required': Requires manual intervention
104+
// We treat 'skipped' and 'neutral' as non-blocking since they indicate
105+
// intentional skips or informational-only workflows.
80106
const failingRuns = Array.from(latestByWorkflow.values()).filter((run) => {
81107
return ['failure', 'timed_out', 'cancelled', 'action_required'].includes(run.conclusion);
82108
});
@@ -87,7 +113,7 @@ runs:
87113
}
88114
89115
const details = failingRuns
90-
.map((run) => `- ${run.name} (run #${run.run_number}) concluded ${run.conclusion}`)
116+
.map((run) => `- [${run.name} #${run.run_number}](${run.html_url}) concluded ${run.conclusion}`)
91117
.join('\n');
92118
93119
core.setFailed(

.github/workflows/gem-tests.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,9 @@ jobs:
8686
8787
rspec-package-tests:
8888
needs: [detect-changes, setup-gem-tests-matrix]
89-
# Run on master OR when Ruby tests needed on PR
89+
# Skip only if: master push AND docs-only changes
90+
# Otherwise run if: on master OR Ruby tests needed
91+
# This allows docs-only commits to skip heavy jobs while ensuring full CI on master for code changes
9092
if: |
9193
!(
9294
github.event_name == 'push' &&

.github/workflows/integration-tests.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,9 @@ jobs:
8686
8787
build-dummy-app-webpack-test-bundles:
8888
needs: [detect-changes, setup-integration-matrix]
89-
# Run on master, workflow_dispatch, OR when tests needed on PR
89+
# Skip only if: master push AND docs-only changes
90+
# Otherwise run if: on master OR workflow_dispatch OR dummy tests needed
91+
# This allows docs-only commits to skip heavy jobs while ensuring full CI on master for code changes
9092
if: |
9193
!(
9294
github.event_name == 'push' &&

.github/workflows/lint-js-and-ruby.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,9 @@ jobs:
6666

6767
build:
6868
needs: detect-changes
69+
# Skip only if: master push AND docs-only changes
70+
# Otherwise run if: on master OR lint needed
71+
# This allows docs-only commits to skip linting while ensuring full CI on master for code changes
6972
if: |
7073
!(
7174
github.event_name == 'push' &&

knip.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -148,14 +148,11 @@ const config: KnipConfig = {
148148
'@rescript/react',
149149
// The Babel plugin fails to detect it
150150
'babel-plugin-transform-react-remove-prop-types',
151-
// Runtime helpers injected by our Babel config, but not statically analyzable
152-
'@babel/runtime',
153151
// This one is weird. It's long-deprecated and shouldn't be necessary.
154152
// Probably need to update the Webpack config.
155153
'node-libs-browser',
156154
// The below dependencies are not detected by the Webpack plugin
157155
// due to the config issue.
158-
'mini-css-extract-plugin',
159156
'expose-loader',
160157
'file-loader',
161158
'imports-loader',
@@ -165,7 +162,7 @@ const config: KnipConfig = {
165162
'url-loader',
166163
// Transitive dependency of shakapacker but listed as direct dependency
167164
'webpack-merge',
168-
// Dependencies not detected in production mode
165+
// Dependencies not detected in production mode (runtime injected or dynamic imports)
169166
'@babel/runtime',
170167
'mini-css-extract-plugin',
171168
'css-loader',

0 commit comments

Comments
 (0)