Skip to content

Commit

Permalink
feat(experimental-release-workflow): update the reusable workflow ver…
Browse files Browse the repository at this point in the history
…sion based on node version
  • Loading branch information
travi committed Feb 2, 2025
1 parent 6b64c3e commit 1efa4d9
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 11 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {loadWorkflowFile, renameWorkflowFile, workflowFileExists} from '@form8ion/github-workflows-core';

import {determineAppropriateWorkflow} from '../reusable-release-workflow.js';
import scaffoldWorkflow from './scaffolder.js';

function workflowPermissionsAreMinimal(existingContents) {
Expand All @@ -8,14 +9,23 @@ function workflowPermissionsAreMinimal(existingContents) {
&& 'read' === existingContents.permissions.contents;
}

async function contentsNeedToBeUpdated({projectRoot, name}) {
function reusableWorkflowVersionAppropriateForNodeVersion({nodeVersion, reusableWorkflow}) {
return reusableWorkflow === determineAppropriateWorkflow(nodeVersion);
}

async function contentsNeedToBeUpdated({projectRoot, name, nodeVersion}) {
const existingContents = await loadWorkflowFile({projectRoot, name});

return existingContents.on.workflow_dispatch || !workflowPermissionsAreMinimal(existingContents);
return existingContents.on.workflow_dispatch
|| !workflowPermissionsAreMinimal(existingContents)
|| !reusableWorkflowVersionAppropriateForNodeVersion({
nodeVersion,
reusableWorkflow: existingContents.jobs.release.uses
});
}

async function releaseWorkflowShouldBeScaffolded({projectRoot, name}) {
return !await workflowFileExists({projectRoot, name}) || contentsNeedToBeUpdated({projectRoot, name});
async function releaseWorkflowShouldBeScaffolded({projectRoot, name, nodeVersion}) {
return !await workflowFileExists({projectRoot, name}) || contentsNeedToBeUpdated({projectRoot, name, nodeVersion});
}

async function renameLegacyReleaseWorkflow(projectRoot, experimentalReleaseWorkflowName) {
Expand All @@ -35,7 +45,7 @@ export default async function ({projectRoot, nodeVersion}) {

await renameLegacyReleaseWorkflow(projectRoot, experimentalReleaseWorkflowName);

if (await releaseWorkflowShouldBeScaffolded({projectRoot, name: experimentalReleaseWorkflowName})) {
if (await releaseWorkflowShouldBeScaffolded({projectRoot, name: experimentalReleaseWorkflowName, nodeVersion})) {
return scaffoldWorkflow({projectRoot, nodeVersion});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,27 @@ import any from '@travi/any';
import {it, describe, vi, expect, beforeEach} from 'vitest';
import {when} from 'jest-when';

import {determineAppropriateWorkflow} from '../reusable-release-workflow.js';
import scaffoldWorkflow from './scaffolder.js';
import lift from './lifter.js';

vi.mock('@form8ion/github-workflows-core');
vi.mock('../reusable-release-workflow.js');
vi.mock('./scaffolder.js');

const experimentalReleaseWorkflowName = 'experimental-release';

describe('experimental release workflow lifter', () => {
const projectRoot = any.string();
const nodeVersion = any.string();
const appropriateReusableReleaseWorkflowVersion = any.string();
const scaffoldResults = any.simpleObject();

beforeEach(() => {
when(scaffoldWorkflow).calledWith({projectRoot, nodeVersion}).mockResolvedValue(scaffoldResults);
when(determineAppropriateWorkflow)
.calledWith(nodeVersion)
.mockReturnValue(appropriateReusableReleaseWorkflowVersion);
});

it('should call the scaffolder when the experimental release workflow does not exist', async () => {
Expand All @@ -32,15 +38,22 @@ describe('experimental release workflow lifter', () => {
when(workflowFileExists).calledWith({projectRoot, name: experimentalReleaseWorkflowName}).mockResolvedValue(true);
when(loadWorkflowFile)
.calledWith({projectRoot, name: experimentalReleaseWorkflowName})
.mockResolvedValue({on: {workflow_dispatch: {}}, permissions: {contents: 'read'}});
.mockResolvedValue({
on: {workflow_dispatch: {}},
permissions: {contents: 'read'},
jobs: {release: {uses: appropriateReusableReleaseWorkflowVersion}}
});

expect(await lift({projectRoot, nodeVersion})).toEqual(scaffoldResults);
expect(renameWorkflowFile).not.toHaveBeenCalled();
});

it('should re-run the scaffolder when permissions have not been restricted', async () => {
when(workflowFileExists).calledWith({projectRoot, name: experimentalReleaseWorkflowName}).mockResolvedValue(true);
when(loadWorkflowFile).calledWith({projectRoot, name: experimentalReleaseWorkflowName}).mockResolvedValue({on: {}});
when(loadWorkflowFile).calledWith({projectRoot, name: experimentalReleaseWorkflowName}).mockResolvedValue({
on: {},
jobs: {release: {uses: appropriateReusableReleaseWorkflowVersion}}
});

expect(await lift({projectRoot, nodeVersion})).toEqual(scaffoldResults);
expect(renameWorkflowFile).not.toHaveBeenCalled();
Expand All @@ -50,7 +63,11 @@ describe('experimental release workflow lifter', () => {
when(workflowFileExists).calledWith({projectRoot, name: experimentalReleaseWorkflowName}).mockResolvedValue(true);
when(loadWorkflowFile)
.calledWith({projectRoot, name: experimentalReleaseWorkflowName})
.mockResolvedValue({on: {}, permissions: any.simpleObject()});
.mockResolvedValue({
on: {},
permissions: any.simpleObject(),
jobs: {release: {uses: appropriateReusableReleaseWorkflowVersion}}
});

expect(await lift({projectRoot, nodeVersion})).toEqual(scaffoldResults);
expect(renameWorkflowFile).not.toHaveBeenCalled();
Expand All @@ -60,7 +77,11 @@ describe('experimental release workflow lifter', () => {
when(workflowFileExists).calledWith({projectRoot, name: experimentalReleaseWorkflowName}).mockResolvedValue(true);
when(loadWorkflowFile)
.calledWith({projectRoot, name: experimentalReleaseWorkflowName})
.mockResolvedValue({on: {}, permissions: {contents: 'write'}});
.mockResolvedValue({
on: {},
permissions: {contents: 'write'},
jobs: {release: {uses: appropriateReusableReleaseWorkflowVersion}}
});

expect(await lift({projectRoot, nodeVersion})).toEqual(scaffoldResults);
expect(renameWorkflowFile).not.toHaveBeenCalled();
Expand All @@ -70,18 +91,45 @@ describe('experimental release workflow lifter', () => {
when(workflowFileExists).calledWith({projectRoot, name: experimentalReleaseWorkflowName}).mockResolvedValue(true);
when(loadWorkflowFile)
.calledWith({projectRoot, name: experimentalReleaseWorkflowName})
.mockResolvedValue({on: {}, permissions: {contents: 'read'}});
.mockResolvedValue({
on: {},
permissions: {contents: 'read'},
jobs: {release: {uses: appropriateReusableReleaseWorkflowVersion}}
});

expect(await lift({projectRoot, nodeVersion})).toBe(undefined);
expect(renameWorkflowFile).not.toHaveBeenCalled();
});

it('should re-run the scaffolder when an inappropriate version of the reusable workflow is referenced', async () => {
const inappropriateReusableReleaseWorkflowVersion = any.string();
when(workflowFileExists).calledWith({projectRoot, name: experimentalReleaseWorkflowName}).mockResolvedValue(true);
when(loadWorkflowFile)
.calledWith({projectRoot, name: experimentalReleaseWorkflowName})
.mockResolvedValue({
on: {},
permissions: {contents: 'read'},
jobs: {release: {uses: inappropriateReusableReleaseWorkflowVersion}}
});

expect(await lift({projectRoot, nodeVersion})).toEqual(scaffoldResults);
expect(renameWorkflowFile).not.toHaveBeenCalled();
});

it('should should rename a legacy release workflow to clarify that it is for experimental releases', async () => {
const legacyReleaseWorkflowName = 'release';
when(workflowFileExists).calledWith({projectRoot, name: legacyReleaseWorkflowName}).mockResolvedValue(true);
when(loadWorkflowFile)
.calledWith({projectRoot, name: legacyReleaseWorkflowName})
.mockResolvedValue({on: {}, permissions: {contents: 'read'}});
.mockResolvedValue(any.simpleObject());
when(workflowFileExists).calledWith({projectRoot, name: experimentalReleaseWorkflowName}).mockResolvedValue(true);
when(loadWorkflowFile)
.calledWith({projectRoot, name: experimentalReleaseWorkflowName})
.mockResolvedValue({
on: {},
permissions: {contents: 'read'},
jobs: {release: {uses: appropriateReusableReleaseWorkflowVersion}}
});

expect(await lift({projectRoot, nodeVersion})).toBe(undefined);
expect(renameWorkflowFile).toHaveBeenCalledWith({
Expand Down

0 comments on commit 1efa4d9

Please sign in to comment.