-
Notifications
You must be signed in to change notification settings - Fork 16
Initial spike on a tool to help maintainers with KEPs #29
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
justinsb
wants to merge
1
commit into
kubernetes-sigs:main
Choose a base branch
from
justinsb:add_kep_explorer
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+887
−0
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| Simple KEP explorer website. | ||
|
|
||
| This is an experiment to see if we can make it easier for | ||
| maintainers to work with KEPs. | ||
|
|
||
| It doesn't do much yet, it is mostly setting up a framework | ||
| for us to start to put value-add ideas. | ||
|
|
||
| ## Running | ||
|
|
||
| First, you should check out the KEPs repo: | ||
|
|
||
| ``` | ||
| git clone https://github.com/kubernetes/enhancements.git | ||
| ``` | ||
|
|
||
| Then, you can run the website: | ||
| ``` | ||
| go run . | ||
| ``` | ||
|
|
||
| Open your browser and go to [http://localhost:8080](http://localhost:8080) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| # Kubernetes KEP Explorer | ||
|
|
||
| This website is a simple website that allows the user to explore kubernetes KEPs. | ||
|
|
||
| We will start with basic "display" features, | ||
| and then add more features over time that streamline the KEP process, | ||
| for maintainers as well as for contributors. | ||
|
|
||
| ## Features | ||
|
|
||
| ### Basic Display Features | ||
|
|
||
| We should be able to display a list of KEPs, | ||
| and for each KEP we have a landing page that displays the KEP content. | ||
|
|
||
| Initially we link to the full KEP content from the landing page, | ||
| showing only keep metadata for each KEP. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| module sigs.k8s.io/maintainers/experiments/keptain | ||
|
|
||
| go 1.23 | ||
|
|
||
| toolchain go1.23.5 | ||
|
|
||
| require ( | ||
| github.com/yuin/goldmark v1.7.8 | ||
| k8s.io/klog/v2 v2.130.1 | ||
| sigs.k8s.io/yaml v1.4.0 | ||
| ) | ||
|
|
||
| require github.com/go-logr/logr v1.4.1 // indirect |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= | ||
| github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= | ||
| github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= | ||
| github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= | ||
| github.com/yuin/goldmark v1.7.8 h1:iERMLn0/QJeHFhxSt3p6PeN9mGnvIKSpG9YYorDMnic= | ||
| github.com/yuin/goldmark v1.7.8/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E= | ||
| gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= | ||
| gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||
| k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= | ||
| k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= | ||
| sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= | ||
| sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| package main | ||
|
|
||
| import ( | ||
| "context" | ||
| "fmt" | ||
| "os" | ||
|
|
||
| "sigs.k8s.io/maintainers/experiments/keptain/pkg/store" | ||
| "sigs.k8s.io/maintainers/experiments/keptain/pkg/website" | ||
| ) | ||
|
|
||
| func main() { | ||
| if err := run(context.Background()); err != nil { | ||
| fmt.Fprintf(os.Stderr, "Error: %v\n", err) | ||
| os.Exit(1) | ||
| } | ||
| } | ||
|
|
||
| func run(ctx context.Context) error { | ||
| // Initialize the KEP repository | ||
| kepRepo, err := store.NewRepository("enhancements") | ||
| if err != nil { | ||
| return fmt.Errorf("error creating KEP repository: %w", err) | ||
| } | ||
|
|
||
| // Start the web server | ||
| server := website.NewServer(kepRepo) | ||
| return server.Run(":8080") | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| package model | ||
|
|
||
| // KEP represents a Kubernetes Enhancement Proposal | ||
| type KEP struct { | ||
| // Path is the path to the KEP file, relative to the repo base | ||
| Path string `json:"path"` | ||
|
|
||
| // Title is the title of the KEP | ||
| Title string `json:"title"` | ||
|
|
||
| // Number is the number of the KEP | ||
| Number string `json:"number"` | ||
|
|
||
| // Authors are the authors of the KEP | ||
| Authors []string `json:"authors"` | ||
|
|
||
| // Status is the status of the KEP | ||
| Status string `json:"status"` | ||
|
|
||
| // TextURL is the URL to the KEP README.md file | ||
| TextURL string `json:"textURL"` | ||
|
|
||
| // TextContents is the contents of the KEP README.md file | ||
| TextContents string `json:"-"` | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,172 @@ | ||
| package store | ||
|
|
||
| import ( | ||
| "fmt" | ||
| "os" | ||
| "path/filepath" | ||
| "strings" | ||
|
|
||
| "sigs.k8s.io/maintainers/experiments/keptain/pkg/model" | ||
| "sigs.k8s.io/yaml" | ||
| ) | ||
|
|
||
| // Repository represents a KEP repository | ||
| type Repository struct { | ||
| basePath string | ||
| keps map[string]*model.KEP | ||
| } | ||
|
|
||
| // NewRepository creates a new KEP repository instance | ||
| func NewRepository(basePath string) (*Repository, error) { | ||
| r := &Repository{ | ||
| basePath: basePath, | ||
| keps: make(map[string]*model.KEP), | ||
| } | ||
| if err := r.loadKEPs(); err != nil { | ||
| return nil, fmt.Errorf("error loading KEPs: %v", err) | ||
| } | ||
| return r, nil | ||
| } | ||
|
|
||
| func (r *Repository) loadKEPs() error { | ||
| // Walk the KEPs directory and load all KEPs | ||
| if err := filepath.Walk(r.basePath, func(path string, info os.FileInfo, err error) error { | ||
| if err != nil { | ||
| return err | ||
| } | ||
| if info.IsDir() { | ||
| return nil | ||
| } | ||
|
|
||
| // We assume there's a metadata file for each KEP called kep.yaml | ||
| if filepath.Base(path) != "kep.yaml" { | ||
| return nil | ||
| } | ||
|
|
||
| relativePath, err := filepath.Rel(r.basePath, path) | ||
| if err != nil { | ||
| return fmt.Errorf("error getting relative path: %w", err) | ||
| } | ||
|
|
||
| dir := filepath.Dir(path) | ||
| relativeDir := filepath.Dir(relativePath) | ||
|
|
||
| b, err := os.ReadFile(path) | ||
| if err != nil { | ||
| return fmt.Errorf("error reading KEP file: %w", err) | ||
| } | ||
| kep, err := r.parseKEPFile(b) | ||
| if err != nil { | ||
| // Log error but continue processing other KEPs | ||
| return fmt.Errorf("error parsing KEP %q: %w", path, err) | ||
| } | ||
|
|
||
| // use the (repo-relative) directory as the identifier for the KEP | ||
| kep.Path = relativeDir | ||
|
|
||
| // See if we have a README.md file | ||
| { | ||
| readme := filepath.Join(dir, "README.md") | ||
| readmeBytes, err := os.ReadFile(readme) | ||
| if err != nil { | ||
| if !os.IsNotExist(err) { | ||
| return fmt.Errorf("error getting README.md: %w", err) | ||
| } | ||
| return nil | ||
| } | ||
|
|
||
| if err == nil { | ||
| kep.TextContents = string(readmeBytes) | ||
| kep.TextURL = fmt.Sprintf("https://github.com/kubernetes/enhancements/blob/master/%s", filepath.Join(relativeDir, "README.md")) | ||
| } | ||
| } | ||
| r.keps[kep.Path] = kep | ||
| return nil | ||
| }); err != nil { | ||
| return fmt.Errorf("error walking KEPs: %w", err) | ||
| } | ||
|
|
||
| return nil | ||
| } | ||
|
|
||
| // ListKEPs returns all KEPs in the repository | ||
| // If query is provided, it will filter the KEPs based on the query | ||
| func (r *Repository) ListKEPs(query string) ([]*model.KEP, error) { | ||
| var ret []*model.KEP | ||
| for _, kep := range r.keps { | ||
| // Filter KEPs if search query is provided | ||
| match := true | ||
| if query != "" { | ||
| query = strings.ToLower(query) | ||
| if strings.Contains(strings.ToLower(kep.Title), query) || | ||
| strings.Contains(strings.ToLower(kep.Number), query) || | ||
| containsAuthor(kep.Authors, query) { | ||
| match = true | ||
| } | ||
| } | ||
|
|
||
| if match { | ||
| ret = append(ret, kep) | ||
| } | ||
| } | ||
| return ret, nil | ||
| } | ||
|
|
||
| func containsAuthor(authors []string, query string) bool { | ||
| for _, author := range authors { | ||
| if strings.Contains(strings.ToLower(author), query) { | ||
| return true | ||
| } | ||
| } | ||
| return false | ||
| } | ||
|
|
||
| // GetKEP returns a specific KEP by number | ||
| func (r *Repository) GetKEP(path string) (*model.KEP, error) { | ||
| kep, ok := r.keps[path] | ||
| if ok { | ||
| return kep, nil | ||
| } | ||
| return nil, fmt.Errorf("KEP %s not found", path) | ||
| } | ||
|
|
||
| // kepFile is the format used in the KEP file. | ||
| type kepFile struct { | ||
| Title string `json:"title"` | ||
| Number string `json:"kep-number"` | ||
| Authors []string `json:"authors"` | ||
| OwningSig string `json:"owning-sig"` | ||
| ParticipatingSigs []string `json:"participating-sigs"` | ||
| Reviewers []string `json:"reviewers"` | ||
| Approvers []string `json:"approvers"` | ||
| Editor string `json:"editor"` | ||
| CreationDate string `json:"creation-date"` | ||
| LastUpdated string `json:"last-updated"` | ||
| Status string `json:"status"` | ||
| SeeAlso []string `json:"see-also"` | ||
| Replaces []string `json:"replaces"` | ||
| SupersededBy []string `json:"superseded-by"` | ||
| } | ||
|
|
||
| // parseKEPFile parses a KEP yaml file | ||
| func (r *Repository) parseKEPFile(data []byte) (*model.KEP, error) { | ||
|
|
||
| var kep kepFile | ||
| if err := yaml.Unmarshal(data, &kep); err != nil { | ||
| return nil, fmt.Errorf("error parsing KEP yaml: %v", err) | ||
| } | ||
|
|
||
| // Extract additional metadata from the yaml | ||
| var rawMap map[string]interface{} | ||
| if err := yaml.Unmarshal(data, &rawMap); err != nil { | ||
| return nil, fmt.Errorf("error parsing KEP metadata: %v", err) | ||
| } | ||
|
|
||
| out := &model.KEP{ | ||
| Title: kep.Title, | ||
| Number: kep.Number, | ||
| Authors: kep.Authors, | ||
| Status: kep.Status, | ||
| } | ||
| return out, nil | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add
Createdto exposecreation-datefrom the KEP spec ?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good call. Right now the order is random (golang maps). I also want to get "in progress" KEPs into the same view, so we have an updated-at timestamp. I would like to claim that I didn't add the date because I figure it will change when we add support for PRs, but that would be a lie :-)