diff --git a/.eslintrc.json b/.eslintrc.json index edc7b57b..7c046d52 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -22,7 +22,7 @@ "no-unused-vars": "off", "@typescript-eslint/no-unused-vars": "error", "node/no-unpublished-import": ["error", { - "allowModules": ["@cfworker/json-schema"] + "allowModules": ["@cfworker/json-schema", "dotenv"] }], "no-process-exit": "off", "no-sync": "off", diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index ce939e9b..735c5aa2 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -84,7 +84,15 @@ jobs: id: matrix_generation env: PROJECT_NAME_TO_TEST: ${{ matrix.projectName }} - 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 "") + run: | + cd tests/${PROJECT_NAME_TO_TEST} && \ + docker run \ + -i \ + --entrypoint "/action/main.js" \ + -v $(realpath .):/github/workspace \ + --env-file=test.env \ + -w=/github/workspace \ + ${TEST_TAG} $(test -r diff && cat diff || echo -n "") - name: "Output generated matrix" uses: sergeysova/jq-action@v2 diff --git a/laminas-ci.schema.json b/laminas-ci.schema.json index 431cd217..45bad5e7 100644 --- a/laminas-ci.schema.json +++ b/laminas-ci.schema.json @@ -183,6 +183,9 @@ "stablePHP": { "$ref": "#/definitions/stablePHP" }, + "backwardCompatibilityCheck": { + "$ref": "#/definitions/backwardCompatibilityCheck" + }, "additional_checks": { "type": "array", "title": "A list of additional checks to be executed", @@ -328,14 +331,20 @@ } }, "stablePHP": { - "type": "string", - "minLength": 1, - "title": "The PHP version to be used for stable checks", - "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.", - "examples": [ - "8.0" - ] - }, + "type": "string", + "minLength": 1, + "title": "The PHP version to be used for stable checks", + "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.", + "examples": [ + "8.0" + ] + }, + "backwardCompatibilityCheck": { + "type": "boolean", + "title": "Flag to enable/disable backwards compatibility check", + "description": "This flag enables/disables backwards compatibility check using roave/backward-compatibility-check.", + "default": false + }, "job": { "type": "object", "title": "The job to be executed", diff --git a/package-lock.json b/package-lock.json index d60d3bd4..4c5ad190 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,6 +17,7 @@ "@types/webpack": "^5.28.0", "@typescript-eslint/eslint-plugin": "^5.41.0", "@typescript-eslint/parser": "^5.41.0", + "dotenv": "^16.3.1", "eslint": "^8.26.0", "eslint-config-incredible": "^2.4.2", "eslint-import-resolver-typescript": "^3.5.2", @@ -2983,6 +2984,18 @@ "node": ">=6.0.0" } }, + "node_modules/dotenv": { + "version": "16.3.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz", + "integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/motdotla/dotenv?sponsor=1" + } + }, "node_modules/electron-to-chromium": { "version": "1.4.438", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.438.tgz", diff --git a/package.json b/package.json index c1d99172..68bda69d 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "@types/webpack": "^5.28.0", "@typescript-eslint/eslint-plugin": "^5.41.0", "@typescript-eslint/parser": "^5.41.0", + "dotenv": "^16.3.1", "eslint": "^8.26.0", "eslint-config-incredible": "^2.4.2", "eslint-import-resolver-typescript": "^3.5.2", diff --git a/src/config/app.spec.ts b/src/config/app.spec.ts index fe862a9a..a8a5ddc4 100644 --- a/src/config/app.spec.ts +++ b/src/config/app.spec.ts @@ -1,6 +1,14 @@ import {PathLike} from 'fs'; +import {configDotenv} from 'dotenv'; import createConfig, {gatherVersions} from './app'; +beforeEach(() => { + jest.resetModules(); + + // Clean enviroment to avoid side-effects + process.env = {}; +}); + describe('config/app', () => { describe('gatherVersions()', () => { test.each` @@ -25,6 +33,7 @@ describe('config/app', () => { describe('createConfig()', () => { const phpIniFromConfigurationPath: PathLike = 'tests/php-ini-from-configuration'; + const roaveBackwardCompatibilityPath: PathLike = 'tests/code-check-roave-backward-compatibility'; it('should return valid config', () => { expect(createConfig( @@ -47,7 +56,41 @@ describe('config/app', () => { phpIni : [ 'error_reporting=E_ALL' ], ignorePhpPlatformRequirements : {}, additionalComposerArguments : [], + backwardCompatibilityCheck : false, + baseReference : null, + }); + }); + + it('should detect GITHUB_BASE_REF', () => { + const environment = process.env; + + configDotenv({path: `${roaveBackwardCompatibilityPath}/test.env`}); + + expect(createConfig( + { + codeChecks : true, + docLinting : true, + }, + `${roaveBackwardCompatibilityPath}/composer.json`, + `${roaveBackwardCompatibilityPath}/composer.lock`, + `${roaveBackwardCompatibilityPath}/.laminas-ci.json` + )).toEqual({ + codeChecks : true, + docLinting : true, + versions : [], + stablePhpVersion : '7.4', + minimumPhpVersion : '7.4', + latestPhpVersion : '7.4', + lockedDependenciesExists : false, + phpExtensions : [], + phpIni : [], + ignorePhpPlatformRequirements : {}, + additionalComposerArguments : [], + backwardCompatibilityCheck : true, + baseReference : '1111222233334444aaaabbbbccccdddd', }); + + process.env = environment; }); }); }); diff --git a/src/config/app.ts b/src/config/app.ts index 3294c2fa..9268ea0e 100644 --- a/src/config/app.ts +++ b/src/config/app.ts @@ -1,7 +1,7 @@ import fs, {PathLike} from 'fs'; import semver from 'semver'; import parseJsonFile from '../json'; -import {Tool, ToolExecutionType} from '../tools'; +import {isToolRunningContainerDefaultPhpVersion, Tool, ToolExecutionType} from '../tools'; import {Logger} from '../logging'; import {CURRENT_STABLE, INSTALLABLE_VERSIONS, InstallablePhpVersionType, isInstallableVersion} from './php'; import {ComposerJson} from './composer'; @@ -85,6 +85,8 @@ export interface Config { readonly phpIni: string[]; readonly ignorePhpPlatformRequirements: IgnorePhpPlatformRequirements; readonly additionalComposerArguments: string[]; + readonly backwardCompatibilityCheck: boolean; + readonly baseReference: string|null; } export interface Requirements { readonly codeChecks: boolean; @@ -283,6 +285,17 @@ function createJob( return createdJob; } +function detectPhpVersionForTool( + tool: Tool, + config: Config +): InstallablePhpVersionType { + if (isToolRunningContainerDefaultPhpVersion(tool)) { + return tool.php; + } + + return config.minimumPhpVersion; +} + function createJobsForTool( config: Config, tool: Tool @@ -302,7 +315,7 @@ function createJobsForTool( tool.name, createJobDefinition( tool.command, - config.minimumPhpVersion, + detectPhpVersionForTool(tool, config), lockedOrLatestDependencySet, config.phpExtensions, config.phpIni, @@ -466,6 +479,8 @@ export default function createConfig( lockedDependenciesExists : fs.existsSync(composerLockJsonFileName), ignorePhpPlatformRequirements : configurationFromFile.ignore_php_platform_requirements ?? {}, additionalComposerArguments : [ ... new Set(configurationFromFile.additional_composer_arguments ?? []) ], + backwardCompatibilityCheck : configurationFromFile.backwardCompatibilityCheck ?? false, + baseReference : process.env.GITHUB_BASE_REF ?? null, }; } diff --git a/src/config/input.ts b/src/config/input.ts index b606b1c5..b4f5e9b0 100644 --- a/src/config/input.ts +++ b/src/config/input.ts @@ -11,6 +11,7 @@ export interface ConfigurationFromFile { ignore_php_platform_requirements?: IgnorePhpPlatformRequirements; stablePHP?: string; additional_composer_arguments?: string[]; + backwardCompatibilityCheck?: boolean; } export interface JobExclusionsFromFile { diff --git a/src/config/php.ts b/src/config/php.ts index 04d5afe8..e75cfc37 100644 --- a/src/config/php.ts +++ b/src/config/php.ts @@ -9,6 +9,7 @@ export const PHP_81 = '8.1'; export const PHP_82 = '8.2'; export const CURRENT_STABLE = PHP_80; +export const CONTAINER_DEFAULT_PHP_VERSION = '@default'; /** * 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 = [ PHP_74, PHP_80, PHP_81, - PHP_82 + PHP_82, + CONTAINER_DEFAULT_PHP_VERSION, ] as const; export type InstallablePhpVersionType = typeof INSTALLABLE_VERSIONS[number]; diff --git a/src/tools.ts b/src/tools.ts index 2178b7a9..4250a265 100644 --- a/src/tools.ts +++ b/src/tools.ts @@ -1,7 +1,8 @@ -import fs, { PathLike } from 'fs'; -import { Config } from './config/app'; -import { ComposerJson } from './config/composer'; +import fs, {PathLike} from 'fs'; +import {Config} from './config/app'; +import {ComposerJson} from './config/composer'; import parseJsonFile from './json'; +import {CONTAINER_DEFAULT_PHP_VERSION} from './config/php'; export enum ToolExecutionType { /** @@ -31,6 +32,10 @@ export type Tool = { lintConfigCommand?: string, } +export type ToolRunningContainerDefaultPhpVersion = Tool & { + php: typeof CONTAINER_DEFAULT_PHP_VERSION, +} + function detectInfectionCommand(): string { const composerJson: ComposerJson = parseJsonFile('composer.json', true) as ComposerJson; @@ -41,8 +46,28 @@ function detectInfectionCommand(): string { return 'phpdbg -qrr ./vendor/bin/infection'; } +function backwardCompatibilityCheckTool(config: Config): ToolRunningContainerDefaultPhpVersion | null { + if (!config.backwardCompatibilityCheck) { + return null; + } + + if (config.baseReference === null) { + return null; + } + + return { + // @TODO need to `git fetch baseSha1` from source repo! + executionType : ToolExecutionType.STATIC, + name : 'Backward Compatibility Check', + command : `roave-backward-compatibility-check check --from="${ config.baseReference }" --install-development-dependencies`, + filesToCheck : [ 'composer.json' ], + toolType : ToolType.CODE_CHECK, + php : CONTAINER_DEFAULT_PHP_VERSION, + } as ToolRunningContainerDefaultPhpVersion; +} + export default function createTools(config: Config): Array { - return [ + const tools = [ { executionType : ToolExecutionType.STATIC, name : 'Documentation Linting', @@ -129,8 +154,11 @@ export default function createTools(config: Config): Array { command : './vendor/bin/php-cs-fixer fix -v --diff --dry-run', filesToCheck : [ '.php-cs-fixer.php', '.php-cs-fixer.dist.php' ], toolType : ToolType.CODE_CHECK, - } - ] + }, + backwardCompatibilityCheckTool(config), + ].filter((tool) => tool !== null) as Tool[]; + + return tools // Remove all tools which do not need to run .filter((tool) => (config.docLinting && tool.toolType === ToolType.LINTER) @@ -146,3 +174,7 @@ export function removeNonExistentFilesToCheck(tool: Tool): Tool { filesToCheck : tool.filesToCheck.filter((file) => fs.existsSync(file)) }; } + +export function isToolRunningContainerDefaultPhpVersion(tool: Tool): tool is ToolRunningContainerDefaultPhpVersion { + return (tool as ToolRunningContainerDefaultPhpVersion).php === CONTAINER_DEFAULT_PHP_VERSION; +} diff --git a/tests/code-check-codeception-dist/test.env b/tests/code-check-codeception-dist/test.env new file mode 100644 index 00000000..e69de29b diff --git a/tests/code-check-codeception-nodist/test.env b/tests/code-check-codeception-nodist/test.env new file mode 100644 index 00000000..e69de29b diff --git a/tests/code-check-composer-require-checker/test.env b/tests/code-check-composer-require-checker/test.env new file mode 100644 index 00000000..e69de29b diff --git a/tests/code-check-deprecated-exclusion-via-config/test.env b/tests/code-check-deprecated-exclusion-via-config/test.env new file mode 100644 index 00000000..e69de29b diff --git a/tests/code-check-exclusion-via-config/test.env b/tests/code-check-exclusion-via-config/test.env new file mode 100644 index 00000000..e69de29b diff --git a/tests/code-check-infection-dist/test.env b/tests/code-check-infection-dist/test.env new file mode 100644 index 00000000..e69de29b diff --git a/tests/code-check-infection-nodist/test.env b/tests/code-check-infection-nodist/test.env new file mode 100644 index 00000000..e69de29b diff --git a/tests/code-check-infection-roave-static-analysis-plugin-dist/test.env b/tests/code-check-infection-roave-static-analysis-plugin-dist/test.env new file mode 100644 index 00000000..e69de29b diff --git a/tests/code-check-infection-roave-static-analysis-plugin-nodist/test.env b/tests/code-check-infection-roave-static-analysis-plugin-nodist/test.env new file mode 100644 index 00000000..e69de29b diff --git a/tests/code-check-locked-dependencies/test.env b/tests/code-check-locked-dependencies/test.env new file mode 100644 index 00000000..e69de29b diff --git a/tests/code-check-phpbench/test.env b/tests/code-check-phpbench/test.env new file mode 100644 index 00000000..e69de29b diff --git a/tests/code-check-phpcs-dist/test.env b/tests/code-check-phpcs-dist/test.env new file mode 100644 index 00000000..e69de29b diff --git a/tests/code-check-phpcs-nodist/test.env b/tests/code-check-phpcs-nodist/test.env new file mode 100644 index 00000000..e69de29b diff --git a/tests/code-check-phpcsfixer-php-dist/test.env b/tests/code-check-phpcsfixer-php-dist/test.env new file mode 100644 index 00000000..e69de29b diff --git a/tests/code-check-phpcsfixer-php/test.env b/tests/code-check-phpcsfixer-php/test.env new file mode 100644 index 00000000..e69de29b diff --git a/tests/code-check-phpunit-dist-due-to-laminas-ci-json-change/test.env b/tests/code-check-phpunit-dist-due-to-laminas-ci-json-change/test.env new file mode 100644 index 00000000..e69de29b diff --git a/tests/code-check-phpunit-dist/test.env b/tests/code-check-phpunit-dist/test.env new file mode 100644 index 00000000..e69de29b diff --git a/tests/code-check-phpunit-nodist/test.env b/tests/code-check-phpunit-nodist/test.env new file mode 100644 index 00000000..e69de29b diff --git a/tests/code-check-psalm-dist/test.env b/tests/code-check-psalm-dist/test.env new file mode 100644 index 00000000..e69de29b diff --git a/tests/code-check-psalm-nodist/test.env b/tests/code-check-psalm-nodist/test.env new file mode 100644 index 00000000..e69de29b diff --git a/tests/code-check-roave-backward-compatibility/.laminas-ci.json b/tests/code-check-roave-backward-compatibility/.laminas-ci.json new file mode 100644 index 00000000..cd9ccadc --- /dev/null +++ b/tests/code-check-roave-backward-compatibility/.laminas-ci.json @@ -0,0 +1,4 @@ +{ + "stablePHP": "7.4", + "backwardCompatibilityCheck": true +} diff --git a/tests/code-check-roave-backward-compatibility/composer.json b/tests/code-check-roave-backward-compatibility/composer.json new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/tests/code-check-roave-backward-compatibility/composer.json @@ -0,0 +1 @@ +{} diff --git a/tests/code-check-roave-backward-compatibility/matrix.json b/tests/code-check-roave-backward-compatibility/matrix.json new file mode 100644 index 00000000..8ef19412 --- /dev/null +++ b/tests/code-check-roave-backward-compatibility/matrix.json @@ -0,0 +1,10 @@ +{ + "include": [ + { + "name": "Backward Compatibility Check [@default, latest]", + "job": "{\"command\":\"roave-backward-compatibility-check check --from=\\\"1111222233334444aaaabbbbccccdddd\\\" --install-development-dependencies\",\"php\":\"@default\",\"extensions\":[],\"ini\":[],\"dependencies\":\"latest\",\"ignore_platform_reqs_8\":false,\"ignore_php_platform_requirement\":false,\"additional_composer_arguments\":[],\"before_script\":[]}", + "operatingSystem": "ubuntu-latest", + "action": "laminas/laminas-continuous-integration-action@v1" + } + ] +} diff --git a/tests/code-check-roave-backward-compatibility/test.env b/tests/code-check-roave-backward-compatibility/test.env new file mode 100644 index 00000000..a749c03b --- /dev/null +++ b/tests/code-check-roave-backward-compatibility/test.env @@ -0,0 +1 @@ +GITHUB_BASE_REF=1111222233334444aaaabbbbccccdddd diff --git a/tests/code-checks-without-linting-due-to-diff/test.env b/tests/code-checks-without-linting-due-to-diff/test.env new file mode 100644 index 00000000..e69de29b diff --git a/tests/configuration-additional-job-latest-php-version/test.env b/tests/configuration-additional-job-latest-php-version/test.env new file mode 100644 index 00000000..e69de29b diff --git a/tests/configuration-additional-job-lowest-php-version/test.env b/tests/configuration-additional-job-lowest-php-version/test.env new file mode 100644 index 00000000..e69de29b diff --git a/tests/configuration-explicit-job-wildcard-dependency-set/test.env b/tests/configuration-explicit-job-wildcard-dependency-set/test.env new file mode 100644 index 00000000..e69de29b diff --git a/tests/configuration-explicit-job-wildcard-php/test.env b/tests/configuration-explicit-job-wildcard-php/test.env new file mode 100644 index 00000000..e69de29b diff --git a/tests/doc-linting-doc-book/test.env b/tests/doc-linting-doc-book/test.env new file mode 100644 index 00000000..e69de29b diff --git a/tests/doc-linting-docs-book/test.env b/tests/doc-linting-docs-book/test.env new file mode 100644 index 00000000..e69de29b diff --git a/tests/doc-linting-mkdocs/test.env b/tests/doc-linting-mkdocs/test.env new file mode 100644 index 00000000..e69de29b diff --git a/tests/doc-linting-readme-due-diff/test.env b/tests/doc-linting-readme-due-diff/test.env new file mode 100644 index 00000000..e69de29b diff --git a/tests/doc-linting-readme/test.env b/tests/doc-linting-readme/test.env new file mode 100644 index 00000000..e69de29b diff --git a/tests/doc-linting-without-code-checks-due-diff/test.env b/tests/doc-linting-without-code-checks-due-diff/test.env new file mode 100644 index 00000000..e69de29b diff --git a/tests/extensions-from-composer-json/test.env b/tests/extensions-from-composer-json/test.env new file mode 100644 index 00000000..e69de29b diff --git a/tests/no-checks-due-to-diff/test.env b/tests/no-checks-due-to-diff/test.env new file mode 100644 index 00000000..e69de29b diff --git a/tests/no-checks/test.env b/tests/no-checks/test.env new file mode 100644 index 00000000..e69de29b diff --git a/tests/php-ini-from-configuration/test.env b/tests/php-ini-from-configuration/test.env new file mode 100644 index 00000000..e69de29b