Fix failing woodpecker pipeline

This commit is contained in:
Jonathon Leight
2026-08-08 19:57:06 -04:00
parent 943d933b14
commit 2b433bca26
2 changed files with 52 additions and 4 deletions
+12 -4
View File
@@ -109,10 +109,18 @@ steps:
deploy:
image: alpine/kubectl:1.35.3
commands:
# No ${...} around shell variables anywhere in this step: Woodpecker does
# its own ${VAR} substitution before the shell runs, so "${IMAGE##*@}"
# would be replaced with an empty string rather than reaching bash. $(…)
# and bare $VAR are untouched, so the digest is cut with a command instead.
# Shell variables stay bare ($IMAGE, never brace-wrapped): Woodpecker runs
# its own substitution over this file before the shell sees it, and it
# replaces a brace-wrapped name with the CI value — empty, for a variable
# only the shell knows. $(…) and bare $VAR are passed through untouched, so
# the digest is cut with a command rather than a brace expansion.
#
# That substitution reads the whole file, comments included, and rejects
# anything brace-wrapped that isn't a valid variable name — which is why
# this comment describes the trap instead of quoting it. A malformed one
# fails the pipeline before any step runs, with "unable to parse variable
# name" and no indication of where. TestWoodpeckerVariablesAreParseable
# catches it locally instead.
- IMAGE="$(cat image-ref)"
- DIGEST="$(cut -d@ -f2 image-ref)"
# Fail loudly rather than deploying with an empty/garbled digest: the app
+40
View File
@@ -273,3 +273,43 @@ func TestImageDigestEnvVarMatchesConfig(t *testing.T) {
t.Errorf(".woodpecker/build.yaml does not set %s, the variable internal/config reads", name)
}
}
// TestWoodpeckerVariablesAreParseable catches a failure with an unusually bad
// signal-to-effort ratio: Woodpecker substitutes variables over the RAW pipeline
// file — comments included — before parsing any YAML. A brace-wrapped token that
// isn't a valid variable name kills the whole pipeline with "unable to parse
// variable name", naming neither the file nor the line, and no step ever runs. A
// comment that merely *describes* the syntax is enough to trigger it.
//
// So: every ${…} in .woodpecker must open with something that could actually be
// a variable name. Bash operators after the name (${VAR##glob}, ${VAR:-default})
// are fine — only the name itself is checked.
func TestWoodpeckerVariablesAreParseable(t *testing.T) {
root := repoRoot(t)
entries, err := os.ReadDir(filepath.Join(root, ".woodpecker"))
if err != nil {
t.Fatalf("read .woodpecker: %v", err)
}
braced := regexp.MustCompile(`\$\{([^}]*)\}`)
validName := regexp.MustCompile(`^[A-Za-z_][A-Za-z0-9_]*`)
checked := 0
for _, e := range entries {
if e.IsDir() || !strings.HasSuffix(e.Name(), ".yaml") {
continue
}
rel := filepath.Join(".woodpecker", e.Name())
body := readRepoFile(t, rel)
for _, m := range braced.FindAllStringSubmatch(body, -1) {
checked++
if !validName.MatchString(m[1]) {
t.Errorf("%s: %q is not a parseable variable reference. Woodpecker rejects the "+
"whole pipeline for this — including in comments, so describe the syntax "+
"rather than quoting it.", rel, m[0])
}
}
}
if checked == 0 {
t.Error("found no ${...} references in .woodpecker — has the CI layout changed?")
}
}