Skip to content

Commit

Permalink
move ensure-next-ahead from a bash script to a tested node script
Browse files Browse the repository at this point in the history
  • Loading branch information
JReinhold committed Oct 4, 2023
1 parent 15b5933 commit ed18a24
Show file tree
Hide file tree
Showing 4 changed files with 189 additions and 18 deletions.
19 changes: 1 addition & 18 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -165,31 +165,14 @@ jobs:
git merge ${{ github.ref_name }}
git push origin ${{ steps.target.outputs.target }}
# This step ensures that next is always one minor ahead of main
# this is needed when releasing a stable from next
# next will be at eg. 7.4.0-alpha.4, and main will be at 7.3.0
# then we release 7.4.0 by merging next to latest-release to main
# we then ensure here that next is bumped to 7.5.0 - without releasing it
# if this is a patch release bumping main to 7.3.1, next will not be touched because it's already ahead
- name: Ensure `next` is a minor version ahead of `main`
if: steps.target.outputs.target == 'main'
run: |
git checkout next
git pull
CODE_PKG_JSON=$(cat ../code/package.json)
VERSION_ON_NEXT=$(echo $CODE_PKG_JSON | jq --raw-output '.version')
VERSION_ON_MAIN="${{ steps.version.outputs.current-version }}"
yarn release:ensure-next-ahead --main-version "${{ steps.version.outputs.current-version }}"
# skip if next is already ahead of main
if NEXT_IS_AHEAD=$(npx semver --include-prerelease --range ">=$VERSION_ON_MAIN" "$VERSION_ON_NEXT" 2>/dev/null); then
return
fi
# temporarily set the version on next to be the same as main...
echo "$CODE_PKG_JSON" | jq --arg version "$VERSION_ON_MAIN" '.version = $version' > ../code/package.json
# ... then bump it by one minor
yarn release:version --release-type minor
git add ..
git commit -m "Bump next to be one minor ahead of main [skip ci]"
git push origin next
Expand Down
1 change: 1 addition & 0 deletions scripts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"lint:js:cmd": "cross-env NODE_ENV=production eslint --cache --cache-location=../.cache/eslint --ext .js,.jsx,.json,.html,.ts,.tsx,.mjs --report-unused-disable-directives",
"lint:package": "sort-package-json",
"migrate-docs": "node --require esbuild-register ./ts-to-ts49.ts",
"release:ensure-next-ahead": "ts-node --swc ./release/ensure-next-ahead.ts",
"release:generate-pr-description": "ts-node --swc ./release/generate-pr-description.ts",
"release:get-changelog-from-file": "ts-node --swc ./release/get-changelog-from-file.ts",
"release:get-current-version": "ts-node --swc ./release/get-current-version.ts",
Expand Down
85 changes: 85 additions & 0 deletions scripts/release/__tests__/ensure-next-ahead.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/* eslint-disable global-require */
/* eslint-disable no-underscore-dangle */
import path from 'path';
import { run as ensureNextAhead } from '../ensure-next-ahead';
import * as gitClient_ from '../utils/git-client';
import * as bumpVersion_ from '../version';

jest.mock('../utils/git-client', () => jest.requireActual('jest-mock-extended').mockDeep());
const gitClient = jest.mocked(gitClient_);

// eslint-disable-next-line jest/no-mocks-import
jest.mock('fs-extra', () => require('../../../code/__mocks__/fs-extra'));
const fsExtra = require('fs-extra');

jest.mock('../version', () => jest.requireActual('jest-mock-extended').mockDeep());
const bumpVersion = jest.mocked(bumpVersion_);

jest.spyOn(console, 'log').mockImplementation(() => {});
jest.spyOn(console, 'warn').mockImplementation(() => {});
jest.spyOn(console, 'error').mockImplementation(() => {});

const CODE_PACKAGE_JSON_PATH = path.join(__dirname, '..', '..', '..', 'code', 'package.json');

