fix(linter): 🐛 yaml parser with escape in regex
This commit is contained in:
@@ -73,6 +73,8 @@ tasks:
|
||||
ignore_error: false
|
||||
- cmd: ./{{.BINARY}} check --branch feat/x testdata/workflow_vars.yml
|
||||
ignore_error: false
|
||||
- cmd: ./{{.BINARY}} check testdata/workflow_escape.yml
|
||||
ignore_error: false
|
||||
- cmd: ./{{.BINARY}} check testdata/variable_refs.yml
|
||||
ignore_error: false
|
||||
- cmd: ./{{.BINARY}} check testdata/variable_refs_included.yml
|
||||
|
||||
@@ -24,6 +24,8 @@ func Parse(path string) (*Pipeline, error) {
|
||||
|
||||
// ParseBytes parses YAML from an in-memory byte slice.
|
||||
func ParseBytes(data []byte) (*Pipeline, error) {
|
||||
data = sanitizeYAMLEscapes(data)
|
||||
|
||||
// First pass: parse into a yaml.Node document to extract job keys with
|
||||
// their exact source line numbers (key nodes carry the line, value nodes
|
||||
// carry the body we decode into Job / map[string]any).
|
||||
@@ -75,3 +77,63 @@ func ParseBytes(data []byte) (*Pipeline, error) {
|
||||
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// sanitizeYAMLEscapes rewrites double-quoted YAML strings, replacing the \/
|
||||
// escape sequence (unrecognised by gopkg.in/yaml.v3) with \\/ so that the
|
||||
// parser produces a literal backslash+slash — preserving regex patterns like
|
||||
// /^us\// that appear in GitLab CI if: expressions.
|
||||
func sanitizeYAMLEscapes(data []byte) []byte {
|
||||
type state int
|
||||
const (
|
||||
stOutside state = iota
|
||||
stSingleQ
|
||||
stDoubleQ
|
||||
stEscape
|
||||
)
|
||||
|
||||
out := make([]byte, 0, len(data))
|
||||
s := stOutside
|
||||
|
||||
for i := 0; i < len(data); i++ {
|
||||
b := data[i]
|
||||
switch s {
|
||||
case stOutside:
|
||||
out = append(out, b)
|
||||
switch b {
|
||||
case '"':
|
||||
s = stDoubleQ
|
||||
case '\'':
|
||||
s = stSingleQ
|
||||
}
|
||||
case stSingleQ:
|
||||
out = append(out, b)
|
||||
if b == '\'' {
|
||||
if i+1 < len(data) && data[i+1] == '\'' {
|
||||
// '' inside a single-quoted string is an escaped single-quote
|
||||
out = append(out, data[i+1])
|
||||
i++
|
||||
} else {
|
||||
s = stOutside
|
||||
}
|
||||
}
|
||||
case stDoubleQ:
|
||||
out = append(out, b)
|
||||
switch b {
|
||||
case '\\':
|
||||
s = stEscape
|
||||
case '"':
|
||||
s = stOutside
|
||||
}
|
||||
case stEscape:
|
||||
if b == '/' {
|
||||
// \/ is not recognised by yaml.v3; rewrite as \\/ which
|
||||
// the parser resolves to a literal backslash + slash.
|
||||
out = append(out, '\\', '/')
|
||||
} else {
|
||||
out = append(out, b)
|
||||
}
|
||||
s = stDoubleQ
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
Vendored
+57
@@ -0,0 +1,57 @@
|
||||
---
|
||||
# workflow_vars.yml
|
||||
# Exercises workflow:rules:variables: injection.
|
||||
# The matching workflow rule sets DEPLOY_TARGET; job rules use it.
|
||||
#
|
||||
# Expected behaviour per context:
|
||||
# (no context) → all jobs active (no context evaluation)
|
||||
# --branch main → deploy-prod active, deploy-staging skipped
|
||||
# --branch develop → deploy-prod skipped, deploy-staging active
|
||||
# --branch feat/x → deploy-prod skipped, deploy-staging skipped (manual)
|
||||
|
||||
stages:
|
||||
- build
|
||||
- deploy
|
||||
|
||||
variables:
|
||||
DEPLOY_TARGET:
|
||||
value: ""
|
||||
description: "Deployment target — set by workflow rules"
|
||||
|
||||
workflow:
|
||||
rules:
|
||||
- if: "
|
||||
$WORKFLOW = 'gitflow' &&
|
||||
$CI_PIPELINE_SOURCE == /(push|web)/ &&
|
||||
$CI_COMMIT_BRANCH =~ /^us\//
|
||||
"
|
||||
variables:
|
||||
DEPLOY_TARGET: production
|
||||
BUILD: true
|
||||
TEST: true
|
||||
- if: '$CI_COMMIT_BRANCH == "develop"'
|
||||
variables:
|
||||
DEPLOY_TARGET: staging
|
||||
- when: always
|
||||
|
||||
build:
|
||||
stage: build
|
||||
script: make build
|
||||
rules:
|
||||
- when: always
|
||||
|
||||
deploy-prod:
|
||||
stage: deploy
|
||||
script: make deploy ENV=production
|
||||
rules:
|
||||
- if: '$DEPLOY_TARGET == "production"'
|
||||
when: on_success
|
||||
- when: never
|
||||
|
||||
deploy-staging:
|
||||
stage: deploy
|
||||
script: make deploy ENV=staging
|
||||
rules:
|
||||
- if: '$DEPLOY_TARGET == "staging"'
|
||||
when: on_success
|
||||
- when: never
|
||||
Reference in New Issue
Block a user