Skip to content

Commit f4a2642

Browse files
authored
fix(ci): stop false-red revert-oracle failures on Dependabot updates (#4084)
* fix(ci): exempt dependency-only Dependabot PRs from revert oracle * fix(ci): cover configured Docker dependency updates * fix(ci): bind dependency manifests to configured workspaces
1 parent bd7f07b commit f4a2642

4 files changed

Lines changed: 89 additions & 0 deletions

File tree

.github/workflows/test.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -460,6 +460,8 @@ jobs:
460460
name: build-output
461461
path: packages
462462
- name: Prove the changed tests observe the production change
463+
env:
464+
PR_AUTHOR_LOGIN: ${{ github.event.pull_request.user.login }}
463465
run: node scripts/check-test-revert-oracle.mjs --base origin/main --ci --json
464466

465467
typecheck:

scripts/check-test-revert-oracle.mjs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ import {
8989
UNOBSERVED,
9090
SURGICAL_ADVICE,
9191
} from './lib/revert-oracle.mjs';
92+
import { isDependabotDependencyOnly } from './lib/revert-oracle-dependabot.mjs';
9293
import { cargoTestOwner } from './lib/revert-oracle-cargo.mjs';
9394
import { ciExitCode } from './lib/revert-oracle-ci.mjs';
9495

@@ -300,6 +301,14 @@ const mergeBase = gitOrDie(['merge-base', baseSha, headSha]).trim();
300301
const entries = parseNameStatus(gitOrDie(['diff', '--name-status', `${mergeBase}`, headSha]));
301302
if (entries.length === 0) die(EXIT_NOTHING_CHECKED, 'the diff is empty; nothing to check.');
302303

304+
if (opts.ci && isDependabotDependencyOnly(process.env.PR_AUTHOR_LOGIN, entries)) {
305+
console.log(
306+
' NOT APPLICABLE: Dependabot changed dependency manifests/lockfiles only; ' +
307+
'the normal build and test lanes provide the compatibility verdict.',
308+
);
309+
process.exit(0);
310+
}
311+
303312
const { production, test: testEntries, ignored, warnings } = classifyDiff(entries);
304313
for (const w of warnings) console.log(` WARNING: ${w}`);
305314

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/* This Source Code Form is subject to the terms of the Mozilla Public
2+
* License, v. 2.0. If a copy of the MPL was not distributed with this
3+
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4+
5+
const NPM_MANIFEST_RE = /^(?:package\.json|(?:packages|apps|examples)\/[^/]+\/package\.json)$/;
6+
const CARGO_MANIFESTS = new Set([
7+
'Cargo.toml',
8+
'rust/core/Cargo.toml',
9+
'rust/geometry/Cargo.toml',
10+
'rust/processing/Cargo.toml',
11+
'rust/clash/Cargo.toml',
12+
'rust/ffi/Cargo.toml',
13+
'rust/export/Cargo.toml',
14+
'rust/wasm-bindings/Cargo.toml',
15+
'apps/server/Cargo.toml',
16+
]);
17+
const LOCKFILES = new Set(['pnpm-lock.yaml', 'package-lock.json', 'yarn.lock', 'Cargo.lock']);
18+
const DOCKERFILES = new Set([
19+
'apps/server/Dockerfile',
20+
'packages/collab-server/Dockerfile',
21+
'tools/ifcopenshell_reference/Dockerfile',
22+
]);
23+
24+
/**
25+
* The normal build and test lanes are the compatibility oracle for a dependency
26+
* bump. Reverting its manifest without its ignored lockfile is not coherent.
27+
* One non-dependency path restores the normal changed-test requirement.
28+
*/
29+
export function isDependabotDependencyOnly(login, entries) {
30+
if (login !== 'dependabot[bot]' || entries.length === 0) return false;
31+
return entries.every(({ path }) =>
32+
NPM_MANIFEST_RE.test(path) || CARGO_MANIFESTS.has(path) || LOCKFILES.has(path) || DOCKERFILES.has(path)
33+
);
34+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/* This Source Code Form is subject to the terms of the Mozilla Public
2+
* License, v. 2.0. If a copy of the MPL was not distributed with this
3+
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4+
5+
import test from 'node:test';
6+
import assert from 'node:assert/strict';
7+
import { isDependabotDependencyOnly } from './revert-oracle-dependabot.mjs';
8+
9+
const entries = (...paths) => paths.map((path) => ({ status: 'M', path }));
10+
11+
test('#4080: Dependabot-only dependency manifests and lockfiles use the normal test lanes', () => {
12+
assert.equal(
13+
isDependabotDependencyOnly(
14+
'dependabot[bot]',
15+
entries(
16+
'package.json',
17+
'packages/viewer/package.json',
18+
'rust/geometry/Cargo.toml',
19+
'pnpm-lock.yaml',
20+
'Cargo.lock',
21+
'apps/server/Dockerfile',
22+
'packages/collab-server/Dockerfile',
23+
'tools/ifcopenshell_reference/Dockerfile',
24+
),
25+
),
26+
true,
27+
);
28+
});
29+
30+
test('#4080: the exception cannot swallow authored or mixed changes', () => {
31+
assert.equal(isDependabotDependencyOnly('BIMvoice', entries('package.json')), false);
32+
assert.equal(isDependabotDependencyOnly('app/dependabot', entries('package.json')), false);
33+
assert.equal(isDependabotDependencyOnly('dependabot[bot]', []), false);
34+
assert.equal(
35+
isDependabotDependencyOnly('dependabot[bot]', entries('package.json', 'packages/viewer/src/index.ts')),
36+
false,
37+
);
38+
assert.equal(isDependabotDependencyOnly('dependabot[bot]', entries('packages/viewer/not-package.json')), false);
39+
assert.equal(isDependabotDependencyOnly('dependabot[bot]', entries('fixtures/package.json')), false);
40+
assert.equal(isDependabotDependencyOnly('dependabot[bot]', entries('packages/viewer/fixture/package.json')), false);
41+
assert.equal(isDependabotDependencyOnly('dependabot[bot]', entries('rust/python/Cargo.toml')), false);
42+
assert.equal(isDependabotDependencyOnly('dependabot[bot]', entries('rust/csg-thread-bench/Cargo.toml')), false);
43+
assert.equal(isDependabotDependencyOnly('dependabot[bot]', entries('apps/viewer/Dockerfile')), false);
44+
});

0 commit comments

Comments
 (0)