describe('Ensure next ahead', () => {
beforeEach(() => {
jest.clearAllMocks();
gitClient.git.status.mockResolvedValue({ current: 'next' } as any);
fsExtra.__setMockFiles({
[CODE_PACKAGE_JSON_PATH]: JSON.stringify({ version: '2.0.0' }),
});
});

it('should throw when main-version is missing', async () => {
await expect(ensureNextAhead({})).rejects.toThrowErrorMatchingInlineSnapshot(`
"[
{
"code": "invalid_type",
"expected": "string",
"received": "undefined",
"path": [
"mainVersion"
],
"message": "Required"
}
]"
`);
});

it('should throw when main-version is not a semver string', async () => {
await expect(ensureNextAhead({ mainVersion: '200' })).rejects
.toThrowErrorMatchingInlineSnapshot(`
"[
{
"code": "custom",
"message": "main-version must be a valid semver version string like '7.4.2'.",
"path": []
}
]"
`);
});

it('should not bump version when next is already ahead of main', async () => {
await expect(ensureNextAhead({ mainVersion: '1.0.0' })).resolves.toBeUndefined();
expect(bumpVersion.run).not.toHaveBeenCalled();
});

it('should bump version to 3.1.0-alpha.0 when main is 3.0.0 and next is 2.0.0', async () => {
await expect(ensureNextAhead({ mainVersion: '3.0.0' })).resolves.toBeUndefined();
expect(bumpVersion.run).toHaveBeenCalledWith({ exact: '3.1.0-alpha.0' });
});

it('should bump version to 2.1.0-alpha.0 when main and next are both 2.0.0', async () => {
await expect(ensureNextAhead({ mainVersion: '2.0.0' })).resolves.toBeUndefined();
expect(bumpVersion.run).toHaveBeenCalledWith({ exact: '2.1.0-alpha.0' });
});

it('should bump version to 2.1.0-alpha.0 when main is 2.0.0 and and next is 2.0.0-rc.10', async () => {
fsExtra.__setMockFiles({
[CODE_PACKAGE_JSON_PATH]: JSON.stringify({ version: '2.0.0-rc.10' }),
});

await expect(ensureNextAhead({ mainVersion: '2.0.0' })).resolves.toBeUndefined();
expect(bumpVersion.run).toHaveBeenCalledWith({ exact: '2.1.0-alpha.0' });
});
});
102 changes: 102 additions & 0 deletions scripts/release/ensure-next-ahead.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/**
* This script ensures that next is always one minor ahead of main.
* This is needed when releasing a stable from next.
* Next will be at eg. 7.4.0-alpha.4, and main will be at 7.3.0.
* Then we release 7.4.0 by merging next to latest-release to main.
* We then ensure here that next is bumped to 7.5.0-alpha.0 - without releasing it.
* If this is a patch release bumping main to 7.3.1, next will not be touched because it's already ahead.
*/

/* eslint-disable no-console */
import chalk from 'chalk';
import path from 'path';
import program from 'commander';
import semver from 'semver';
import { z } from 'zod';
import { readJson } from 'fs-extra';
import dedent from 'ts-dedent';
import { run as bumpVersion } from './version';
import { git } from './utils/git-client';

program
.name('ensure-next-ahead')
.description('ensure the "next" branch is always a minor version ahead of "main"')
.requiredOption('-M, --main-version <mainVersion>', 'The version currently on the "main" branch');

const optionsSchema = z
.object({
mainVersion: z.string(),
})
.refine((schema) => semver.valid(schema.mainVersion), {
message: "main-version must be a valid semver version string like '7.4.2'.",
});

type Options = {
mainVersion: string;
};

const CODE_DIR_PATH = path.join(__dirname, '..', '..', 'code');
const CODE_PACKAGE_JSON_PATH = path.join(CODE_DIR_PATH, 'package.json');

const validateOptions = (options: { [key: string]: any }): options is Options => {
optionsSchema.parse(options);
return true;
};

const getCurrentVersion = async () => {
const { version } = await readJson(CODE_PACKAGE_JSON_PATH);
console.log(`📐 Current version of Storybook is ${chalk.green(version)}`);
return version;
};

export const run = async (options: unknown) => {
if (!validateOptions(options)) {
return;
}
const { mainVersion } = options;

const { current: currentGitBranch } = await git.status();

if (currentGitBranch !== 'next') {
console.warn(
`🚧 The current branch is not "next" but "${currentGitBranch}", this only really makes sense to run on the "next" branch.`
);
}

// Get the current version from code/package.json
const currentNextVersion = await getCurrentVersion();
if (semver.gt(currentNextVersion, mainVersion)) {
console.log(
`✅ The version on next (${chalk.green(
currentNextVersion
)}) is already ahead of the version on main (${chalk.green(mainVersion)}), no action needed.`
);
return;
}

const nextNextVersion = `${semver.inc(mainVersion, 'minor')}-alpha.0`;

console.log(
`🤜 The version on next (${chalk.green(
currentNextVersion
)}) is behind the version on main (${chalk.green(mainVersion)}), bumping to ${chalk.blue(
nextNextVersion
)}...`
);

await bumpVersion({ exact: nextNextVersion });

console.log(
`✅ bumped all versions to ${chalk.green(
nextNextVersion
)}, remember to commit and push to next.`
);
};

if (require.main === module) {
const parsed = program.parse();
run(parsed.opts()).catch((err) => {
console.error(err);
process.exit(1);
});
}

0 comments on commit ed18a24

Please sign in to comment.