Skip to content

Commit c741f71

Browse files
authored
Merge pull request #226 from boesing/feature/roave-bc-checker
Verify BC breakages on pull requests, by comparing PR against base branch
2 parents 772d2ad + ef9f76f commit c741f71

File tree

49 files changed

+159
-19
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+159
-19
lines changed

.eslintrc.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
"no-unused-vars": "off",
2323
"@typescript-eslint/no-unused-vars": "error",
2424
"node/no-unpublished-import": ["error", {
25-
"allowModules": ["@cfworker/json-schema"]
25+
"allowModules": ["@cfworker/json-schema", "dotenv"]
2626
}],
2727
"no-process-exit": "off",
2828
"no-sync": "off",

.github/workflows/continuous-integration.yml

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,15 @@ jobs:
8484
id: matrix_generation
8585
env:
8686
PROJECT_NAME_TO_TEST: ${{ matrix.projectName }}
87-
run: cd tests/${PROJECT_NAME_TO_TEST} && docker run -i --entrypoint "/action/main.js" -v $(realpath .):/github/workspace -w=/github/workspace ${TEST_TAG} $(test -r diff && cat diff || echo -n "")
87+
run: |
88+
cd tests/${PROJECT_NAME_TO_TEST} && \
89+
docker run \
90+
-i \
91+
--entrypoint "/action/main.js" \
92+
-v $(realpath .):/github/workspace \
93+
--env-file=test.env \
94+
-w=/github/workspace \
95+
${TEST_TAG} $(test -r diff && cat diff || echo -n "")
8896
8997
- name: "Output generated matrix"
9098
uses: sergeysova/jq-action@v2

