From 4689ee011943a625315bda71b160288a5fb3fc1b Mon Sep 17 00:00:00 2001 From: Snailedlt Date: Wed, 28 Oct 2026 10:11:59 +0100 Subject: [PATCH 1/2] ci: check PR titles with a workflow instead of the semantic-prs app The Ezard/semantic-prs app parses titles as conventional commits, whose types are one word, so the configured 'new icon' / 'update icon' types never match and every icon PR fails 'Semantic PR'. pr_title.yml checks the same rules (plus the icon formats) with a few lines of github-script, keeps the check name, and explains the expected format in the job summary. semantic.yml now only turns the app off. Co-Authored-By: Claude Opus 5.5 --- .github/semantic.yml | 28 +++++-------------------- .github/workflows/pr_title.yml | 38 ++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 23 deletions(-) create mode 100644 .github/workflows/pr_title.yml diff --git a/.github/semantic.yml b/.github/semantic.yml index 62ebf5e07..b58a1136f 100644 --- a/.github/semantic.yml +++ b/.github/semantic.yml @@ -1,24 +1,6 @@ --- -# This is the configuration file for https://github.com/Ezard/semantic-prs - -enabled: true -titleOnly: true # We only use the PR title as we squash and merge -commitsOnly: false -titleAndCommits: false -anyCommit: false -allowMergeCommits: false -allowRevertCommits: true -targetUrl: 'https://github.com/devicons/devicon/blob/master/.github/semantic.yml' -types: - # default conventional commit types (for non icon PRs) - - 'chore' - - 'ci' - - 'docs' - - 'feat' - - 'fix' - - 'refactor' - - 'style' - - 'test' - # custom devicon types (for icon PRs) - - 'new icon' - - 'update icon' +# Configuration for the Ezard/semantic-prs GitHub App, which is turned off: +# its parser only accepts one-word types, so "new icon:" / "update icon:" +# titles always failed. PR titles are checked by .github/workflows/pr_title.yml +# instead. Once the app is uninstalled, this file can be deleted. +enabled: false diff --git a/.github/workflows/pr_title.yml b/.github/workflows/pr_title.yml new file mode 100644 index 000000000..bb0883723 --- /dev/null +++ b/.github/workflows/pr_title.yml @@ -0,0 +1,38 @@ +name: PR Title +# PRs are squash-merged, so the PR title becomes the commit message +# and a line in the release notes. This checks it follows the format: +# new icon: () update icon: () +# [(scope)][!]: (conventional commits, for everything else) +# Fixing the title re-runs this check. +# +# Replaces the Ezard/semantic-prs app (see .github/semantic.yml): its parser +# only accepts one-word types, so it failed every "new icon:" title. +on: + pull_request: + types: [opened, edited, synchronize, reopened] + +permissions: {} + +jobs: + title: + name: Semantic PR + runs-on: ubuntu-24.04 + steps: + - uses: actions/github-script@v8 + with: + script: | + const title = context.payload.pull_request.title; + const types = ['new icon', 'update icon', 'feat', 'fix', 'docs', 'chore', 'ci', 'refactor', 'style', 'test', 'perf', 'build', 'revert']; + const conventional = new RegExp(`^(${types.join('|')})(\\([\\w$.\\-*/ ,]+\\))?!?: \\S`); + if (conventional.test(title) || /^Revert ".+"$/.test(title)) { + core.info(`"${title}" is a valid PR title.`); + return; + } + core.summary.addRaw([ + `"${title}" isn't a valid PR title. The title becomes the commit message and the release-notes line, so it needs one of these formats:`, + '', + '- `new icon: ()` or `update icon: ()`, e.g. `new icon: vlang (original, plain)`', + `- \`: \`, e.g. \`fix: correct the jekyll wordmark\`, where type is one of ${types.slice(2).map(t => '`' + t + '`').join(', ')}`, + ].join('\n')); + await core.summary.write(); + core.setFailed(`Invalid PR title: "${title}". See the job summary for the expected format.`); From 30e48e1964fd191b23d1131e5c1f8d800e84a060 Mon Sep 17 00:00:00 2001 From: Snailedlt Date: Wed, 28 Oct 2026 14:09:43 +0100 Subject: [PATCH 2/2] ci: address review of the PR title check - delete semantic.yml (the app gets uninstalled) - run on ubuntu-latest - pin github-script to a commit SHA - show the title as text in the job summary - drop the history comment Co-Authored-By: Claude Opus 5.5 --- .github/semantic.yml | 6 ------ .github/workflows/pr_title.yml | 11 +++++------ 2 files changed, 5 insertions(+), 12 deletions(-) delete mode 100644 .github/semantic.yml diff --git a/.github/semantic.yml b/.github/semantic.yml deleted file mode 100644 index b58a1136f..000000000 --- a/.github/semantic.yml +++ /dev/null @@ -1,6 +0,0 @@ ---- -# Configuration for the Ezard/semantic-prs GitHub App, which is turned off: -# its parser only accepts one-word types, so "new icon:" / "update icon:" -# titles always failed. PR titles are checked by .github/workflows/pr_title.yml -# instead. Once the app is uninstalled, this file can be deleted. -enabled: false diff --git a/.github/workflows/pr_title.yml b/.github/workflows/pr_title.yml index bb0883723..927048cf8 100644 --- a/.github/workflows/pr_title.yml +++ b/.github/workflows/pr_title.yml @@ -4,9 +4,6 @@ name: PR Title # new icon: () update icon: () # [(scope)][!]: (conventional commits, for everything else) # Fixing the title re-runs this check. -# -# Replaces the Ezard/semantic-prs app (see .github/semantic.yml): its parser -# only accepts one-word types, so it failed every "new icon:" title. on: pull_request: types: [opened, edited, synchronize, reopened] @@ -16,9 +13,9 @@ permissions: {} jobs: title: name: Semantic PR - runs-on: ubuntu-24.04 + runs-on: ubuntu-latest steps: - - uses: actions/github-script@v8 + - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 with: script: | const title = context.payload.pull_request.title; @@ -28,8 +25,10 @@ jobs: core.info(`"${title}" is a valid PR title.`); return; } + // The summary is rendered as HTML/Markdown: show the title as text + const shown = title.replace(/[<>&`]/g, c => `&#${c.charCodeAt(0)};`); core.summary.addRaw([ - `"${title}" isn't a valid PR title. The title becomes the commit message and the release-notes line, so it needs one of these formats:`, + `"${shown}" isn't a valid PR title. The title becomes the commit message and the release-notes line, so it needs one of these formats:`, '', '- `new icon: ()` or `update icon: ()`, e.g. `new icon: vlang (original, plain)`', `- \`: \`, e.g. \`fix: correct the jekyll wordmark\`, where type is one of ${types.slice(2).map(t => '`' + t + '`').join(', ')}`,