1
0
forked from wrenn/wrenn
This commit is contained in:
2026-04-16 19:24:25 +00:00
parent 172413e91e
commit 605ad666a0
239 changed files with 19966 additions and 3454 deletions

View File

@ -24,9 +24,11 @@ type Step struct {
Raw string // original string, preserved for logging
Shell string // KindRUN, KindSTART: the shell command text
Timeout time.Duration // KindRUN: 0 means use caller's default
Key string // KindENV: variable name
Key string // KindENV: variable name; KindUSER: username
Value string // KindENV: variable value
Path string // KindWORKDIR: directory path
Srcs []string // KindCOPY: source paths (relative to build archive)
Dst string // KindCOPY: destination path inside sandbox
}
// ParseStep parses a single recipe instruction string into a Step.
@ -61,9 +63,9 @@ func ParseStep(s string) (Step, error) {
case "WORKDIR":
return parseWORKDIR(s, rest)
case "USER":
return Step{Kind: KindUSER, Raw: s}, nil
return parseUSER(s, rest)
case "COPY":
return Step{Kind: KindCOPY, Raw: s}, nil
return parseCOPY(s, rest)
default:
return Step{}, fmt.Errorf("unknown instruction %q (expected RUN, START, ENV, WORKDIR, USER, or COPY)", keyword)
}
@ -127,3 +129,33 @@ func parseWORKDIR(raw, path string) (Step, error) {
}
return Step{Kind: KindWORKDIR, Raw: raw, Path: path}, nil
}
func parseUSER(raw, username string) (Step, error) {
if username == "" {
return Step{}, fmt.Errorf("USER requires a username: %q", raw)
}
// Validate: alphanumeric, hyphens, underscores only; must start with a letter or underscore.
for i, c := range username {
if i == 0 && !((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_') {
return Step{}, fmt.Errorf("USER username must start with a letter or underscore: %q", raw)
}
if !((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_' || c == '-') {
return Step{}, fmt.Errorf("USER username contains invalid character %q: %q", string(c), raw)
}
}
return Step{Kind: KindUSER, Raw: raw, Key: username}, nil
}
func parseCOPY(raw, rest string) (Step, error) {
if rest == "" {
return Step{}, fmt.Errorf("COPY requires <src>... <dst>: %q", raw)
}
parts := strings.Fields(rest)
if len(parts) < 2 {
return Step{}, fmt.Errorf("COPY requires <src>... <dst>: %q", raw)
}
// Last argument is the destination, everything before is sources.
dst := parts[len(parts)-1]
srcs := parts[:len(parts)-1]
return Step{Kind: KindCOPY, Raw: raw, Srcs: srcs, Dst: dst}, nil
}