From b2c142465252998361c5844a31a08de0001467ea Mon Sep 17 00:00:00 2001 From: Benny Powers Date: Thu, 14 Mar 2024 14:42:41 +0200 Subject: [PATCH 1/3] chore: changeset validating github action --- .github/workflows/validate-pr.yml | 25 +++++++++++++ scripts/validate-prs.js | 59 +++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+) create mode 100644 .github/workflows/validate-pr.yml create mode 100644 scripts/validate-prs.js diff --git a/.github/workflows/validate-pr.yml b/.github/workflows/validate-pr.yml new file mode 100644 index 0000000000..3bb682f067 --- /dev/null +++ b/.github/workflows/validate-pr.yml @@ -0,0 +1,25 @@ +name: Validate PRs + +on: + pull_request: + types: + - opened + - reopened + - synchronize + - edited + +jobs: + validate: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: '20' + cache: 'npm' + - run: npm i semantic-release @changesets/read --prefer-offline + - uses: actions/github-script@v7 + with: + script: | + const { validate } = await import('${{ github.workspace }}/scripts/validate-prs.js'); + await validate({ context }); diff --git a/scripts/validate-prs.js b/scripts/validate-prs.js new file mode 100644 index 0000000000..8e887dd226 --- /dev/null +++ b/scripts/validate-prs.js @@ -0,0 +1,59 @@ +/* eslint-env node */ +import { default as read } from '@changesets/read'; +import semanticRelease from 'semantic-release'; + +/** @typedef {'major'|'minor'|'patch'} ReleaseType */ + +async function getReleaseType(title, mergeType) { + if (mergeType === 'rebase') { + const result = await semanticRelease({ + dryRun: true, + branches: ['main'], + }) ?? {}; + /** @type {ReleaseType} */ + const type = result?.nextRelease?.type; + return type; + } else { + const { commitType, bang } = title.match(/(?feat|fix|chore|docs|style).*(?!)/)?.groups ?? {}; + if (bang) { + return 'major'; + } else { + switch (commitType) { + case 'feat': return 'minor'; + case 'fix': return 'patch'; + } + } + } +} + +export async function validate({ context }) { + const { base, title, auto_merge: autoMerge } = context.payload.pull_request; + + if (base.ref.startsWith('staging/')) { + return true; + } + + const type = await getReleaseType(title, autoMerge?.merge_method) ?? null; + const sets = await read(process.cwd()); + + /** @type {ReleaseType} */ + const release = sets.reduce((greatest, type) => { + switch (greatest) { + case null: + case 'major': + return greatest; + case 'minor': + return type === 'major' ? type : greatest; + case 'patch': + return (type === 'major' || type === 'minor') ? type : greatest; + } + }, null); + + if (!release && type?.match(/m(aj|in)or/)) { + throw new Error(`PR conventional commit title has type (${type}) but no changesets were detected.`); + } + + if (type !== release) { + throw new Error(`PR conventional commit title type (${type}) does not match release type (${release}).`); + } +} From 96d483e9282e490fa69b98d0703e087420994f9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benny=20Powers=20-=20=D7=A2=D7=9D=20=D7=99=D7=A9=D7=A8?= =?UTF-8?q?=D7=90=D7=9C=20=D7=97=D7=99!?= Date: Fri, 15 Mar 2024 09:24:06 +0200 Subject: [PATCH 2/3] test: Create metal-birds-share.md --- .changeset/metal-birds-share.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/metal-birds-share.md diff --git a/.changeset/metal-birds-share.md b/.changeset/metal-birds-share.md new file mode 100644 index 0000000000..5dde4075a1 --- /dev/null +++ b/.changeset/metal-birds-share.md @@ -0,0 +1,5 @@ +--- +"@rhds/elements": major +--- +DELETE THIS FILE BEFORE MERGING +chore: changeset validating github action From 5ac26cdd0a81ed47f643ff65db86b01f9daa3f85 Mon Sep 17 00:00:00 2001 From: Benny Powers Date: Wed, 8 May 2024 12:45:31 +0300 Subject: [PATCH 3/3] style: lint --- scripts/validate-prs.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/scripts/validate-prs.js b/scripts/validate-prs.js index 8e887dd226..2ad2fa5b21 100644 --- a/scripts/validate-prs.js +++ b/scripts/validate-prs.js @@ -1,9 +1,17 @@ -/* eslint-env node */ import { default as read } from '@changesets/read'; import semanticRelease from 'semantic-release'; /** @typedef {'major'|'minor'|'patch'} ReleaseType */ +/** + * named capture group 1 `commitType`: + * > Either `feat`, `fix`, `chore`, `docs`, or `style` + * **ANY** (_>= 0x_) + * named capture group 2 `bang`: + * > `!` + */ +const COMMIT_TYPE_RE = /(?feat|fix|chore|docs|style).*(?!)/; + async function getReleaseType(title, mergeType) { if (mergeType === 'rebase') { const result = await semanticRelease({ @@ -14,7 +22,7 @@ async function getReleaseType(title, mergeType) { const type = result?.nextRelease?.type; return type; } else { - const { commitType, bang } = title.match(/(?feat|fix|chore|docs|style).*(?!)/)?.groups ?? {}; + const { commitType, bang } = title.match(COMMIT_TYPE_RE)?.groups ?? {}; if (bang) { return 'major'; } else {