@form8ion/github-workflows-core 5.7.0 → 5.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +3 -2
- package/src/index.js +14 -0
- package/src/jobs/remover.js +13 -0
- package/src/jobs/remover.test.js +26 -0
- package/src/scaffolder.js +3 -0
- package/src/steps/remover.js +3 -0
- package/src/steps/remover.test.js +20 -0
- package/src/steps/scaffolders.js +28 -0
- package/src/steps/scaffolders.test.js +38 -0
- package/src/tester.js +5 -0
- package/src/tester.test.js +25 -0
- package/src/workflow-file/existence-checker.js +5 -0
- package/src/workflow-file/existence-checker.test.js +20 -0
- package/src/workflow-file/index.js +4 -0
- package/src/workflow-file/loader.js +5 -0
- package/src/workflow-file/loader.test.js +22 -0
- package/src/workflow-file/renamer.js +8 -0
- package/src/workflow-file/renamer.test.js +23 -0
- package/src/workflow-file/writer.js +5 -0
- package/src/workflow-file/writer.test.js +25 -0
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@form8ion/github-workflows-core",
|
|
3
3
|
"description": "core functionality for form8ion plugins that manage github workflows",
|
|
4
4
|
"license": "MIT",
|
|
5
|
-
"version": "5.7.
|
|
5
|
+
"version": "5.7.1",
|
|
6
6
|
"type": "commonjs",
|
|
7
7
|
"engines": {
|
|
8
8
|
"node": "^18.17 || >=20.6.1"
|
|
@@ -52,7 +52,8 @@
|
|
|
52
52
|
},
|
|
53
53
|
"files": [
|
|
54
54
|
"example.js",
|
|
55
|
-
"lib/"
|
|
55
|
+
"lib/",
|
|
56
|
+
"src/"
|
|
56
57
|
],
|
|
57
58
|
"publishConfig": {
|
|
58
59
|
"access": "public",
|
package/src/index.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export {
|
|
2
|
+
executeVerification as scaffoldVerificationStep,
|
|
3
|
+
installDependencies as scaffoldDependencyInstallationStep,
|
|
4
|
+
setupNode as scaffoldNodeSetupStep,
|
|
5
|
+
checkout as scaffoldCheckoutStep
|
|
6
|
+
} from './steps/scaffolders.js';
|
|
7
|
+
export {
|
|
8
|
+
fileExists as workflowFileExists,
|
|
9
|
+
load as loadWorkflowFile,
|
|
10
|
+
write as writeWorkflowFile,
|
|
11
|
+
rename as renameWorkflowFile
|
|
12
|
+
} from './workflow-file/index.js';
|
|
13
|
+
export {default as test} from './tester.js';
|
|
14
|
+
export {removeActionFromJobs} from './jobs/remover.js';
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import {removeActionFromSteps} from '../steps/remover.js';
|
|
2
|
+
|
|
3
|
+
export function removeActionFromJobs(jobs, actionName) {
|
|
4
|
+
return Object.fromEntries(
|
|
5
|
+
Object.entries(jobs).map(([jobName, job]) => ([
|
|
6
|
+
jobName,
|
|
7
|
+
{
|
|
8
|
+
...job,
|
|
9
|
+
.../* job.steps && */{steps: removeActionFromSteps(job.steps, actionName)}
|
|
10
|
+
}
|
|
11
|
+
]))
|
|
12
|
+
);
|
|
13
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import {describe, it, expect, vi} from 'vitest';
|
|
2
|
+
import {when} from 'vitest-when';
|
|
3
|
+
import any from '@travi/any';
|
|
4
|
+
import zip from 'lodash.zip';
|
|
5
|
+
|
|
6
|
+
import {removeActionFromSteps} from '../steps/remover.js';
|
|
7
|
+
import {removeActionFromJobs} from './remover.js';
|
|
8
|
+
|
|
9
|
+
vi.mock('../steps/remover.js');
|
|
10
|
+
|
|
11
|
+
describe('jobs-level remover', () => {
|
|
12
|
+
const anySteps = () => any.listOf(any.simpleObject);
|
|
13
|
+
|
|
14
|
+
it('should remove an action by name from an indexed list of jobs', async () => {
|
|
15
|
+
const actionName = any.word();
|
|
16
|
+
const jobNames = any.listOf(any.word);
|
|
17
|
+
const jobs = jobNames.map(() => ({...any.simpleObject(), steps: anySteps()}));
|
|
18
|
+
const updatedJobs = jobs.map(job => ({...job, steps: anySteps()}));
|
|
19
|
+
zip(jobs, updatedJobs).forEach(([job, updatedJob]) => {
|
|
20
|
+
when(removeActionFromSteps).calledWith(job.steps, actionName).thenReturn(updatedJob.steps);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
expect(removeActionFromJobs(Object.fromEntries(zip(jobNames, jobs)), actionName))
|
|
24
|
+
.toEqual(Object.fromEntries(zip(jobNames, updatedJobs)));
|
|
25
|
+
});
|
|
26
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import {describe, it, expect} from 'vitest';
|
|
2
|
+
import any from '@travi/any';
|
|
3
|
+
|
|
4
|
+
import {removeActionFromSteps} from './remover.js';
|
|
5
|
+
|
|
6
|
+
describe('steps-level remover', () => {
|
|
7
|
+
const anyStep = ({actionName} = {}) => ({
|
|
8
|
+
...any.simpleObject(),
|
|
9
|
+
...actionName && {uses: `${any.string()}${actionName}${any.string()}`}
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it('should remove an action by name from a list of steps', async () => {
|
|
13
|
+
const stepsBeforeRemoved = any.listOf(anyStep);
|
|
14
|
+
const stepsAfterRemoved = any.listOf(anyStep);
|
|
15
|
+
const actionName = any.word();
|
|
16
|
+
const steps = [...stepsBeforeRemoved, anyStep({actionName}), ...stepsAfterRemoved];
|
|
17
|
+
|
|
18
|
+
expect(removeActionFromSteps(steps, actionName)).toEqual([...stepsBeforeRemoved, ...stepsAfterRemoved]);
|
|
19
|
+
});
|
|
20
|
+
});
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export function setupNode({versionDeterminedBy}) {
|
|
2
|
+
return {
|
|
3
|
+
name: 'Setup node',
|
|
4
|
+
uses: 'actions/setup-node@v6.1.0',
|
|
5
|
+
with: {
|
|
6
|
+
...'nvmrc' === versionDeterminedBy && {'node-version-file': '.nvmrc'},
|
|
7
|
+
// eslint-disable-next-line no-template-curly-in-string
|
|
8
|
+
...'matrix' === versionDeterminedBy && {'node-version': '${{ matrix.node }}'},
|
|
9
|
+
cache: 'npm'
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function executeVerification() {
|
|
15
|
+
return {run: 'npm test'};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function installDependencies() {
|
|
19
|
+
return [
|
|
20
|
+
{run: 'npm clean-install'},
|
|
21
|
+
{run: 'npm install --global corepack@latest'},
|
|
22
|
+
{run: 'corepack npm audit signatures'}
|
|
23
|
+
];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function checkout() {
|
|
27
|
+
return {uses: 'actions/checkout@v6.0.1'};
|
|
28
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import {describe, expect, it} from 'vitest';
|
|
2
|
+
|
|
3
|
+
import {checkout, executeVerification, installDependencies, setupNode} from './scaffolders.js';
|
|
4
|
+
|
|
5
|
+
describe('step scaffolders', () => {
|
|
6
|
+
it('should scaffold the details of the checkout step', () => {
|
|
7
|
+
expect(checkout()).toEqual({uses: 'actions/checkout@v6.0.1'});
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
it('should set up node correctly when the version is determined from the `.nvmrc`', () => {
|
|
11
|
+
expect(setupNode({versionDeterminedBy: 'nvmrc'})).toEqual({
|
|
12
|
+
name: 'Setup node',
|
|
13
|
+
uses: 'actions/setup-node@v6.1.0',
|
|
14
|
+
with: {'node-version-file': '.nvmrc', cache: 'npm'}
|
|
15
|
+
});
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('should set up node correctly when the version is determined based on a matrix', () => {
|
|
19
|
+
expect(setupNode({versionDeterminedBy: 'matrix'})).toEqual({
|
|
20
|
+
name: 'Setup node',
|
|
21
|
+
uses: 'actions/setup-node@v6.1.0',
|
|
22
|
+
// eslint-disable-next-line no-template-curly-in-string
|
|
23
|
+
with: {'node-version': '${{ matrix.node }}', cache: 'npm'}
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('should install dependencies correctly', () => {
|
|
28
|
+
expect(installDependencies()).toEqual([
|
|
29
|
+
{run: 'npm clean-install'},
|
|
30
|
+
{run: 'npm install --global corepack@latest'},
|
|
31
|
+
{run: 'corepack npm audit signatures'}
|
|
32
|
+
]);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('should execute verification correctly', () => {
|
|
36
|
+
expect(executeVerification()).toEqual({run: 'npm test'});
|
|
37
|
+
});
|
|
38
|
+
});
|
package/src/tester.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import any from '@travi/any';
|
|
2
|
+
import {directoryExists} from '@form8ion/core';
|
|
3
|
+
|
|
4
|
+
import {it, expect, describe, vi} from 'vitest';
|
|
5
|
+
import {when} from 'vitest-when';
|
|
6
|
+
|
|
7
|
+
import projectUsesGithubWorkflows from './tester.js';
|
|
8
|
+
|
|
9
|
+
vi.mock('@form8ion/core');
|
|
10
|
+
|
|
11
|
+
describe('workflows predicate', () => {
|
|
12
|
+
const projectRoot = any.string();
|
|
13
|
+
|
|
14
|
+
it('should return `true` when the project uses GitHub workflows', async () => {
|
|
15
|
+
when(directoryExists).calledWith(`${projectRoot}/.github/workflows`).thenResolve(true);
|
|
16
|
+
|
|
17
|
+
expect(await projectUsesGithubWorkflows({projectRoot})).toEqual(true);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('should return `false` when the project does not use GitHub workflows', async () => {
|
|
21
|
+
when(directoryExists).calledWith(`${projectRoot}/.github/workflows`).thenResolve(false);
|
|
22
|
+
|
|
23
|
+
expect(await projectUsesGithubWorkflows({projectRoot})).toEqual(false);
|
|
24
|
+
});
|
|
25
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import {fileExists} from '@form8ion/core';
|
|
2
|
+
|
|
3
|
+
import {when} from 'vitest-when';
|
|
4
|
+
import any from '@travi/any';
|
|
5
|
+
import {describe, expect, it, vi} from 'vitest';
|
|
6
|
+
|
|
7
|
+
import workflowFileExists from './existence-checker.js';
|
|
8
|
+
|
|
9
|
+
vi.mock('@form8ion/core');
|
|
10
|
+
|
|
11
|
+
describe('workflow existence checker', () => {
|
|
12
|
+
it('should check for existence of the workflow file in the workflows directory', async () => {
|
|
13
|
+
const projectRoot = any.string();
|
|
14
|
+
const name = any.word();
|
|
15
|
+
const exists = any.boolean();
|
|
16
|
+
when(fileExists).calledWith(`${projectRoot}/.github/workflows/${name}.yml`).thenResolve(exists);
|
|
17
|
+
|
|
18
|
+
expect(await workflowFileExists({projectRoot, name})).toBe(exists);
|
|
19
|
+
});
|
|
20
|
+
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import {fileTypes, loadConfigFile} from '@form8ion/core';
|
|
2
|
+
|
|
3
|
+
import {when} from 'vitest-when';
|
|
4
|
+
import any from '@travi/any';
|
|
5
|
+
import {describe, expect, it, vi} from 'vitest';
|
|
6
|
+
|
|
7
|
+
import loadWorkflowFile from './loader.js';
|
|
8
|
+
|
|
9
|
+
vi.mock('@form8ion/core');
|
|
10
|
+
|
|
11
|
+
describe('workflow loader', () => {
|
|
12
|
+
it('should load the workflow from the workflows directory', async () => {
|
|
13
|
+
const projectRoot = any.string();
|
|
14
|
+
const name = any.word();
|
|
15
|
+
const workflowDetails = any.simpleObject();
|
|
16
|
+
when(loadConfigFile)
|
|
17
|
+
.calledWith({path: `${projectRoot}/.github/workflows`, name, format: fileTypes.YAML})
|
|
18
|
+
.thenResolve(workflowDetails);
|
|
19
|
+
|
|
20
|
+
expect(await loadWorkflowFile({projectRoot, name})).toEqual(workflowDetails);
|
|
21
|
+
});
|
|
22
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import {promises as fs} from 'node:fs';
|
|
2
|
+
|
|
3
|
+
import any from '@travi/any';
|
|
4
|
+
import {describe, expect, it, vi} from 'vitest';
|
|
5
|
+
|
|
6
|
+
import renameWorkflowFile from './renamer.js';
|
|
7
|
+
|
|
8
|
+
vi.mock('node:fs');
|
|
9
|
+
|
|
10
|
+
describe('workflow file renamer', () => {
|
|
11
|
+
it('should rename the workflow file in the workflows directory', async () => {
|
|
12
|
+
const projectRoot = any.string();
|
|
13
|
+
const oldName = any.word();
|
|
14
|
+
const newName = any.word();
|
|
15
|
+
|
|
16
|
+
await renameWorkflowFile({projectRoot, oldName, newName});
|
|
17
|
+
|
|
18
|
+
expect(fs.rename).toHaveBeenCalledWith(
|
|
19
|
+
`${projectRoot}/.github/workflows/${oldName}.yml`,
|
|
20
|
+
`${projectRoot}/.github/workflows/${newName}.yml`
|
|
21
|
+
);
|
|
22
|
+
});
|
|
23
|
+
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import {fileTypes, writeConfigFile} from '@form8ion/core';
|
|
2
|
+
|
|
3
|
+
import any from '@travi/any';
|
|
4
|
+
import {describe, expect, it, vi} from 'vitest';
|
|
5
|
+
|
|
6
|
+
import writeWorkflowFile from './writer.js';
|
|
7
|
+
|
|
8
|
+
vi.mock('@form8ion/core');
|
|
9
|
+
|
|
10
|
+
describe('workflow writer', () => {
|
|
11
|
+
it('should write the workflow to the workflows directory as a yaml file', async () => {
|
|
12
|
+
const projectRoot = any.string();
|
|
13
|
+
const name = any.word();
|
|
14
|
+
const config = any.simpleObject();
|
|
15
|
+
|
|
16
|
+
await writeWorkflowFile({projectRoot, config, name});
|
|
17
|
+
|
|
18
|
+
expect(writeConfigFile).toHaveBeenCalledWith({
|
|
19
|
+
path: `${projectRoot}/.github/workflows`,
|
|
20
|
+
name,
|
|
21
|
+
config,
|
|
22
|
+
format: fileTypes.YAML
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
});
|