diff --git a/package-lock.json b/package-lock.json index 0cd6c0e2..cd0cea23 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,10 +11,9 @@ "dependencies": { "@form8ion/config-file": "^1.0.1", "@form8ion/core": "^4.6.1", - "@form8ion/github-workflows-core": "^5.1.0", + "@form8ion/github-workflows-core": "^5.3.0", "@form8ion/javascript-core": "^11.0.0", - "deepmerge": "^4.2.2", - "js-yaml": "^4.1.0" + "deepmerge": "^4.2.2" }, "devDependencies": { "@babel/register": "7.24.6", @@ -37,6 +36,7 @@ "gherkin-lint": "4.2.4", "husky": "9.1.5", "jest-when": "3.6.0", + "js-yaml": "4.1.0", "lockfile-lint": "4.14.0", "ls-engines": "0.9.3", "mocha": "10.7.3", @@ -3758,9 +3758,9 @@ } }, "node_modules/@form8ion/core": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/@form8ion/core/-/core-4.6.1.tgz", - "integrity": "sha512-vpDOmoIFWQ+LmGNS1A7pw3xPN1p5+7gQkZmRwjNX5GHMIk1yAAIfATegMCOFF6lsObAIZFLz9PdhpjCit65c4A==", + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/@form8ion/core/-/core-4.7.1.tgz", + "integrity": "sha512-mIcMwO5ql6NDt5kFQAWH0cnmakKnF3YvLOL+KmfJXn9zO2BPTmzEnJ68gUcIG2J5gPJmxg1rRAB4d2X/Cm2PjQ==", "license": "MIT", "dependencies": { "@hapi/hoek": "^11.0.2", @@ -3823,12 +3823,12 @@ } }, "node_modules/@form8ion/github-workflows-core": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@form8ion/github-workflows-core/-/github-workflows-core-5.1.0.tgz", - "integrity": "sha512-t7gvAyG5/MGxXd/5E4iq5jawq4sZHYdzHN3WsP43AZwN76wLYkkIb2ZRaUJOTm1U/E9dOTYWTCXSiOv95uFCLQ==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@form8ion/github-workflows-core/-/github-workflows-core-5.3.0.tgz", + "integrity": "sha512-HaUpdOXcW+qVMG89i/v1RAz5zcr0yYXUFhn3zFq2+rCJ1VW2m7Pd53c/Y/GFK3FRMW4KcvPrLf4PC6itEonSYw==", "license": "MIT", "dependencies": { - "@form8ion/core": "^4.6.1" + "@form8ion/core": "^4.7.1" }, "engines": { "node": "^18.17 || >=20.6.1" @@ -14954,6 +14954,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "license": "MIT", "dependencies": { "argparse": "^2.0.1" }, diff --git a/package.json b/package.json index e8257bda..62081c67 100644 --- a/package.json +++ b/package.json @@ -63,10 +63,9 @@ "dependencies": { "@form8ion/config-file": "^1.0.1", "@form8ion/core": "^4.6.1", - "@form8ion/github-workflows-core": "^5.1.0", + "@form8ion/github-workflows-core": "^5.3.0", "@form8ion/javascript-core": "^11.0.0", - "deepmerge": "^4.2.2", - "js-yaml": "^4.1.0" + "deepmerge": "^4.2.2" }, "devDependencies": { "@babel/register": "7.24.6", @@ -89,6 +88,7 @@ "gherkin-lint": "4.2.4", "husky": "9.1.5", "jest-when": "3.6.0", + "js-yaml": "4.1.0", "lockfile-lint": "4.14.0", "ls-engines": "0.9.3", "mocha": "10.7.3", diff --git a/src/semantic-release/ci-providers/github-workflows/experimental-release-workflow/lifter.js b/src/semantic-release/ci-providers/github-workflows/experimental-release-workflow/lifter.js index 4c2e700d..2ee55542 100644 --- a/src/semantic-release/ci-providers/github-workflows/experimental-release-workflow/lifter.js +++ b/src/semantic-release/ci-providers/github-workflows/experimental-release-workflow/lifter.js @@ -1,6 +1,5 @@ import {promises as fs} from 'fs'; -import {load} from 'js-yaml'; -import {fileExists} from '@form8ion/core'; +import {loadWorkflowFile, workflowFileExists} from '@form8ion/github-workflows-core'; import scaffolder from './scaffolder.js'; @@ -10,26 +9,34 @@ function workflowPermissionsAreMinimal(existingContents) { && 'read' === existingContents.permissions.contents; } -async function contentsNeedToBeUpdated(pathToReleaseWorkflowFile) { - const existingContents = load(await fs.readFile(pathToReleaseWorkflowFile, 'utf-8')); +async function contentsNeedToBeUpdated({projectRoot, name}) { + const existingContents = await loadWorkflowFile({projectRoot, name}); return existingContents.on.workflow_dispatch || !workflowPermissionsAreMinimal(existingContents); } -async function releaseWorkflowShouldBeScaffolded(pathToReleaseWorkflowFile) { - return !await fileExists(pathToReleaseWorkflowFile) || contentsNeedToBeUpdated(pathToReleaseWorkflowFile); +async function releaseWorkflowShouldBeScaffolded({projectRoot, name}) { + return !await workflowFileExists({projectRoot, name}) || contentsNeedToBeUpdated({projectRoot, name}); } -export default async function ({projectRoot, nodeVersion}) { +async function renameLegacyReleaseWorkflow(projectRoot, experimentalReleaseWorkflowName) { const workflowsDirectory = `${projectRoot}/.github/workflows`; - const pathToExperimentalReleaseWorkflowFile = `${workflowsDirectory}/experimental-release.yml`; - const pathToLegacyReleaseWorkflowFile = `${workflowsDirectory}/release.yml`; + const legacyReleaseWorkflowName = 'release'; - if (await fileExists(pathToLegacyReleaseWorkflowFile)) { - await fs.rename(pathToLegacyReleaseWorkflowFile, pathToExperimentalReleaseWorkflowFile); + if (await workflowFileExists({projectRoot, name: legacyReleaseWorkflowName})) { + await fs.rename( + `${workflowsDirectory}/${legacyReleaseWorkflowName}.yml`, + `${workflowsDirectory}/${experimentalReleaseWorkflowName}.yml` + ); } +} + +export default async function ({projectRoot, nodeVersion}) { + const experimentalReleaseWorkflowName = 'experimental-release'; + + await renameLegacyReleaseWorkflow(projectRoot, experimentalReleaseWorkflowName); - if (await releaseWorkflowShouldBeScaffolded(pathToExperimentalReleaseWorkflowFile)) { + if (await releaseWorkflowShouldBeScaffolded({projectRoot, name: experimentalReleaseWorkflowName})) { return scaffolder({projectRoot, nodeVersion}); } diff --git a/src/semantic-release/ci-providers/github-workflows/lifter.js b/src/semantic-release/ci-providers/github-workflows/lifter.js index a0bb2d43..821705e1 100644 --- a/src/semantic-release/ci-providers/github-workflows/lifter.js +++ b/src/semantic-release/ci-providers/github-workflows/lifter.js @@ -1,6 +1,4 @@ -import {promises as fs} from 'fs'; -import {load} from 'js-yaml'; -import {writeWorkflowFile} from '@form8ion/github-workflows-core'; +import {loadWorkflowFile, writeWorkflowFile} from '@form8ion/github-workflows-core'; import determineTriggerNeedsFrom from './release-trigger-needs.js'; import {lift as liftReleaseWorkflow} from './experimental-release-workflow/index.js'; @@ -17,11 +15,11 @@ function removeCycjimmyActionFrom(otherJobs) { } export default async function ({projectRoot, nodeVersion}) { - const workflowsDirectory = `${projectRoot}/.github/workflows`; + const ciWorkflowName = 'node-ci'; await liftReleaseWorkflow({projectRoot, nodeVersion}); - const parsedVerificationWorkflowDetails = load(await fs.readFile(`${workflowsDirectory}/node-ci.yml`, 'utf-8')); + const parsedVerificationWorkflowDetails = await loadWorkflowFile({projectRoot, name: ciWorkflowName}); parsedVerificationWorkflowDetails.on.push.branches = [ ...parsedVerificationWorkflowDetails.on.push.branches.filter(branch => 'alpha' !== branch), @@ -48,7 +46,7 @@ export default async function ({projectRoot, nodeVersion}) { await writeWorkflowFile({ projectRoot, - name: 'node-ci', + name: ciWorkflowName, config: parsedVerificationWorkflowDetails }); } diff --git a/test/integration/features/step_definitions/github-workflows-steps.js b/test/integration/features/step_definitions/github-workflows-steps.js index d41e8b9c..993e8012 100644 --- a/test/integration/features/step_definitions/github-workflows-steps.js +++ b/test/integration/features/step_definitions/github-workflows-steps.js @@ -1,19 +1,20 @@ import {promises as fs} from 'fs'; -import {fileExists} from '@form8ion/core'; +import {loadWorkflowFile, workflowFileExists} from '@form8ion/github-workflows-core'; import {Given, Then} from '@cucumber/cucumber'; import {assert} from 'chai'; -import {load} from 'js-yaml'; -async function loadReleaseWorkflowDefinition() { +const experimentalReleaseWorkflowName = 'experimental-release'; +const legacyReleaseWorkflowName = 'release'; +const ciWorkflowName = 'node-ci'; + +async function loadReleaseWorkflowDefinition({projectRoot}) { assert.isTrue( - await fileExists(`${process.cwd()}/.github/workflows/experimental-release.yml`), - 'Release workflow is missing' + await workflowFileExists({projectRoot, name: experimentalReleaseWorkflowName}), + 'Experimental-Release workflow is missing' ); - const {on: triggers, jobs} = load( - await fs.readFile(`${process.cwd()}/.github/workflows/experimental-release.yml`, 'utf-8') - ); + const {on: triggers, jobs} = await loadWorkflowFile({projectRoot, name: experimentalReleaseWorkflowName}); return {triggers, jobs}; } @@ -85,7 +86,7 @@ Given('no conventional verification workflow is defined', async function () { }); Then('the experimental release workflow calls the reusable workflow for alpha branches', async function () { - const {triggers, jobs} = await loadReleaseWorkflowDefinition(); + const {triggers, jobs} = await loadReleaseWorkflowDefinition({projectRoot: this.projectRoot}); assert.isUndefined(triggers.workflow_dispatch); assert.deepEqual(triggers.push.branches, ['alpha']); @@ -93,13 +94,13 @@ Then('the experimental release workflow calls the reusable workflow for alpha br }); Then('the legacy experimental release workflow has been renamed', async function () { - assert.isFalse(await fileExists(`${process.cwd()}/.github/workflows/release.yml`)); + assert.isFalse(await workflowFileExists({projectRoot: this.projectRoot, name: legacyReleaseWorkflowName})); }); Then( 'the experimental release workflow calls the reusable workflow for semantic-release v19 for alpha branches', async function () { - const {triggers, jobs} = await loadReleaseWorkflowDefinition(); + const {triggers, jobs} = await loadReleaseWorkflowDefinition({projectRoot: this.projectRoot}); assert.isUndefined(triggers.workflow_dispatch); assert.deepEqual(triggers.push.branches, ['alpha']); @@ -111,14 +112,11 @@ Then( ); Then('the release workflow is not defined', async function () { - assert.isFalse(await fileExists(`${process.cwd()}/.github/workflows/release.yml`)); + assert.isFalse(await workflowFileExists({projectRoot: this.projectRoot, name: experimentalReleaseWorkflowName})); }); Then('the verification workflow calls the reusable release workflow', async function () { - const verificationWorkflowDefinition = load(await fs.readFile( - `${process.cwd()}/.github/workflows/node-ci.yml`, - 'utf-8' - )); + const verificationWorkflowDefinition = await loadWorkflowFile({projectRoot: this.projectRoot, name: ciWorkflowName}); const branchTriggers = verificationWorkflowDefinition.on.push.branches; assert.include(branchTriggers, 'master'); @@ -148,10 +146,7 @@ Then('the verification workflow calls the reusable release workflow', async func }); Then('the verification workflow calls the reusable release workflow for semantic-release v19', async function () { - const verificationWorkflowDefinition = load(await fs.readFile( - `${process.cwd()}/.github/workflows/node-ci.yml`, - 'utf-8' - )); + const verificationWorkflowDefinition = await loadWorkflowFile({projectRoot: this.projectRoot, name: ciWorkflowName}); const branchTriggers = verificationWorkflowDefinition.on.push.branches; assert.include(branchTriggers, 'master'); @@ -172,19 +167,13 @@ Then('the verification workflow calls the reusable release workflow for semantic }); Then('the verification workflow does not trigger the release workflow', async function () { - const verificationWorkflowDefinition = load(await fs.readFile( - `${process.cwd()}/.github/workflows/node-ci.yml`, - 'utf-8' - )); + const verificationWorkflowDefinition = await loadWorkflowFile({projectRoot: this.projectRoot, name: ciWorkflowName}); assert.isUndefined(verificationWorkflowDefinition.jobs['trigger-release']); }); Then('the release is not called until verification completes', async function () { - const verificationWorkflowDefinition = load(await fs.readFile( - `${process.cwd()}/.github/workflows/node-ci.yml`, - 'utf-8' - )); + const verificationWorkflowDefinition = await loadWorkflowFile({projectRoot: this.projectRoot, name: ciWorkflowName}); const triggerReleaseJob = verificationWorkflowDefinition.jobs.release; assert.include(triggerReleaseJob.needs, 'verify');