diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 0b8febd540b4..02e8841cedf8 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -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 diff --git a/scripts/release/__tests__/write-changelog.test.ts b/scripts/release/__tests__/write-changelog.test.ts new file mode 100644 index 000000000000..383d3169dec4 --- /dev/null +++ b/scripts/release/__tests__/write-changelog.test.ts @@ -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", + } + `); + }); +}); diff --git a/scripts/release/write-changelog.ts b/scripts/release/write-changelog.ts index 2fe20f88d4c6..7481c46e5468 100644 --- a/scripts/release/write-changelog.ts +++ b/scripts/release/write-changelog.ts @@ -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 @@ -54,7 +54,7 @@ const validateOptions = (args: unknown[], options: { [key: string]: any }): opti return true; }; -const writeToFile = async ({ +const writeToChangelogFile = async ({ changelogText, version, verbose, @@ -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'; + 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; @@ -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`); };