From bacb9d80ebd5d682c267f1055ff4ca860558c93e Mon Sep 17 00:00:00 2001 From: Evan Anderson Date: Tue, 7 Jan 2025 13:25:21 -0800 Subject: [PATCH] Add governance category and mechanism for criteria renaming Signed-off-by: Evan Anderson --- README.md | 3 ++- baseline.yaml | 27 ++++++++++++++++++++------- cmd/main.go | 27 +++++++++++++++++++++++++++ cmd/template.md | 5 +++++ 4 files changed, 54 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index d224688..5d51b52 100644 --- a/README.md +++ b/README.md @@ -20,8 +20,9 @@ Each entry has the following values: - Access Control - Build & Release - Documentation - - Quality + - Governance - Legal + - Quality - **Criterion**: - A concise statement of the requirement - Contains `MUST` or `MUST NOT` and is written in present tense diff --git a/baseline.yaml b/baseline.yaml index 8c425d4..f35d0ee 100644 --- a/baseline.yaml +++ b/baseline.yaml @@ -359,12 +359,14 @@ criteria: scorecard_probe: # TODO - id: OSPS-DO-01 + replaced_by: OSPS-GV-01 + + - id: OSPS-GV-01 maturity_level: 1 - category: Documentation + category: Governance criterion: | The project MUST have one or more mechanisms - for public discussions about proposed - changes and usage obstacles. + for public discussions about project functionality. rationale: | Encourages open communication and collaboration within the project community, @@ -372,19 +374,24 @@ criteria: discuss proposed changes or usage challenges. details: | - Establish one or more mechanisms for public + Establish and document one or more mechanisms for public discussions within the project, such as mailing lists, instant messaging, or issue trackers, to facilitate open communication - and feedback. + and feedback. The presence of a repository-linked issue tracker, + wiki, or a "Feedback" section in the project's README file + would meet this criterion. control_mappings: # TODO security_insights_value: # TODO scorecard_probe: - # None yet - id: OSPS-DO-02 + replaced_by: OSPS-GV-02 + + - id: OSPS-GV-02 maturity_level: 1 - category: Documentation + category: Governance criterion: | The project documentation MUST include an explanation of the contribution process. @@ -479,8 +486,11 @@ criteria: security_insights_value: # TODO - id: OSPS-DO-06 + replaced_by: OSPS-GV-03 + + - id: OSPS-GV-03 maturity_level: 2 - category: Documentation + category: Governance criterion: | The project documentation MUST include a guide for code contributors that includes @@ -595,6 +605,9 @@ criteria: - # TODO: this is about policy, but we should also look for evidence of SCA - id: OSPS-DO-11 + replaced_by: OSPS-GV-04 + + - id: OSPS-GV-04 maturity_level: 2 category: Documentation criterion: | diff --git a/cmd/main.go b/cmd/main.go index 9e992c2..f353368 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -5,6 +5,7 @@ import ( "log" "os" "path/filepath" + "reflect" "regexp" "slices" "strings" @@ -18,6 +19,8 @@ import ( // Struct for representing each entry type Criterion struct { ID string `yaml:"id"` + // If ReplacedBy is set, no other fields (beyond ID) should be set + ReplacedBy string `yaml:"replaced_by"` MaturityLevel int `yaml:"maturity_level"` Category string `yaml:"category"` CriterionText string `yaml:"criterion"` @@ -110,6 +113,7 @@ func readYAMLFile() error { return fmt.Errorf("error decoding YAML: %v", err) } var entryIDs []string + retiredIDs := map[string]string{} for i, entry := range baseline.Criteria { // if entry in entryIDs if slices.Contains(entryIDs, entry.ID) { @@ -118,6 +122,14 @@ func readYAMLFile() error { if entry.ID == "" { return fmt.Errorf("missing ID for criterion entry %d: %s", i, entry.ID) } + if entry.ReplacedBy != "" { + retiredIDs[entry.ID] = entry.ReplacedBy + // minimalEntry := + if !reflect.DeepEqual(entry, Criterion{ID: entry.ID, ReplacedBy: entry.ReplacedBy}){ + return fmt.Errorf("retired criterion entry %s has additional fields", entry.ID) + } + continue + } if entry.CriterionText == "" { return fmt.Errorf("missing criterion text for entry #%d: %s", i, entry.ID) } @@ -129,6 +141,20 @@ func readYAMLFile() error { } entryIDs = append(entryIDs, entry.ID) } + // ensure that retired IDs reference only valid IDs + for retired, replacement := range retiredIDs { + if !slices.Contains(entryIDs, replacement) { + return fmt.Errorf("retired criterion %s references invalid replacement %s", retired, replacement) + } + if _, ok := retiredIDs[replacement]; ok { + return fmt.Errorf("retired criterion %s references another retired criterion %s", retired, replacement) + } + } + + slices.SortFunc(baseline.Criteria, func(a, b Criterion) int { + return strings.Compare(a.ID, b.ID) + }) + Data = baseline return nil } @@ -238,6 +264,7 @@ func generateBaselineMdFile() (err error) { "asLink": func(s string) string { return asLinkTemplateFunction(s) }, + "toLower": strings.ToLower, }).Parse(string(templateContent)) if err != nil { return fmt.Errorf("error parsing template: %w", err) diff --git a/cmd/template.md b/cmd/template.md index a5057fe..3d0f50b 100644 --- a/cmd/template.md +++ b/cmd/template.md @@ -49,6 +49,9 @@ For more information on the project and to make contributions, visit the [GitHub ### {{ .ID }} +{{ if ne .ReplacedBy "" }} +**Replaced By:** [{{ .ReplacedBy }}](#{{ .ReplacedBy | toLower }}) +{{ else }} **Criterion:** {{ .CriterionText | addLinks }} @@ -87,6 +90,8 @@ _No security insights identified._ _No scorecard probe identified._ {{- end }} +{{- end }} + --- {{- end }}