From cf1bdf737f53b08fd740d68d3ec1d3a5cb744b91 Mon Sep 17 00:00:00 2001 From: Kevin Eady <8634912+KevinEady@users.noreply.github.com> Date: Thu, 16 May 2024 13:23:26 +0200 Subject: [PATCH 1/3] Update sync-headers to close old PRs --- .github/workflows/sync-headers.yml | 17 +++++++++++++++-- scripts/update-headers.js | 2 +- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/.github/workflows/sync-headers.yml b/.github/workflows/sync-headers.yml index 7fc4732..6ad2e83 100644 --- a/.github/workflows/sync-headers.yml +++ b/.github/workflows/sync-headers.yml @@ -39,15 +39,28 @@ jobs: echo "Branch does not exists." echo "BRANCH_NAME=$BRANCH_NAME" >> $GITHUB_OUTPUT echo "COMMIT_MESSAGE=$COMMIT_MESSAGE" >> $GITHUB_OUTPUT + echo "VERSION=$VERSION" >> $GITHUB_OUTPUT fi fi - name: Create Pull Request - uses: peter-evans/create-pull-request@v4 + id: cpr + uses: peter-evans/create-pull-request@6d6857d36972b65feb161a90e484f2984215f83e # v6.0.5 if: ${{ steps.check-changes.outputs.BRANCH_NAME }} with: branch: ${{ steps.check-changes.outputs.BRANCH_NAME }} commit-message: ${{ steps.check-changes.outputs.COMMIT_MESSAGE }} title: ${{ steps.check-changes.outputs.COMMIT_MESSAGE }} author: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> - body: null + body: Automated sync of headers with Node.js version ${{ steps.check-changes.outputs.VERSION }} + labels: headers delete-branch: true + - name: Close existing PRs + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + if: ${{ steps.cpr.outputs.pull-request-number }} + run: | + gh pr list --json number,author,title,labels --jq '[ .[] | select(.author.login == "app/github-actions" and .number != ${{ steps.cpr.outputs.pull-request-number }} and (.labels[]| select(.name =="headers" )))]' | jq -c '.[]' | + while IFS=$"\n" read -r c; do + pr_number=$(echo "$c" | jq -r '.number') + gh pr close $pr_number --delete-branch --comment "Closing in favor of [#${{ steps.cpr.outputs.pull-request-number }}](${{ steps.cpr.outputs.pull-request-url }})." + done diff --git a/scripts/update-headers.js b/scripts/update-headers.js index 03e7c1f..c0ab8de 100644 --- a/scripts/update-headers.js +++ b/scripts/update-headers.js @@ -160,7 +160,7 @@ async function main() { }, }); - console.log(`Update headers from nodejs/node tag ${tag}`); + console.log(`headers: update headers from nodejs/node tag ${tag}`); const files = ['js_native_api_types.h', 'js_native_api.h', 'node_api_types.h', 'node_api.h']; From 690b5bd2fbbef857566981b01e205785b37a626c Mon Sep 17 00:00:00 2001 From: Kevin Eady <8634912+KevinEady@users.noreply.github.com> Date: Thu, 16 May 2024 13:25:27 +0200 Subject: [PATCH 2/3] Create prepare-release action, update-changelog script --- .github/workflows/prepare-release.yml | 59 +++++++++++ package.json | 1 + scripts/update-changelog.js | 141 ++++++++++++++++++++++++++ 3 files changed, 201 insertions(+) create mode 100644 .github/workflows/prepare-release.yml create mode 100644 scripts/update-changelog.js diff --git a/.github/workflows/prepare-release.yml b/.github/workflows/prepare-release.yml new file mode 100644 index 0000000..2aa1eb6 --- /dev/null +++ b/.github/workflows/prepare-release.yml @@ -0,0 +1,59 @@ +name: Prepare Release + +on: + workflow_dispatch: + push: + branches: ['main'] + paths: ['include/**', 'def/**'] + +permissions: + contents: write + pull-requests: write + +jobs: + build: + runs-on: ubuntu-latest + name: Prepare Release + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - uses: actions/setup-node@v4 + with: + node-version: 18 + - name: Set up ghauth config (Ubuntu) + run: | + mkdir -p ~/.config/changelog-maker/ + echo '{"user": "github-actions[bot]", "token": "'${{ secrets.GITHUB_TOKEN }}'"}' > ~/.config/changelog-maker/config.json + - name: Update package version + run: npm version --no-git-tag-version minor + - name: Update changelog + run: npm run --silent update-changelog + - shell: bash + id: pr-vars + name: Compute Pull Request Variables + run: | + VERSION=$(jq -r ".version" package.json) + COMMIT_MESSAGE="release: v$VERSION" + BRANCH_NAME="release/v$VERSION" + echo $COMMIT_MESSAGE + if git ls-remote --exit-code --heads $GITHUB_SERVER_URL/$GITHUB_REPOSITORY $BRANCH_NAME >/dev/null; then + echo "Branch exists. Nothing to do." + else + echo "Branch does not exists." + echo "BRANCH_NAME=$BRANCH_NAME" >> $GITHUB_OUTPUT + echo "COMMIT_MESSAGE=$COMMIT_MESSAGE" >> $GITHUB_OUTPUT + echo "VERSION=$VERSION" >> $GITHUB_OUTPUT + fi + - name: Create Pull Request + id: cpr + uses: peter-evans/create-pull-request@6d6857d36972b65feb161a90e484f2984215f83e # v6.0.5 + if: ${{ steps.pr-vars.outputs.BRANCH_NAME }} + with: + branch: ${{ steps.pr-vars.outputs.BRANCH_NAME }} + commit-message: ${{ steps.pr-vars.outputs.COMMIT_MESSAGE }} + title: ${{ steps.pr-vars.outputs.COMMIT_MESSAGE }} + author: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> + body: Automated release for version v${{ steps.pr-vars.outputs.VERSION }} + labels: release + delete-branch: true diff --git a/package.json b/package.json index fa44e84..80311e5 100644 --- a/package.json +++ b/package.json @@ -51,6 +51,7 @@ }, "scripts": { "update-headers": "node --no-warnings scripts/update-headers.js", + "update-changelog": "node --no-warnings scripts/update-changelog.js", "write-symbols": "node --no-warnings scripts/write-symbols.js", "write-win32-def": "node --no-warnings scripts/write-win32-def.js" }, diff --git a/scripts/update-changelog.js b/scripts/update-changelog.js new file mode 100644 index 0000000..026e0fd --- /dev/null +++ b/scripts/update-changelog.js @@ -0,0 +1,141 @@ +const { exec, spawn } = require("node:child_process"); +const { createReadStream } = require("node:fs"); +const { createInterface } = require("node:readline"); +const { resolve: resolvePath } = require("node:path"); +const { writeFile } = require("node:fs/promises"); + +/** + * Returns a string of the new changelog entries by running `npx changelog-maker + * --format=markdown`. + * + * @returns {Promise} + */ +async function getNewChangelogEntries() { + const { stdout, stderr } = await new Promise((resolve, reject) => { + // Echo an empty string to pass as the GitHub Personal Access Token + // (PAT). This causes the process to error if no PAT is found in the + // changelog-maker configuration file. + exec("echo '' | npx changelog-maker --format=markdown", (err, stdout, stderr) => { + if (err) { + reject(err); + } else { + resolve({ stdout, stderr }); + } + }); + + }); + + return { stdout, stderr }; +} + +/** + * Returns the text of the changelog file, excluding header lines. + * + * @param {string} changelogPath Path to changelog file + * @returns {Promise} + */ +async function getExistingChangelogText(changelogPath) { + const data = await new Promise((resolve, reject) => { + try { + const rl = createInterface(createReadStream(changelogPath)); + + let lines = ""; + let lineNumber = 1; + + rl.on('line', function (line) { + if (lineNumber > 2) { + lines += line + "\n"; + } + + lineNumber++; + }); + + rl.on('close', () => { + resolve(lines); + }); + + rl.on('error', (err) => { + reject(err); + }); + + } catch (e) { + reject(e); + } + }); + + return data; +} + + +/** + * Returns the string for the new changelog file. + * + * @param {string} newEntries New changelog entries + * @param {string} existingText Existing changelog text + * @param {string} author Author of the release + * @returns {string} + */ +function generateChangelogText(newEntries, existingText, author = "github-actions\\[bot]") { + const packageVersion = require("../package.json").version; + const currentDateString = new Date().toISOString().split(/T/)[0]; + + const notableChanges = Array.from(newEntries.matchAll(/ (- [^(]+) \([^)]+\)( \[#\d+]\([^)]+\))?/g)) + .map(matches => matches[1]) + .join("\n"); + + return `# node-api-headers Changelog + +## ${currentDateString} Version ${packageVersion}, ${author} + +### Notable changes + +${notableChanges} + +### Commits + +${newEntries.trim()} + +${existingText.trim()} +`; +} + +/** + * Throws an error (asynchronously) if there are uncommitted changes to the changelog file. + * + * @param {string} changelogPath Path to changelog file + * @returns {Promise} + */ +function assertCleanChangelog(changelogPath) { + return new Promise((resolve, reject) => { + const spawned = spawn("git", ["diff", "--quiet", changelogPath]); + spawned.on('exit', function (exitCode) { + if (exitCode === 0) { + resolve(undefined); + } else { + reject(new Error(`There are uncommitted changes to ${changelogPath}. Commit, revert, or stash changes first.`)); + } + }); + + spawned.on('error', function (err) { + reject(err); + }); + }); +} + +async function main() { + const changelogPath = resolvePath(__dirname, "..", "CHANGELOG.md"); + await assertCleanChangelog(changelogPath); + const [{ stdout: newEntires, stderr }, existingText] = await Promise.all([getNewChangelogEntries(), getExistingChangelogText(changelogPath)]); + const changelogText = generateChangelogText(newEntires, existingText); + + await writeFile(changelogPath, changelogText); + if (stderr) { + console.error("stderr from changelog-maker:\n", stderr) + } + console.log(`Changelog written to ${changelogPath}`); +} + +main().catch(e => { + console.error(e); + process.exitCode = 1; +}); From d80977f4df8cae82f8f787ebd26c1859fe89905b Mon Sep 17 00:00:00 2001 From: Kevin Eady <8634912+KevinEady@users.noreply.github.com> Date: Thu, 16 May 2024 13:26:57 +0200 Subject: [PATCH 3/3] Create publish-release action --- .github/workflows/publish-release.yml | 53 +++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 .github/workflows/publish-release.yml diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml new file mode 100644 index 0000000..e351760 --- /dev/null +++ b/.github/workflows/publish-release.yml @@ -0,0 +1,53 @@ +name: Publish Release + +on: + workflow_dispatch: + + # Uncomment below to enable automated running of publish task on changes to + # package.json on main branch. + + # push: + # branches: ['main'] + # paths: ['package.json'] + +permissions: + contents: write + pull-requests: write + +jobs: + build: + runs-on: ubuntu-latest + name: Publish Release + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 20 + registry-url: 'https://registry.npmjs.org' + - shell: bash + id: release-vars + name: Compute Release Variables + run: | + VERSION=$(jq -r ".version" package.json) + PACKAGE_NAME=$(jq -r ".name" package.json) + npm show $PACKAGE_NAME@$VERSION && true + SHOULD_PUBLISH_VERSION="$?" + echo "VERSION=$VERSION PACKAGE_NAME=$PACKAGE_NAME SHOULD_PUBLISH_VERSION=$SHOULD_PUBLISH_VERSION" + echo "VERSION=$VERSION" >> $GITHUB_OUTPUT + echo "SHOULD_PUBLISH_VERSION=$SHOULD_PUBLISH_VERSION" >> $GITHUB_OUTPUT + - name: Publish to npm + if: ${{ steps.release-vars.outputs.SHOULD_PUBLISH_VERSION != '0' }} + run: | + npm publish --provenance --access public + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + - name: Create GitHub Release + if: ${{ steps.release-vars.outputs.SHOULD_PUBLISH_VERSION != '0' }} + uses: ncipollo/release-action@v1 + with: + tag: v${{ steps.release-vars.outputs.VERSION }} + commit: main + name: Release ${{ steps.release-vars.outputs.VERSION }} + generateReleaseNotes: true + token: ${{ secrets.GITHUB_TOKEN }} + skipIfReleaseExists: true