fix: prevent sticky progress bar ghost lines from terminal wrapping #109
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
| # SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. | |
| # SPDX-License-Identifier: Apache-2.0 | |
| name: "Linked Issue Check" | |
| on: | |
| # Re-check when PR is opened or body is edited (author adds Fixes #N). | |
| pull_request_target: | |
| types: [opened, edited, synchronize, reopened] | |
| branches: [main] | |
| # Re-check open PRs when a maintainer adds the "triaged" label to an issue. | |
| issues: | |
| types: [labeled] | |
| permissions: | |
| contents: read | |
| pull-requests: write | |
| issues: read | |
| jobs: | |
| # ── Job 1: validate linked issue on PR events ───────────────────────── | |
| # SECURITY: This workflow uses pull_request_target to get write access for | |
| # posting comments on fork PRs. It MUST NOT check out or execute code from | |
| # the PR branch. All inputs from the PR (body, author) are read via API | |
| # only. Adding actions/checkout here would run untrusted fork code with | |
| # base repo write permissions. | |
| check: | |
| if: >- | |
| github.repository_owner == 'NVIDIA-NeMo' | |
| && github.event_name != 'issues' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Check author permissions | |
| id: author | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| PR_AUTHOR: ${{ github.event.pull_request.user.login }} | |
| run: | | |
| USER="$PR_AUTHOR" | |
| # Bots that are always allowed (match DCO allowlist pattern). | |
| if [ "$USER" = "dependabot[bot]" ]; then | |
| echo "is_collaborator=true" >> "$GITHUB_OUTPUT" | |
| exit 0 | |
| fi | |
| PERMISSION=$(gh api "repos/${{ github.repository }}/collaborators/${USER}/permission" \ | |
| --jq '.permission' 2>/dev/null || echo "none") | |
| echo "permission=${PERMISSION}" | |
| if [ "$PERMISSION" = "admin" ] || [ "$PERMISSION" = "write" ]; then | |
| echo "is_collaborator=true" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "is_collaborator=false" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Parse issue reference from PR body | |
| id: parse | |
| if: steps.author.outputs.is_collaborator != 'true' | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| PR_BODY: ${{ github.event.pull_request.body }} | |
| run: | | |
| if [ -z "$PR_BODY" ] || [ "$PR_BODY" = "null" ]; then | |
| echo "issue_num=" >> "$GITHUB_OUTPUT" | |
| echo "No PR body found" | |
| exit 0 | |
| fi | |
| # Case-insensitive match for Fixes #N, Closes #N, Resolves #N. | |
| printf '%s' "$PR_BODY" > /tmp/pr-body-raw.txt | |
| ISSUE_NUM=$(grep -ioP '(?:fixes|closes|resolves)\s+#\K\d+' /tmp/pr-body-raw.txt | head -1 || true) | |
| echo "issue_num=${ISSUE_NUM}" >> "$GITHUB_OUTPUT" | |
| echo "Parsed issue number: ${ISSUE_NUM:-<none>}" | |
| - name: Validate issue exists and is triaged | |
| id: validate | |
| if: steps.author.outputs.is_collaborator != 'true' && steps.parse.outputs.issue_num != '' | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| ISSUE_NUM: ${{ steps.parse.outputs.issue_num }} | |
| run: | | |
| RESPONSE=$(gh api "repos/${{ github.repository }}/issues/${ISSUE_NUM}" 2>/dev/null) || { | |
| echo "issue_exists=false" >> "$GITHUB_OUTPUT" | |
| echo "is_triaged=false" >> "$GITHUB_OUTPUT" | |
| echo "Issue #${ISSUE_NUM} not found" | |
| exit 0 | |
| } | |
| # Verify it's an issue, not a PR (GitHub's issues API returns both). | |
| IS_PR=$(echo "$RESPONSE" | jq -r 'has("pull_request")') | |
| if [ "$IS_PR" = "true" ]; then | |
| echo "issue_exists=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" | |
| TRIAGED=$(echo "$RESPONSE" | jq -r '[.labels[].name] | any(. == "triaged")') | |
| echo "is_triaged=${TRIAGED}" >> "$GITHUB_OUTPUT" | |
| echo "Issue #${ISSUE_NUM} exists, triaged=${TRIAGED}" | |
| - name: Build comment body and post result | |
| id: comment | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| IS_COLLABORATOR: ${{ steps.author.outputs.is_collaborator }} | |
| ISSUE_NUM: ${{ steps.parse.outputs.issue_num }} | |
| ISSUE_EXISTS: ${{ steps.validate.outputs.issue_exists }} | |
| 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" \ | |
| 2>/dev/null || echo "") | |
| if [ "$IS_COLLABORATOR" = "true" ]; then | |
| echo "status=pass" >> "$GITHUB_OUTPUT" | |
| # Clean up any leftover comment from before the author became a collaborator. | |
| if [ -n "$COMMENT_ID" ]; then | |
| gh api -X DELETE "repos/${REPO}/issues/comments/${COMMENT_ID}" || true | |
| fi | |
| exit 0 | |
| fi | |
| # Build comment body. | |
| if [ -z "$ISSUE_NUM" ]; then | |
| STATUS="fail" | |
| cat > /tmp/comment-body.md <<'MSG' | |
| <!-- linked-issue-check --> | |
| ### Linked Issue Check | |
| This PR does not reference an issue. External contributions must link to | |
| a triaged issue before the PR can be merged. | |
| Add one of the following to your PR description: | |
| - `Fixes #<issue-number>` | |
| - `Closes #<issue-number>` | |
| - `Resolves #<issue-number>` | |
| If no issue exists yet, [open one](https://github.com/NVIDIA-NeMo/DataDesigner/issues/new/choose) | |
| and a maintainer will triage it. | |
| See [CONTRIBUTING.md](https://github.com/NVIDIA-NeMo/DataDesigner/blob/main/CONTRIBUTING.md) | |
| for details. | |
| MSG | |
| elif [ "$ISSUE_EXISTS" != "true" ]; then | |
| STATUS="fail" | |
| cat > /tmp/comment-body.md <<MSG | |
| <!-- linked-issue-check --> | |
| ### Linked Issue Check | |
| The referenced issue #${ISSUE_NUM} was not found. Please check the issue | |
| number 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. | |
| You can continue working on the PR in the meantime. The check will | |
| re-run automatically once the issue is triaged. | |
| MSG | |
| else | |
| STATUS="pass" | |
| fi | |
| echo "status=${STATUS}" >> "$GITHUB_OUTPUT" | |
| # Post, update, or delete the comment. | |
| if [ "$STATUS" = "fail" ]; then | |
| if [ -n "$COMMENT_ID" ]; then | |
| gh api -X PATCH "repos/${REPO}/issues/comments/${COMMENT_ID}" \ | |
| -f body="$(cat /tmp/comment-body.md)" | |
| else | |
| gh api "repos/${REPO}/issues/${PR_NUMBER}/comments" \ | |
| -f body="$(cat /tmp/comment-body.md)" | |
| fi | |
| elif [ -n "$COMMENT_ID" ]; then | |
| gh api -X DELETE "repos/${REPO}/issues/comments/${COMMENT_ID}" || true | |
| fi | |
| - name: Set check result | |
| if: steps.comment.outputs.status == 'fail' | |
| run: | | |
| echo "::error::Linked issue check failed. See the PR comment for details." | |
| exit 1 | |
| # ── Job 2: re-trigger check when an issue gets triaged ──────────────── | |
| retrigger: | |
| if: >- | |
| github.repository_owner == 'NVIDIA-NeMo' | |
| && github.event_name == 'issues' | |
| && github.event.label.name == 'triaged' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Find PRs referencing this issue | |
| id: find-prs | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| ISSUE_NUMBER: ${{ github.event.issue.number }} | |
| run: | | |
| # 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] | .[]") | |
| if [ -z "$PRS" ]; then | |
| echo "No open PRs reference issue #${ISSUE_NUMBER}" | |
| echo "prs=" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "Found PRs: ${PRS}" | |
| echo "prs=$(echo "$PRS" | tr '\n' ' ')" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Re-trigger linked issue check | |
| if: steps.find-prs.outputs.prs != '' | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| ISSUE_NUMBER: ${{ github.event.issue.number }} | |
| PR_NUMBERS: ${{ steps.find-prs.outputs.prs }} | |
| run: | | |
| TIMESTAMP=$(date -u +%Y-%m-%dT%H:%M:%SZ) | |
| for PR_NUM in $PR_NUMBERS; do | |
| echo "Re-triggering check for PR #${PR_NUM}..." | |
| # Read current PR body to a file to avoid shell expansion issues. | |
| gh pr view "$PR_NUM" --repo "${{ github.repository }}" --json body -q '.body' > /tmp/current-body.txt | |
| # Append or update hidden timestamp to trigger the 'edited' event, | |
| # which re-runs the check job. | |
| MARKER="<!-- triaged-recheck" | |
| if grep -q "$MARKER" /tmp/current-body.txt; then | |
| sed "s|<!-- triaged-recheck[^>]*-->|<!-- triaged-recheck: ${TIMESTAMP} -->|" \ | |
| /tmp/current-body.txt > /tmp/pr-body.md | |
| else | |
| cp /tmp/current-body.txt /tmp/pr-body.md | |
| printf '\n<!-- triaged-recheck: %s -->' "$TIMESTAMP" >> /tmp/pr-body.md | |
| fi | |
| gh pr edit "$PR_NUM" --repo "${{ github.repository }}" --body-file /tmp/pr-body.md | |
| # 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." | |
| done |