mirror of
https://github.com/github/codeql-action.git
synced 2026-11-05 04:29:40 +00:00
Merge pull request #4285 from github/henrymercer/automated-change-notes
Migrate bundle update workflow to new changenote tooling
This commit is contained in:
7
.github/workflows/update-bundle.yml
vendored
7
.github/workflows/update-bundle.yml
vendored
@@ -113,11 +113,12 @@ jobs:
|
||||
echo "CLI_VERSION=$cli_version" | tee -a "$GITHUB_ENV"
|
||||
echo "PR_URL=$pr_url" | tee -a "$GITHUB_ENV"
|
||||
|
||||
- name: Create changelog note
|
||||
- name: Create changenote
|
||||
run: |
|
||||
npx tsx pr-checks/bundle-changelog.ts
|
||||
|
||||
- name: Push changelog note
|
||||
- name: Push changenote
|
||||
run: |
|
||||
git commit -am "Add changelog note"
|
||||
git add unreleased-change-notes
|
||||
git commit -m "Add changenote"
|
||||
git push
|
||||
|
||||
@@ -10,17 +10,13 @@ import { afterEach, beforeEach, describe, it } from "node:test";
|
||||
|
||||
import {
|
||||
CLI_VERSION_ENV_VAR,
|
||||
getChangenote,
|
||||
getCLIVersion,
|
||||
getPRNumber,
|
||||
getPRUrl,
|
||||
PR_URL_ENV_VAR,
|
||||
updateChangelog,
|
||||
} from "./bundle-changelog";
|
||||
import {
|
||||
EMPTY_CHANGELOG,
|
||||
NO_CHANGES_STR,
|
||||
UNRELEASED_PLACEHOLDER,
|
||||
} from "./changelog";
|
||||
import { isValidChangenoteContent } from "./changelog/validate";
|
||||
|
||||
let testDir: string;
|
||||
|
||||
@@ -85,58 +81,15 @@ describe("getPRNumber", async () => {
|
||||
});
|
||||
});
|
||||
|
||||
const testChangelog = `${EMPTY_CHANGELOG.trimEnd()}
|
||||
|
||||
## 4.23.7
|
||||
|
||||
- Other change
|
||||
|
||||
## 4.23.6
|
||||
|
||||
${NO_CHANGES_STR}`;
|
||||
|
||||
const expectedChangelog = `# CodeQL Action Changelog
|
||||
|
||||
## ${UNRELEASED_PLACEHOLDER}
|
||||
|
||||
- Update default CodeQL bundle version to
|
||||
|
||||
## 4.23.7
|
||||
|
||||
- Other change
|
||||
|
||||
## 4.23.6
|
||||
|
||||
${NO_CHANGES_STR}`;
|
||||
|
||||
describe("updateChangelog", async () => {
|
||||
await it("removes `NO_CHANGES_STR` if present in [UNRELEASED] section", async () => {
|
||||
const result = updateChangelog(EMPTY_CHANGELOG, "");
|
||||
assert.ok(!result.includes(NO_CHANGES_STR));
|
||||
});
|
||||
|
||||
await it("doesn't remove `NO_CHANGES_STR` if present in versioned section", async () => {
|
||||
const result = updateChangelog(
|
||||
EMPTY_CHANGELOG.replace(UNRELEASED_PLACEHOLDER, "1.2.3"),
|
||||
"",
|
||||
describe("getChangenote", async () => {
|
||||
await it("returns a note linking to the bundle release and the PR", async () => {
|
||||
assert.equal(
|
||||
getChangenote("1.2.3", testPrUrl),
|
||||
"- Update default CodeQL bundle version to [1.2.3](https://github.com/github/codeql-action/releases/tag/codeql-bundle-v1.2.3). [#42](https://github.com/github/codeql-action/pulls/42)\n",
|
||||
);
|
||||
assert.ok(result.includes(NO_CHANGES_STR));
|
||||
});
|
||||
|
||||
await it("throws if there are no sections", async () => {
|
||||
assert.throws(() => {
|
||||
updateChangelog(
|
||||
"# CodeQL Action Changelog",
|
||||
"- Update default CodeQL bundle version to",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
await it("adds note at the end of the first section", async () => {
|
||||
const result = updateChangelog(
|
||||
testChangelog,
|
||||
"- Update default CodeQL bundle version to",
|
||||
);
|
||||
assert.deepEqual(result, expectedChangelog);
|
||||
await it("returns valid changenote content", async () => {
|
||||
assert.ok(isValidChangenoteContent(getChangenote("1.2.3", testPrUrl)));
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,17 +1,13 @@
|
||||
#!/usr/bin/env npx tsx
|
||||
|
||||
/**
|
||||
* Updates the changelog with a change note for an updated CodeQL CLI bundle.
|
||||
* Creates a changenote file for an update to the default CodeQL CLI bundle.
|
||||
*/
|
||||
|
||||
import * as fs from "node:fs";
|
||||
import * as path from "node:path";
|
||||
|
||||
import {
|
||||
parseChangelog,
|
||||
renderChangelog,
|
||||
UNRELEASED_PLACEHOLDER,
|
||||
} from "./changelog";
|
||||
import { CHANGELOG_FILE, CLI_BUNDLE_RELEASE_URL_PREFIX } from "./config";
|
||||
import { CHANGENOTES_DIR, CLI_BUNDLE_RELEASE_URL_PREFIX } from "./config";
|
||||
import { getErrorMessage } from "./util";
|
||||
|
||||
export const CLI_VERSION_ENV_VAR = "CLI_VERSION";
|
||||
@@ -58,42 +54,19 @@ export function getPRNumber(prUrl: string) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates `changelog` by adding `changelogNote` to the first section.
|
||||
* Returns the contents of the changenote file for an update to the default
|
||||
* CodeQL CLI bundle.
|
||||
*
|
||||
* @param contents The existing changelog contents.
|
||||
* @param changelogNote The note to add to the first section.
|
||||
* @param cliVersion The CodeQL CLI version of the new default bundle.
|
||||
* @param prUrl The URL of the PR that updates the default bundle.
|
||||
* @throws If the PR number can't be determined from `prUrl`.
|
||||
*/
|
||||
export function updateChangelog(contents: string, changelogNote: string) {
|
||||
// If the "[UNRELEASED]" section starts with "no user facing changes", remove that line.
|
||||
contents = contents.replace(
|
||||
`## ${UNRELEASED_PLACEHOLDER}\n\nNo user facing changes.`,
|
||||
`## ${UNRELEASED_PLACEHOLDER}\n`,
|
||||
);
|
||||
export function getChangenote(cliVersion: string, prUrl: string): string {
|
||||
// The GitHub Release for the new bundle version.
|
||||
const bundleReleaseUrl = `${CLI_BUNDLE_RELEASE_URL_PREFIX}${cliVersion}`;
|
||||
|
||||
const changelog = parseChangelog(contents);
|
||||
|
||||
if (changelog.sections.length === 0) {
|
||||
throw new Error("The changelog contains no existing sections.");
|
||||
}
|
||||
|
||||
// Add the changelog note to the bottom of the first section.
|
||||
const firstSection = changelog.sections[0];
|
||||
const lastLine = firstSection.bodyLines.pop();
|
||||
|
||||
if (lastLine !== undefined && lastLine.trim() !== "") {
|
||||
// We expect the last line to be empty. If it isn't for some reason,
|
||||
// add it back.
|
||||
firstSection.bodyLines.push(lastLine);
|
||||
}
|
||||
|
||||
firstSection.bodyLines.push(changelogNote);
|
||||
|
||||
// If the last line is empty as expected, then add it back in after the new note.
|
||||
if (lastLine?.trim() === "") {
|
||||
firstSection.bodyLines.push(lastLine);
|
||||
}
|
||||
|
||||
return renderChangelog(changelog);
|
||||
const prNumber = getPRNumber(prUrl);
|
||||
return `- Update default CodeQL bundle version to [${cliVersion}](${bundleReleaseUrl}). [#${prNumber}](${prUrl})\n`;
|
||||
}
|
||||
|
||||
function main() {
|
||||
@@ -101,22 +74,19 @@ function main() {
|
||||
const cliVersion = getCLIVersion();
|
||||
const prUrl = getPRUrl();
|
||||
|
||||
// The GitHub Release for the new bundle version.
|
||||
const bundleReleaseUrl = `${CLI_BUNDLE_RELEASE_URL_PREFIX}${cliVersion}`;
|
||||
|
||||
// Get the PR number from the PR URL.
|
||||
const prNumber = getPRNumber(prUrl);
|
||||
const changelogNote = `- Update default CodeQL bundle version to [${cliVersion}](${bundleReleaseUrl}). [#${prNumber}](${prUrl})`;
|
||||
|
||||
let changelog = fs.readFileSync(CHANGELOG_FILE, "utf-8");
|
||||
|
||||
changelog = updateChangelog(changelog, changelogNote);
|
||||
|
||||
fs.writeFileSync(CHANGELOG_FILE, changelog);
|
||||
// Include the CLI version in the file name so that changenotes for
|
||||
// different bundle updates don't conflict.
|
||||
const changenotePath = path.join(
|
||||
CHANGENOTES_DIR,
|
||||
`update-bundle-${cliVersion}.md`,
|
||||
);
|
||||
fs.writeFileSync(changenotePath, getChangenote(cliVersion, prUrl));
|
||||
|
||||
return 0;
|
||||
} catch (err) {
|
||||
console.error(`Failed to bundle changelog: ${getErrorMessage(err)}`);
|
||||
console.error(
|
||||
`Failed to create changenote for bundle update: ${getErrorMessage(err)}`,
|
||||
);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user