diff --git a/.github/workflows/verify-pr.yml b/.github/workflows/verify-pr.yml index 23734056..fe441410 100644 --- a/.github/workflows/verify-pr.yml +++ b/.github/workflows/verify-pr.yml @@ -49,20 +49,21 @@ jobs: - uses: actions/setup-node@v4 with: node-version: 'lts/*' + - run: npm install - name: Validate that changed packages are versioned as snapshots if: ${{ needs.check_for_postrelease_keyword.outputs.is-postrelease == 'false' }} run: | BASE_SHA=${{ github.event.pull_request.base.sha }} HEAD_SHA=${{ github.event.pull_request.head.sha }} - git diff --name-only $HEAD_SHA $BASE_SHA > changed_files.txt - node ./.github/workflows/verify-pr/validate-changed-package-versions.js changed_files.txt + git diff --name-only $HEAD_SHA $BASE_SHA > `pwd`/changed_files.txt + node ./.node-scripts/validate-changed-package-versions.js `pwd`/changed_files.txt - name: Validate that packages properly depend on each other if: ${{ needs.check_for_postrelease_keyword.outputs.is-postrelease == 'false' }} run: | cd packages PACKAGE_NAMES=`ls` cd .. - node ./.github/workflows/verify-pr/validate-package-interdependencies.js "$PACKAGE_NAMES" + node ./.node-scripts/validate-package-interdependencies.js "$PACKAGE_NAMES" run_tests: runs-on: ${{ matrix.os }} strategy: diff --git a/.gitignore b/.gitignore index 802265ff..016e90ff 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,5 @@ dist/ coverage/ *.tsbuildinfo *.tgz -.sfdx \ No newline at end of file +.sfdx +.scratchfile diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100644 index 00000000..0230fac6 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,6 @@ +git diff --name-only --cached --diff-filter=ACMR > `pwd`/.scratchfile +node ./.node-scripts/validate-changed-package-versions.js `pwd`/.scratchfile +cd packages +PACKAGE_NAMES=`ls` +cd .. +node ./.node-scripts/validate-package-interdependencies.js "$PACKAGE_NAMES" diff --git a/.github/workflows/verify-pr/validate-changed-package-versions.js b/.node-scripts/validate-changed-package-versions.js similarity index 73% rename from .github/workflows/verify-pr/validate-changed-package-versions.js rename to .node-scripts/validate-changed-package-versions.js index f825b6ee..831e6d8e 100644 --- a/.github/workflows/verify-pr/validate-changed-package-versions.js +++ b/.node-scripts/validate-changed-package-versions.js @@ -1,5 +1,7 @@ const path = require('path'); const fs = require('fs'); +const cp = require('child_process'); +const semver = require('semver'); function main() { const changedFiles = readChangedFilesFile(process.argv[2]); @@ -39,7 +41,7 @@ function displayList(header, list) { } function readChangedFilesFile(changedFilesFileName) { - return fs.readFileSync(path.join(__dirname, '..', '..', '..', changedFilesFileName), 'utf-8').split('\n').map(s => s.trim()); + return fs.readFileSync(changedFilesFileName, 'utf-8').split('\n').map(s => s.trim()); } function identifyMeaningfullyChangedPackages(changedFiles) { @@ -74,18 +76,31 @@ function isFileInTestFolder(changedFile) { function identifyIncorrectlyVersionedPackages(changedPackages) { const incorrectlyVersionedPackages = []; for (const changedPackage of changedPackages) { - //A temporary workaround for the rename of the flowtest-engine package to flow-engine - if (changedPackage === 'packages/code-analyzer-flowtest-engine') { - continue; - } const packageVersion = getPackageVersion(changedPackage); if (!packageVersion.endsWith('-SNAPSHOT')) { incorrectlyVersionedPackages.push(`${changedPackage} (currently versioned as ${packageVersion}) lacks a trailing "-SNAPSHOT"`); + continue; + } + const releasedPackageVersion = getLatestReleasedVersion(changedPackage); + if (semver.lte(semver.parse(packageVersion.slice(0, packageVersion.length - 9)), semver.parse(releasedPackageVersion))) { + incorrectlyVersionedPackages.push(`${changedPackage} (currently versioned as ${packageVersion}) is not semantically ahead of latest published release ${releasedPackageVersion}`); } } return incorrectlyVersionedPackages; } +function getLatestReleasedVersion(changedPackage) { + const publishedPackageName = JSON.parse(fs.readFileSync(path.join(changedPackage, 'package.json'), 'utf-8')).name; + try { + + return cp.execSync(`npm view ${publishedPackageName} version`, { + encoding: 'utf-8' + }); + } catch (e) { + console.log(`NOTE: Could not fetch latest release version of ${publishedPackageName} (located in ${changedPackage}). Is that an error?`); + return undefined; + } +} function getPackageVersion(changedPackage) { const packageJsonPath = path.join(changedPackage, 'package.json'); diff --git a/.github/workflows/verify-pr/validate-package-interdependencies.js b/.node-scripts/validate-package-interdependencies.js similarity index 100% rename from .github/workflows/verify-pr/validate-package-interdependencies.js rename to .node-scripts/validate-package-interdependencies.js diff --git a/package-lock.json b/package-lock.json index 8b1da131..3eb7ed6a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,8 +10,10 @@ ], "devDependencies": { "cross-env": "^10.0.0", + "husky": "^9.1.7", "jest": "^30.0.5", "rimraf": "^6.0.1", + "semver": "^7.7.2", "ts-jest": "^29.4.1" } }, @@ -4862,6 +4864,22 @@ "node": ">=10.17.0" } }, + "node_modules/husky": { + "version": "9.1.7", + "resolved": "https://registry.npmjs.org/husky/-/husky-9.1.7.tgz", + "integrity": "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==", + "dev": true, + "license": "MIT", + "bin": { + "husky": "bin.js" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/typicode" + } + }, "node_modules/ignore": { "version": "7.0.5", "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", diff --git a/package.json b/package.json index c3872a8f..c89cd0e7 100644 --- a/package.json +++ b/package.json @@ -17,12 +17,15 @@ "scrub": "npm run clean && npm run scrub --workspaces --if-present && rimraf node_modules && rimraf package-lock.json", "showcoverage-java": "npm run showcoverage-java --workspaces --if-present", "showcoverage-typescript": "open ./coverage/lcov-report/index.html", - "showcoverage": "npm run showcoverage-java && npm run showcoverage-typescript" + "showcoverage": "npm run showcoverage-java && npm run showcoverage-typescript", + "prepare": "husky" }, "devDependencies": { "cross-env": "^10.0.0", + "husky": "^9.1.7", "jest": "^30.0.5", "rimraf": "^6.0.1", + "semver": "^7.7.2", "ts-jest": "^29.4.1" }, "jest": {