Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
f5430d3
rename release preparement workflows
JReinhold Aug 28, 2023
8d582d7
merge stable to latest-release instead of next-release, resolving mer…
JReinhold Aug 29, 2023
1161b7a
more renaming of new release workflows
JReinhold Aug 29, 2023
d6297bf
only commit changelog changes when there are actual changes
JReinhold Aug 30, 2023
670bc51
add comments
JReinhold Aug 30, 2023
d5df684
simplify
JReinhold Aug 30, 2023
431ec63
ensure next is always ahead of main during stable releases
JReinhold Aug 31, 2023
5075165
improve readability of publish script
JReinhold Aug 31, 2023
44e988c
pul all release workflows in same concurrency group
JReinhold Aug 31, 2023
bff632d
add todos
JReinhold Sep 1, 2023
8eddd45
Merge branch 'next' of github.com:storybookjs/storybook into release-…
JReinhold Sep 8, 2023
ba8e5fe
cancel any release-preparation runs in progress
JReinhold Sep 8, 2023
a84e270
cleanup
JReinhold Sep 8, 2023
e1a2172
only consider open PRs when looking for frozen state
JReinhold Sep 8, 2023
4d569f2
update tests
JReinhold Sep 8, 2023
00a9089
Merge branch 'next' of github.com:storybookjs/storybook into release-…
JReinhold Sep 24, 2023
4bf6ba4
cleanup
JReinhold Sep 24, 2023
25ebf5c
fix type errors
JReinhold Sep 24, 2023
32ba0d9
Merge branch 'next' of github.com:storybookjs/storybook into release-…
JReinhold Sep 25, 2023
9ca7ed4
cancel when 0 patches to pick
JReinhold Sep 25, 2023
22f12e6
temp rename workflow to test
JReinhold Sep 25, 2023
e719c53
try number when reading pr-count
JReinhold Sep 25, 2023
d757590
compare less than 1
JReinhold Sep 25, 2023
8466a08
check non-null in pr count
JReinhold Sep 25, 2023
d83d422
rename patch workflow
JReinhold Sep 25, 2023
ca7f6f8
hotfix -> patch, next-release -> non-patch-release
JReinhold Oct 4, 2023
15b5933
more renaming
JReinhold Oct 4, 2023
ed18a24
move ensure-next-ahead from a bash script to a tested node script
JReinhold Oct 4, 2023
930adf4
move bash script to cancel preparation runs to tested node script
JReinhold Oct 4, 2023
0b35c18
Merge branch 'next' into release-stable-to-latest
JReinhold Oct 4, 2023
72837ae
fix tests
JReinhold Oct 4, 2023
406e0a5
Merge branch 'release-stable-to-latest' of github.com:storybookjs/sto…
JReinhold Oct 4, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
move ensure-next-ahead from a bash script to a tested node script
  • Loading branch information
JReinhold committed Oct 4, 2023
commit ed18a24d6477ddd3de7a41d6a91d31fb7ac22451
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);
});
}