Skip to content

[fix](ci) Accept a hyphen in the PR title, and stop ignoring .github - #67491

Merged
hello-stephen merged 2 commits into
apache:masterfrom
morningman:fix-title-checker-hyphen
Sep 3, 2026
Merged

[fix](ci) Accept a hyphen in the PR title, and stop ignoring .github#67491
hello-stephen merged 2 commits into
apache:masterfrom
morningman:fix-title-checker-hyphen

Conversation

@morningman

@morningmanmorningman commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

What problem does this PR solve?

Issue Number: close #xxx

Related PR: #66957 (introduced the title-checker regression), #67487 (a PR currently blocked by it)

Problem Summary:

Two independent bugs in the repository's .github tooling, both of which make it easy to get a PR wrong for reasons unrelated to its content.


1. The PR title checker rejects any hyphen in the type or scope

[fix](arrow-flight) ...
[feature](inverted-index) ...
[improvement](github-actions) ...

88 of the last 1500 commits on master use such a title, including #66957 itself — the change that introduced the current check. Its own title, [improvement](github-actions) Reduce redundant GitHub Actions runs and checkouts, would not pass the checker it added.

Root cause.#66957 replaced the deepakputhraya/action-pr-title action with an inline grep -qE and kept the action's regex verbatim, with the comment "Same regex as the previously used ... submodule". But that action is JavaScript, where \- inside a character class is a valid escape for a literal hyphen. POSIX ERE has no such escape — a backslash inside a bracket expression is just a backslash. So

[a-zA-Z0-9 \-_]

does not mean "letters, digits, space, hyphen, underscore". It makes \ a member and then reads -_ as a range endpoint, which leaves the hyphen itself out of the set. Porting the pattern from JS to grep silently changed its meaning.

The fix puts the literal hyphen last in the bracket expression, which is how POSIX spells it:

-if ! grep -qE '\[([a-zA-Z0-9 \-_])+\]\(([a-zA-Z0-9 \-_])+\)(.*)' <<< "${TITLE}"; then+if ! grep -qE '\[([a-zA-Z0-9 _-])+\]\(([a-zA-Z0-9 _-])+\)(.*)' <<< "${TITLE}"; then

A comment now records why the JS form cannot be restored verbatim, so the pattern is not "fixed back" later.

2. .gitignore ignores .github

.gitignore has had a bare .github entry under its # other section since 9b5a464 ([Feature][external catalog/lakesoul] support lakesoul catalog, #32164) — a change that otherwise has nothing to do with CI and touched only those two .gitignore lines, so it looks accidental.

The existing files under .github survived only because they were already tracked when the entry was added; .gitignore does not affect tracked files. The entry therefore has no useful effect today, and two harmful ones:

  • Any new file under .github — a workflow, an action, CODEOWNERS, an issue template — is silently ignored. git status does not list it and git add refuses it without -f, so it is easy to open a PR that is missing it.
  • Even for a tracked file, git add .github/workflows/foo.yml prints The following paths are ignored by one of your .gitignore files and exits non-zero, which breaks git add ... && git commit ... in scripts. This happened while preparing this very PR.
$ git check-ignore -v --no-index .github/workflows/new-thing.yml
.gitignore:155:.github .github/workflows/new-thing.yml

Release note

None

Check List (For Author)

  • Test
    • Regression test
    • Unit Test
    • Manual test (add detailed scripts or steps below)
    • No need to test or manual test. Explain why:
      • This is a refactor/code format and no logic has been changed.
      • Previous test can cover this change.
      • No code files have been changed.
      • Other reason: one workflow regex and one .gitignore line; both are exercised directly, see the evidence below.

Title checker. The old expression is not merely permissive-but-wrong — BSD grep rejects it outright:

$ /usr/bin/grep -qE '\[([a-zA-Z0-9 \-_])+\]\(([a-zA-Z0-9 \-_])+\)(.*)' <<< '[fix](arrow-flight) x'
grep: invalid character range

Old vs new, on the two grep implementations available to me locally:

Titleold (BSD)new (BSD)old (ugrep)new (ugrep)
[fix](arrow-flight) point queryFAILPASSPASSPASS
[fix](arrow-flight-sql) xFAILPASSPASSPASS
[improvement](github-actions) Reduce redundant runsFAILPASSPASSPASS
[fix](arrow_flight) underscoreFAILPASSPASSPASS
[fix](nereids) plainFAILPASSPASSPASS
[opt](build) Decouple status.hFAILPASSPASSPASS
no bracketsFAILFAILFAILFAIL
[fix] no scopeFAILFAILFAILFAIL

