diff --git a/.github/workflows/plan-review.yml b/.github/workflows/plan-review.yml
index bd0da40..f6ba776 100644
--- a/.github/workflows/plan-review.yml
+++ b/.github/workflows/plan-review.yml
@@ -66,38 +66,10 @@ jobs:
- name: Post review to PR
if: github.event_name == 'pull_request'
- uses: actions/github-script@v7
- with:
- script: |
- const fs = require('fs');
- let review = '';
- try {
- review = fs.readFileSync('review-output.txt', 'utf8');
- } catch (e) {
- review = 'LLM review output not available.';
- }
- let resultJson = {};
- try {
- resultJson = JSON.parse(fs.readFileSync('review-result.json', 'utf8'));
- } catch (e) {
- resultJson = {risk: 'FAILED'};
- }
- const riskEmoji = {LOW: '\u{1F7E2}', MEDIUM: '\u{1F7E1}', HIGH: '\u{1F534}', FAILED: '\u26AA'}[resultJson.risk] || '\u26AA';
-
- const body = [
- '## LLM Plan Review',
- '',
- `**Risk: ${riskEmoji} ${resultJson.risk}**`,
- '',
- review
- ].join('\n');
-
- await github.rest.issues.createComment({
- issue_number: context.issue.number,
- owner: context.repo.owner,
- repo: context.repo.repo,
- body: body
- });
+ env:
+ GH_TOKEN: ${{ github.token }}
+ PR_NUMBER: ${{ github.event.pull_request.number }}
+ run: sh platform/scripts/post-review-comment.sh
- name: Alert Slack on HIGH risk
if: github.event_name == 'push' && github.ref == 'refs/heads/main' && steps.review.outputs.risk_level == 'HIGH'
diff --git a/.github/workflows/platform-ci.yml b/.github/workflows/platform-ci.yml
index c897032..f32a518 100644
--- a/.github/workflows/platform-ci.yml
+++ b/.github/workflows/platform-ci.yml
@@ -90,43 +90,10 @@ jobs:
- name: Post plan to PR
if: github.event_name == 'pull_request'
- uses: actions/github-script@v7
- with:
- script: |
- const fs = require('fs');
- const planPath = '${{ env.TF_ROOT }}/plan-output.txt';
- let plan = '';
- try {
- plan = fs.readFileSync(planPath, 'utf8');
- } catch (e) {
- plan = 'Plan output not available.';
- }
- // Truncate to fit GitHub comment limits
- if (plan.length > 60000) {
- plan = plan.substring(0, 60000) + '\n\n... (truncated)';
- }
- const hasChanges = '${{ steps.plan.outputs.has_changes }}' === 'true';
- const status = hasChanges ? '**Changes detected** — review required.' : '**No changes** — infrastructure is up to date.';
- const body = [
- '## Terraform Plan',
- '',
- status,
- '',
- 'Plan output
',
- '',
- '```',
- plan,
- '```',
- '',
- ' '
- ].join('\n');
-
- await github.rest.issues.createComment({
- issue_number: context.issue.number,
- owner: context.repo.owner,
- repo: context.repo.repo,
- body: body
- });
+ env:
+ GH_TOKEN: ${{ github.token }}
+ PR_NUMBER: ${{ github.event.pull_request.number }}
+ run: sh scripts/post-plan-comment.sh "${{ env.TF_ROOT }}/plan-output.txt" "${{ steps.plan.outputs.has_changes }}"
# --------------------------------------------------------------------------
# Review — LLM risk analysis via Bedrock
@@ -158,38 +125,10 @@ jobs:
- name: Post review to PR
if: github.event_name == 'pull_request'
- uses: actions/github-script@v7
- with:
- script: |
- const fs = require('fs');
- let review = '';
- try {
- review = fs.readFileSync('review-output.txt', 'utf8');
- } catch (e) {
- review = 'LLM review output not available.';
- }
- let resultJson = {};
- try {
- resultJson = JSON.parse(fs.readFileSync('review-result.json', 'utf8'));
- } catch (e) {
- resultJson = {risk: 'FAILED'};
- }
- const riskEmoji = {LOW: '🟢', MEDIUM: '🟡', HIGH: '🔴', FAILED: '⚪'}[resultJson.risk] || '⚪';
-
- const body = [
- '## 🧠 LLM Plan Review',
- '',
- `**Risk: ${riskEmoji} ${resultJson.risk}**`,
- '',
- review
- ].join('\n');
-
- await github.rest.issues.createComment({
- issue_number: context.issue.number,
- owner: context.repo.owner,
- repo: context.repo.repo,
- body: body
- });
+ env:
+ GH_TOKEN: ${{ github.token }}
+ PR_NUMBER: ${{ github.event.pull_request.number }}
+ run: sh scripts/post-review-comment.sh
- name: Post HIGH risk to Slack
if: steps.review.outputs.risk_level == 'HIGH' && github.ref == 'refs/heads/main'
diff --git a/.github/workflows/tf-plan.yml b/.github/workflows/tf-plan.yml
index 4a0264d..5e24654 100644
--- a/.github/workflows/tf-plan.yml
+++ b/.github/workflows/tf-plan.yml
@@ -106,41 +106,7 @@ jobs:
- name: Post plan to PR
if: github.event_name == 'pull_request'
- uses: actions/github-script@v7
- with:
- script: |
- const fs = require('fs');
- const planPath = '${{ inputs.tf_root }}/plan-output.txt';
- let plan = '';
- try {
- plan = fs.readFileSync(planPath, 'utf8');
- } catch (e) {
- plan = 'Plan output not available.';
- }
- if (plan.length > 60000) {
- plan = plan.substring(0, 60000) + '\n\n... (truncated)';
- }
- const hasChanges = '${{ steps.plan.outputs.has_changes }}' === 'true';
- const status = hasChanges
- ? '**Changes detected** — review required.'
- : '**No changes** — infrastructure is up to date.';
- const body = [
- '## Terraform Plan',
- '',
- status,
- '',
- 'Plan output
',
- '',
- '```',
- plan,
- '```',
- '',
- ' '
- ].join('\n');
-
- await github.rest.issues.createComment({
- issue_number: context.issue.number,
- owner: context.repo.owner,
- repo: context.repo.repo,
- body: body
- });
+ env:
+ GH_TOKEN: ${{ github.token }}
+ PR_NUMBER: ${{ github.event.pull_request.number }}
+ run: sh .platform/scripts/post-plan-comment.sh "${{ inputs.tf_root }}/plan-output.txt" "${{ steps.plan.outputs.has_changes }}"
diff --git a/scripts/post-plan-comment.sh b/scripts/post-plan-comment.sh
new file mode 100644
index 0000000..70580d3
--- /dev/null
+++ b/scripts/post-plan-comment.sh
@@ -0,0 +1,35 @@
+#!/bin/sh
+# Post terraform plan output as a PR comment.
+#
+# Usage: post-plan-comment.sh
+#
+# Env: GH_TOKEN (or gh auth), GITHUB_REPOSITORY, PR_NUMBER
+
+set -e
+
+PLAN_FILE="$1"
+HAS_CHANGES="$2"
+
+if [ "$HAS_CHANGES" = "true" ]; then
+ STATUS="**Changes detected** — review required."
+else
+ STATUS="**No changes** — infrastructure is up to date."
+fi
+
+PLAN=$(head -c 60000 "$PLAN_FILE" 2>/dev/null || echo "Plan output not available.")
+
+cat > /tmp/plan-comment.md <Plan output
+
+\`\`\`
+${PLAN}
+\`\`\`
+
+
+EOF
+
+gh pr comment "$PR_NUMBER" --repo "$GITHUB_REPOSITORY" --body-file /tmp/plan-comment.md
diff --git a/scripts/post-review-comment.sh b/scripts/post-review-comment.sh
new file mode 100644
index 0000000..605ab7e
--- /dev/null
+++ b/scripts/post-review-comment.sh
@@ -0,0 +1,29 @@
+#!/bin/sh
+# Post LLM review result as a PR comment.
+#
+# Usage: post-review-comment.sh
+#
+# Reads review-result.json and review-output.txt from current directory.
+# Env: GH_TOKEN (or gh auth), GITHUB_REPOSITORY, PR_NUMBER
+
+set -e
+
+RISK=$(jq -r '.risk // "FAILED"' review-result.json 2>/dev/null || echo "FAILED")
+REVIEW=$(cat review-output.txt 2>/dev/null || echo "LLM review output not available.")
+
+case "$RISK" in
+ LOW) EMOJI="🟢" ;;
+ MEDIUM) EMOJI="🟡" ;;
+ HIGH) EMOJI="🔴" ;;
+ *) EMOJI="⚪" ;;
+esac
+
+cat > /tmp/review-comment.md <