This is an automated email from the ASF dual-hosted git repository.
janhoy pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/solr.git
The following commit(s) were added to refs/heads/main by this push:
new 52fba69a4eb Better handling of "no-changelog" label (#4941)
52fba69a4eb is described below
commit 52fba69a4eb3c3787680605cef83d1c5458e1e3c
Author: Jan Høydahl <[email protected]>
AuthorDate: Wed Oct 7 15:10:19 2026 +0200
Better handling of "no-changelog" label (#4941)
Co-authored-by: Eric Pugh <[email protected]>
Co-authored-by: David Smiley <[email protected]>
---
.github/workflows/changelog-comment-command.yml | 150 ++++++++++++++++++++++++
.github/workflows/validate-changelog.yml | 35 ++++--
dev-docs/changelog.adoc | 19 ++-
3 files changed, 194 insertions(+), 10 deletions(-)
diff --git a/.github/workflows/changelog-comment-command.yml
b/.github/workflows/changelog-comment-command.yml
new file mode 100644
index 00000000000..4d227b9f8cc
--- /dev/null
+++ b/.github/workflows/changelog-comment-command.yml
@@ -0,0 +1,150 @@
+name: Changelog Comment Commands
+
+# Lets a PR author (or any committer) toggle the 'no-changelog' label by
+# commenting on the PR, without needing the repo write access that setting a
+# label directly requires:
+#
+# /no-changelog - mark the PR as not needing a changelog entry
+# /needs-changelog - undo that
+#
+# The command applies/removes the label, and the label change re-triggers the
+# "Validate Changelog" workflow (which listens for labeled/unlabeled events),
+# so the red check turns green on its own.
+#
+# This workflow deliberately never checks out or executes PR code: it only
+# reads the comment payload and calls the labels API, so running with a
+# write-scoped token is safe.
+on:
+ issue_comment:
+ types:
+ - created
+
+concurrency:
+ group: changelog-comment-command-${{ github.event.issue.number }}
+
+permissions:
+ issues: write # add/remove labels, add a reaction, post a reply
+ pull-requests: write
+
+jobs:
+ handle-command:
+ # Pull request comments only, from a human, that mention one of our
commands.
+ # The exact (line-anchored) match happens in the parse step below.
+ if: |
+ github.event.issue.pull_request &&
+ github.event.comment.user.type != 'Bot' &&
+ (contains(github.event.comment.body, '/no-changelog') ||
+ contains(github.event.comment.body, '/needs-changelog'))
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Parse command
+ id: parse
+ env:
+ BODY: ${{ github.event.comment.body }}
+ run: |
+ set -euo pipefail
+ # A command must be alone on its own line, so that quoted text (">
/no-changelog")
+ # or a mention inside a sentence does not trigger it.
+ command=none
+ if printf '%s\n' "$BODY" | grep -iE
'^[[:space:]]*/no-changelog[[:space:]]*\r?$' > /dev/null; then
+ command=add
+ fi
+ if printf '%s\n' "$BODY" | grep -iE
'^[[:space:]]*/needs-changelog[[:space:]]*\r?$' > /dev/null; then
+ if [ "$command" = "add" ]; then
+ command=conflict
+ else
+ command=remove
+ fi
+ fi
+ echo "command=${command}" >> "$GITHUB_OUTPUT"
+ echo "Parsed command: ${command}"
+
+ - name: Check who is asking
+ id: authz
+ if: steps.parse.outputs.command != 'none'
+ env:
+ COMMENTER: ${{ github.event.comment.user.login }}
+ PR_AUTHOR: ${{ github.event.issue.user.login }}
+ ASSOCIATION: ${{ github.event.comment.author_association }}
+ run: |
+ set -euo pipefail
+ # The PR author may always toggle it on their own PR; otherwise the
+ # commenter needs write access, which author_association reflects.
+ if [ "$COMMENTER" = "$PR_AUTHOR" ]; then
+ allowed=true
+ else
+ case "$ASSOCIATION" in
+ OWNER|MEMBER|COLLABORATOR) allowed=true ;;
+ *) allowed=false ;;
+ esac
+ fi
+ echo "allowed=${allowed}" >> "$GITHUB_OUTPUT"
+ echo "Commenter '${COMMENTER}' (${ASSOCIATION}) allowed: ${allowed}"
+
+ - name: Apply label change
+ if: steps.authz.outputs.allowed == 'true' &&
(steps.parse.outputs.command == 'add' || steps.parse.outputs.command ==
'remove')
+ env:
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ REPO: ${{ github.repository }}
+ PR_NUMBER: ${{ github.event.issue.number }}
+ COMMAND: ${{ steps.parse.outputs.command }}
+ run: |
+ set -euo pipefail
+ LABEL=no-changelog
+
+ has_label=$(gh api "repos/${REPO}/issues/${PR_NUMBER}/labels" \
+ --jq "[.[].name] | index(\"${LABEL}\") != null")
+
+ if [ "$COMMAND" = "remove" ]; then
+ if [ "$has_label" = "true" ]; then
+ gh api -X DELETE
"repos/${REPO}/issues/${PR_NUMBER}/labels/${LABEL}" --silent
+ echo "::notice::Removed '${LABEL}' from PR#${PR_NUMBER}"
+ else
+ echo "::notice::PR#${PR_NUMBER} does not have '${LABEL}';
nothing to do"
+ fi
+ exit 0
+ fi
+
+ if [ "$has_label" = "true" ]; then
+ echo "::notice::PR#${PR_NUMBER} already has '${LABEL}'; nothing to
do"
+ exit 0
+ fi
+ gh api -X POST "repos/${REPO}/issues/${PR_NUMBER}/labels" -f
"labels[]=${LABEL}" --silent
+ echo "::notice::Added '${LABEL}' to PR#${PR_NUMBER}"
+
+ - name: Acknowledge with a reaction
+ if: steps.authz.outputs.allowed == 'true' &&
(steps.parse.outputs.command == 'add' || steps.parse.outputs.command ==
'remove')
+ continue-on-error: true
+ env:
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ REPO: ${{ github.repository }}
+ COMMENT_ID: ${{ github.event.comment.id }}
+ run: |
+ gh api -X POST
"repos/${REPO}/issues/comments/${COMMENT_ID}/reactions" \
+ -f content='+1' --silent
+
+ - name: Explain why the command was not applied
+ if: |
+ steps.parse.outputs.command != 'none' &&
+ (steps.authz.outputs.allowed != 'true' ||
steps.parse.outputs.command == 'conflict')
+ env:
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ REPO: ${{ github.repository }}
+ PR_NUMBER: ${{ github.event.issue.number }}
+ ALLOWED: ${{ steps.authz.outputs.allowed }}
+ COMMENTER: ${{ github.event.comment.user.login }}
+ run: |
+ set -euo pipefail
+ if [ "$ALLOWED" != "true" ]; then
+ printf '%s\n' \
+ "Sorry @${COMMENTER}, only the author of this pull request or a
Solr committer" \
+ "can toggle the \`no-changelog\` label. Please ask a committer
to do it for you." \
+ > reply.md
+ else
+ printf '%s\n' \
+ "That comment contains both changelog commands, so nothing was
changed." \
+ "Please comment just one of them, alone on its own line." \
+ > reply.md
+ fi
+ gh pr comment "$PR_NUMBER" --repo "$REPO" --body-file reply.md
diff --git a/.github/workflows/validate-changelog.yml
b/.github/workflows/validate-changelog.yml
index f4adbb83d71..66fcc1c0481 100644
--- a/.github/workflows/validate-changelog.yml
+++ b/.github/workflows/validate-changelog.yml
@@ -2,6 +2,14 @@ name: Validate Changelog
on:
pull_request:
+ # 'labeled'/'unlabeled' are included so that adding or removing the
+ # 'no-changelog' label re-runs this check on an already-failed PR.
+ types:
+ - opened
+ - reopened
+ - synchronize
+ - labeled
+ - unlabeled
branches:
- '*'
@@ -12,8 +20,15 @@ concurrency:
jobs:
validate-changelog:
name: Check changelog entry
- # Skip validation for Renovate PRs (solrbot) - they get changelog entries
automatically
- if: github.event.pull_request.user.login != 'solrbot'
+ # Skip validation for Renovate PRs (solrbot) - they get changelog entries
automatically.
+ # On label events, only re-run for the 'no-changelog' label; other label
churn
+ # (e.g. from the labeler workflow) leaves the existing check result
untouched.
+ if: |
+ github.event.pull_request.user.login != 'solrbot' &&
+ (
+ (github.event.action != 'labeled' && github.event.action !=
'unlabeled') ||
+ github.event.label.name == 'no-changelog'
+ )
runs-on: ubuntu-latest
steps:
@@ -24,12 +39,13 @@ jobs:
- name: Check for no-changelog label
id: check-label
+ env:
+ HAS_NO_CHANGELOG_LABEL: ${{
contains(github.event.pull_request.labels.*.name, 'no-changelog') }}
run: |
- LABELS='${{ toJson(github.event.pull_request.labels) }}'
- if echo "$LABELS" | grep -q '"no-changelog"'; then
- echo "skip=true" >> $GITHUB_OUTPUT
+ if [ "$HAS_NO_CHANGELOG_LABEL" = "true" ]; then
+ echo "skip=true" >> "$GITHUB_OUTPUT"
else
- echo "skip=false" >> $GITHUB_OUTPUT
+ echo "skip=false" >> "$GITHUB_OUTPUT"
fi
- name: Check for changelog entry
@@ -47,8 +63,8 @@ jobs:
# Check if only docs/tests/comments were changed (common exceptions)
HAS_NON_DOCS_CHANGES=false
while IFS= read -r file; do
- # Skip changelog, docs, tests, and certain config files
- if ! echo "$file" | grep -qE
"(^changelog/|^solr/solr-ref-guide/|^dev-docs/|\.md$|\.adoc$|^solr/.*/test|\.gradle$|\.properties$|README|NOTICE|LICENSE)";
then
+ # Skip changelog, docs, tests, CI config and certain config files
+ if ! echo "$file" | grep -qE
"(^changelog/|^solr/solr-ref-guide/|^dev-docs/|^\.github/|\.md$|\.adoc$|^solr/.*/test|\.gradle$|\.properties$|README|NOTICE|LICENSE)";
then
HAS_NON_DOCS_CHANGES=true
break
fi
@@ -69,7 +85,8 @@ jobs:
echo "For more information, see: dev-docs/changelog.adoc"
echo ""
echo "If this PR should not have a changelog entry (e.g., refactoring,
internal cleanup),"
- echo "add the 'no-changelog' label to this PR."
+ echo "comment '/no-changelog' on the PR (or add the 'no-changelog'
label)."
+ echo "This check then re-runs automatically. To undo, comment
'/needs-changelog'."
exit 1
- name: Validate changelog YAML structure
diff --git a/dev-docs/changelog.adoc b/dev-docs/changelog.adoc
index 0a0e9e5a1bd..70536cc0f04 100644
--- a/dev-docs/changelog.adoc
+++ b/dev-docs/changelog.adoc
@@ -127,7 +127,24 @@ The `validate-changelog` GitHub workflow automatically
checks that:
. **CHANGES.txt is not edited directly** - All changes must use the YAML
fragment approach
. **A changelog entry is added** - Code changes must include a corresponding
YAML file in `changelog/unreleased/`
-If your change does not require a changelog entry, it is still possible to
merge the PR.
+Pull requests that only touch documentation, tests, build files or CI
configuration
+are exempt and pass without an entry. Subjectively, some minor changes don't
need one either.
+
+=== 4.1 Opting out of the changelog requirement
+
+If your change does not require a changelog entry (refactoring, internal
cleanup,
+and so on), it is still possible to merge the PR. Mark it by adding the
+`no-changelog` label, which you can set in either of two ways:
+
+* Comment `/no-changelog` on the pull request, alone on its own line. This
works
+ for the PR author as well as for committers, so a contributor without write
+ access does not need to ask anyone to set the label. Comment
`/needs-changelog`
+ to undo it. Use a regular comment in the "Conversation" tab; commands inside
+ code review comments are not picked up.
+* Add or remove the `no-changelog` label directly (committers only).
+
+Either way the check re-runs automatically on the label change, so an already
+failed "Check changelog entry" run turns green without pushing a new commit.
== 5. For release managers