SD4 (step key parser): ParseStepKey reads both forms and answers the asker
<asker>.<node>.<id> and main's <node>.<id> (no asker); a third form is refused. sdk/buildrun follows. Builds: design 47 G §484, §649 Claude-Session: https://claude.ai/code/session_01JG73KEq4zoDfM9WtYpcQgZ
This commit is contained in:
@@ -164,7 +164,7 @@ func EndStale(scan Scan, node string, before time.Time, logf func(string, ...any
|
||||
}
|
||||
var ended []string
|
||||
for _, k := range keys {
|
||||
n, _, perr := buildstep.ParseStepKey(k)
|
||||
_, n, _, perr := buildstep.ParseStepKey(k)
|
||||
if perr != nil || n != node {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -68,3 +68,22 @@ func TestTwoIDsInOneMillisecondDiffer(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 1966 (G §484, §649): ParseStepKey reads both forms of a key, main's answering no asker, and refuses a third.
|
||||
func TestParseStepKeyReadsBothFormsAndRefusesAThird(t *testing.T) {
|
||||
for _, tc := range []struct{ key, asker, node, id string }{
|
||||
{"ace.abc123", "", "ace", "abc123"},
|
||||
{"runner.ace.abc123", "runner", "ace", "abc123"},
|
||||
{"builder.g14.x1", "builder", "g14", "x1"},
|
||||
} {
|
||||
asker, node, id, err := ParseStepKey(tc.key)
|
||||
if err != nil || asker != tc.asker || node != tc.node || id != tc.id {
|
||||
t.Errorf("%s: %q %q %q %v", tc.key, asker, node, id, err)
|
||||
}
|
||||
}
|
||||
for _, key := range []string{"ace", "a.b.c.d", "Runner.ace.abc123", "runner.ace.ABC", ".ace.abc123", "ace."} {
|
||||
if _, _, _, err := ParseStepKey(key); err == nil {
|
||||
t.Errorf("%q was read as a step key", key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+18
-8
@@ -68,6 +68,7 @@ func Ended(s Step) bool { return s.State != StepRunning && s.State != StepAgain
|
||||
var (
|
||||
nameOK = regexp.MustCompile(`^[-/_=a-zA-Z0-9]+$`)
|
||||
stepIDOK = regexp.MustCompile(`^[a-z0-9]+$`)
|
||||
askerOK = regexp.MustCompile(`^[a-z][a-z0-9-]*$`)
|
||||
askIDOK = regexp.MustCompile(`^[0-9a-z]{8,9}[0-9a-f]{6}$`)
|
||||
)
|
||||
|
||||
@@ -89,22 +90,31 @@ func StepKey(node, id string) (string, error) {
|
||||
return node + "." + id, nil
|
||||
}
|
||||
|
||||
// ParseStepKey is the inverse of StepKey.
|
||||
func ParseStepKey(key string) (node, id string, err error) {
|
||||
node, id, ok := strings.Cut(key, ".")
|
||||
if !ok {
|
||||
return "", "", fmt.Errorf("buildstep: %q is not a step key, <node>.<id>", key)
|
||||
// ParseStepKey reads both forms of a step's key (design 47 G §484, §649): `<asker>.<node>.<id>`, and main's `<node>.<id>`,
|
||||
// which answers no asker (an empty asker). A third form is refused.
|
||||
func ParseStepKey(key string) (asker, node, id string, err error) {
|
||||
parts := strings.Split(key, ".")
|
||||
switch len(parts) {
|
||||
case 2:
|
||||
node, id = parts[0], parts[1]
|
||||
case 3:
|
||||
asker, node, id = parts[0], parts[1], parts[2]
|
||||
if !askerOK.MatchString(asker) {
|
||||
return "", "", "", fmt.Errorf("buildstep: %q is not a step key: %q is not an asker", key, asker)
|
||||
}
|
||||
default:
|
||||
return "", "", "", fmt.Errorf("buildstep: %q is not a step key, <asker>.<node>.<id> or <node>.<id>", key)
|
||||
}
|
||||
if _, err := StepKey(node, id); err != nil {
|
||||
return "", "", fmt.Errorf("buildstep: %q is not a step key: %w", key, err)
|
||||
return "", "", "", fmt.Errorf("buildstep: %q is not a step key: %w", key, err)
|
||||
}
|
||||
return node, id, nil
|
||||
return asker, node, id, nil
|
||||
}
|
||||
|
||||
// ParseAskStepKey is ParseStepKey for the key of a pulled ask's step, whose id is the ask's (base-36 milliseconds
|
||||
// and six hex digits).
|
||||
func ParseAskStepKey(key string) (node, askID string, err error) {
|
||||
node, askID, err = ParseStepKey(key)
|
||||
_, node, askID, err = ParseStepKey(key)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user