fix(ci): replace heredoc with printf to fix YAML block scalar breakage
Some checks failed
Test / rust-fmt-check (pull_request) Successful in 1m34s
Test / frontend-tests (pull_request) Successful in 1m33s
Test / frontend-typecheck (pull_request) Successful in 1m35s
Test / rust-clippy (pull_request) Successful in 3m10s
PR Review Automation / review (pull_request) Failing after 4m19s
Test / rust-tests (pull_request) Successful in 4m23s

Shell heredocs with unindented bodies (line 1 content) terminate YAML
run: | block scalars. The YAML parser sees the unindented heredoc body
as leaving the block, making the workflow file unparseable -- Gitea
silently stops creating runs for a workflow with invalid YAML.

Replace the single-quoted heredoc prompt with a group of printf + cat
calls. Every line stays properly indented within the YAML block scalar.
Use jq --rawfile instead of --arg to load the prompt from a temp file,
which also eliminates shell escaping hazards for large strings.
This commit is contained in:
Shaun Arman 2026-05-31 15:06:09 -05:00
parent f8c0d247e8
commit 3d6270fb33

View File

@ -148,86 +148,50 @@ jobs:
run: | run: |
set -euo pipefail set -euo pipefail
CHANGED_FILES=$(tr '\n' ' ' < /tmp/pr_files.txt) CHANGED_FILES=$(tr '\n' ' ' < /tmp/pr_files.txt)
INDEX=$(cat /tmp/codebase_index.txt)
CONTEXT=$(cat /tmp/pr_context.txt)
# Build the prompt via a single-quoted heredoc so the shell never # Build prompt with printf + cat so every line stays indented within
# interprets backticks, dollar signs, or other special characters inside. # the YAML run: | block. Heredocs with unindented bodies terminate the
# Variables that must expand ($PR_TITLE etc.) are spliced in by jq --arg, # YAML block scalar, breaking the workflow file entirely.
# not by shell interpolation, so the prompt text itself is always literal. {
PROMPT_TEMPLATE=$(cat << 'ENDPROMPT' printf 'You are a senior engineer performing a code review.\n\n'
You are a senior engineer performing a code review for the following pull request. printf 'PR Title: %s\n' "$PR_TITLE"
printf 'Files changed: %s\n\n' "$CHANGED_FILES"
PR Title: __PR_TITLE__ printf -- '---\n'
Files changed: __CHANGED_FILES__ cat /tmp/codebase_index.txt
printf -- '---\n\n'
--- printf '## Changed file contents\n\n'
__INDEX__ printf 'Each section is the COMPLETE, FINAL file after PR changes (not a diff).\n'
--- printf 'Files over 500 lines show only changed sections with surrounding context.\n\n'
printf -- '---\n'
## Changed file contents cat /tmp/pr_context.txt
printf -- '---\n\n'
Each section below contains the COMPLETE, FINAL content of one changed file after printf '## Instructions\n\n'
the PR's changes have been applied. This is the full file — not a diff. For files printf 'Before raising any finding:\n'
over 500 lines, only the changed sections are shown with surrounding context. printf '1. Confirm every symbol you cite exists in the CODEBASE INDEX or file\n'
printf ' contents above. If absent from both, discard the finding.\n'
--- printf '2. Quote the exact line(s) from the file contents that support it.\n'
__CONTEXT__ printf '3. Confirm the issue is genuine, not intentional design.\n'
--- printf '4. If any step fails, discard silently -- do not mention it.\n\n'
printf 'Do NOT show reasoning. Only output confirmed issues.\n\n'
## Instructions printf 'Severity:\n'
printf '- BLOCKER: fails to compile, corrupts data, or security vulnerability\n'
Before raising any finding: printf '- WARNING: real risk to address before merge\n'
1. Confirm every symbol (function name, command name, variable) you cite exists in printf '- SUGGESTION: minor improvement, follow-up PR fine\n\n'
the CODEBASE INDEX above or in the file contents above. If it appears in neither, printf 'Focus: security bugs, logic errors, data loss, injection, unhandled errors.\n'
discard the finding — it does not exist in this project. printf 'Ignore: style, missing comments, speculative future concerns.\n\n'
2. Quote the exact line(s) from the file contents that support the finding. printf '## Output format (strict)\n\n'
3. Confirm the issue is a genuine problem, not intentional design. printf '**Summary** (2-3 sentences)\n\n'
4. If any step fails, discard the finding silently — do not mention it. printf '**Findings**\n'
printf '- [SEVERITY] file:line -- description\n'
Do NOT show your reasoning process. Do NOT mention discarded findings. printf ' Evidence: quoted line\n'
Output only confirmed issues. printf ' Fix: concrete change\n\n'
printf '(Write "No findings." if none.)\n\n'
Severity levels: printf '**Verdict**: APPROVE / APPROVE WITH COMMENTS / REQUEST CHANGES\n'
- BLOCKER: will fail to compile, corrupt data, or introduce a security vulnerability } > /tmp/prompt.txt
- WARNING: real risk that should be addressed before merge
- SUGGESTION: minor improvement, acceptable as a follow-up PR
Focus on: security bugs, logic errors, data loss, injection vectors, unhandled
error paths that could silently corrupt state.
Ignore: style preferences, missing comments, speculative future concerns.
## Output format (do not deviate)
**Summary** (2-3 sentences: what the PR does and your overall assessment)
**Findings**
- [SEVERITY] file:line -- description
Evidence: quoted line from the file above
Fix: concrete suggested change
(Write "No findings." if there are none.)
**Verdict**: APPROVE / APPROVE WITH COMMENTS / REQUEST CHANGES
ENDPROMPT
)
# Splice runtime values into the template using sed so nothing is eval'd
PROMPT=$(printf '%s' "$PROMPT_TEMPLATE" \
| sed "s|__PR_TITLE__|${PR_TITLE}|g" \
| sed "s|__CHANGED_FILES__|${CHANGED_FILES}|g")
# INDEX and CONTEXT may contain special sed chars — use python for those
PROMPT=$(python3 -c "
import sys
template = sys.stdin.read()
index = open('/tmp/codebase_index.txt').read()
context = open('/tmp/pr_context.txt').read()
print(template.replace('__INDEX__', index).replace('__CONTEXT__', context), end='')
" <<< "$PROMPT")
BODY=$(jq -cn \ BODY=$(jq -cn \
--arg model "qwen3-coder-next" \ --arg model "qwen3-coder-next" \
--arg content "$PROMPT" \ --rawfile content /tmp/prompt.txt \
'{model: $model, messages: [{role: "user", content: $content}], stream: false}') '{model: $model, messages: [{role: "user", content: $content}], stream: false}')
echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] PR #${PR_NUMBER} - Calling liteLLM API (${#BODY} bytes)..." echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] PR #${PR_NUMBER} - Calling liteLLM API (${#BODY} bytes)..."
HTTP_CODE=$(curl -s --max-time 300 --connect-timeout 30 \ HTTP_CODE=$(curl -s --max-time 300 --connect-timeout 30 \
@ -364,4 +328,4 @@ print(template.replace('__INDEX__', index).replace('__CONTEXT__', context), end=
- name: Cleanup - name: Cleanup
if: always() if: always()
shell: bash shell: bash
run: rm -f /tmp/pr_diff.txt /tmp/pr_context.txt /tmp/codebase_index.txt /tmp/llm_response.json /tmp/pr_review.txt /tmp/review_post_response.json /tmp/pr_files.txt run: rm -f /tmp/pr_diff.txt /tmp/pr_context.txt /tmp/codebase_index.txt /tmp/prompt.txt /tmp/llm_response.json /tmp/pr_review.txt /tmp/review_post_response.json /tmp/pr_files.txt