Automate issue status through a project board - #62
Merged
Conversation
Editing an already merged pull request's title or body could pull an issue backwards out of Ready for release, since the pull request job had no allowed-current guard on its non-closed path. Add one. Also skip the job entirely for fork-originated pull requests, since GitHub withholds secrets from those runs and the first API call would fail for a reason the contributor cannot fix. Scope GITHUB_TOKEN permissions down to none, since this workflow only ever uses the PAT in GH_TOKEN. And track failures across the issue loop instead of letting only the last invocation decide the step's exit code.
Down-merges from master to dev can repeat a Closes reference for an issue that already shipped; the merged-into-dev path now allows every status except Done, so a merge still beats an earlier state without dragging a shipped issue backwards. An empty PREV_TAG with tags already present in the repository now fails fast instead of silently scanning all of history and closing every issue any pull request ever referenced; a genuine first release with no tags at all is unaffected. A branch deletion after its pull request merges can fire a push event with no branch left to check out; the push job now skips deleted refs.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #60.
Problem
Issues here carry no state between open and closed, and nothing closes on its
own. The cause of the second half is specific and was verified rather than
assumed: GitHub honors
Closes #NNonly when the pull request merges into therepository's default branch. The default is
master, every feature pull requesttargets
dev, so the keyword never fires. Promotingdevtomasterdoes notfire it either, because the promotion's merge commit carries no keyword.
closedByPullRequestsReferencesis empty for #36, #48, #49 and #53, every one ofwhich has a merged pull request whose body says
Closes #NN. GitHub records noclosing link at all for a pull request based on a non-default branch, so this
automation owns the link and parses pull request bodies itself.
What this adds
An organization project with a Status field, and the automation that drives it.
issue-<number>-<slug>branchIn progress, only fromTodoor unsetIn review, only fromTodo, unset orIn progressdevReady for release, from any state exceptDonemasterDoneClosing by containment
An issue closes when the commit that fixed it is contained in the commit being
released, rather than by clearing the
Ready for releasecolumn. An immediatefix branched off
masterships a release whiledevstill holds merged work,and clearing the column would close issues that are not in the shipped jar.
dev,devpromoted tomasterdev, an immediate fix released insteadReady for releasemasterReady for releaseThe third row is why containment is the rule rather than a patch on top of column
scanning. An immediate fix never passes through
dev, so theReady for releaseautomation never touches it, and containment still closes it.
PREV_TAGis captured beforeCommit and tagcreates the new tag. Takenafterwards the range is empty and nothing closes.
Files
scripts/issue-status.sh-closing_refsandpr_numbers_from_log, pure texthandling, no network.
scripts/test-issue-status.sh- their contract, wired intotests.yml.scripts/set-issue-status.sh- the Projects v2 writes.scripts/close-released-issues.sh- called from a new final step inrelease.yml..github/workflows/issue-status.yml- the push and pull request triggers.release.ymlgainsissues: write,pull-requests: read, the tag capture,the closing step, and a dry-run preview.
Notes for review
Several decisions are worth knowing about rather than rediscovering.
jqanywhere. It is not installed on the developmentmachine, which is why
test-publish-bukkitdev.shskips locally. Everythinguses
gh's built-in--jq, so all of it runs locally as well as in CI.closing step runs after publishing. If every pull request lookup fails, or any
issue fails to close, the step goes red. A green step that closed nothing would
never be noticed, and there is no rerun because the release is spent.
editedis guarded. Editing the title of an already merged pull requestfires
edited. Without a guard that wroteIn reviewand dragged the issueback out of
Ready for release.Done. A down-merge pull request repeating aCloses #NNwould otherwise move an already shipped, already closed issue backto
Ready for releasepermanently, since the release step skips issues thatare not open.
PREV_TAGaborts when the repository has tags, rather thanscanning all history and closing every issue ever referenced. A genuine first
release, with no tags at all, still scans full history.
and the job would redden every external contribution.
${{ }}appears inside anyrun:block.GH_TOKENuses the existingRELEASE_TOKEN, which needs theprojectscope.GITHUB_TOKENcannot reach an organization project.Two conventions are now load bearing
Recorded in CLAUDE.md. Branch names must be
issue-<number>-<slug>, and a pullrequest body must name every issue it resolves with a closing keyword. Rebase
merging must also stay disabled, because a rebase merge leaves no pull request
number in any commit subject and the release would close nothing, silently.
Tests
mvn -B test: 385 tests, Failures 0, Errors 0, Skipped 0.test-issue-status.sh: 35 passed, 0 failed. New file, 20 test functions.test-require-changeset.sh: 35 passed, 0 failed.test-release.sh: 49 passed,0 failed. Both unchanged.
Every guard in the new suite was verified by breaking it and confirming the
matching test fails, then restoring, per this repository's convention. The
parsers were also checked against real history:
pr_numbers_from_logreturns31overv1.2.2..origin/master, andclosing_refsover #58's body returnsexactly
53, not the 41, 46 and 56 it mentions in passing.No changeset: everything here is under
.github/orscripts/, both exempt.Not yet verified
The end to end path has never run on GitHub. Opening this pull request is the
first live exercise of the trigger. Still unproven: the Release workflow's
closing step against a real promotion, and whether
RELEASE_TOKENcarries theprojectscope, which will show as a loud failure on the first board write if itdoes not.
Infra-only work never closes at all, since it ships nothing to server owners. This
issue will sit at
Ready for releaseuntil an unrelated release runs, and thesame is true of every docs and CI issue. No transition covers that case.