diff --git a/.github/workflows/publish-undici-types.yml b/.github/workflows/publish-undici-types.yml deleted file mode 100644 index 5ebbce01505..00000000000 --- a/.github/workflows/publish-undici-types.yml +++ /dev/null @@ -1,26 +0,0 @@ -name: Publish undici-types - -on: - push: - tags: - - 'v*' - workflow_dispatch: - -permissions: - contents: read - -jobs: - publish: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 - - uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 - with: - node-version: lts/* - registry-url: 'https://registry.npmjs.org' - - run: npm install - - run: node scripts/generate-undici-types-package-json.js - - run: npm publish - working-directory: './types' - env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/.github/workflows/release-create-pr.yml b/.github/workflows/release-create-pr.yml new file mode 100644 index 00000000000..98ac1e68c88 --- /dev/null +++ b/.github/workflows/release-create-pr.yml @@ -0,0 +1,57 @@ +name: Create release PR + +permissions: + contents: read + +on: + workflow_dispatch: + inputs: + version: + description: 'The version number to release (has priority over release_type)' + type: string + release_type: + description: Type of release + type: choice + default: patch + options: + - patch + - minor + - major + +jobs: + create-pr: + runs-on: ubuntu-latest + + permissions: + contents: write + pull-requests: write + + outputs: + version: ${{ steps.bump.outputs.version }} + + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: '20' + - name: Git Config + run: | + git config --global user.email "github-actions@github.com" + git config --global user.name "github-actions" + - name: Change version number and push + id: bump + run: | + npm version ${{ inputs.version || inputs.release_type }} --git-tag-version=false + VERSION=`jq -r ".version" package.json` + RELEASE_BRANCH="release/v$VERSION" + git add -u + git commit -m "Bumped v$VERSION" + git push origin "HEAD:$RELEASE_BRANCH" + echo "version=$VERSION" >> $GITHUB_OUTPUT + - name: Create PR + uses: actions/github-script@v7 + with: + script: | + const defaultBranch = "${{ github.event.repository.default_branch }}" + const versionTag = "v${{ steps.bump.outputs.version }}" + await require('./scripts/release').generatePr({ github, context, defaultBranch, versionTag }) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000000..bb21a930754 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,72 @@ +name: Create release + +on: + push: + branches: + - main + paths: + - package.json + +permissions: + contents: read + +jobs: + check-release-version: + runs-on: ubuntu-latest + outputs: + release-version: ${{ steps.set-release-version.outputs.result }} + steps: + - uses: actions/checkout@v4 + - uses: actions/github-script@v7 + id: set-release-version + with: + result-encoding: string + script: | + const { owner, repo } = context.repo + const version = require("./package.json").version + const versionTag = `v${version}` + + const { data: releases } = await github.rest.repos.listReleases({ + owner, + repo + }) + + if (versionTag !== releases[0]?.tag_name) { + return versionTag + } + + release: + runs-on: ubuntu-latest + needs: check-release-version + if: ${{ startsWith(needs.check-release-version.outputs.release-version, 'v') }} + + permissions: + contents: write + id-token: write + + environment: release + + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: '20' + registry-url: 'https://registry.npmjs.org' + - run: npm install -g npm@latest + - run: npm install + - name: Create NPM release + run: npm publish --provenance --access public + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + - run: node scripts/generate-undici-types-package-json.js + - run: npm publish --provenance + working-directory: './types' + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + - name: Create GitHub release + uses: actions/github-script@v7 + with: + script: | + const defaultBranch = "${{ github.event.repository.default_branch }}" + const versionTag = "${{ needs.check-release-version.outputs.release-version }}" + await require('./scripts/release').release({ github, context, defaultBranch, versionTag }) \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7a910263bd1..08d81670b73 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -5,6 +5,7 @@ * [Lint](#lint) * [Test](#test) * [Coverage](#coverage) + * [Releases](#releases) * [Update `WPTs`](#update-wpts) * [Building for externally shared node builtins](#external-builds) * [Developer's Certificate of Origin 1.1](#developers-certificate-of-origin) @@ -166,6 +167,12 @@ npm run test npm run coverage ``` + +### Issuing Releases + +Release is automatic on commit to main which bumps the package.json version field. +Use the "Create release PR" github action to generate a release PR. + ### Building for externally shared node builtins diff --git a/scripts/release.js b/scripts/release.js new file mode 100644 index 00000000000..b901f8854e7 --- /dev/null +++ b/scripts/release.js @@ -0,0 +1,71 @@ +'use strict' + +// Called from .github/workflows + +const generateReleaseNotes = async ({ github, owner, repo, versionTag, defaultBranch }) => { + const { data: releases } = await github.rest.repos.listReleases({ + owner, + repo + }) + + const { data: { body } } = await github.rest.repos.generateReleaseNotes({ + owner, + repo, + tag_name: versionTag, + target_commitish: defaultBranch, + previous_tag_name: releases[0]?.tag_name + }) + + const bodyWithoutReleasePr = body.split('\n') + .filter((line) => !line.includes('[Release] v')) + .join('\n') + + return bodyWithoutReleasePr +} + +const generatePr = async ({ github, context, defaultBranch, versionTag }) => { + const { owner, repo } = context.repo + const releaseNotes = await generateReleaseNotes({ github, owner, repo, versionTag, defaultBranch }) + + await github.rest.pulls.create({ + owner, + repo, + head: `release/${versionTag}`, + base: defaultBranch, + title: `[Release] ${versionTag}`, + body: releaseNotes + }) +} + +const release = async ({ github, context, defaultBranch, versionTag }) => { + const { owner, repo } = context.repo + const releaseNotes = await generateReleaseNotes({ github, owner, repo, versionTag, defaultBranch }) + + await github.rest.repos.createRelease({ + owner, + repo, + tag_name: versionTag, + target_commitish: defaultBranch, + name: versionTag, + body: releaseNotes, + draft: false, + prerelease: false, + generate_release_notes: false + }) + + try { + await github.rest.git.deleteRef({ + owner, + repo, + ref: `heads/release/${versionTag}` + }) + } catch (err) { + console.log("Couldn't delete release PR ref") + console.log(err) + } +} + +module.exports = { + generatePr, + release +}