fix(#447): close-labeler strips stale board:* and sets Status directly

The auto-label-closed job only stripped a stale board:* label when
board:done was ALREADY present, so a normal close (no board:done yet)
added board:done and left the prior board:in-progress/todo/testing in
place. The label was also added with GITHUB_TOKEN, which does not cascade
to the sync job, so the project Status field was never updated on close.

Rewrite the close path with actions/github-script + PROJECT_PAT to:
strip every non-board:done board:* label, ensure board:done, and set the
project Status field to Done directly via GraphQL. Enforces the same
board:* mutual exclusivity the labeled path already has.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
pull/454/head
Strycher 2 months ago
parent f935a48ef0
commit ddb2e20448

@ -13,31 +13,82 @@ on:
types: [labeled, unlabeled, closed]
jobs:
# -- Auto-label closed issues board:done (mutual-exclusion cleanup) --
# -- Closed issue -> board:done, strip any stale board:* label, set Status --
#
# The `sync` job below only runs on labeled/unlabeled, and a label added with
# the default GITHUB_TOKEN does not cascade to trigger it. So the close path
# must be self-contained: enforce board:* mutual exclusivity AND set the
# project Status field itself, using PROJECT_PAT (which can write the org
# project). Fixes #447 (closed cards left in their old column).
auto-label-closed:
runs-on: ubuntu-latest
permissions:
issues: write
if: github.event.action == 'closed'
steps:
- name: Auto-label closed issues
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_REPO: ${{ github.repository }}
run: |
labels=$(gh issue view ${{ github.event.issue.number }} --json labels -q '.labels[].name')
if ! echo "$labels" | grep -q "board:done"; then
gh issue edit ${{ github.event.issue.number }} --add-label "board:done"
else
stale=$(echo "$labels" | grep '^board:' | grep -v '^board:done$' || true)
if [ -n "$stale" ]; then
for lbl in $stale; do
gh issue edit ${{ github.event.issue.number }} --remove-label "$lbl"
done
gh issue edit ${{ github.event.issue.number }} --remove-label "board:done"
gh issue edit ${{ github.event.issue.number }} --add-label "board:done"
fi
fi
- name: Mark closed issue Done (strip stale board:*, set Status)
uses: actions/github-script@v7
with:
github-token: ${{ secrets.PROJECT_PAT }}
script: |
const PROJECT_ID = 'PVT_kwDOEXsS3c4BaleY';
const STATUS_FIELD_ID = 'PVTSSF_lADOEXsS3c4BaleYzhVcBOA';
const DONE_OPTION_ID = '72944195'; // board:done
const issueNumber = context.payload.issue.number;
const issueNodeId = context.payload.issue.node_id;
const labels = context.payload.issue.labels.map(l => l.name);
// 1. Strip any board:* label that is not board:done (mutual exclusion).
const stale = labels.filter(l => l.startsWith('board:') && l !== 'board:done');
for (const lbl of stale) {
try {
await github.rest.issues.removeLabel({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issueNumber,
name: lbl,
});
core.info(`Removed stale label: ${lbl}`);
} catch (e) {
core.warning(`Could not remove "${lbl}": ${e.message}`);
}
}
// 2. Ensure board:done is present.
if (!labels.includes('board:done')) {
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issueNumber,
labels: ['board:done'],
});
core.info('Added board:done');
}
// 3. Set the project Status field to Done directly (do not rely on a
// label-add cascade the GITHUB_TOKEN path cannot trigger).
const addResult = await github.graphql(`
mutation($projectId: ID!, $contentId: ID!) {
addProjectV2ItemById(input: { projectId: $projectId, contentId: $contentId }) {
item { id }
}
}`, { projectId: PROJECT_ID, contentId: issueNodeId });
const itemId = addResult.addProjectV2ItemById.item.id;
await github.graphql(`
mutation($projectId: ID!, $itemId: ID!, $fieldId: ID!, $optionId: String!) {
updateProjectV2ItemFieldValue(input: {
projectId: $projectId
itemId: $itemId
fieldId: $fieldId
value: { singleSelectOptionId: $optionId }
}) { projectV2Item { id } }
}`, {
projectId: PROJECT_ID,
itemId: itemId,
fieldId: STATUS_FIELD_ID,
optionId: DONE_OPTION_ID,
});
core.info(`#${issueNumber} Status -> Done`);
# -- Sync board/priority labels to project fields --
sync:

Loading…
Cancel
Save

Powered by TurnKey Linux.