indexer, mesh-catalog contract: a branch named candidate or entry is refused as reserved branch name
Builds: design 47 G §704 (branch named candidate or entry) Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h
This commit is contained in:
@@ -948,10 +948,11 @@ type RegistrationAsked struct {
|
||||
|
||||
// The values of RegistrationAsked's refused.
|
||||
const (
|
||||
RegistrationAskedRefusedUnprotectedBranch = "unprotected branch"
|
||||
RegistrationAskedRefusedReviewRequired = "review required"
|
||||
RegistrationAskedRefusedUnknownModule = "unknown module"
|
||||
RegistrationAskedRefusedNothingMoves = "nothing moves"
|
||||
RegistrationAskedRefusedUnprotectedBranch = "unprotected branch"
|
||||
RegistrationAskedRefusedReservedBranchName = "reserved branch name"
|
||||
RegistrationAskedRefusedReviewRequired = "review required"
|
||||
RegistrationAskedRefusedUnknownModule = "unknown module"
|
||||
RegistrationAskedRefusedNothingMoves = "nothing moves"
|
||||
)
|
||||
|
||||
// StepStatuses is the status of each pipeline step (design 47 *Pipeline steps and the policy*); a step left out keeps the module's.
|
||||
|
||||
@@ -151,6 +151,8 @@ func TestFixtures(t *testing.T) {
|
||||
decode: func(b []byte) (any, error) { var v RegisterInput; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "register refused for an unprotected branch", subject: "", given: "{\"module\":\"nats\",\"refused\":\"unprotected branch\",\"words\":\"the branch beta of novox/mesh-catalog admits a direct push\"}", wire: "{\"module\":\"nats\",\"refused\":\"unprotected branch\",\"words\":\"the branch beta of novox/mesh-catalog admits a direct push\"}",
|
||||
decode: func(b []byte) (any, error) { var v RegisterAnswer; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "register refused for a reserved branch name", subject: "", given: "{\"module\":\"nats\",\"refused\":\"reserved branch name\",\"words\":\"the branch candidate of novox/mesh-catalog is a name the catalog keeps for its own revisions\"}", wire: "{\"module\":\"nats\",\"refused\":\"reserved branch name\",\"words\":\"the branch candidate of novox/mesh-catalog is a name the catalog keeps for its own revisions\"}",
|
||||
decode: func(b []byte) (any, error) { var v RegisterAnswer; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "policy moves one artifact's unit step", subject: "", given: "{\"artifact\":\"program\",\"module\":\"nats\",\"steps\":{\"unit\":\"bypass\"},\"why\":\"its tests need hardware\"}", wire: "{\"artifact\":\"program\",\"module\":\"nats\",\"steps\":{\"unit\":\"bypass\"},\"why\":\"its tests need hardware\"}",
|
||||
decode: func(b []byte) (any, error) { var v PolicyInput; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "policy asked, naming the moved status", subject: "", given: "{\"ask\":\"ask-m2kx9a1b3c4d5e\",\"lines\":[\"nats:program unit: required -\\u003e bypass\"],\"module\":\"nats\"}", wire: "{\"ask\":\"ask-m2kx9a1b3c4d5e\",\"lines\":[\"nats:program unit: required -\\u003e bypass\"],\"module\":\"nats\"}",
|
||||
|
||||
@@ -296,6 +296,15 @@
|
||||
"words": "the branch beta of novox/mesh-catalog admits a direct push"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "register refused for a reserved branch name",
|
||||
"of": "verb:register.answer",
|
||||
"given": {
|
||||
"module": "nats",
|
||||
"refused": "reserved branch name",
|
||||
"words": "the branch candidate of novox/mesh-catalog is a name the catalog keeps for its own revisions"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "policy moves one artifact's unit step",
|
||||
"of": "verb:policy.input",
|
||||
|
||||
@@ -2424,6 +2424,7 @@
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"unprotected branch",
|
||||
"reserved branch name",
|
||||
"review required",
|
||||
"unknown module",
|
||||
"nothing moves"
|
||||
|
||||
+1
-1
@@ -448,7 +448,7 @@ export interface RegistrationAsked {
|
||||
"lines"?: string[];
|
||||
"module": string;
|
||||
/** why no ask was raised */
|
||||
"refused"?: "unprotected branch" | "review required" | "unknown module" | "nothing moves";
|
||||
"refused"?: "unprotected branch" | "reserved branch name" | "review required" | "unknown module" | "nothing moves";
|
||||
/** the refusal in words, naming the branch, the step or the module */
|
||||
"words"?: string;
|
||||
}
|
||||
|
||||
@@ -205,7 +205,8 @@ func refused(module, reason, words string) meshcatalog.RegistrationAsked {
|
||||
return meshcatalog.RegistrationAsked{Module: module, Refused: reason, Words: words}
|
||||
}
|
||||
|
||||
// forbiddenBranch refuses the two names the catalog's keys use (§343).
|
||||
// forbiddenBranch refuses the two names the catalog's keys use (§343); the refusal's reason is `reserved branch name`
|
||||
// (design 47 G §704).
|
||||
func forbiddenBranch(b string) string {
|
||||
if b == "candidate" || b == "entry" {
|
||||
return fmt.Sprintf("the branch %q cannot be watched: the catalog keeps its revisions under <branch>.<revision> beside the key forms candidate and entry", b)
|
||||
@@ -218,24 +219,26 @@ func reviewRequired(s meshcatalog.StepStatuses) bool {
|
||||
return s.Review == meshcatalog.StepStatusesApprovalRequired
|
||||
}
|
||||
|
||||
// protect refuses a watched branch the forge does not protect (§5), naming it.
|
||||
func (r *Registry) protect(repository string, branches []string) (string, error) {
|
||||
// protect refuses a watched branch that is named as the catalog's keys are (reason `reserved branch name`, §704) or that
|
||||
// the forge does not protect (reason `unprotected branch`, §5), naming it; both empty when every branch is accepted.
|
||||
func (r *Registry) protect(repository string, branches []string) (reason, words string, err error) {
|
||||
for _, b := range branches {
|
||||
if w := forbiddenBranch(b); w != "" {
|
||||
return w, nil
|
||||
return meshcatalog.RegistrationAskedRefusedReservedBranchName, w, nil
|
||||
}
|
||||
a, err := r.Branch(repository, b)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("the seat git does not answer for %s %s: %w", repository, b, err)
|
||||
return "", "", fmt.Errorf("the seat git does not answer for %s %s: %w", repository, b, err)
|
||||
}
|
||||
if a.Error != "" {
|
||||
return "", fmt.Errorf("the seat git cannot read %s %s: %s", repository, b, a.Error)
|
||||
return "", "", fmt.Errorf("the seat git cannot read %s %s: %s", repository, b, a.Error)
|
||||
}
|
||||
if !a.Protected {
|
||||
return fmt.Sprintf("the branch %s of %s is not protected: no rule refuses a direct push and admits only merges", b, repository), nil
|
||||
return meshcatalog.RegistrationAskedRefusedUnprotectedBranch,
|
||||
fmt.Sprintf("the branch %s of %s is not protected: no rule refuses a direct push and admits only merges", b, repository), nil
|
||||
}
|
||||
}
|
||||
return "", nil
|
||||
return "", "", nil
|
||||
}
|
||||
|
||||
// stepLines names each step status a policy moves, one line each.
|
||||
@@ -467,12 +470,12 @@ func (r *Registry) Propose(ctx context.Context, verb, caller string, change json
|
||||
branches = []string{branch}
|
||||
}
|
||||
if len(branches) > 0 {
|
||||
words, err := r.protect(next.Origin, branches)
|
||||
reason, words, err := r.protect(next.Origin, branches)
|
||||
if err != nil {
|
||||
return meshcatalog.RegistrationAsked{}, err
|
||||
}
|
||||
if words != "" {
|
||||
return refused(module, meshcatalog.RegistrationAskedRefusedUnprotectedBranch, words), nil
|
||||
if reason != "" {
|
||||
return refused(module, reason, words), nil
|
||||
}
|
||||
}
|
||||
if len(lines) == 0 {
|
||||
|
||||
@@ -101,15 +101,12 @@ func TestRegistrationsAreWrittenOnTheirWarrantOnly(t *testing.T) {
|
||||
}{
|
||||
{"review", meshcatalog.RegistrationAskedRefusedReviewRequired, reg("a", meshcatalog.StepStatuses{Review: "required"}, "main")},
|
||||
{"unprotected", meshcatalog.RegistrationAskedRefusedUnprotectedBranch, reg("a", meshcatalog.StepStatuses{}, "beta")},
|
||||
{"candidate", meshcatalog.RegistrationAskedRefusedUnprotectedBranch, reg("a", meshcatalog.StepStatuses{}, "candidate")},
|
||||
{"candidate", meshcatalog.RegistrationAskedRefusedReservedBranchName, reg("a", meshcatalog.StepStatuses{}, "candidate")},
|
||||
{"entry", meshcatalog.RegistrationAskedRefusedReservedBranchName, reg("a", meshcatalog.StepStatuses{}, "entry")},
|
||||
} {
|
||||
if c.name == "candidate" {
|
||||
// the name is refused whatever the forge says of it
|
||||
c.why = meshcatalog.RegistrationAskedRefusedUnprotectedBranch
|
||||
}
|
||||
got, err := g.r.Propose(ctx, "register", "session@node", c.change)
|
||||
if err != nil || got.Refused == "" || got.Ask != "" || got.Words == "" {
|
||||
t.Fatalf("%s: answer %+v, %v: want a refusal naming why and no ask", c.name, got, err)
|
||||
if err != nil || got.Refused == "" || got.Ask != "" || got.Words == "" || got.Refused != c.why {
|
||||
t.Fatalf("%s: answer %+v, %v: want a refusal for %q naming why and no ask (design 47 G §704)", c.name, got, err, c.why)
|
||||
}
|
||||
}
|
||||
if len(g.asks) != 0 {
|
||||
|
||||
Reference in New Issue
Block a user