Woodpecker plan step fails with shell escaping when tofu plan output contains certain quote patterns #307
Labels
No labels
domain:backend
domain:devops
domain:frontend
status:approved
status:in-progress
status:needs-fix
status:qa
type:bug
type:devops
type:feature
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
ldraney/pal-e-platform#307
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Type
Bug
Lineage
Long-standing CI infra issue — repeatedly forces force-merges on infra PRs because the failing step is the PR-comment-posting wrapper, NOT the actual
tofu plan(which succeeds). Surfaced again 2026-04-25 on PR #304 (admin-app Postgres Job). User authorized force-merge as workaround pending this fix.Repo
forgejo_admin/pal-e-platform
What Broke
The
planstep in.woodpecker.yaml(line 173) uses fragile nested shell quoting to build a JSON payload for the PR comment containing the plan output:When
tofu planoutput contains certain quote patterns (e.g., Kubernetes metadata labels like"app.kubernetes.io/managed-by" = "pal-e-platform-terraform", sensitive-value markers, multi-line resource diffs), the nested sed sequence inside$$(...)produces an unterminated quoted string in the wrapping shell, causing:The step exits with code 2 → pipeline marked
failure→ forgejo combined commit statusfailure→check-infra-validation.shhook blocks merge.The actual plan succeeds —
Plan: 4 to add, 1 to change, 0 to destroyis visible in the logs above the syntax error. The infrastructure change is sound. Only the comment-posting wrapper is broken.Repro Steps
forgejo_admin/pal-e-platformwhosetofu planoutput contains kubernetes resource diffs with labels (e.g., any kubernetes_secret_v1, kubernetes_namespace_v1, kubernetes_service_v1 addition)planstep showstofu planran successfully then crashes on/bin/sh: syntax error: unterminated quoted stringcheck-infra-validation.shhook blocks PR merge despite the plan being validExpected Behavior
planstep posts the plan output as a PR comment regardless of quote characters in the plan bodytofu plansucceeded, NOT whether the comment-posting wrapper had a quoting accidenttofu planoutput can merge through the standard hook gateEnvironment
.woodpecker.yaml(line 173 specifically; also potentially line 162tr '\n' ', ')ghcr.io/opentofu/opentofu:1.9(uses Alpine/bin/sh, not bash — fewer escape niceties)User Story
story:platform-stability — As an operator merging infra PRs, I need the Woodpecker plan step to fail only when the actual
tofu planfails, not when the comment-posting wrapper hits a shell quoting edge case, so that valid infra changes don't require force-merge bypasses that defeat the safety the hook is meant to enforce.Context
Pattern repeats:
feedback_ci_pipeline_lessonslists 12 prior root causes already fixed in this CI; this is the 13th. The escaping approach in the comment-posting block is the architectural smell. Better options:jq -Rs '.'to JSON-encode the plan output safely (jq is reliable for this; the fragile bit is the hand-rolled escape).printfto a heredoc then post — let the shell handle the bytes raw, encode at the JSON layer.image: alpinefor the comment-posting sub-step and installjqonce — separate concerns from the tofu container.wget --post-datawith URL-encoding rather than building JSON by hand — simpler shape.File Targets
~/pal-e-platform/.woodpecker.yamlline 168-178 (theBODY=/printf/wgetblock in theplanstep)applystep (verify by inspection)Acceptance Criteria
tofu planitself, not the comment-posting wrapperTest Expectations
unterminated quoted stringerrorcheck-infra-validation.shhook reportssuccessfor the plan stepConstraints
tofu planclean?) must remain enforcedjqis needed, run the comment-post in a separate Alpine stepChecklist
Closes #THISRelated
feedback_ci_pipeline_lessons— 12 prior fixes; this is the next onefeedback_discovered_scope_always_tracked,feedback_no_workarounds_just_fixPR opened: forgejo_admin/pal-e-platform#309
Fix uses approach #1 from the ticket Context section: replaced the sed-based JSON escaping in the plan step's comment-posting wrapper with
jq -n --rawfile body /tmp/comment-body.txt '{body: $body}'. Addedapk add --no-cache jqsince the opentofu image (Alpine-based) does not ship jq.Apply step audit: the
applystep runs onevent: pushto main and posts no PR comment, so no parallel fix needed. Thecross-pillar-reviewstep already uses jq correctly.Verification will land on this PR's own CI run — watch for the comment to post cleanly.