Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 25 additions & 25 deletions portfolio/plugins.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@
"phpunit": {
"config": "phpunit.xml.dist",
"files": [
{ "path": "composer.json", "sha256": "2a86fefc11dacc40abccb5d81fa181969ed249d56271c33832a7d271421342ff" },
{ "path": "composer.lock", "sha256": "24418414a9cbb488fe4cb760ef1404bd2ef5a97ea48db6ab1dcfa8752a0da7d2" },
{ "path": "composer.json", "sha256": "a0f05b2ae1fdd5a59a636d8494addfd2fbb3e0c1ffac86be65345e3490a3c2d4" },
{ "path": "composer.lock", "sha256": "daeb71dd77a6b35edb1ce2932303afb8d6ba94d300f111c4e0958940c6146785" },
{ "path": "phpunit.xml.dist", "sha256": "5f91d0a818fe95020feccdeaa2bedf676297657e91b096ab96b97432375c5366" },
{ "path": "tests/bootstrap.php", "sha256": "c4798bd43326fe3bac514498e307636e87c9caad1311b27442710214547704db" }
{ "path": "tests/bootstrap.php", "sha256": "0d84571462a1881329dcaf1b154f101677cb42143169b27394481afb4dcac73e" }
]
}
},
Expand Down Expand Up @@ -44,8 +44,8 @@
"phpunit": {
"config": "phpunit.xml.dist",
"files": [
{ "path": "composer.json", "sha256": "f323bf5c9daffa7547f820f5fcf5646a98c23d0ca2a3a6c9c124d6b5c6375e6f" },
{ "path": "composer.lock", "sha256": "234ed8efc8d004e1a2d1bd2ffa52cc6059ad9e4bbf2c852e41e6d9ded05ddc33" },
{ "path": "composer.json", "sha256": "f3edff09db4f93818a96be4bf6223e6f9343c5a4b5e82e36dbc20ec813596552" },
{ "path": "composer.lock", "sha256": "dccc5e9541103ebffd188df27567f0172507ff0659b940821f4fffdecf138638" },
{ "path": "phpunit.xml.dist", "sha256": "1e9f6232d0f55f82fc4211c1689c3a8db80cc1f8aa7959eca9a2c0b0b4d65327" },
{ "path": "tests/bootstrap.php", "sha256": "e4403cfe2f517b3788879831c55536bfc471abd9b616afa61899d9ebf1875b8b" }
]
Expand Down Expand Up @@ -85,7 +85,7 @@
{ "path": "composer.json", "sha256": "74782b1130ca176259626ce18439f5d54730f8b583ab7d35069123949252110c" },
{ "path": "composer.lock", "sha256": "553319c0d0f10b2765c7beb0af198bf9a1dbe1ad902439d14e1d74132ee4eab7" },
{ "path": "phpunit.xml.dist", "sha256": "a51a3fb9d0a54d88f2f3d1ab04dda9375cf6700966f1b8f2b4fd0715936365c7" },
{ "path": "tests/bootstrap.php", "sha256": "5d46013ecc675591f97089db55e679a3eb434387b436ecaca2ac92dc5eac9223" }
{ "path": "tests/bootstrap.php", "sha256": "cdc13526cbf8024198363bf37d66d21784794eaa2c780dc4dd2ccdda131c5510" }
]
}
},
Expand Down Expand Up @@ -152,8 +152,8 @@
"phpunit": {
"config": "phpunit.xml.dist",
"files": [
{ "path": "composer.json", "sha256": "b731fd784932b9c2ed239e1aef5fb4cdcd01f9e6c949b7a2f1dfe416ab604152" },
{ "path": "composer.lock", "sha256": "4c18ed167cc85e1e6eb0e8c5b90f9ed66095372e3ba7f396a38a8c35e914ff3b" },
{ "path": "composer.json", "sha256": "70ee76ad4ac3489dc27af587bda1c76182710414d6636ce840f5e7e71a400d67" },
{ "path": "composer.lock", "sha256": "c6d7269d957ccea3236c319c97b63ab59d7b00215e681ed6fed27dbfa2d8874a" },
{ "path": "phpunit.xml.dist", "sha256": "b3d7ea362089a835aedb77e47f647d413f06f020578aefa2fc009836530e43ba" },
{ "path": "tests/bootstrap.php", "sha256": "8915f7184852b960c0eabcd2c7883c2cb7dc15193b530bb17720af62ebd507ea" }
]
Expand Down Expand Up @@ -242,8 +242,8 @@
"phpunit": {
"config": "phpunit.xml.dist",
"files": [
{ "path": "composer.json", "sha256": "cde20dfacd5021c0e5fff0c46b39d93ab6aafe283f8f1f2567a6ea8cd7f5605e" },
{ "path": "composer.lock", "sha256": "16d1fec986461ce188b64ebbf91271781f5356b60497920116d5c5186100c6cc" },
{ "path": "composer.json", "sha256": "ce9a18b73bfbbe056f52c13e0988303396fe3e1f5fa0088c1c3d99bb4f520b16" },
{ "path": "composer.lock", "sha256": "04db6d314a1a4bde8af9e8cca4b446cb295b3f87d60ac4aab942c270f30d56a7" },
{ "path": "phpunit.xml.dist", "sha256": "93980825969bc906cf7a434a0b393da3c0fb186365be3e18f952f2fd40bd51fb" },
{ "path": "tests/bootstrap.php", "sha256": "cbfefee52510e920fdbd8d58e8943e03a01c507f8a41e365273eb1e2b20a60ba" }
]
Expand Down Expand Up @@ -327,8 +327,8 @@
"phpunit": {
"config": "phpunit.xml.dist",
"files": [
{ "path": "composer.json", "sha256": "3d520cbaf53c97c701ee1cb2f729bc09466f2ebb5ee262e2dde5f6e0fbfdaa39" },
{ "path": "composer.lock", "sha256": "0602d3f55992efbb407e66081d9483ac370c42c7cdbe0a9a1011d8ba0df080a6" },
{ "path": "composer.json", "sha256": "1105e49fadd16917daa3a01543dc85488f2ceeed6174d92c6b31e749695f832c" },
{ "path": "composer.lock", "sha256": "5413458c84f6815cafee6ea255051300bdcc0905f64389699697f55959a3b065" },
{ "path": "phpunit.xml.dist", "sha256": "1f1877783a07ed91172dfbb0c7ce9b42c7246f5c4b5dcd7ee1d479528fe09861" },
{ "path": "tests/bootstrap.php", "sha256": "503a09317a0def999c45f81478aca9cf0b165512efcaac1b47d52512b120b8ac" }
]
Expand Down Expand Up @@ -362,8 +362,8 @@
"phpunit": {
"config": "phpunit.xml.dist",
"files": [
{ "path": "composer.json", "sha256": "c76d358c2ad88cfaa343d3e3910207d536243de020b2d99a887a609b9d7d7859" },
{ "path": "composer.lock", "sha256": "5b604320f1628a9ac63b8bdfcc79a900da71f39b0aca990fa3952e2fab99d955" },
{ "path": "composer.json", "sha256": "ac1788c75e0b6e45618cb8c26eb1158a9c9fcf48590eac6df1d2c9346dffeccb" },
{ "path": "composer.lock", "sha256": "deb1e089550dde49dee2d3cafc124e9634f8f591e9acbc482a09baa60007a35a" },
{ "path": "phpunit.xml.dist", "sha256": "c23c3afa83ae468cd04eb74991e71e9a69f95bf8520a54d9c5689b28e302a365" },
{ "path": "tests/bootstrap.php", "sha256": "45ef4fbfb2c85f4bab2c7bcd38218c59b01742d1fe86e61e30f1058f66134f8a" }
]
Expand Down Expand Up @@ -397,8 +397,8 @@
"phpunit": {
"config": "phpunit.xml.dist",
"files": [
{ "path": "composer.json", "sha256": "184e63eddd9ea61eb9f39e58e918798767535cb5c8ac71c4ff48ace433bdbe19" },
{ "path": "composer.lock", "sha256": "cd508765eb8870da29ba6ac8021d30feca2264895adff8242a26c9f812ed4c4c" },
{ "path": "composer.json", "sha256": "58756a64309c8450a8d2b280b8533f87cb21406cb8304c86c3655ab70a6b814a" },
{ "path": "composer.lock", "sha256": "4a70277a3cf46ea5c2621b5d943a990475448bbd3fcc86314320f7c42d7664b2" },
{ "path": "phpunit.xml.dist", "sha256": "3e0a40a7d519b655d28bfe84c1e1018d9363f61a321bf005a07c4968de944066" },
{ "path": "tests/bootstrap.php", "sha256": "3e9ce13d43d8fb1742634de5dad4a6388700c23419d461279232113c4cae4d77" }
]
Expand Down Expand Up @@ -426,10 +426,10 @@
"phpunit": {
"config": "phpunit.xml.dist",
"files": [
{ "path": "composer.json", "sha256": "e431d9aed0f0b39bc4e1729e0e6da64dc4da5e859ea032c7feb44489837310fc" },
{ "path": "composer.lock", "sha256": "f83d953a43c60c7b6e22290078ccad0b33466e11a7a5d5fd7c13f3dadd5fe4dd" },
{ "path": "composer.json", "sha256": "28c221775e62d1ac31999681d0ec51e3fd7398a44e84aabc4d3be9e6982ea7ab" },
{ "path": "composer.lock", "sha256": "6b93339b33dff6de43f7507f2f50f2fcdae4eee14547c428b65f024f108d2d9a" },
{ "path": "phpunit.xml.dist", "sha256": "78d45dc8e6316665d752afa2ed801c82742201f512d06bc76464556d5445baa6" },
{ "path": "tests/bootstrap.php", "sha256": "bf157f2399a9b9e5757239e921bcf4cb22ecd31b65915409d0209b1d4bc281e1" }
{ "path": "tests/bootstrap.php", "sha256": "babaefde10d66233bb531111409fd87c72beeb4ed70575bf62626e0227bf13f0" }
]
}
},
Expand Down Expand Up @@ -461,10 +461,10 @@
"phpunit": {
"config": "phpunit.xml.dist",
"files": [
{ "path": "composer.json", "sha256": "0a0ba88076a3c9e311dbfd66b9da226a3e3f7c2f42fcda416e64192c4fd00b9a" },
{ "path": "composer.lock", "sha256": "dfa7412474360ee12e80ec6ddc1965314b461f1944634ec3eafea0c868f36a04" },
{ "path": "composer.json", "sha256": "4aa3c793c7ed616b84abc8a0eaa39aedc3e7f0260fb8b3aa1937a6b01d1141ed" },
{ "path": "composer.lock", "sha256": "c9b97f0f32b758a82c532f46127c9f245927ce4142358b214fefac584d045e69" },
{ "path": "phpunit.xml.dist", "sha256": "51c99ea65a60b1af445c05fe9a78a13eaba1ad9be6b1032635ef691ea9fa0d4e" },
{ "path": "tests/bootstrap.php", "sha256": "1255c3bb96672e05c7cf1c9e541107e9ea5eb828cb61693d7544a994532f46c6" }
{ "path": "tests/bootstrap.php", "sha256": "ac2db4be18f7c6fdec1cbd81aba1927f2426a6b70110972750089ff5dc93d2b3" }
]
}
},
Expand Down Expand Up @@ -496,10 +496,10 @@
"phpunit": {
"config": "phpunit.xml.dist",
"files": [
{ "path": "composer.json", "sha256": "953f8a120136cd4e1ff639b7cba0cc51746a7c52df9a8706b6871a272ea11275" },
{ "path": "composer.lock", "sha256": "63fe118ffb61c32823bf3993f406c42c2da90a5607ef7ead4e1db268359cb651" },
{ "path": "composer.json", "sha256": "385b2ef780f65389932fe0750a18a4d7d902158c5e23f5aaa582d0aa61e77d5f" },
{ "path": "composer.lock", "sha256": "7258d903cacc8a4646266a86718ed6921c2fd06ca1276c3119c2153886a2954a" },
{ "path": "phpunit.xml.dist", "sha256": "ce4fc4cab25c42bd9e4d8f432dbbbf577a093192d60cf388bb2ddcdcff7c34a7" },
{ "path": "tests/bootstrap.php", "sha256": "8ca8b217e5c06cad564c44d1fe1ad747a1f0878de6055ab492a32dadd9d8f194" }
{ "path": "tests/bootstrap.php", "sha256": "d56e578a35669ef19e49e5a1c6e925b69c8d389cc3427aaef67e3d5ab51c9946" }
]
}
},
Expand Down
44 changes: 42 additions & 2 deletions scripts/check-static-analysis-baselines.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -671,6 +671,37 @@ function isMonotonicWordPressMinimumChange(base, head) {
&& compareDottedVersions(headConfig.value, baseConfig.value) > 0;
}

function phpstanLevel(source) {
if (source === null || source.includes('\r')) return null;
const matches = [...source.matchAll(/^[ \t]+level: ([0-9]+)$/gmu)];
if (matches.length !== 1) return null;
const level = Number(matches[0][1]);
return Number.isSafeInteger(level) && level <= 10 ? level : null;
}

function isClearedPhpstanBaselineMigration(base, head) {
const baseLevel = phpstanLevel(base);
const headLevel = phpstanLevel(head);
if (baseLevel === null || headLevel === null || headLevel <= baseLevel) return false;

const baselineInclude = /^includes:\n[ \t]+- phpstan-baseline\.neon\n\n/u;
if (!baselineInclude.test(base) || head.includes('phpstan-baseline.neon') || head.includes('ignoreErrors')) {
return false;
}

// Permit only the WordPress compatibility stub used by the level-7 migration.
// The following quality step still runs the unchanged Composer analyzer command.
const stubInclude = ' stubFiles:\n - phpstan-wordpress-compat.stub\n';
let normalizedHead = head;
if (!base.includes('stubFiles:') && head.includes(stubInclude)) {
if (readHead('phpstan-wordpress-compat.stub') === null) return false;
normalizedHead = head.replace(stubInclude, '');
}

const normalizeLevel = (source) => source.replace(/^([ \t]+level: )[0-9]+$/mu, '$1__LEVEL__');
return normalizeLevel(base.replace(baselineInclude, '')) === normalizeLevel(normalizedHead);
}

function protectIntroducedAnalyzerContract(baselinePath, centralPhpcs = false) {
if (baselinePath === 'phpcs-baseline.json' && centralPhpcs) return;
const contract = ANALYZER_CONTRACTS[baselinePath];
Expand Down Expand Up @@ -712,7 +743,7 @@ function protectIntroducedAnalyzerContract(baselinePath, centralPhpcs = false) {
}
}

function protectAnalyzerContract(baseRevision, baselinePath, centralPhpcs = false) {
function protectAnalyzerContract(baseRevision, baselinePath, centralPhpcs = false, baselineRemoved = false) {
if (baselinePath === 'phpcs-baseline.json' && centralPhpcs) return;
const contract = ANALYZER_CONTRACTS[baselinePath];
const baseComposer = parseComposer(readAtRevision(baseRevision, 'composer.json'), baselinePath);
Expand All @@ -727,6 +758,15 @@ function protectAnalyzerContract(baseRevision, baselinePath, centralPhpcs = fals

let phpcsMinimumChangeUsed = false;
for (const path of contract.configurations) {
if (baselinePath === 'phpstan-baseline.neon'
&& baselineRemoved
&& baseConfigurations.length === 1
&& headConfigurations.length === 1
&& baseConfigurations[0] === path
&& headConfigurations[0] === path
&& isClearedPhpstanBaselineMigration(readAtRevision(baseRevision, path), readHead(path))) {
continue;
}
if (baselinePath === 'phpcs-baseline.json'
&& isMonotonicWordPressMinimumChange(readAtRevision(baseRevision, path), readHead(path))) {
if (baseConfigurations.length !== 1
Expand Down Expand Up @@ -780,7 +820,7 @@ export function checkBaselines(baseRevision, { centralPhpcs = false } = {}) {
continue;
}

protectAnalyzerContract(baseRevision, path, centralPhpcs);
protectAnalyzerContract(baseRevision, path, centralPhpcs, headSource === null);

const base = parse(baseSource, `${path} at ${baseRevision}`);
const head = headSource === null ? new Map() : parse(headSource, path);
Expand Down
8 changes: 4 additions & 4 deletions tests/integration-check-policy.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -157,8 +157,8 @@ test('only reviewed WordPress integration pilots are enrolled', () => {
assert.deepEqual(userAvatars.managed_paths, ['ci', 'release', 'dependabot']);
assert.equal(userAvatars.checks.phpunit.config, 'phpunit.xml.dist');
assert.deepEqual(userAvatars.checks.phpunit.files, [
{ path: 'composer.json', sha256: '3d520cbaf53c97c701ee1cb2f729bc09466f2ebb5ee262e2dde5f6e0fbfdaa39' },
{ path: 'composer.lock', sha256: '0602d3f55992efbb407e66081d9483ac370c42c7cdbe0a9a1011d8ba0df080a6' },
{ path: 'composer.json', sha256: '1105e49fadd16917daa3a01543dc85488f2ceeed6174d92c6b31e749695f832c' },
{ path: 'composer.lock', sha256: '5413458c84f6815cafee6ea255051300bdcc0905f64389699697f55959a3b065' },
{ path: 'phpunit.xml.dist', sha256: '1f1877783a07ed91172dfbb0c7ce9b42c7246f5c4b5dcd7ee1d479528fe09861' },
{ path: 'tests/bootstrap.php', sha256: '503a09317a0def999c45f81478aca9cf0b165512efcaac1b47d52512b120b8ac' },
]);
Expand All @@ -181,8 +181,8 @@ test('only reviewed WordPress integration pilots are enrolled', () => {
assert.deepEqual(userPreferences.managed_paths, ['ci', 'release', 'dependabot']);
assert.equal(userPreferences.checks.phpunit.config, 'phpunit.xml.dist');
assert.deepEqual(userPreferences.checks.phpunit.files, [
{ path: 'composer.json', sha256: 'c76d358c2ad88cfaa343d3e3910207d536243de020b2d99a887a609b9d7d7859' },
{ path: 'composer.lock', sha256: '5b604320f1628a9ac63b8bdfcc79a900da71f39b0aca990fa3952e2fab99d955' },
{ path: 'composer.json', sha256: 'ac1788c75e0b6e45618cb8c26eb1158a9c9fcf48590eac6df1d2c9346dffeccb' },
{ path: 'composer.lock', sha256: 'deb1e089550dde49dee2d3cafc124e9634f8f591e9acbc482a09baa60007a35a' },
{ path: 'phpunit.xml.dist', sha256: 'c23c3afa83ae468cd04eb74991e71e9a69f95bf8520a54d9c5689b28e302a365' },
{ path: 'tests/bootstrap.php', sha256: '45ef4fbfb2c85f4bab2c7bcd38218c59b01742d1fe86e61e30f1058f66134f8a' },
]);
Expand Down
59 changes: 59 additions & 0 deletions tests/static-analysis-baselines-integration.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,65 @@ test('existing baseline protects conventional analyzer configurations', (t) => {
assert.match(result.stderr, /may not add, remove, or change phpstan\.neon/u);
});

function phpstanRetirementRepository() {
return createRepository({
'composer.json': JSON.stringify({ scripts: { phpstan: 'phpstan analyse --no-progress' } }, null, 2) + '\n',
'phpstan-baseline.neon': canonicalPhpstanBaseline,
'phpstan.neon.dist': [
'includes:',
' - phpstan-baseline.neon',
'',
'parameters:',
' level: 5',
' phpVersion: 70400',
' paths:',
' - includes',
'',
].join('\n'),
});
}

function retirePhpstanBaseline(fixture, { level = 7, path = 'includes', stub = true, ignoreErrors = false } = {}) {
unlinkSync(join(fixture.root, 'phpstan-baseline.neon'));
writeFixtureFile(fixture.root, 'phpstan.neon.dist', [
'parameters:',
' level: ' + level,
' phpVersion: 70400',
' paths:',
' - ' + path,
' stubFiles:',
' - phpstan-wordpress-compat.stub',
...(ignoreErrors ? [' ignoreErrors: []'] : []),
'',
].join('\n'));
if (stub) writeFixtureFile(fixture.root, 'phpstan-wordpress-compat.stub', '<?php\n');
}

test('cleared PHPStan baseline permits a higher level and one named compatibility stub', (t) => {
const fixture = phpstanRetirementRepository();
t.after(() => rmSync(fixture.root, { force: true, recursive: true }));
retirePhpstanBaseline(fixture);
const result = runChecker(fixture.root, fixture.revision);
assert.equal(result.status, 0, result.stderr);
});

test('cleared PHPStan baseline still rejects analyzer configuration drift', (t) => {
for (const variant of [
{ name: 'unchanged level', level: 5 },
{ name: 'lower level', level: 4 },
{ name: 'changed paths', path: 'tests' },
{ name: 'missing stub', stub: false },
{ name: 'new ignored errors', ignoreErrors: true },
]) {
const fixture = phpstanRetirementRepository();
t.after(() => rmSync(fixture.root, { force: true, recursive: true }));
retirePhpstanBaseline(fixture, variant);
const result = runChecker(fixture.root, fixture.revision);
assert.equal(result.status, 2, variant.name + ': ' + result.stderr);
assert.match(result.stderr, /may not add, remove, or change phpstan\.neon/u);
}
});

test('existing baseline protects a directly named repository runner', (t) => {
const fixture = createRepository({
'composer.json': `${JSON.stringify({ scripts: { phpcs: 'php scripts/check-phpcs-baseline.php' } }, null, 2)}\n`,
Expand Down
Loading