Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 68 additions & 12 deletions .github/workflows/pr-linked-issue.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ on:
types: [opened, edited, synchronize, reopened]
branches: [main]

# Re-check open PRs when a maintainer adds the "triaged" label to an issue.
# Re-check open PRs when linked-issue validity changes.
issues:
types: [labeled]
types: [labeled, unlabeled, closed, reopened]

permissions:
contents: read
Expand Down Expand Up @@ -88,7 +88,7 @@ jobs:
echo "issue_num=${ISSUE_NUM}" >> "$GITHUB_OUTPUT"
echo "Parsed issue number: ${ISSUE_NUM:-<none>}"

- name: Validate issue exists and is triaged
- name: Validate issue is open and triaged
id: validate
if: steps.author.outputs.is_collaborator != 'true' && steps.parse.outputs.issue_num != ''
env:
Expand All @@ -97,6 +97,7 @@ jobs:
run: |
RESPONSE=$(gh api "repos/${{ github.repository }}/issues/${ISSUE_NUM}" 2>/dev/null) || {
echo "issue_exists=false" >> "$GITHUB_OUTPUT"
echo "is_open=false" >> "$GITHUB_OUTPUT"
echo "is_triaged=false" >> "$GITHUB_OUTPUT"
echo "Issue #${ISSUE_NUM} not found"
exit 0
Expand All @@ -106,16 +107,25 @@ jobs:
IS_PR=$(echo "$RESPONSE" | jq -r 'has("pull_request")')
if [ "$IS_PR" = "true" ]; then
echo "issue_exists=false" >> "$GITHUB_OUTPUT"
echo "is_open=false" >> "$GITHUB_OUTPUT"
echo "is_triaged=false" >> "$GITHUB_OUTPUT"
echo "#${ISSUE_NUM} is a pull request, not an issue"
exit 0
fi

echo "issue_exists=true" >> "$GITHUB_OUTPUT"

ISSUE_OPEN=$(echo "$RESPONSE" | jq -r '.state == "open"')
echo "is_open=${ISSUE_OPEN}" >> "$GITHUB_OUTPUT"
if [ "$ISSUE_OPEN" != "true" ]; then
echo "is_triaged=false" >> "$GITHUB_OUTPUT"
echo "Issue #${ISSUE_NUM} is closed"
exit 0
fi

TRIAGED=$(echo "$RESPONSE" | jq -r '[.labels[].name] | any(. == "triaged")')
echo "is_triaged=${TRIAGED}" >> "$GITHUB_OUTPUT"
echo "Issue #${ISSUE_NUM} exists, triaged=${TRIAGED}"
echo "Issue #${ISSUE_NUM} is open, triaged=${TRIAGED}"

- name: Build comment body and post result
id: comment
Expand All @@ -124,16 +134,19 @@ jobs:
IS_COLLABORATOR: ${{ steps.author.outputs.is_collaborator }}
ISSUE_NUM: ${{ steps.parse.outputs.issue_num }}
ISSUE_EXISTS: ${{ steps.validate.outputs.issue_exists }}
ISSUE_OPEN: ${{ steps.validate.outputs.is_open }}
IS_TRIAGED: ${{ steps.validate.outputs.is_triaged }}
PR_NUMBER: ${{ github.event.pull_request.number }}
REPO: ${{ github.repository }}
run: |
MARKER="<!-- linked-issue-check -->"

# Find existing bot comment with our marker.
COMMENT_ID=$(gh api "repos/${REPO}/issues/${PR_NUMBER}/comments" \
--jq "[.[] | select(.user.login == \"github-actions[bot]\") | select(.body | contains(\"${MARKER}\"))] | last | .id // empty" \
COMMENT_IDS=$(gh api --paginate \
"repos/${REPO}/issues/${PR_NUMBER}/comments?per_page=100" \
--jq ".[] | select(.user.login == \"github-actions[bot]\") | select(.body | contains(\"${MARKER}\")) | .id" \
2>/dev/null || echo "")
COMMENT_ID=$(printf '%s\n' "$COMMENT_IDS" | tail -1)

if [ "$IS_COLLABORATOR" = "true" ]; then
echo "status=pass" >> "$GITHUB_OUTPUT"
Expand All @@ -152,7 +165,7 @@ jobs:
### Linked Issue Check

This PR does not reference an issue. External contributions must link to
a triaged issue before the PR can be merged.
an open, triaged issue for this check to pass.

Add one of the following to your PR description:
- `Fixes #<issue-number>`
Expand All @@ -174,14 +187,23 @@ jobs:
The referenced issue #${ISSUE_NUM} was not found. Please check the issue
number in your PR description.
MSG
elif [ "$ISSUE_OPEN" != "true" ]; then
STATUS="fail"
cat > /tmp/comment-body.md <<MSG
<!-- linked-issue-check -->
### Linked Issue Check

Issue #${ISSUE_NUM} is closed. Please link an open, triaged issue in your
PR description.
MSG
elif [ "$IS_TRIAGED" != "true" ]; then
STATUS="fail"
cat > /tmp/comment-body.md <<MSG
<!-- linked-issue-check -->
### Linked Issue Check

Issue #${ISSUE_NUM} has not been triaged yet. A maintainer needs to review
the issue and add the \`triaged\` label before this PR can be merged.
the issue and add the \`triaged\` label for this check to pass.

You can continue working on the PR in the meantime. The check will
re-run automatically once the issue is triaged.
Expand Down Expand Up @@ -211,12 +233,20 @@ jobs:
echo "::error::Linked issue check failed. See the PR comment for details."
exit 1

# ── Job 2: re-trigger check when an issue gets triaged ────────────────
# ── Job 2: re-trigger check when linked-issue validity changes ───────
retrigger:
if: >-
github.repository_owner == 'NVIDIA-NeMo'
&& github.event_name == 'issues'
&& github.event.label.name == 'triaged'
&& (
github.event.action == 'closed'
|| github.event.action == 'reopened'
|| (
(github.event.action == 'labeled' || github.event.action == 'unlabeled')
&& github.event.label.name == 'triaged'
&& github.event.issue.state == 'open'
)
)
runs-on: ubuntu-latest
Comment on lines 239 to 250

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Issue state changes leave stale checks

When a linked issue is closed or reopened after its PR check completes, neither transition triggers revalidation, leaving a successful check green for a closed issue or a prior failure red for a reopened issue until an unrelated PR event occurs.

Prompt To Fix With AI
This is a comment left during a code review.
Path: .github/workflows/pr-linked-issue.yml
Line: 239-243

Comment:
**Issue state changes leave stale checks**

When a linked issue is closed or reopened after its PR check completes, neither transition triggers revalidation, leaving a successful check green for a closed issue or a prior failure red for a reopened issue until an unrelated PR event occurs.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

steps:
- name: Find PRs referencing this issue
Expand All @@ -228,7 +258,13 @@ jobs:
# List open PRs and find those whose body references this issue.
PRS=$(gh pr list --repo "${{ github.repository }}" --state open \
--json number,body --limit 200 \
| jq -r "[.[] | select(.body != null) | select(.body | test(\"(?i)(fixes|closes|resolves)\\\\s+#${ISSUE_NUMBER}\\\\b\")) | .number] | .[]")
| jq -r --arg issue "$ISSUE_NUMBER" '
.[]
| select(.body != null)
| (.body | ascii_downcase | capture("(?:fixes|closes|resolves)\\s+#(?<issue>[0-9]+)")?) as $reference
| select($reference.issue == $issue)
| .number
')

if [ -z "$PRS" ]; then
echo "No open PRs reference issue #${ISSUE_NUMBER}"
Expand All @@ -242,11 +278,31 @@ jobs:
if: steps.find-prs.outputs.prs != ''
env:
GH_TOKEN: ${{ github.token }}
ISSUE_ACTION: ${{ github.event.action }}
ISSUE_NUMBER: ${{ github.event.issue.number }}
PR_NUMBERS: ${{ steps.find-prs.outputs.prs }}
run: |
TIMESTAMP=$(date -u +%Y-%m-%dT%H:%M:%SZ)

case "$ISSUE_ACTION" in

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I think there will be an issue here with the pull_request_target retrigger. Since gh pr edit uses the workflow’s GITHUB_TOKEN, GitHub will update the body but won't trigger another workflow run, so the linked-issue check would remain stale.

We could instead find the existing Linked Issue Check run and call gh run rerun with actions: write. That should work with the built-in token and avoid editing the PR body. The other option is using a PAT or GitHub App token for the edit, but that adds another credential to manage. The rerun approach seems simpler.

labeled)
EVENT_DESCRIPTION='received the `triaged` label'
;;
unlabeled)
EVENT_DESCRIPTION='lost the `triaged` label'
;;
closed)
EVENT_DESCRIPTION="was closed"
;;
reopened)
EVENT_DESCRIPTION="was reopened"
;;
*)
echo "::error::Unsupported issue action: ${ISSUE_ACTION}"
exit 1
;;
esac

for PR_NUM in $PR_NUMBERS; do
echo "Re-triggering check for PR #${PR_NUM}..."

Expand All @@ -268,5 +324,5 @@ jobs:

# Post a visible comment so the author knows what happened.
gh pr comment "$PR_NUM" --repo "${{ github.repository }}" --body \
"Issue #${ISSUE_NUMBER} has been triaged. The linked issue check is being re-evaluated."
"Issue #${ISSUE_NUMBER} ${EVENT_DESCRIPTION}. The linked issue check is being re-evaluated."
done
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ The repository includes skills for common development tasks. These are located i

## Pull Requests

- PRs must link to the issue they address (`Fixes #NNN` or `Closes #NNN`). For external contributors, this is enforced by a required status check: the linked issue must exist and carry the `triaged` label (added by a maintainer after review). Collaborators are exempt from this check. You can open the PR before the issue is triaged - the check re-runs automatically once a maintainer adds the label.
- PRs should link to the issue they address (`Fixes #NNN` or `Closes #NNN`). For external contributors, the linked-issue workflow reports whether the issue exists, is open, and carries the `triaged` label (added by a maintainer after review). Collaborators are exempt from this check. Repository rules must separately require `Linked Issue Check / check` for the result to block merging. You can open the PR before the issue is triaged - the check re-runs automatically when the selected issue gains or loses the label, is closed, or is reopened.
- PRs with failing checks that remain inactive are automatically reminded after 7 days and closed after 14 days (collaborators: 14/28 days). Push an update or leave a comment to reset the timer. If you need more time, ask a maintainer to add the `keep-open` label.
- Use the `create-pr` skill for well-formatted PR descriptions, or follow the PR template
- Ensure all checks pass before requesting review:
Expand Down
Loading