Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Release tooling: Update docs version files when releasing #23235

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
11 changes: 11 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,17 @@ jobs:
git commit -m "Update CHANGELOG.md for v${{ steps.version.outputs.current-version }}"
git push origin next

- name: Sync versions/next.json from `next` to `main`
if: github.ref_name == 'next-release'
run: |
git fetch origin main
git checkout main
git pull
git checkout origin/next ./docs/versions/next.json
git add ./docs/versions/next.json
git commit -m "Update versions/next.json for v${{ steps.version.outputs.current-version }}"
git push origin main

# Force push from next to main if it is not a prerelease, and this release is from next-release
# This happens when eg. next has been tracking 7.1.0-alpha.X, and now we want to release 7.1.0
# This will keep release-next, next and main all tracking v7.1.0
Expand Down
120 changes: 120 additions & 0 deletions scripts/release/__tests__/write-changelog.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
/* eslint-disable global-require */
/* eslint-disable no-underscore-dangle */
import path from 'path';
import dedent from 'ts-dedent';
import { run as writeChangelog } from '../write-changelog';
import * as changesUtils from '../utils/get-changes';

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

const getChangesMock = jest.spyOn(changesUtils, 'getChanges');

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

const STABLE_CHANGELOG_PATH = path.join(__dirname, '..', '..', '..', 'CHANGELOG.md');
const PRERELEASE_CHANGELOG_PATH = path.join(__dirname, '..', '..', '..', 'CHANGELOG.prerelease.md');
const LATEST_VERSION_PATH = path.join(
__dirname,
'..',
'..',
'..',
'docs',
'versions',
'latest.json'
);
const NEXT_VERSION_PATH = path.join(__dirname, '..', '..', '..', 'docs', 'versions', 'next.json');

beforeEach(() => {
jest.clearAllMocks();
});

const EXISTING_STABLE_CHANGELOG = dedent`## 7.0.0

- Core: Some change`;

const EXISTING_PRERELEASE_CHANGELOG = dedent`## 7.1.0-alpha.20

- CLI: Super fast now`;

fsExtra.__setMockFiles({
[STABLE_CHANGELOG_PATH]: EXISTING_STABLE_CHANGELOG,
[PRERELEASE_CHANGELOG_PATH]: EXISTING_PRERELEASE_CHANGELOG,
});

describe('Write changelog', () => {
it('should write to stable changelogs and version files in docs', async () => {
getChangesMock.mockResolvedValue({
changes: [],
changelogText: `## 7.0.1

- React: Make it reactive
- CLI: Not UI`,
});

await writeChangelog(['7.0.1'], {});

expect(fsExtra.writeFile).toHaveBeenCalledTimes(1);
expect(fsExtra.writeFile.mock.calls[0][0]).toBe(STABLE_CHANGELOG_PATH);
expect(fsExtra.writeFile.mock.calls[0][1]).toMatchInlineSnapshot(`
"## 7.0.1

- React: Make it reactive
- CLI: Not UI

## 7.0.0

- Core: Some change"
`);
expect(fsExtra.writeJson).toBeCalledTimes(1);
expect(fsExtra.writeJson.mock.calls[0][0]).toBe(LATEST_VERSION_PATH);
expect(fsExtra.writeJson.mock.calls[0][1]).toMatchInlineSnapshot(`
{
"info": {
"plain": "- React: Make it reactive
- CLI: Not UI",
},
"version": "7.0.1",
}
`);
});

it('should write to prerelase changelogs and version files in docs', async () => {
getChangesMock.mockResolvedValue({
changes: [],
changelogText: `## 7.1.0-alpha.21

- React: Make it reactive
- CLI: Not UI`,
});

await writeChangelog(['7.1.0-alpha.21'], {});

expect(fsExtra.writeFile).toHaveBeenCalledTimes(1);
expect(fsExtra.writeFile.mock.calls[0][0]).toBe(PRERELEASE_CHANGELOG_PATH);
expect(fsExtra.writeFile.mock.calls[0][1]).toMatchInlineSnapshot(`
"## 7.1.0-alpha.21

- React: Make it reactive
- CLI: Not UI

## 7.1.0-alpha.20

- CLI: Super fast now"
`);
expect(fsExtra.writeJson).toBeCalledTimes(1);
expect(fsExtra.writeJson.mock.calls[0][0]).toBe(NEXT_VERSION_PATH);
expect(fsExtra.writeJson.mock.calls[0][1]).toMatchInlineSnapshot(`
{
"info": {
"plain": "- React: Make it reactive
- CLI: Not UI",
},
"version": "7.1.0-alpha.21",
}
`);
});
});
36 changes: 33 additions & 3 deletions scripts/release/write-changelog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import path from 'path';
import program from 'commander';
import semver from 'semver';
import { z } from 'zod';
import { readFile, writeFile } from 'fs-extra';
import { readFile, writeFile, writeJSON } from 'fs-extra';
import { getChanges } from './utils/get-changes';

program
Expand Down Expand Up @@ -54,7 +54,7 @@ const validateOptions = (args: unknown[], options: { [key: string]: any }): opti
return true;
};

const writeToFile = async ({
const writeToChangelogFile = async ({
changelogText,
version,
verbose,
Expand All @@ -77,6 +77,35 @@ const writeToFile = async ({
await writeFile(changelogPath, nextChangelog);
};

const writeToDocsVersionFile = async ({
changelogText,
version,
verbose,
}: {
changelogText: string;
version: string;
verbose?: boolean;
}) => {
const isPrerelease = semver.prerelease(version) !== null;
const filename = isPrerelease ? 'next.json' : 'latest.json';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume that you ensure that this never gets called in any other scenarios? Future release? Canary release?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a good point. For now we only support the two release types, so it's hard to come up with how the logic would be if there were other types of releases here.

const filepath = path.join(__dirname, '..', '..', 'docs', 'versions', filename);

if (verbose) {
console.log(`📝 Writing changelog to ${chalk.blue(path)}`);
}

const textWithoutHeading = changelogText.split('\n').slice(2).join('\n');

const content = {
version,
info: {
plain: textWithoutHeading,
},
};

await writeJSON(filepath, content);
};

export const run = async (args: unknown[], options: unknown) => {
if (!validateOptions(args, options)) {
return;
Expand All @@ -97,7 +126,8 @@ export const run = async (args: unknown[], options: unknown) => {
return;
}

await writeToFile({ changelogText, version, verbose });
await writeToChangelogFile({ changelogText, version, verbose });
await writeToDocsVersionFile({ changelogText, version, verbose });

console.log(`✅ Wrote Changelog to file`);
};
Expand Down