Merge remote-tracking branch 'origin/main' into feat/r4g
mesh/delivery superseded: a newer head of the same pull request
mesh/check failed: unit: git.novox.be/novox/mesh-catalog/chec… | b85d65b53f9e on 3bec357968da | judged by the candidate's mesh-build-go at f4ff9305030a
mesh/delivery superseded: a newer head of the same pull request
mesh/check failed: unit: git.novox.be/novox/mesh-catalog/chec… | b85d65b53f9e on 3bec357968da | judged by the candidate's mesh-build-go at f4ff9305030a
This commit is contained in:
@@ -320,7 +320,7 @@ func syncer(t *testing.T, f *fakeForge) (*Syncer, *[]string) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var said []string
|
||||
return &Syncer{Forge: forgehttp.Client{URL: srv.URL, User: admin, PasswordFile: pw}, Org: org, Admin: admin, Tools: tools,
|
||||
return &Syncer{Forge: forgehttp.Client{URL: srv.URL, User: admin, PasswordFile: pw}, Org: org, Admin: admin, Tools: tools, KeepMainRules: true,
|
||||
Log: func(format string, a ...any) { said = append(said, fmt.Sprintf(format, a...)) }}, &said
|
||||
}
|
||||
|
||||
|
||||
@@ -48,8 +48,9 @@ func runSync() int {
|
||||
toolsAccount = forgehttp.ToolsAccount
|
||||
}
|
||||
s := &Syncer{Forge: forge, Org: "novox", Admin: forge.User, Tools: toolsAccount,
|
||||
Log: func(format string, a ...any) { fmt.Printf(format+"\n", a...) },
|
||||
WaitFor: 3 * time.Minute, Poll: 5 * time.Second}
|
||||
Log: func(format string, a ...any) { fmt.Printf(format+"\n", a...) },
|
||||
KeepMainRules: ReadKeepMainRules(file),
|
||||
WaitFor: 3 * time.Minute, Poll: 5 * time.Second}
|
||||
value, err := ReadValue(file)
|
||||
if err != nil {
|
||||
o := Outcome{At: time.Now().UTC(), Org: s.Org, Failed: err.Error()}
|
||||
|
||||
@@ -58,12 +58,18 @@ func TestManifestDeclaresTheValueAndItsTrigger(t *testing.T) {
|
||||
}
|
||||
filled := filepath.Join(t.TempDir(), "repositories.json")
|
||||
value := `{"mesh-delivery":{"private":false,"description":"d"}}`
|
||||
if err := os.WriteFile(filled, []byte(strings.ReplaceAll(string(text), "${config:"+ConfigKey+"}", value)), 0o600); err != nil {
|
||||
if err := os.WriteFile(filled, []byte(strings.ReplaceAll(strings.ReplaceAll(string(text), "${config:"+ConfigKey+"}", value), "${config:keep-main-rules}", "false")), 0o600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if v, err := ReadValue(filled); err != nil || len(v) != 1 || v["mesh-delivery"].Description != "d" {
|
||||
t.Errorf("the filled template reads as %v, %v; want mesh-delivery", v, err)
|
||||
}
|
||||
if ReadKeepMainRules(filled) {
|
||||
t.Error("the filled template with keep-main-rules false reads as true")
|
||||
}
|
||||
if k := m.Config["keep-main-rules"]; k == nil || k["type"] != "boolean" || k["default"] != false {
|
||||
t.Errorf("config keep-main-rules = %v, want a boolean defaulting to false", k)
|
||||
}
|
||||
for _, r := range m.Resources {
|
||||
if r["type"] == "process" && has(strs(r["restart-on"]), file["id"].(string)) {
|
||||
process = r
|
||||
|
||||
@@ -103,6 +103,30 @@ type Repo struct {
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
// LeftAsSet is what a pass says when keep-main-rules is false.
|
||||
const LeftAsSet = "main's rules are left as set at the forge (keep-main-rules is false): no existing repository's protection was changed"
|
||||
|
||||
// ReadKeepMainRules reads the setting keep-main-rules from the file the mesh renders; absent or not true is false.
|
||||
func ReadKeepMainRules(file string) bool {
|
||||
raw, err := os.ReadFile(file)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
var whole struct {
|
||||
Keep any `json:"keep-main-rules"`
|
||||
}
|
||||
if json.Unmarshal(raw, &whole) != nil {
|
||||
return false
|
||||
}
|
||||
switch v := whole.Keep.(type) {
|
||||
case bool:
|
||||
return v
|
||||
case string:
|
||||
return v == "true"
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Value is the config value: each repository of the organisation by name.
|
||||
type Value map[string]Repo
|
||||
|
||||
@@ -147,6 +171,8 @@ type Outcome struct {
|
||||
Org string `json:"org"`
|
||||
Failed string `json:"failed,omitempty"` // the pass did not run: why
|
||||
Repositories []Result `json:"repositories"`
|
||||
// MainRules says, when keep-main-rules is false, that no existing repository's protection was changed.
|
||||
MainRules string `json:"main_rules,omitempty"`
|
||||
}
|
||||
|
||||
// Refused names the repositories a step of was refused.
|
||||
@@ -167,7 +193,11 @@ type Syncer struct {
|
||||
Org string // the organisation, novox
|
||||
Admin string // the setup login, under whose own account a repository is made
|
||||
Tools string // the tools' account, the only one that merges into main
|
||||
Log func(format string, a ...any)
|
||||
// KeepMainRules is the setting keep-main-rules: only when true does a pass put right the main rule of a repository
|
||||
// that already exists or swap statuses on one the value does not list. False (the default) makes new repositories
|
||||
// and leaves every existing repository's protection as people set it at the forge.
|
||||
KeepMainRules bool
|
||||
Log func(format string, a ...any)
|
||||
// WaitFor is how long to wait for the forge to answer; none when zero.
|
||||
WaitFor, Poll time.Duration
|
||||
}
|
||||
@@ -199,6 +229,11 @@ func (s *Syncer) Sync(ctx context.Context, v Value) (Outcome, error) {
|
||||
}
|
||||
out.Repositories = append(out.Repositories, r)
|
||||
}
|
||||
if !s.KeepMainRules {
|
||||
out.MainRules = LeftAsSet
|
||||
s.say("%s", LeftAsSet)
|
||||
return out, nil
|
||||
}
|
||||
for _, r := range s.switchUnlisted(ctx, v) {
|
||||
if r.Did == DidRefused {
|
||||
s.say("refused: %s/%s at the step %s: %s", s.Org, r.Name, r.Step, r.Said)
|
||||
@@ -306,6 +341,9 @@ func (s *Syncer) one(ctx context.Context, name string, r Repo) Result {
|
||||
if err != nil {
|
||||
return refused(name, StepRead, err)
|
||||
}
|
||||
if inOrg && !s.KeepMainRules {
|
||||
return Result{Name: name, Did: DidUnchanged}
|
||||
}
|
||||
if inOrg {
|
||||
changed, err := s.putRight(ctx, s.Org, name)
|
||||
if err != nil {
|
||||
|
||||
@@ -430,3 +430,50 @@ func keysOf(m map[string]string) []string {
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// The operator, 2026-10-23: "disable the gitea branch protection reset thing". With keep-main-rules false (the
|
||||
// default) a pass makes new repositories but writes nothing to any existing repository's protection, listed or not.
|
||||
func TestKeepMainRulesFalseChangesNoExistingProtection(t *testing.T) {
|
||||
f := newFake()
|
||||
hq := f.add("novox/hq", map[string]map[string]any{mainRule: {
|
||||
"enable_push": true, "enable_status_check": true, "status_check_contexts": []any{"mesh/merge-gate"},
|
||||
}}, "main")
|
||||
old := f.add("novox/old", map[string]map[string]any{mainRule: {
|
||||
"enable_push": true, "enable_status_check": true, "status_check_contexts": []any{"mesh/merge-gate"},
|
||||
}}, "main")
|
||||
s, said := syncer(t, f)
|
||||
s.KeepMainRules = false
|
||||
o := run(t, s, Value{"hq": {}, "fresh": {}})
|
||||
|
||||
for _, c := range f.writes() {
|
||||
if strings.Contains(c, "/novox/hq") || strings.Contains(c, "/novox/old") || strings.Contains(c, "/repos/novox/hq") || strings.Contains(c, "/repos/novox/old") {
|
||||
t.Errorf("an existing repository was written: %s", c)
|
||||
}
|
||||
}
|
||||
if hq.Rules[mainRule]["enable_push"] != true || old.Rules[mainRule]["enable_push"] != true {
|
||||
t.Error("an existing repository's rule was changed")
|
||||
}
|
||||
if f.repos["novox/fresh"] == nil {
|
||||
t.Error("the new repository was not made")
|
||||
}
|
||||
if r := result(o, "hq"); r.Did != DidUnchanged {
|
||||
t.Errorf("hq outcome %+v", r)
|
||||
}
|
||||
if o.MainRules != LeftAsSet || !strings.Contains(strings.Join(*said, "\n"), "keep-main-rules is false") {
|
||||
t.Errorf("the outcome does not say the rules were left: %q", o.MainRules)
|
||||
}
|
||||
}
|
||||
|
||||
func TestReadKeepMainRules(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
for body, want := range map[string]bool{`{}`: false, `{"keep-main-rules": false}`: false, `{"keep-main-rules": true}`: true, `{"keep-main-rules": "true"}`: true, `not json`: false} {
|
||||
f := filepath.Join(dir, "v.json")
|
||||
_ = os.WriteFile(f, []byte(body), 0o644)
|
||||
if got := ReadKeepMainRules(f); got != want {
|
||||
t.Errorf("%s: got %v, want %v", body, got, want)
|
||||
}
|
||||
}
|
||||
if ReadKeepMainRules(filepath.Join(dir, "missing")) {
|
||||
t.Error("a missing file must be false")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user