laminas-ci.schema.json

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,9 @@
183183
"stablePHP": {
184184
"$ref": "#/definitions/stablePHP"
185185
},
186+
"backwardCompatibilityCheck": {
187+
"$ref": "#/definitions/backwardCompatibilityCheck"
188+
},
186189
"additional_checks": {
187190
"type": "array",
188191
"title": "A list of additional checks to be executed",
@@ -328,14 +331,20 @@
328331
}
329332
},
330333
"stablePHP": {
331-
"type": "string",
332-
"minLength": 1,
333-
"title": "The PHP version to be used for stable checks",
334-
"description": "This PHP version is used for all QA check jobs. The default depends on the `composer.json` of the project and usually reflects the minimum supported PHP version of that project.",
335-
"examples": [
336-
"8.0"
337-
]
338-
},
334+
"type": "string",
335+
"minLength": 1,
336+
"title": "The PHP version to be used for stable checks",
337+
"description": "This PHP version is used for all QA check jobs. The default depends on the `composer.json` of the project and usually reflects the minimum supported PHP version of that project.",
338+
"examples": [
339+
"8.0"
340+
]
341+
},
342+
"backwardCompatibilityCheck": {
343+
"type": "boolean",
344+
"title": "Flag to enable/disable backwards compatibility check",
345+
"description": "This flag enables/disables backwards compatibility check using roave/backward-compatibility-check.",
346+
"default": false
347+
},
339348
"job": {
340349
"type": "object",
341350
"title": "The job to be executed",

package-lock.json

Lines changed: 13 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
"@types/webpack": "^5.28.0",
2424
"@typescript-eslint/eslint-plugin": "^5.41.0",
2525
"@typescript-eslint/parser": "^5.41.0",
26+
"dotenv": "^16.3.1",
2627
"eslint": "^8.26.0",
2728
"eslint-config-incredible": "^2.4.2",
2829
"eslint-import-resolver-typescript": "^3.5.2",

src/config/app.spec.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
11
import {PathLike} from 'fs';
2+
import {configDotenv} from 'dotenv';
23
import createConfig, {gatherVersions} from './app';
34

5+
beforeEach(() => {
6+
jest.resetModules();
7+
8+
// Clean enviroment to avoid side-effects
9+
process.env = {};
10+
});
11+
412
describe('config/app', () => {
513
describe('gatherVersions()', () => {
614
test.each`
@@ -25,6 +33,7 @@ describe('config/app', () => {
2533

2634
describe('createConfig()', () => {
2735
const phpIniFromConfigurationPath: PathLike = 'tests/php-ini-from-configuration';
36+
const roaveBackwardCompatibilityPath: PathLike = 'tests/code-check-roave-backward-compatibility';
2837

2938
it('should return valid config', () => {
3039
expect(createConfig(
@@ -47,7 +56,41 @@ describe('config/app', () => {
4756
phpIni : [ 'error_reporting=E_ALL' ],
4857
ignorePhpPlatformRequirements : {},
4958
additionalComposerArguments : [],
59+
backwardCompatibilityCheck : false,
60+
baseReference : null,
61+
});
62+
});
63+
64+
it('should detect GITHUB_BASE_REF', () => {
65+
const environment = process.env;
66+
67+
configDotenv({path: `${roaveBackwardCompatibilityPath}/test.env`});
68+
69+
expect(createConfig(
70+
{
71+
codeChecks : true,
72+
docLinting : true,
73+
},
74+
`${roaveBackwardCompatibilityPath}/composer.json`,
75+
`${roaveBackwardCompatibilityPath}/composer.lock`,
76+
`${roaveBackwardCompatibilityPath}/.laminas-ci.json`
77+
)).toEqual({
78+
codeChecks : true,
79+
docLinting : true,
80+
versions : [],
81+
stablePhpVersion : '7.4',
82+
minimumPhpVersion : '7.4',
83+
latestPhpVersion : '7.4',
84+
lockedDependenciesExists : false,
85+
phpExtensions : [],
86+
phpIni : [],
87+
ignorePhpPlatformRequirements : {},
88+
additionalComposerArguments : [],
89+
backwardCompatibilityCheck : true,
90+
baseReference : '1111222233334444aaaabbbbccccdddd',
5091
});
92+
93+
process.env = environment;
5194
});
5295
});
5396
});

src/config/app.ts

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import fs, {PathLike} from 'fs';
22
import semver from 'semver';
33
import parseJsonFile from '../json';
4-
import {Tool, ToolExecutionType} from '../tools';
4+
import {isToolRunningContainerDefaultPhpVersion, Tool, ToolExecutionType} from '../tools';
55
import {Logger} from '../logging';
66
import {CURRENT_STABLE, INSTALLABLE_VERSIONS, InstallablePhpVersionType, isInstallableVersion} from './php';
77
import {ComposerJson} from './composer';
@@ -85,6 +85,8 @@ export interface Config {
8585
readonly phpIni: string[];
8686
readonly ignorePhpPlatformRequirements: IgnorePhpPlatformRequirements;
8787
readonly additionalComposerArguments: string[];
88+
readonly backwardCompatibilityCheck: boolean;
89+
readonly baseReference: string|null;
8890
}
8991
export interface Requirements {
9092
readonly codeChecks: boolean;
@@ -283,6 +285,17 @@ function createJob(
283285
return createdJob;
284286
}
285287

288+
function detectPhpVersionForTool(
289+
tool: Tool,
290+
config: Config
291+
): InstallablePhpVersionType {
292+
if (isToolRunningContainerDefaultPhpVersion(tool)) {
293+
return tool.php;
294+
}
295+
296+
return config.minimumPhpVersion;
297+
}
298+
286299
function createJobsForTool(
287300
config: Config,
288301
tool: Tool
@@ -302,7 +315,7 @@ function createJobsForTool(
302315
tool.name,
303316
createJobDefinition(
304317
tool.command,
305-
config.minimumPhpVersion,
318+
detectPhpVersionForTool(tool, config),
306319
lockedOrLatestDependencySet,
307320
config.phpExtensions,
308321
config.phpIni,
@@ -466,6 +479,8 @@ export default function createConfig(
466479
lockedDependenciesExists : fs.existsSync(composerLockJsonFileName),
467480
ignorePhpPlatformRequirements : configurationFromFile.ignore_php_platform_requirements ?? {},
468481
additionalComposerArguments : [ ... new Set(configurationFromFile.additional_composer_arguments ?? []) ],
482+
backwardCompatibilityCheck : configurationFromFile.backwardCompatibilityCheck ?? false,
483+
baseReference : process.env.GITHUB_BASE_REF ?? null,
469484
};
470485
}
471486

src/config/input.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export interface ConfigurationFromFile {
1111
ignore_php_platform_requirements?: IgnorePhpPlatformRequirements;
1212
stablePHP?: string;
1313
additional_composer_arguments?: string[];
14+
backwardCompatibilityCheck?: boolean;
1415
}
1516

1617
export interface JobExclusionsFromFile {

src/config/php.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ export const PHP_81 = '8.1';
99
export const PHP_82 = '8.2';
1010

1111
export const CURRENT_STABLE = PHP_80;
12+
export const CONTAINER_DEFAULT_PHP_VERSION = '@default';
1213

1314
/**
1415
* NOTE: Please keep this list ordered as the ordering is used to detect the minimum supported version of a project
@@ -23,7 +24,8 @@ export const INSTALLABLE_VERSIONS = [
2324
PHP_74,
2425
PHP_80,
2526
PHP_81,
26-
PHP_82
27+
PHP_82,
28+
CONTAINER_DEFAULT_PHP_VERSION,
2729
] as const;
2830

2931
export type InstallablePhpVersionType = typeof INSTALLABLE_VERSIONS[number];

src/tools.ts

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
import fs, { PathLike } from 'fs';
2-
import { Config } from './config/app';
3-
import { ComposerJson } from './config/composer';
1+
import fs, {PathLike} from 'fs';
2+
import {Config} from './config/app';
3+
import {ComposerJson} from './config/composer';
44
import parseJsonFile from './json';
5+
import {CONTAINER_DEFAULT_PHP_VERSION} from './config/php';
56

67
export enum ToolExecutionType {
78
/**
@@ -31,6 +32,10 @@ export type Tool = {
3132
lintConfigCommand?: string,
3233
}
3334

35+
export type ToolRunningContainerDefaultPhpVersion = Tool & {
36+
php: typeof CONTAINER_DEFAULT_PHP_VERSION,
37+
}
38+
3439
function detectInfectionCommand(): string {
3540
const composerJson: ComposerJson = parseJsonFile('composer.json', true) as ComposerJson;
3641

@@ -41,8 +46,28 @@ function detectInfectionCommand(): string {
4146
return 'phpdbg -qrr ./vendor/bin/infection';
4247
}
4348

49+
function backwardCompatibilityCheckTool(config: Config): ToolRunningContainerDefaultPhpVersion | null {
50+
if (!config.backwardCompatibilityCheck) {
51+
return null;
52+
}
53+
54+
if (config.baseReference === null) {
55+
return null;
56+
}
57+
58+
return {
59+
// @TODO need to `git fetch baseSha1` from source repo!
60+
executionType : ToolExecutionType.STATIC,
61+
name : 'Backward Compatibility Check',
62+
command : `roave-backward-compatibility-check check --from="${ config.baseReference }" --install-development-dependencies`,
63+
filesToCheck : [ 'composer.json' ],
64+
toolType : ToolType.CODE_CHECK,
65+
php : CONTAINER_DEFAULT_PHP_VERSION,
66+
} as ToolRunningContainerDefaultPhpVersion;
67+
}
68+
4469
export default function createTools(config: Config): Array<Tool> {
45-
return [
70+
const tools = [
4671
{
4772
executionType : ToolExecutionType.STATIC,
4873
name : 'Documentation Linting',
@@ -129,8 +154,11 @@ export default function createTools(config: Config): Array<Tool> {
129154
command : './vendor/bin/php-cs-fixer fix -v --diff --dry-run',
130155
filesToCheck : [ '.php-cs-fixer.php', '.php-cs-fixer.dist.php' ],
131156
toolType : ToolType.CODE_CHECK,
132-
}
133-
]
157+
},
158+
backwardCompatibilityCheckTool(config),
159+
].filter((tool) => tool !== null) as Tool[];
160+
161+
return tools
134162
// Remove all tools which do not need to run
135163
.filter((tool) =>
136164
(config.docLinting && tool.toolType === ToolType.LINTER)
@@ -146,3 +174,7 @@ export function removeNonExistentFilesToCheck(tool: Tool): Tool {
146174
filesToCheck : tool.filesToCheck.filter((file) => fs.existsSync(file))
147175
};
148176
}
177+
178+
export function isToolRunningContainerDefaultPhpVersion(tool: Tool): tool is ToolRunningContainerDefaultPhpVersion {
179+
return (tool as ToolRunningContainerDefaultPhpVersion).php === CONTAINER_DEFAULT_PHP_VERSION;
180+
}

0 commit comments

Comments
 (0)