From 7d9425d29b025a220e626de7cc0a457d73df2011 Mon Sep 17 00:00:00 2001 From: Tyler Butler Date: Tue, 10 Sep 2024 17:21:11 -0700 Subject: [PATCH 01/21] feat(build-cli): New command transform:releaseNotes --- build-tools/packages/build-cli/README.md | 1 + .../packages/build-cli/docs/transform.md | 30 ++++ build-tools/packages/build-cli/package.json | 8 +- .../src/commands/transform/releaseNotes.ts | 108 +++++++++++++ .../build-cli/src/library/markdown.ts | 148 +++++++++++++++++- build-tools/pnpm-lock.yaml | 18 +++ 6 files changed, 309 insertions(+), 4 deletions(-) create mode 100644 build-tools/packages/build-cli/docs/transform.md create mode 100644 build-tools/packages/build-cli/src/commands/transform/releaseNotes.ts diff --git a/build-tools/packages/build-cli/README.md b/build-tools/packages/build-cli/README.md index 09c28407ae46..16a4fe4e57ad 100644 --- a/build-tools/packages/build-cli/README.md +++ b/build-tools/packages/build-cli/README.md @@ -48,6 +48,7 @@ USAGE * [`flub release`](docs/release.md) - Release commands are used to manage the Fluid release process. * [`flub rename-types`](docs/rename-types.md) - Renames type declaration files from .d.ts to .d.mts. * [`flub run`](docs/run.md) - Generate a report from input bundle stats collected through the collect bundleStats command. +* [`flub transform`](docs/transform.md) - Transform commands are used to transform code, docs, etc. into alternative forms. * [`flub typetests`](docs/typetests.md) - Updates configuration for type tests in package.json files. If the previous version changes after running preparation, then npm install must be run before building. diff --git a/build-tools/packages/build-cli/docs/transform.md b/build-tools/packages/build-cli/docs/transform.md new file mode 100644 index 000000000000..0344ff3d7d0c --- /dev/null +++ b/build-tools/packages/build-cli/docs/transform.md @@ -0,0 +1,30 @@ +`flub transform` +================ + +Transform commands are used to transform code, docs, etc. into alternative forms. + +* [`flub transform releaseNotes`](#flub-transform-releasenotes) + +## `flub transform releaseNotes` + +Transforms a markdown release notes file into a format appropriate for use in a GitHub Release. This is used to transform in-repo release notes such that they can be automatically posted to our GitHub Releases. + +``` +USAGE + $ flub transform releaseNotes --inFile --outFile [--json] [-v | --quiet] + +FLAGS + --inFile= (required) A release notes file that was generated using 'flub generate releaseNotes'. The file + must not already contain heading links. That is, the input file must not have been generated with + the --headingLinks flag. + --outFile= (required) Output the transformed content to this file. + +LOGGING FLAGS + -v, --verbose Enable verbose logging. + --quiet Disable all logging. + +GLOBAL FLAGS + --json Format output as json. +``` + +_See code: [src/commands/transform/releaseNotes.ts](https://github.com/microsoft/FluidFramework/blob/main/build-tools/packages/build-cli/src/commands/transform/releaseNotes.ts)_ diff --git a/build-tools/packages/build-cli/package.json b/build-tools/packages/build-cli/package.json index 1d23fe48997f..20431bb23128 100644 --- a/build-tools/packages/build-cli/package.json +++ b/build-tools/packages/build-cli/package.json @@ -112,6 +112,8 @@ "jssm": "5.98.2", "latest-version": "^5.1.0", "mdast": "^3.0.0", + "mdast-util-to-string": "^4.0.0", + "mdast-util-heading-range": "^4.0.0", "minimatch": "^7.4.6", "node-fetch": "^3.3.2", "npm-check-updates": "^16.14.20", @@ -135,7 +137,8 @@ "table": "^6.8.1", "ts-morph": "^22.0.0", "type-fest": "^2.19.0", - "unist-util-visit": "^5.0.0" + "unist-util-visit": "^5.0.0", + "unified": "^11.0.5" }, "devDependencies": { "@biomejs/biome": "~1.8.3", @@ -238,6 +241,9 @@ }, "promote": { "description": "Promote commands are used to promote packages published to an npm registry." + }, + "transform": { + "description": "Transform commands are used to transform code, docs, etc. into alternative forms." } } } diff --git a/build-tools/packages/build-cli/src/commands/transform/releaseNotes.ts b/build-tools/packages/build-cli/src/commands/transform/releaseNotes.ts new file mode 100644 index 000000000000..10edb04bedf6 --- /dev/null +++ b/build-tools/packages/build-cli/src/commands/transform/releaseNotes.ts @@ -0,0 +1,108 @@ +/*! + * Copyright (c) Microsoft Corporation and contributors. All rights reserved. + * Licensed under the MIT License. + */ + +import { readFile, writeFile } from "node:fs/promises"; +import { Flags } from "@oclif/core"; +import { format as prettier } from "prettier"; +import { remark } from "remark"; +import remarkGfm from "remark-gfm"; +import remarkGithub, { defaultBuildUrl } from "remark-github"; +import admonitions from "remark-github-beta-blockquote-admonitions"; +import remarkToc from "remark-toc"; + +import { BaseCommand } from "../../library/index.js"; +import { + remarkHeadingLinks, + removeHeadingsAtLevel, + removeSectionContent, + stripSoftBreaks, + updateTocLinks, + // eslint-disable-next-line import/no-internal-modules +} from "../../library/markdown.js"; + +/** + * Transforms a markdown release notes file into a format appropriate for use in a GitHub Release. + */ +export default class TransformReleaseNotesCommand extends BaseCommand< + typeof TransformReleaseNotesCommand +> { + static readonly summary = + `Transforms a markdown release notes file into a format appropriate for use in a GitHub Release. This is used to transform in-repo release notes such that they can be automatically posted to our GitHub Releases.`; + + // Enables the global JSON flag in oclif. + static readonly enableJsonFlag = true; + + static readonly flags = { + inFile: Flags.file({ + description: `A release notes file that was generated using 'flub generate releaseNotes'. The file must not already contain heading links. That is, the input file must not have been generated with the --headingLinks flag.`, + required: true, + exists: true, + }), + outFile: Flags.file({ + description: `Output the transformed content to this file.`, + required: true, + }), + ...BaseCommand.flags, + } as const; + + static readonly examples = [ + { + description: `Transform the release notes from version 2.2.0 and output the results to out.md.`, + command: "<%= config.bin %> <%= command.id %> --inFile RELEASE_NOTES/2.2.0.md --outFile out.md", + }, + ]; + + public async run(): Promise { + const { inFile, outFile } = this.flags; + const input = await readFile(inFile, { encoding: "utf8" }); + const processor = remark() + // Remove the H1 if it exists. + .use(removeHeadingsAtLevel, { level: 1 }) + // Remove the existing TOC section because its links are incorrect; we'll regenerate it. + .use(removeSectionContent, { heading: "Contents" }) + // Update the "back to TOC" links to prepent 'user-content-' because that's what GH Releases does. + .use(updateTocLinks, { newUrl: "#user-content-contents" }) + // Parse the markdown as GitHub-Flavored Markdown + .use(remarkGfm) + // Strip any single-line breaks. See the docs for the stripSoftBreaks function for more details. + .use(stripSoftBreaks) + // Parse any GitHub admonitions/alerts/callouts + .use(admonitions, { + titleTextMap: (title) => ({ + // By default the `[!` prefix and `]` suffix are removed; we don't want that, so we override the default and + // return he title as-is. + displayTitle: title, + checkedTitle: title, + }), + }) + // Regenerate the TOC with the user-content- prefix. + .use(remarkToc, { + maxDepth: 3, + skip: ".*Start Building Today.*", + // Add the user-content- prefix to the links when we generate our own headingLinks, because GitHub will + // prepend that to all our custom anchor IDs. + prefix: "user-content-", + }) + // Transform any issue and commit references into links. + .use(remarkGithub, { + buildUrl(values) { + // Disable linking mentions + return values.type === "mention" ? false : defaultBuildUrl(values); + }, + }) + // Add custom achor tags with IDs to all the headings. + .use(remarkHeadingLinks); + + const contents = String(await processor.process(input)); + + this.info(`Writing output file: ${outFile}`); + await writeFile( + outFile, + await prettier(contents, { proseWrap: "never", parser: "markdown" }), + ); + + return contents; + } +} diff --git a/build-tools/packages/build-cli/src/library/markdown.ts b/build-tools/packages/build-cli/src/library/markdown.ts index 0af6305e7158..a54febb366aa 100644 --- a/build-tools/packages/build-cli/src/library/markdown.ts +++ b/build-tools/packages/build-cli/src/library/markdown.ts @@ -4,9 +4,11 @@ */ import GithubSlugger from "github-slugger"; -import type { Heading, Html } from "mdast"; -import type { Node } from "unist"; -import { visit } from "unist-util-visit"; +import type { Heading, Html, Link, Root } from "mdast"; +import { headingRange } from "mdast-util-heading-range"; +import { toString } from "mdast-util-to-string"; +import type { Node, Parent } from "unist"; +import { EXIT, SKIP, visit } from "unist-util-visit"; /** * Using the same instance for all slug generation ensures that no duplicate IDs are generated. @@ -41,3 +43,143 @@ export function remarkHeadingLinks(): (tree: Node) => void { }); }; } + +/** + * A regular expression that extracts an admonition title from a string UNLESS the admonition title is the only thing on + * the line. + * + * Capture group 1 is the admonition type/title. Capture group 2 is any trailing whitespace. + * + * @remarks + * + * Description of the regular expression: + * + * This regular expression matches patterns in the form of `[!WORD]` where WORD can be CAUTION, IMPORTANT, NOTE, TIP, or + * WARNING. It ensures that the pattern is not followed by only whitespace characters until the end of the line. + * Additionally, it captures any whitespace characters that follow the matched pattern. + */ +const ADMONITION_REGEX = /(\[!(?:CAUTION|IMPORTANT|NOTE|TIP|WARNING)])(?!\s*$)(\s*)/gm; + +/** + * A regular expression to remove single line breaks from text. This is used to remove extraneous line breaks in text + * nodes in markdown. This is useful because GitHub sometimes renders single line breaks, and sometimes it ignores them + * like the CommonMark spec describes. Removing them ensures that markdown renders as expected across GitHub. + */ +const SOFT_BREAK_REGEX = /$[^$]/gms; + +/** + * A remarkjs/unist plugin that strips soft line breaks. This is a workaround for GitHub's inconsistent markdown + * rendering in GitHub Releases. According to CommonMark, Markdown paragraphs are denoted by two line breaks, and single + * line breaks should be ignored. But in GitHub releases, single line breaks are rendered. This plugin removes the soft + * line breaks so that the markdown is correctly rendered. + */ +export function stripSoftBreaks(): (tree: Node) => void { + return (tree: Node): void => { + // strip soft breaks + visit(tree, "text", (node: { value: string }) => { + node.value = node.value.replace(SOFT_BREAK_REGEX, " "); + }); + + // preserve GitHub admonitions; without this the line breaks in the alert are lost and it doesn't render correctly. + visit(tree, "blockquote", (node: Node) => { + visit(node, "text", (innerNode: { value: string }) => { + // If the text is an admonition title, split + innerNode.value = innerNode.value.replace(ADMONITION_REGEX, "$1\n"); + }); + }); + }; +} + +export function removeHeadingAndContent(headingText: string) { + return () => + // eslint-disable-next-line unicorn/consistent-function-scoping + (tree: Node): void => { + let remove = false; + let headingDepth = 0; + + visit(tree, "heading", (node: Heading, index: number | undefined, parent: Parent) => { + const text = node.children.map((child) => toString(child)).join(""); + if (text === headingText) { + remove = true; + headingDepth = node.depth; + if (index === undefined) { + throw new Error("index is undefined"); + } + parent.children.splice(index, 1); + return [SKIP, index]; + } + }); + + if (remove) { + visit(tree, (node: Node, index: number | undefined, parent: Parent) => { + if (node.type === "heading" && (node as Heading).depth <= headingDepth) { + remove = false; + return EXIT; + } + if (remove && index !== undefined) { + // if (index === undefined) { + // throw new Error("index is undefined"); + // } + parent.children.splice(index, 1); + return [SKIP, index]; + } + }); + } + }; +} + +export function removeSectionContent(options: { heading: string | RegExp }): ( + tree: Root, +) => void { + return function (tree: Root) { + headingRange(tree, options.heading, (start, nodes, end, info) => { + console.log(`removing section ${options.heading}`); + return [ + start, + // { type: "paragraph", children: [{ type: "text", value: "Qux." }] }, + end, + ]; + }); + }; +} + +export function removeHeadingsAtLevel(options: { level: 1 | 2 | 3 | 4 | 5 | 6 }): ( + tree: Root, +) => void { + return (tree: Root) => { + let headingRemoved = false; + + visit( + tree, + "heading", + (node: Heading, index: number | undefined, parent: Parent | undefined) => { + if ( + !headingRemoved && + // node.children?.[0].type === "text" && + // node.children[0].value === "⬆️ Table of contents" && + node.depth === options.level && + index !== undefined + ) { + parent?.children.splice(index, 1); + headingRemoved = true; + return [SKIP, index]; + } + }, + ); + }; +} + +export function updateTocLinks(options: { newUrl: string }): (tree: Root) => void { + const { newUrl } = options; + + return (tree: Root) => { + visit(tree, "link", (node: Link) => { + if ( + node.children?.[0].type === "text" && + node.children[0].value === "⬆️ Table of contents" + ) { + node.url = newUrl; + } + }); + }; +} diff --git a/build-tools/pnpm-lock.yaml b/build-tools/pnpm-lock.yaml index dd85268689ed..834d5e8278dd 100644 --- a/build-tools/pnpm-lock.yaml +++ b/build-tools/pnpm-lock.yaml @@ -192,6 +192,12 @@ importers: mdast: specifier: ^3.0.0 version: 3.0.0 + mdast-util-heading-range: + specifier: ^4.0.0 + version: 4.0.0 + mdast-util-to-string: + specifier: ^4.0.0 + version: 4.0.0 minimatch: specifier: ^7.4.6 version: 7.4.6 @@ -261,6 +267,9 @@ importers: type-fest: specifier: ^2.19.0 version: 2.19.0 + unified: + specifier: ^11.0.5 + version: 11.0.5 unist-util-visit: specifier: ^5.0.0 version: 5.0.0 @@ -7827,6 +7836,15 @@ packages: transitivePeerDependencies: - supports-color + /mdast-util-heading-range@4.0.0: + resolution: {integrity: sha512-9qadnTU+W0MR69yITfUr/52eoVXcqUpFhN1ThjGSn59KGOdxgaOr4Nx4swa60SaXEq8/tjQZcq2sVPp2yJMNCA==} + dependencies: + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + devlop: 1.1.0 + mdast-util-to-string: 4.0.0 + dev: false + /mdast-util-phrasing@4.1.0: resolution: {integrity: sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==} dependencies: From 15d36fbc56865e2854775871ba98c7810516c92e Mon Sep 17 00:00:00 2001 From: Tyler Butler Date: Tue, 10 Sep 2024 17:21:32 -0700 Subject: [PATCH 02/21] pipeline example --- .../data/release-notes-issue-intro.njk | 2 +- .github/workflows/push-tag-create-release.yml | 28 +++++++++---------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/.github/workflows/data/release-notes-issue-intro.njk b/.github/workflows/data/release-notes-issue-intro.njk index 20512e6a95b2..57ebab24bc63 100644 --- a/.github/workflows/data/release-notes-issue-intro.njk +++ b/.github/workflows/data/release-notes-issue-intro.njk @@ -12,6 +12,6 @@ pnpm flub generate releaseNotes -g client -t minor --outFile RELEASE_NOTES/{{ ve pnpm flub generate releaseNotes -g client -t minor --headingLinks --excludeH1 --outFile temporary-file.md ``` -You can then copy and paste the contents of the `temporary-file.md` into the GitHub Release. +This should be done automatically as part of the release process, but if you need to generate the release notes manually, you can use the above command. --- diff --git a/.github/workflows/push-tag-create-release.yml b/.github/workflows/push-tag-create-release.yml index 99db3e2e6413..97c34fc216b6 100644 --- a/.github/workflows/push-tag-create-release.yml +++ b/.github/workflows/push-tag-create-release.yml @@ -72,7 +72,7 @@ jobs: with: name: release-metadata path: release-metadata.json - retention-days: 3 + retention-days: 30 - name: Load release metadata into env variable run: | echo "RELEASE_JSON=$(cat release-metadata.json)" >> $GITHUB_ENV @@ -101,10 +101,10 @@ jobs: path: reports retention-days: 7 - # Generate changelog - - name: Generate changelog - # This changelog is only for client patch releases and build-tools releases - if: (fromJson(env.RELEASE_JSON).releaseType == 'patch' && fromJson(env.RELEASE_JSON).packageOrReleaseGroup == 'client') || fromJson(env.RELEASE_JSON).packageOrReleaseGroup == 'build-tools' + # Generate release notes + - name: Generate patch release notes + # This content is only for patch releases + if: (fromJson(env.RELEASE_JSON).releaseType == 'patch' run: | # We only need the root dependencies pnpm install -w --frozen-lockfile @@ -116,11 +116,16 @@ jobs: --tag-prefix ${{ fromJson(env.RELEASE_JSON).packageOrReleaseGroup }}_v \ --output auto-changelog.md \ --template .github/workflows/data/patch-changelog.hbs - - name: Generate changelog - # This changelog is a basically empty one used for everything except client patches and build-tools. - if: ${{ !(fromJson(env.RELEASE_JSON).releaseType == 'patch' && fromJson(env.RELEASE_JSON).packageOrReleaseGroup == 'client') && fromJson(env.RELEASE_JSON).packageOrReleaseGroup != 'build-tools' }} + + - name: Generate minor/major release notes + # This content is the "default" release notes - this should be used for minor and major releases of client and + # server release groups. To use this, a release group needs to be using changesets. Build-tools does not use + # changesets, so it is excluded. + if: ${{ fromJson(env.RELEASE_JSON).releaseType != 'patch' && (fromJson(env.RELEASE_JSON).packageOrReleaseGroup == 'client' || fromJson(env.RELEASE_JSON).packageOrReleaseGroup == 'server') }} run: | - echo "This is a **${{ fromJson(env.RELEASE_JSON).releaseType }}** release." > auto-changelog.md + flub publish releaseNotes \ + --inFile RELEASE_NOTES/${{ fromJson(env.RELEASE_JSON).version }}.md + --outFile auto-changelog.md # Only creates GH releases for client, server, and build-tools releases. - name: Create GH release @@ -133,11 +138,6 @@ jobs: # Will skip if a published (non-draft) release already exists. skipIfReleaseExists: true - # If the release is NOT a patch, leave it as a draft. Only patch releases are auto-published because their - # auto-generated release notes are sufficient for those releases. Minor and major releases currently require - # some curation of the release notes. - draft: ${{ fromJson(env.RELEASE_JSON).releaseType != 'patch' }} - # Don't change the draft state when updating an existing release. This setting is not really necessary for us # in most cases because we don't pre-create releases, so this workflow always creates a new GH release. It's # included mostly for safety reasons, to ensure that existing drafts aren't published accidentally. From d8a5e18af4d5c1ba6e31f39e7d7476c8cd380239 Mon Sep 17 00:00:00 2001 From: Tyler Butler Date: Tue, 10 Sep 2024 17:35:43 -0700 Subject: [PATCH 03/21] updates --- .../packages/build-cli/docs/transform.md | 5 +++ .../src/commands/transform/releaseNotes.ts | 3 +- .../build-cli/src/library/markdown.ts | 43 +------------------ 3 files changed, 9 insertions(+), 42 deletions(-) diff --git a/build-tools/packages/build-cli/docs/transform.md b/build-tools/packages/build-cli/docs/transform.md index 0344ff3d7d0c..ff0718e9fb6e 100644 --- a/build-tools/packages/build-cli/docs/transform.md +++ b/build-tools/packages/build-cli/docs/transform.md @@ -25,6 +25,11 @@ LOGGING FLAGS GLOBAL FLAGS --json Format output as json. + +EXAMPLES + Transform the release notes from version 2.2.0 and output the results to out.md. + + $ flub transform releaseNotes --inFile RELEASE_NOTES/2.2.0.md --outFile out.md ``` _See code: [src/commands/transform/releaseNotes.ts](https://github.com/microsoft/FluidFramework/blob/main/build-tools/packages/build-cli/src/commands/transform/releaseNotes.ts)_ diff --git a/build-tools/packages/build-cli/src/commands/transform/releaseNotes.ts b/build-tools/packages/build-cli/src/commands/transform/releaseNotes.ts index 10edb04bedf6..623739c76e96 100644 --- a/build-tools/packages/build-cli/src/commands/transform/releaseNotes.ts +++ b/build-tools/packages/build-cli/src/commands/transform/releaseNotes.ts @@ -50,7 +50,8 @@ export default class TransformReleaseNotesCommand extends BaseCommand< static readonly examples = [ { description: `Transform the release notes from version 2.2.0 and output the results to out.md.`, - command: "<%= config.bin %> <%= command.id %> --inFile RELEASE_NOTES/2.2.0.md --outFile out.md", + command: + "<%= config.bin %> <%= command.id %> --inFile RELEASE_NOTES/2.2.0.md --outFile out.md", }, ]; diff --git a/build-tools/packages/build-cli/src/library/markdown.ts b/build-tools/packages/build-cli/src/library/markdown.ts index a54febb366aa..00e112445e4f 100644 --- a/build-tools/packages/build-cli/src/library/markdown.ts +++ b/build-tools/packages/build-cli/src/library/markdown.ts @@ -6,9 +6,8 @@ import GithubSlugger from "github-slugger"; import type { Heading, Html, Link, Root } from "mdast"; import { headingRange } from "mdast-util-heading-range"; -import { toString } from "mdast-util-to-string"; import type { Node, Parent } from "unist"; -import { EXIT, SKIP, visit } from "unist-util-visit"; +import { SKIP, visit } from "unist-util-visit"; /** * Using the same instance for all slug generation ensures that no duplicate IDs are generated. @@ -90,44 +89,6 @@ export function stripSoftBreaks(): (tree: Node) => void { }; } -export function removeHeadingAndContent(headingText: string) { - return () => - // eslint-disable-next-line unicorn/consistent-function-scoping - (tree: Node): void => { - let remove = false; - let headingDepth = 0; - - visit(tree, "heading", (node: Heading, index: number | undefined, parent: Parent) => { - const text = node.children.map((child) => toString(child)).join(""); - if (text === headingText) { - remove = true; - headingDepth = node.depth; - if (index === undefined) { - throw new Error("index is undefined"); - } - parent.children.splice(index, 1); - return [SKIP, index]; - } - }); - - if (remove) { - visit(tree, (node: Node, index: number | undefined, parent: Parent) => { - if (node.type === "heading" && (node as Heading).depth <= headingDepth) { - remove = false; - return EXIT; - } - if (remove && index !== undefined) { - // if (index === undefined) { - // throw new Error("index is undefined"); - // } - parent.children.splice(index, 1); - return [SKIP, index]; - } - }); - } - }; -} - export function removeSectionContent(options: { heading: string | RegExp }): ( tree: Root, ) => void { @@ -136,7 +97,7 @@ export function removeSectionContent(options: { heading: string | RegExp }): ( console.log(`removing section ${options.heading}`); return [ start, - // { type: "paragraph", children: [{ type: "text", value: "Qux." }] }, + // No child nodes - effectively empties the section. end, ]; }); From a8083e197d91bc9106baee8fe5341336edd9ada6 Mon Sep 17 00:00:00 2001 From: Tyler Butler Date: Tue, 10 Sep 2024 18:26:39 -0700 Subject: [PATCH 04/21] policy --- build-tools/packages/build-cli/package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build-tools/packages/build-cli/package.json b/build-tools/packages/build-cli/package.json index 20431bb23128..9e241552555f 100644 --- a/build-tools/packages/build-cli/package.json +++ b/build-tools/packages/build-cli/package.json @@ -112,8 +112,8 @@ "jssm": "5.98.2", "latest-version": "^5.1.0", "mdast": "^3.0.0", - "mdast-util-to-string": "^4.0.0", "mdast-util-heading-range": "^4.0.0", + "mdast-util-to-string": "^4.0.0", "minimatch": "^7.4.6", "node-fetch": "^3.3.2", "npm-check-updates": "^16.14.20", @@ -137,8 +137,8 @@ "table": "^6.8.1", "ts-morph": "^22.0.0", "type-fest": "^2.19.0", - "unist-util-visit": "^5.0.0", - "unified": "^11.0.5" + "unified": "^11.0.5", + "unist-util-visit": "^5.0.0" }, "devDependencies": { "@biomejs/biome": "~1.8.3", From 335f473d5cd7d16081fe847d21fe12e9a0e2017f Mon Sep 17 00:00:00 2001 From: Tyler Butler Date: Wed, 11 Sep 2024 09:49:10 -0700 Subject: [PATCH 05/21] Update .github/workflows/push-tag-create-release.yml Co-authored-by: Alex Villarreal <716334+alexvy86@users.noreply.github.com> --- .github/workflows/push-tag-create-release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/push-tag-create-release.yml b/.github/workflows/push-tag-create-release.yml index 97c34fc216b6..b777aa5e2054 100644 --- a/.github/workflows/push-tag-create-release.yml +++ b/.github/workflows/push-tag-create-release.yml @@ -104,7 +104,7 @@ jobs: # Generate release notes - name: Generate patch release notes # This content is only for patch releases - if: (fromJson(env.RELEASE_JSON).releaseType == 'patch' + if: fromJson(env.RELEASE_JSON).releaseType == 'patch' run: | # We only need the root dependencies pnpm install -w --frozen-lockfile From 58549eacddff1b4caad5d8a7ba67a4e3ebe08541 Mon Sep 17 00:00:00 2001 From: Tyler Butler Date: Wed, 11 Sep 2024 09:52:37 -0700 Subject: [PATCH 06/21] Update build-tools/packages/build-cli/src/commands/transform/releaseNotes.ts Co-authored-by: Alex Villarreal <716334+alexvy86@users.noreply.github.com> --- .../packages/build-cli/src/commands/transform/releaseNotes.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build-tools/packages/build-cli/src/commands/transform/releaseNotes.ts b/build-tools/packages/build-cli/src/commands/transform/releaseNotes.ts index 623739c76e96..75c6b583017e 100644 --- a/build-tools/packages/build-cli/src/commands/transform/releaseNotes.ts +++ b/build-tools/packages/build-cli/src/commands/transform/releaseNotes.ts @@ -63,7 +63,7 @@ export default class TransformReleaseNotesCommand extends BaseCommand< .use(removeHeadingsAtLevel, { level: 1 }) // Remove the existing TOC section because its links are incorrect; we'll regenerate it. .use(removeSectionContent, { heading: "Contents" }) - // Update the "back to TOC" links to prepent 'user-content-' because that's what GH Releases does. + // Update the "back to TOC" links to prepend 'user-content-' because that's what GH Releases does. .use(updateTocLinks, { newUrl: "#user-content-contents" }) // Parse the markdown as GitHub-Flavored Markdown .use(remarkGfm) From 9b7d2d6ed6ea9da930347c9b5ec40ff589f486d8 Mon Sep 17 00:00:00 2001 From: Tyler Butler Date: Wed, 11 Sep 2024 10:55:53 -0700 Subject: [PATCH 07/21] consistent if checks in workflow --- .github/workflows/push-tag-create-release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/push-tag-create-release.yml b/.github/workflows/push-tag-create-release.yml index b777aa5e2054..4db1baee850c 100644 --- a/.github/workflows/push-tag-create-release.yml +++ b/.github/workflows/push-tag-create-release.yml @@ -121,7 +121,7 @@ jobs: # This content is the "default" release notes - this should be used for minor and major releases of client and # server release groups. To use this, a release group needs to be using changesets. Build-tools does not use # changesets, so it is excluded. - if: ${{ fromJson(env.RELEASE_JSON).releaseType != 'patch' && (fromJson(env.RELEASE_JSON).packageOrReleaseGroup == 'client' || fromJson(env.RELEASE_JSON).packageOrReleaseGroup == 'server') }} + if: fromJson(env.RELEASE_JSON).releaseType != 'patch' && (fromJson(env.RELEASE_JSON).packageOrReleaseGroup == 'client' || fromJson(env.RELEASE_JSON).packageOrReleaseGroup == 'server') run: | flub publish releaseNotes \ --inFile RELEASE_NOTES/${{ fromJson(env.RELEASE_JSON).version }}.md From 86424b1b2301045be716c57414c6a9853f66c010 Mon Sep 17 00:00:00 2001 From: Tyler Butler Date: Wed, 11 Sep 2024 10:56:17 -0700 Subject: [PATCH 08/21] Remove all heading not just the first one --- .../packages/build-cli/src/library/markdown.ts | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/build-tools/packages/build-cli/src/library/markdown.ts b/build-tools/packages/build-cli/src/library/markdown.ts index 00e112445e4f..7d8fc769f490 100644 --- a/build-tools/packages/build-cli/src/library/markdown.ts +++ b/build-tools/packages/build-cli/src/library/markdown.ts @@ -104,25 +104,24 @@ export function removeSectionContent(options: { heading: string | RegExp }): ( }; } +/** + * Removes all the headings at a particular level. Most useful to remove the top-level H1 headings from a document. + * + * @param options - The `level` property must be set to the level of heading to remove. + */ export function removeHeadingsAtLevel(options: { level: 1 | 2 | 3 | 4 | 5 | 6 }): ( tree: Root, ) => void { return (tree: Root) => { - let headingRemoved = false; - visit( tree, "heading", (node: Heading, index: number | undefined, parent: Parent | undefined) => { if ( - !headingRemoved && - // node.children?.[0].type === "text" && - // node.children[0].value === "⬆️ Table of contents" && node.depth === options.level && index !== undefined ) { parent?.children.splice(index, 1); - headingRemoved = true; return [SKIP, index]; } }, From 8fde4431ba5c9ce85ac06a1c3aa10af919fb0809 Mon Sep 17 00:00:00 2001 From: Tyler Butler Date: Wed, 11 Sep 2024 11:15:06 -0700 Subject: [PATCH 09/21] feedback --- .../src/commands/generate/releaseNotes.ts | 4 +-- .../src/commands/transform/releaseNotes.ts | 11 ++++---- .../build-cli/src/library/markdown.ts | 26 ++++++++++++++----- .../build-cli/src/library/releaseNotes.ts | 9 +++++++ 4 files changed, 35 insertions(+), 15 deletions(-) create mode 100644 build-tools/packages/build-cli/src/library/releaseNotes.ts diff --git a/build-tools/packages/build-cli/src/commands/generate/releaseNotes.ts b/build-tools/packages/build-cli/src/commands/generate/releaseNotes.ts index 411e10e36057..e4a0928be41b 100644 --- a/build-tools/packages/build-cli/src/commands/generate/releaseNotes.ts +++ b/build-tools/packages/build-cli/src/commands/generate/releaseNotes.ts @@ -26,7 +26,7 @@ import { loadChangesets, } from "../../library/index.js"; // eslint-disable-next-line import/no-internal-modules -import { remarkHeadingLinks } from "../../library/markdown.js"; +import { addHeadingLinks } from "../../library/markdown.js"; /** * Generates release notes from individual changeset files. @@ -217,7 +217,7 @@ export default class GenerateReleaseNotesCommand extends BaseCommand< .use(remarkToc, { maxDepth: 3, skip: ".*Start Building Today.*" }); const processor = flags.headingLinks - ? baseProcessor.use(remarkHeadingLinks) + ? baseProcessor.use(addHeadingLinks) : baseProcessor; const contents = String( diff --git a/build-tools/packages/build-cli/src/commands/transform/releaseNotes.ts b/build-tools/packages/build-cli/src/commands/transform/releaseNotes.ts index 75c6b583017e..8caa5ec6b0a5 100644 --- a/build-tools/packages/build-cli/src/commands/transform/releaseNotes.ts +++ b/build-tools/packages/build-cli/src/commands/transform/releaseNotes.ts @@ -14,13 +14,15 @@ import remarkToc from "remark-toc"; import { BaseCommand } from "../../library/index.js"; import { - remarkHeadingLinks, + addHeadingLinks, removeHeadingsAtLevel, removeSectionContent, stripSoftBreaks, updateTocLinks, // eslint-disable-next-line import/no-internal-modules } from "../../library/markdown.js"; +// eslint-disable-next-line import/no-internal-modules +import { RELEASE_NOTES_TOC_LINK_TEXT } from "../../library/releaseNotes.js"; /** * Transforms a markdown release notes file into a format appropriate for use in a GitHub Release. @@ -31,9 +33,6 @@ export default class TransformReleaseNotesCommand extends BaseCommand< static readonly summary = `Transforms a markdown release notes file into a format appropriate for use in a GitHub Release. This is used to transform in-repo release notes such that they can be automatically posted to our GitHub Releases.`; - // Enables the global JSON flag in oclif. - static readonly enableJsonFlag = true; - static readonly flags = { inFile: Flags.file({ description: `A release notes file that was generated using 'flub generate releaseNotes'. The file must not already contain heading links. That is, the input file must not have been generated with the --headingLinks flag.`, @@ -64,7 +63,7 @@ export default class TransformReleaseNotesCommand extends BaseCommand< // Remove the existing TOC section because its links are incorrect; we'll regenerate it. .use(removeSectionContent, { heading: "Contents" }) // Update the "back to TOC" links to prepend 'user-content-' because that's what GH Releases does. - .use(updateTocLinks, { newUrl: "#user-content-contents" }) + .use(updateTocLinks, { checkValue: RELEASE_NOTES_TOC_LINK_TEXT, newUrl: "#user-content-contents" }) // Parse the markdown as GitHub-Flavored Markdown .use(remarkGfm) // Strip any single-line breaks. See the docs for the stripSoftBreaks function for more details. @@ -94,7 +93,7 @@ export default class TransformReleaseNotesCommand extends BaseCommand< }, }) // Add custom achor tags with IDs to all the headings. - .use(remarkHeadingLinks); + .use(addHeadingLinks); const contents = String(await processor.process(input)); diff --git a/build-tools/packages/build-cli/src/library/markdown.ts b/build-tools/packages/build-cli/src/library/markdown.ts index 7d8fc769f490..647e761906c9 100644 --- a/build-tools/packages/build-cli/src/library/markdown.ts +++ b/build-tools/packages/build-cli/src/library/markdown.ts @@ -21,7 +21,7 @@ const slugger = new GithubSlugger(); * * For more details, see: https://github.com/orgs/community/discussions/48311#discussioncomment-10436184 */ -export function remarkHeadingLinks(): (tree: Node) => void { +export function addHeadingLinks(): (tree: Node) => void { return (tree: Node): void => { visit(tree, "heading", (node: Heading) => { if (node.children?.length > 0) { @@ -47,7 +47,7 @@ export function remarkHeadingLinks(): (tree: Node) => void { * A regular expression that extracts an admonition title from a string UNLESS the admonition title is the only thing on * the line. * - * Capture group 1 is the admonition type/title. Capture group 2 is any trailing whitespace. + * Capture group 1 is the admonition type/title. * * @remarks * @@ -57,7 +57,7 @@ export function remarkHeadingLinks(): (tree: Node) => void { * WARNING. It ensures that the pattern is not followed by only whitespace characters until the end of the line. * Additionally, it captures any whitespace characters that follow the matched pattern. */ -const ADMONITION_REGEX = /(\[!(?:CAUTION|IMPORTANT|NOTE|TIP|WARNING)])(?!\s*$)(\s*)/gm; +const ADMONITION_REGEX = /(\[!(?:CAUTION|IMPORTANT|NOTE|TIP|WARNING)])(?!\s*$)\s*/gm; /** * A regular expression to remove single line breaks from text. This is used to remove extraneous line breaks in text @@ -89,12 +89,18 @@ export function stripSoftBreaks(): (tree: Node) => void { }; } +/** + * Given a heading string or regex, removes all the content in sections under that heading. Most useful for removing a + * table of contents section that will later be regenerated, Note that the section heading remains - only the inner + * content is removed. + * + * @param options - `heading` is a string or regex that a section's heading must match to be removed. + */ export function removeSectionContent(options: { heading: string | RegExp }): ( tree: Root, ) => void { return function (tree: Root) { headingRange(tree, options.heading, (start, nodes, end, info) => { - console.log(`removing section ${options.heading}`); return [ start, // No child nodes - effectively empties the section. @@ -129,14 +135,20 @@ export function removeHeadingsAtLevel(options: { level: 1 | 2 | 3 | 4 | 5 | 6 }) }; } -export function updateTocLinks(options: { newUrl: string }): (tree: Root) => void { - const { newUrl } = options; +/** + * Updates URLs of links whose value match a provided value. + * + * @param options - `checkValue` is a string that will be compared against the link text. Only matching nodes will be + * updated. `newUrl` is the new URL to assign to the link. + */ +export function updateTocLinks(options: { checkValue: string, newUrl: string }): (tree: Root) => void { + const { checkValue, newUrl } = options; return (tree: Root) => { visit(tree, "link", (node: Link) => { if ( node.children?.[0].type === "text" && - node.children[0].value === "⬆️ Table of contents" + node.children[0].value === checkValue ) { node.url = newUrl; } diff --git a/build-tools/packages/build-cli/src/library/releaseNotes.ts b/build-tools/packages/build-cli/src/library/releaseNotes.ts new file mode 100644 index 000000000000..e0037eb0181f --- /dev/null +++ b/build-tools/packages/build-cli/src/library/releaseNotes.ts @@ -0,0 +1,9 @@ +/*! + * Copyright (c) Microsoft Corporation and contributors. All rights reserved. + * Licensed under the MIT License. + */ + +/** + * The test used in release notes links that point back to the table of contents in the document. + */ +export const RELEASE_NOTES_TOC_LINK_TEXT = "⬆️ Table of contents"; From 2e81fa057cd5a72abc67a44583eb53ed356c1689 Mon Sep 17 00:00:00 2001 From: Tyler Butler Date: Wed, 11 Sep 2024 11:20:25 -0700 Subject: [PATCH 10/21] build and format --- build-tools/packages/build-cli/docs/transform.md | 5 +---- .../src/commands/generate/releaseNotes.ts | 4 +--- .../src/commands/transform/releaseNotes.ts | 5 ++++- .../packages/build-cli/src/library/markdown.ts | 14 +++++--------- 4 files changed, 11 insertions(+), 17 deletions(-) diff --git a/build-tools/packages/build-cli/docs/transform.md b/build-tools/packages/build-cli/docs/transform.md index ff0718e9fb6e..f4874ac093b8 100644 --- a/build-tools/packages/build-cli/docs/transform.md +++ b/build-tools/packages/build-cli/docs/transform.md @@ -11,7 +11,7 @@ Transforms a markdown release notes file into a format appropriate for use in a ``` USAGE - $ flub transform releaseNotes --inFile --outFile [--json] [-v | --quiet] + $ flub transform releaseNotes --inFile --outFile [-v | --quiet] FLAGS --inFile= (required) A release notes file that was generated using 'flub generate releaseNotes'. The file @@ -23,9 +23,6 @@ LOGGING FLAGS -v, --verbose Enable verbose logging. --quiet Disable all logging. -GLOBAL FLAGS - --json Format output as json. - EXAMPLES Transform the release notes from version 2.2.0 and output the results to out.md. diff --git a/build-tools/packages/build-cli/src/commands/generate/releaseNotes.ts b/build-tools/packages/build-cli/src/commands/generate/releaseNotes.ts index e4a0928be41b..5b5c9ac8d41a 100644 --- a/build-tools/packages/build-cli/src/commands/generate/releaseNotes.ts +++ b/build-tools/packages/build-cli/src/commands/generate/releaseNotes.ts @@ -216,9 +216,7 @@ export default class GenerateReleaseNotesCommand extends BaseCommand< }) .use(remarkToc, { maxDepth: 3, skip: ".*Start Building Today.*" }); - const processor = flags.headingLinks - ? baseProcessor.use(addHeadingLinks) - : baseProcessor; + const processor = flags.headingLinks ? baseProcessor.use(addHeadingLinks) : baseProcessor; const contents = String( await processor.process(`${header}\n\n${intro}\n\n${body.toString()}\n\n${footer}`), diff --git a/build-tools/packages/build-cli/src/commands/transform/releaseNotes.ts b/build-tools/packages/build-cli/src/commands/transform/releaseNotes.ts index 8caa5ec6b0a5..fa9f81fc9909 100644 --- a/build-tools/packages/build-cli/src/commands/transform/releaseNotes.ts +++ b/build-tools/packages/build-cli/src/commands/transform/releaseNotes.ts @@ -63,7 +63,10 @@ export default class TransformReleaseNotesCommand extends BaseCommand< // Remove the existing TOC section because its links are incorrect; we'll regenerate it. .use(removeSectionContent, { heading: "Contents" }) // Update the "back to TOC" links to prepend 'user-content-' because that's what GH Releases does. - .use(updateTocLinks, { checkValue: RELEASE_NOTES_TOC_LINK_TEXT, newUrl: "#user-content-contents" }) + .use(updateTocLinks, { + checkValue: RELEASE_NOTES_TOC_LINK_TEXT, + newUrl: "#user-content-contents", + }) // Parse the markdown as GitHub-Flavored Markdown .use(remarkGfm) // Strip any single-line breaks. See the docs for the stripSoftBreaks function for more details. diff --git a/build-tools/packages/build-cli/src/library/markdown.ts b/build-tools/packages/build-cli/src/library/markdown.ts index 647e761906c9..fe74fb7066fa 100644 --- a/build-tools/packages/build-cli/src/library/markdown.ts +++ b/build-tools/packages/build-cli/src/library/markdown.ts @@ -123,10 +123,7 @@ export function removeHeadingsAtLevel(options: { level: 1 | 2 | 3 | 4 | 5 | 6 }) tree, "heading", (node: Heading, index: number | undefined, parent: Parent | undefined) => { - if ( - node.depth === options.level && - index !== undefined - ) { + if (node.depth === options.level && index !== undefined) { parent?.children.splice(index, 1); return [SKIP, index]; } @@ -141,15 +138,14 @@ export function removeHeadingsAtLevel(options: { level: 1 | 2 | 3 | 4 | 5 | 6 }) * @param options - `checkValue` is a string that will be compared against the link text. Only matching nodes will be * updated. `newUrl` is the new URL to assign to the link. */ -export function updateTocLinks(options: { checkValue: string, newUrl: string }): (tree: Root) => void { +export function updateTocLinks(options: { checkValue: string; newUrl: string }): ( + tree: Root, +) => void { const { checkValue, newUrl } = options; return (tree: Root) => { visit(tree, "link", (node: Link) => { - if ( - node.children?.[0].type === "text" && - node.children[0].value === checkValue - ) { + if (node.children?.[0].type === "text" && node.children[0].value === checkValue) { node.url = newUrl; } }); From 5d49bb4494826b0c55b931b3eab5f847f85ef1be Mon Sep 17 00:00:00 2001 From: Tyler Butler Date: Wed, 11 Sep 2024 11:59:14 -0700 Subject: [PATCH 11/21] format --- build-tools/packages/build-cli/src/library/markdown.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build-tools/packages/build-cli/src/library/markdown.ts b/build-tools/packages/build-cli/src/library/markdown.ts index 21ffbfde2c01..a037567aeb43 100644 --- a/build-tools/packages/build-cli/src/library/markdown.ts +++ b/build-tools/packages/build-cli/src/library/markdown.ts @@ -6,9 +6,9 @@ import GithubSlugger from "github-slugger"; import type { Heading, Html, Link, Root } from "mdast"; import { headingRange } from "mdast-util-heading-range"; +import { toString } from "mdast-util-to-string"; import type { Node, Parent } from "unist"; import { SKIP, visit } from "unist-util-visit"; -import { toString } from "mdast-util-to-string"; /** * Using the same instance for all slug generation ensures that no duplicate IDs are generated. From 712b9644cd934e9c5f46f60a85a396e38cca2b43 Mon Sep 17 00:00:00 2001 From: Tyler Butler Date: Wed, 11 Sep 2024 12:10:53 -0700 Subject: [PATCH 12/21] fixes --- build-tools/packages/build-cli/docs/transform.md | 4 +--- .../packages/build-cli/src/commands/transform/releaseNotes.ts | 2 +- build-tools/packages/build-cli/src/library/markdown.ts | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/build-tools/packages/build-cli/docs/transform.md b/build-tools/packages/build-cli/docs/transform.md index f4874ac093b8..30f206562f14 100644 --- a/build-tools/packages/build-cli/docs/transform.md +++ b/build-tools/packages/build-cli/docs/transform.md @@ -14,9 +14,7 @@ USAGE $ flub transform releaseNotes --inFile --outFile [-v | --quiet] FLAGS - --inFile= (required) A release notes file that was generated using 'flub generate releaseNotes'. The file - must not already contain heading links. That is, the input file must not have been generated with - the --headingLinks flag. + --inFile= (required) A release notes file that was generated using 'flub generate releaseNotes'. --outFile= (required) Output the transformed content to this file. LOGGING FLAGS diff --git a/build-tools/packages/build-cli/src/commands/transform/releaseNotes.ts b/build-tools/packages/build-cli/src/commands/transform/releaseNotes.ts index fa9f81fc9909..d6653fd48cc2 100644 --- a/build-tools/packages/build-cli/src/commands/transform/releaseNotes.ts +++ b/build-tools/packages/build-cli/src/commands/transform/releaseNotes.ts @@ -35,7 +35,7 @@ export default class TransformReleaseNotesCommand extends BaseCommand< static readonly flags = { inFile: Flags.file({ - description: `A release notes file that was generated using 'flub generate releaseNotes'. The file must not already contain heading links. That is, the input file must not have been generated with the --headingLinks flag.`, + description: `A release notes file that was generated using 'flub generate releaseNotes'.`, required: true, exists: true, }), diff --git a/build-tools/packages/build-cli/src/library/markdown.ts b/build-tools/packages/build-cli/src/library/markdown.ts index a037567aeb43..0889a323ff7f 100644 --- a/build-tools/packages/build-cli/src/library/markdown.ts +++ b/build-tools/packages/build-cli/src/library/markdown.ts @@ -25,7 +25,7 @@ const slugger = new GithubSlugger(); export function addHeadingLinks(): (tree: Node) => void { return (tree: Node): void => { visit(tree, "heading", (node: Heading) => { - if (node.children?.length > 0) { + if (node.children?.length > 0 && node.children[0].type === "text") { // Calling toString on the whole node ensures that embedded nodes (e.g. formatted text in the heading) are // included in the slugged string. const slug = slugger.slug(toString(node)); From 88aafeae9a397eae56cf190a629f93a8e928dfcd Mon Sep 17 00:00:00 2001 From: Tyler Butler Date: Wed, 11 Sep 2024 12:12:52 -0700 Subject: [PATCH 13/21] use chared const --- .../packages/build-cli/src/commands/generate/releaseNotes.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/build-tools/packages/build-cli/src/commands/generate/releaseNotes.ts b/build-tools/packages/build-cli/src/commands/generate/releaseNotes.ts index 8e538a459117..0857643f8ae9 100644 --- a/build-tools/packages/build-cli/src/commands/generate/releaseNotes.ts +++ b/build-tools/packages/build-cli/src/commands/generate/releaseNotes.ts @@ -27,6 +27,7 @@ import { } from "../../library/index.js"; // eslint-disable-next-line import/no-internal-modules import { addHeadingLinks, stripSoftBreaks } from "../../library/markdown.js"; +import { RELEASE_NOTES_TOC_LINK_TEXT } from "../../library/releaseNotes.js"; /** * Generates release notes from individual changeset files. @@ -196,7 +197,7 @@ export default class GenerateReleaseNotesCommand extends BaseCommand< .join(""); body.append(`Affected packages:\n\n${affectedPackages}\n\n`); body.append( - `[⬆️ Table of contents](#${flags.headingLinks ? "user-content-" : ""}contents)\n\n`, + `[${RELEASE_NOTES_TOC_LINK_TEXT}](#${flags.headingLinks ? "user-content-" : ""}contents)\n\n`, ); } else { this.info( From bd3e3f6647000b43d8ff34003a78c2b07ebda747 Mon Sep 17 00:00:00 2001 From: Tyler Butler Date: Wed, 11 Sep 2024 15:38:16 -0700 Subject: [PATCH 14/21] lint disable --- .../packages/build-cli/src/commands/generate/releaseNotes.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/build-tools/packages/build-cli/src/commands/generate/releaseNotes.ts b/build-tools/packages/build-cli/src/commands/generate/releaseNotes.ts index 0857643f8ae9..8df1e7fd46ae 100644 --- a/build-tools/packages/build-cli/src/commands/generate/releaseNotes.ts +++ b/build-tools/packages/build-cli/src/commands/generate/releaseNotes.ts @@ -27,6 +27,7 @@ import { } from "../../library/index.js"; // eslint-disable-next-line import/no-internal-modules import { addHeadingLinks, stripSoftBreaks } from "../../library/markdown.js"; +// eslint-disable-next-line import/no-internal-modules import { RELEASE_NOTES_TOC_LINK_TEXT } from "../../library/releaseNotes.js"; /** From 88ac56341bd521f0347034af24db558c7440f0e7 Mon Sep 17 00:00:00 2001 From: Tyler Butler Date: Wed, 11 Sep 2024 16:11:28 -0700 Subject: [PATCH 15/21] Update releaseNotes.ts Co-authored-by: jzaffiro <110866475+jzaffiro@users.noreply.github.com> --- .../packages/build-cli/src/commands/transform/releaseNotes.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build-tools/packages/build-cli/src/commands/transform/releaseNotes.ts b/build-tools/packages/build-cli/src/commands/transform/releaseNotes.ts index d6653fd48cc2..06b4711c159c 100644 --- a/build-tools/packages/build-cli/src/commands/transform/releaseNotes.ts +++ b/build-tools/packages/build-cli/src/commands/transform/releaseNotes.ts @@ -75,7 +75,7 @@ export default class TransformReleaseNotesCommand extends BaseCommand< .use(admonitions, { titleTextMap: (title) => ({ // By default the `[!` prefix and `]` suffix are removed; we don't want that, so we override the default and - // return he title as-is. + // return the title as-is. displayTitle: title, checkedTitle: title, }), From fe487b25d63e6b29f0561e25a2d8c9b5c1cdb93e Mon Sep 17 00:00:00 2001 From: Tyler Butler Date: Wed, 11 Sep 2024 16:11:34 -0700 Subject: [PATCH 16/21] Update releaseNotes.ts Co-authored-by: jzaffiro <110866475+jzaffiro@users.noreply.github.com> --- .../packages/build-cli/src/commands/transform/releaseNotes.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build-tools/packages/build-cli/src/commands/transform/releaseNotes.ts b/build-tools/packages/build-cli/src/commands/transform/releaseNotes.ts index 06b4711c159c..fbb3d54275a7 100644 --- a/build-tools/packages/build-cli/src/commands/transform/releaseNotes.ts +++ b/build-tools/packages/build-cli/src/commands/transform/releaseNotes.ts @@ -95,7 +95,7 @@ export default class TransformReleaseNotesCommand extends BaseCommand< return values.type === "mention" ? false : defaultBuildUrl(values); }, }) - // Add custom achor tags with IDs to all the headings. + // Add custom anchor tags with IDs to all the headings. .use(addHeadingLinks); const contents = String(await processor.process(input)); From 7c45b394badc9f2b352d9c2ea2e8f3d32443d9e0 Mon Sep 17 00:00:00 2001 From: Tyler Butler Date: Wed, 11 Sep 2024 16:12:11 -0700 Subject: [PATCH 17/21] Update markdown.ts Co-authored-by: jzaffiro <110866475+jzaffiro@users.noreply.github.com> --- build-tools/packages/build-cli/src/library/markdown.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build-tools/packages/build-cli/src/library/markdown.ts b/build-tools/packages/build-cli/src/library/markdown.ts index 0889a323ff7f..4821e5daa7d5 100644 --- a/build-tools/packages/build-cli/src/library/markdown.ts +++ b/build-tools/packages/build-cli/src/library/markdown.ts @@ -97,7 +97,7 @@ export function stripSoftBreaks(): (tree: Node) => void { /** * Given a heading string or regex, removes all the content in sections under that heading. Most useful for removing a - * table of contents section that will later be regenerated, Note that the section heading remains - only the inner + * table of contents section that will later be regenerated. Note that the section heading remains - only the inner * content is removed. * * @param options - `heading` is a string or regex that a section's heading must match to be removed. From 62523e99a0265b687211479ba6fea6a982423674 Mon Sep 17 00:00:00 2001 From: Tyler Butler Date: Wed, 11 Sep 2024 16:20:19 -0700 Subject: [PATCH 18/21] Update releaseNotes.ts --- build-tools/packages/build-cli/src/library/releaseNotes.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build-tools/packages/build-cli/src/library/releaseNotes.ts b/build-tools/packages/build-cli/src/library/releaseNotes.ts index e0037eb0181f..cfdd9ad6977a 100644 --- a/build-tools/packages/build-cli/src/library/releaseNotes.ts +++ b/build-tools/packages/build-cli/src/library/releaseNotes.ts @@ -4,6 +4,6 @@ */ /** - * The test used in release notes links that point back to the table of contents in the document. + * The text used in release notes links that point back to the table of contents in the document. */ export const RELEASE_NOTES_TOC_LINK_TEXT = "⬆️ Table of contents"; From fc88b0a52923fa089c9141fadd6f4673d3a4d2c3 Mon Sep 17 00:00:00 2001 From: Tyler Butler Date: Fri, 13 Sep 2024 16:47:53 -0700 Subject: [PATCH 19/21] rm unused dep --- build-tools/packages/build-cli/package.json | 1 - build-tools/pnpm-lock.yaml | 3 --- 2 files changed, 4 deletions(-) diff --git a/build-tools/packages/build-cli/package.json b/build-tools/packages/build-cli/package.json index 73805da2732b..5f7124b39db4 100644 --- a/build-tools/packages/build-cli/package.json +++ b/build-tools/packages/build-cli/package.json @@ -137,7 +137,6 @@ "table": "^6.8.1", "ts-morph": "^22.0.0", "type-fest": "^2.19.0", - "unified": "^11.0.5", "unist-util-visit": "^5.0.0" }, "devDependencies": { diff --git a/build-tools/pnpm-lock.yaml b/build-tools/pnpm-lock.yaml index 834d5e8278dd..c478f1ecaa36 100644 --- a/build-tools/pnpm-lock.yaml +++ b/build-tools/pnpm-lock.yaml @@ -267,9 +267,6 @@ importers: type-fest: specifier: ^2.19.0 version: 2.19.0 - unified: - specifier: ^11.0.5 - version: 11.0.5 unist-util-visit: specifier: ^5.0.0 version: 5.0.0 From c8535f2c55ec8d4483c932c2c5bdf919c07d045f Mon Sep 17 00:00:00 2001 From: Tyler Butler Date: Fri, 13 Sep 2024 16:51:16 -0700 Subject: [PATCH 20/21] feedback --- build-tools/packages/build-cli/src/library/markdown.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/build-tools/packages/build-cli/src/library/markdown.ts b/build-tools/packages/build-cli/src/library/markdown.ts index 4821e5daa7d5..49a21395015e 100644 --- a/build-tools/packages/build-cli/src/library/markdown.ts +++ b/build-tools/packages/build-cli/src/library/markdown.ts @@ -25,7 +25,13 @@ const slugger = new GithubSlugger(); export function addHeadingLinks(): (tree: Node) => void { return (tree: Node): void => { visit(tree, "heading", (node: Heading) => { - if (node.children?.length > 0 && node.children[0].type === "text") { + if ( + node.children?.length > 0 && + // This check ensures that we don't add links to headings that already have them. In such cases the first child + // node's type will be html, not text. Note that this check could ignore some node types other than text that + // would be fine to add headings to, but we've not come across any such cases. + node.children[0].type === "text" + ) { // Calling toString on the whole node ensures that embedded nodes (e.g. formatted text in the heading) are // included in the slugged string. const slug = slugger.slug(toString(node)); From 1664b8cf0f48634f4e86b5572d77d6129283d0a6 Mon Sep 17 00:00:00 2001 From: Tyler Butler Date: Fri, 13 Sep 2024 16:53:05 -0700 Subject: [PATCH 21/21] revert --- .../data/release-notes-issue-intro.njk | 2 +- .github/workflows/push-tag-create-release.yml | 28 +++++++++---------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/.github/workflows/data/release-notes-issue-intro.njk b/.github/workflows/data/release-notes-issue-intro.njk index 57ebab24bc63..20512e6a95b2 100644 --- a/.github/workflows/data/release-notes-issue-intro.njk +++ b/.github/workflows/data/release-notes-issue-intro.njk @@ -12,6 +12,6 @@ pnpm flub generate releaseNotes -g client -t minor --outFile RELEASE_NOTES/{{ ve pnpm flub generate releaseNotes -g client -t minor --headingLinks --excludeH1 --outFile temporary-file.md ``` -This should be done automatically as part of the release process, but if you need to generate the release notes manually, you can use the above command. +You can then copy and paste the contents of the `temporary-file.md` into the GitHub Release. --- diff --git a/.github/workflows/push-tag-create-release.yml b/.github/workflows/push-tag-create-release.yml index 4db1baee850c..99db3e2e6413 100644 --- a/.github/workflows/push-tag-create-release.yml +++ b/.github/workflows/push-tag-create-release.yml @@ -72,7 +72,7 @@ jobs: with: name: release-metadata path: release-metadata.json - retention-days: 30 + retention-days: 3 - name: Load release metadata into env variable run: | echo "RELEASE_JSON=$(cat release-metadata.json)" >> $GITHUB_ENV @@ -101,10 +101,10 @@ jobs: path: reports retention-days: 7 - # Generate release notes - - name: Generate patch release notes - # This content is only for patch releases - if: fromJson(env.RELEASE_JSON).releaseType == 'patch' + # Generate changelog + - name: Generate changelog + # This changelog is only for client patch releases and build-tools releases + if: (fromJson(env.RELEASE_JSON).releaseType == 'patch' && fromJson(env.RELEASE_JSON).packageOrReleaseGroup == 'client') || fromJson(env.RELEASE_JSON).packageOrReleaseGroup == 'build-tools' run: | # We only need the root dependencies pnpm install -w --frozen-lockfile @@ -116,16 +116,11 @@ jobs: --tag-prefix ${{ fromJson(env.RELEASE_JSON).packageOrReleaseGroup }}_v \ --output auto-changelog.md \ --template .github/workflows/data/patch-changelog.hbs - - - name: Generate minor/major release notes - # This content is the "default" release notes - this should be used for minor and major releases of client and - # server release groups. To use this, a release group needs to be using changesets. Build-tools does not use - # changesets, so it is excluded. - if: fromJson(env.RELEASE_JSON).releaseType != 'patch' && (fromJson(env.RELEASE_JSON).packageOrReleaseGroup == 'client' || fromJson(env.RELEASE_JSON).packageOrReleaseGroup == 'server') + - name: Generate changelog + # This changelog is a basically empty one used for everything except client patches and build-tools. + if: ${{ !(fromJson(env.RELEASE_JSON).releaseType == 'patch' && fromJson(env.RELEASE_JSON).packageOrReleaseGroup == 'client') && fromJson(env.RELEASE_JSON).packageOrReleaseGroup != 'build-tools' }} run: | - flub publish releaseNotes \ - --inFile RELEASE_NOTES/${{ fromJson(env.RELEASE_JSON).version }}.md - --outFile auto-changelog.md + echo "This is a **${{ fromJson(env.RELEASE_JSON).releaseType }}** release." > auto-changelog.md # Only creates GH releases for client, server, and build-tools releases. - name: Create GH release @@ -138,6 +133,11 @@ jobs: # Will skip if a published (non-draft) release already exists. skipIfReleaseExists: true + # If the release is NOT a patch, leave it as a draft. Only patch releases are auto-published because their + # auto-generated release notes are sufficient for those releases. Minor and major releases currently require + # some curation of the release notes. + draft: ${{ fromJson(env.RELEASE_JSON).releaseType != 'patch' }} + # Don't change the draft state when updating an existing release. This setting is not really necessary for us # in most cases because we don't pre-create releases, so this workflow always creates a new GH release. It's # included mostly for safety reasons, to ensure that existing drafts aren't published accidentally.