The last two rows are the point: the check is not weakened, malformed titles are still rejected.

I do not have GNU grep on this machine, so I did not run the new expression under it. GNU grep rejecting the old expression is directly observed — it is what the failing Check PR title run on #67487 shows. That the new expression works there follows from the POSIX rule the fix relies on: a hyphen last in a bracket expression is a literal hyphen on any conforming engine, and GNU grep documents this explicitly.

Note this PR's own title deliberately uses (ci) rather than a hyphenated scope, because a fork PR is checked by the workflow on the base branch — that is, the broken one this PR fixes.

.gitignore. Removing the entry exposes no untracked files; git status --untracked-files=all .github/ is empty afterwards. The 33 tracked entries under .github are unchanged (the 5 that have no file on disk are uninitialised submodule gitlinks, unrelated to this change).

🤖 Generated with Claude Code

https://claude.ai/code/session_017omcvWDsxEc9AyU83aBZ2g

The PR title checker rejects every title whose type or scope contains a hyphen,
for example:
[fix](arrow-flight) ...
[feature](inverted-index) ...
[improvement](github-actions) ...
88 of the last 1500 commits on master use such a title, including apache#66957, the
change that introduced the current check -- its own title
`[improvement](github-actions) Reduce redundant GitHub Actions runs and checkouts`
would not pass the checker it added.
apache#66957 replaced the deepakputhraya/action-pr-title action with an inline
`grep -qE` and kept the action's regex verbatim. That action is JavaScript,
where `\-` inside a character class is a valid escape for a literal hyphen.
POSIX ERE has no such escape: a backslash inside a bracket expression is just a
backslash. So `[a-zA-Z0-9 \-_]` does not mean "... space, hyphen, underscore" --
it makes `\` a member and reads `-_` as a range endpoint, leaving the hyphen
itself out of the set. The port silently changed the meaning.
Put the literal hyphen last in the bracket expression instead, which is how
POSIX spells it.
Verified: the old expression is not merely permissive-but-wrong, BSD grep
rejects it outright with `invalid character range`. With the new expression both
grep implementations available locally accept every valid title above and still
reject `no brackets` and `[fix] no scope`, so the check is not weakened. GNU
grep rejecting the old expression is what the failing run on apache#67487 shows.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017omcvWDsxEc9AyU83aBZ2g
@hello-stephen

Copy link
Copy Markdown
Contributor

Thank you for your contribution to Apache Doris.
Don't know what should be done next? See How to process your PR.

Please clearly describe your PR:

  1. What problem was fixed (it's best to include specific error reporting information). How it was fixed.
  2. Which behaviors were modified. What was the previous behavior, what is it now, why was it modified, and what possible impacts might there be.
  3. What features were added. Why was this function added?
  4. Which code was refactored and why was this part of the code refactored?
  5. Which functions were optimized and what is the difference before and after the optimization?

`.gitignore` has had a bare `.github` entry under its "# other" section since
9b5a464 ("[Feature][external catalog/lakesoul] support lakesoul catalog",
apache#32164), a change that otherwise has nothing to do with CI and touched only
those two .gitignore lines -- it looks accidental.
The existing files under .github survived only because they were already
tracked when the entry was added; .gitignore does not affect tracked files. The
entry therefore has no useful effect today, and two harmful ones:
* Any NEW file under .github -- a workflow, an action, CODEOWNERS, an issue
template -- is silently ignored. `git status` does not list it and `git add`
refuses it without -f, so it is easy to open a PR that is missing it.
* Even for a tracked file, `git add .github/workflows/foo.yml` prints
"The following paths are ignored by one of your .gitignore files" and exits
non-zero, which breaks `git add ... && git commit ...` in scripts.
Removing the entry exposes no untracked files: `git status --untracked-files=all
.github/` is empty afterwards.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017omcvWDsxEc9AyU83aBZ2g
@morningmanmorningman changed the title [fix](ci) Accept a hyphen in the PR title type and scope again[fix](ci) Accept a hyphen in the PR title, and stop ignoring .githubSep 3, 2026
@morningman

Copy link
Copy Markdown
ContributorAuthor

skip buildall

@hello-stephen
hello-stephen merged commit b514878 into apache:masterSep 3, 2026
32 of 33 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@morningman@hello-stephen