From 29136d0540f7a23239956556b0f1ef5d08700a7a Mon Sep 17 00:00:00 2001 From: Steven Welch Date: Sun, 30 Aug 2026 11:47:13 -0600 Subject: [PATCH 1/3] refactor(opentofu): add in-progress PR comment updates and unify comment headers Post an in-progress update to the pinned test/plan PR comments before each job runs, preserving the previous run's output in a collapsible section. Unify both comments on the plan-style header (#### OpenTofu Test/Plan with a passed/failed status line), drop the "Validation output (redacted, last 12000 characters)" line, and wrap the output body in a collapsible "View run output" section. --- .github/workflows/opentofu.yml | 100 ++++++++++++++++++++++++++++++++- 1 file changed, 98 insertions(+), 2 deletions(-) diff --git a/.github/workflows/opentofu.yml b/.github/workflows/opentofu.yml index 18ec044..032f649 100644 --- a/.github/workflows/opentofu.yml +++ b/.github/workflows/opentofu.yml @@ -56,6 +56,43 @@ jobs: DOCS_APP_KEY_AVAILABLE: ${{ secrets.CHART_UPDATER_GITHUB_APP_PRIVATE_KEY != '' }} runs-on: ${{ inputs.runs-on }} steps: + - name: Mark test run in progress + if: >- + github.event_name == 'pull_request' && + github.event.pull_request.head.repo.full_name == github.repository + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + with: + script: | + const marker = ''; + const workflowUrl = `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`; + const { data: comments } = await github.rest.issues.listComments({ owner: context.repo.owner, repo: context.repo.repo, issue_number: context.issue.number }); + const existing = comments.find((comment) => comment.body?.includes(marker)); + let previous = ''; + if (existing) { + if (existing.body.includes('this comment will be updated when the run completes')) { + const match = existing.body.match(/
Previous run output<\/summary>\n\n([\s\S]*)\n\n<\/details>\s*$/); + previous = match ? match[1].trim() : ''; + } else { + previous = existing.body.replace(marker, '').trim(); + } + } + const parts = [ + marker, + '#### OpenTofu Test', + '', + 'OpenTofu test is in progress; this comment will be updated when the run completes.', + '', + `[View the workflow run](${workflowUrl}).`, + ]; + if (previous) { + parts.push('', '
Previous run output', '', previous, '', '
'); + } + const body = parts.join('\n'); + if (existing) { + await github.rest.issues.updateComment({ owner: context.repo.owner, repo: context.repo.repo, comment_id: existing.id, body }); + } else { + await github.rest.issues.createComment({ owner: context.repo.owner, repo: context.repo.repo, issue_number: context.issue.number, body }); + } - name: Checkout id: checkout continue-on-error: true @@ -182,13 +219,28 @@ jobs: const maxLength = 12000; const output = fs.existsSync('validation-output.txt') ? fs.readFileSync('validation-output.txt', 'utf8') - .replace(/\u001b\[[0-?]*[ -/]*[@-~]/g, '') + .replace(/\[[0-?]*[ -/]*[@-~]/g, '') .replace(/\b(?:gh[pousr]_[A-Za-z0-9_]{20,}|github_pat_[A-Za-z0-9_]{20,})\b/g, '***REDACTED***') .replace(/````/g, '\\`\\`\\`\\`') : 'No validation output was captured.'; const excerpt = output.length > maxLength ? output.slice(-maxLength) : output; const status = failed ? 'failed' : 'passed'; - const body = `${marker}\n## OpenTofu CI ${status}\n\n[View the workflow run](${workflowUrl}).\n\n#### Validation output (redacted, last ${maxLength} characters)\n\n\`\`\`\`text\n${excerpt}\n\`\`\`\``; + const body = [ + marker, + '#### OpenTofu Test', + '', + `OpenTofu test ${status}.`, + '', + `[View the workflow run](${workflowUrl}).`, + '', + '
View run output', + '', + '````text', + excerpt, + '````', + '', + '
', + ].join('\n'); const { data: comments } = await github.rest.issues.listComments({ owner: context.repo.owner, repo: context.repo.repo, issue_number: context.issue.number }); const existing = comments.find((comment) => comment.body?.includes(marker)); if (existing) { @@ -216,6 +268,40 @@ jobs: if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository needs: [test] steps: + - name: Mark plan run in progress + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + with: + script: | + const marker = ''; + const workflowUrl = `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`; + const { data: comments } = await github.rest.issues.listComments({ owner: context.repo.owner, repo: context.repo.repo, issue_number: context.issue.number }); + const existing = comments.find((comment) => comment.body?.includes(marker)); + let previous = ''; + if (existing) { + if (existing.body.includes('this comment will be updated when the run completes')) { + const match = existing.body.match(/
Previous run output<\/summary>\n\n([\s\S]*)\n\n<\/details>\s*$/); + previous = match ? match[1].trim() : ''; + } else { + previous = existing.body.replace(marker, '').trim(); + } + } + const parts = [ + marker, + '#### OpenTofu Plan', + '', + 'OpenTofu plan is in progress; this comment will be updated when the run completes.', + '', + `[View the workflow run](${workflowUrl}).`, + ]; + if (previous) { + parts.push('', '
Previous run output', '', previous, '', '
'); + } + const body = parts.join('\n'); + if (existing) { + await github.rest.issues.updateComment({ owner: context.repo.owner, repo: context.repo.repo, comment_id: existing.id, body }); + } else { + await github.rest.issues.createComment({ owner: context.repo.owner, repo: context.repo.repo, issue_number: context.issue.number, body }); + } - name: Checkout uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: @@ -280,12 +366,22 @@ jobs: { echo '' echo '#### OpenTofu Plan' + echo if [ "$PLAN_EXIT_CODE" -ne 0 ]; then echo "OpenTofu plan failed with exit code $PLAN_EXIT_CODE." + else + echo 'OpenTofu plan passed.' fi + echo + echo "[View the workflow run]($GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID)." + echo + echo '
View run output' + echo echo '````diff' cat plan-filtered.txt echo '````' + echo + echo '
' } > comment-body.md - name: comment uses: johanwulf/replace-comment@73e5abab2457a7742d94f71355f878f25e950c71 # v1.0.1 From c25237c93daf2b4fd66f8572e5ee16f68b251073 Mon Sep 17 00:00:00 2001 From: Steven Welch Date: Sun, 30 Aug 2026 11:49:31 -0600 Subject: [PATCH 2/3] =?UTF-8?q?fix(opentofu):=20restore=20literal=20=1B=20?= =?UTF-8?q?escape=20in=20ANSI=20strip=20regex?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Write the escape as the six-character sequence  instead of a raw ESC control byte, which is not a valid YAML character. --- .github/workflows/opentofu.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/opentofu.yml b/.github/workflows/opentofu.yml index 032f649..15ee881 100644 --- a/.github/workflows/opentofu.yml +++ b/.github/workflows/opentofu.yml @@ -219,7 +219,7 @@ jobs: const maxLength = 12000; const output = fs.existsSync('validation-output.txt') ? fs.readFileSync('validation-output.txt', 'utf8') - .replace(/\[[0-?]*[ -/]*[@-~]/g, '') + .replace(/\\u001b\[[0-?]*[ -/]*[@-~]/g, '') .replace(/\b(?:gh[pousr]_[A-Za-z0-9_]{20,}|github_pat_[A-Za-z0-9_]{20,})\b/g, '***REDACTED***') .replace(/````/g, '\\`\\`\\`\\`') : 'No validation output was captured.'; From 4fa532125e9730038e13d60e22708f7be4cfb190 Mon Sep 17 00:00:00 2001 From: Steven Welch Date: Sun, 30 Aug 2026 11:52:07 -0600 Subject: [PATCH 3/3] fix(opentofu): use single-backslash unicode escape in ANSI strip regex --- .github/workflows/opentofu.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/opentofu.yml b/.github/workflows/opentofu.yml index 15ee881..6b7d936 100644 --- a/.github/workflows/opentofu.yml +++ b/.github/workflows/opentofu.yml @@ -219,7 +219,7 @@ jobs: const maxLength = 12000; const output = fs.existsSync('validation-output.txt') ? fs.readFileSync('validation-output.txt', 'utf8') - .replace(/\\u001b\[[0-?]*[ -/]*[@-~]/g, '') + .replace(/\u001b\[[0-?]*[ -/]*[@-~]/g, '') .replace(/\b(?:gh[pousr]_[A-Za-z0-9_]{20,}|github_pat_[A-Za-z0-9_]{20,})\b/g, '***REDACTED***') .replace(/````/g, '\\`\\`\\`\\`') : 'No validation output was captured.';