From 2cb3b0f005d67869340158fde337dd8745058fa1 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Thu, 15 Jun 2017 19:48:05 +0200 Subject: [PATCH] Remove duplication --- .travis.yml | 1 + Makefile | 14 +- generator/.gitignore | 1 + generator/Dockerfile | 8 +- generator/Dockerfile.test | 8 - generator/README.md | 6 +- generator/app.go | 163 ++++++++---------- generator/app_test.go | 127 +++++++++++++- generator/{sig_list.tmpl => list.tmpl} | 2 +- generator/{sig_index.tmpl => sig_readme.tmpl} | 0 generator/testdata/custom_content.md | 2 - generator/testdata/example.md | 2 - generator/testdata/no_custom_content.md | 2 - generator/testdata/sigs.yaml | 5 + generator/{wg_index.tmpl => wg_readme.tmpl} | 2 +- sigs.yaml | 4 +- 16 files changed, 217 insertions(+), 130 deletions(-) create mode 100644 generator/.gitignore delete mode 100644 generator/Dockerfile.test rename generator/{sig_list.tmpl => list.tmpl} (75%) rename generator/{sig_index.tmpl => sig_readme.tmpl} (100%) create mode 100644 generator/testdata/sigs.yaml rename generator/{wg_index.tmpl => wg_readme.tmpl} (96%) diff --git a/.travis.yml b/.travis.yml index 1b870ea51e6..e6db9e8acc2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,4 +6,5 @@ services: - docker script: + - make test-unit - bash ./scripts/verify.sh diff --git a/Makefile b/Makefile index 7406d2810cd..1061bd6304e 100644 --- a/Makefile +++ b/Makefile @@ -1,3 +1,5 @@ +IMAGE_NAME=kube-communitydocs + all: \ build-image \ gen-docs \ @@ -6,14 +8,10 @@ reset-docs: git checkout HEAD -- sig-list.md sig-* build-image: - docker build -t sigdocs -f generator/Dockerfile generator - -gen-doc: - docker run -e WG=${WG} -e SIG=${SIG} -v $(shell pwd):/go/src/app sigdocs + docker build -t $(IMAGE_NAME) -f generator/Dockerfile generator gen-docs: - docker run -v $(shell pwd):/go/src/app sigdocs + docker run --rm -e WG -e SIG -v $(shell pwd):/go/src/app/generated $(IMAGE_NAME) app -test: - docker build -t sigdocs-test -f generator/Dockerfile.test generator - docker run sigdocs-test +test: build-image + docker run --rm $(IMAGE_NAME) go test -v ./... diff --git a/generator/.gitignore b/generator/.gitignore new file mode 100644 index 00000000000..9ab870da897 --- /dev/null +++ b/generator/.gitignore @@ -0,0 +1 @@ +generated/ diff --git a/generator/Dockerfile b/generator/Dockerfile index f1b21921793..deffa8a9ba6 100644 --- a/generator/Dockerfile +++ b/generator/Dockerfile @@ -1 +1,7 @@ -FROM golang:1.8-onbuild +FROM golang:1.8 + +WORKDIR /go/src/app +COPY . . + +RUN go-wrapper download +RUN go-wrapper install diff --git a/generator/Dockerfile.test b/generator/Dockerfile.test deleted file mode 100644 index f88078313d8..00000000000 --- a/generator/Dockerfile.test +++ /dev/null @@ -1,8 +0,0 @@ -FROM golang:1.8 - -WORKDIR /go/src/app -COPY . . - -RUN go-wrapper download - -CMD ["go", "test", "./..."] diff --git a/generator/README.md b/generator/README.md index 3fd2230ebc5..d2382aa525f 100644 --- a/generator/README.md +++ b/generator/README.md @@ -33,9 +33,9 @@ make all To build docs for one SIG, run these commands: ```bash -make SIG=sig-apps gen-doc -make SIG=sig-testing gen-doc -make WG=resource-management gen-doc +make SIG=sig-apps gen-docs +make SIG=sig-testing gen-docs +make WG=resource-management gen-docs ``` where the `SIG` or `WG` var refers to the directory being built. diff --git a/generator/app.go b/generator/app.go index 9cfb45b84f2..633441319eb 100644 --- a/generator/app.go +++ b/generator/app.go @@ -30,24 +30,29 @@ import ( ) var ( - sigsYamlFile = "sigs.yaml" - sigIndexTemplate = "sig_index.tmpl" - wgIndexTemplate = "wg_index.tmpl" - listTemplate = "sig_list.tmpl" - headerTemplate = "header.tmpl" - sigListOutput = "sig-list.md" - sigIndexOutput = "README.md" - githubTeamNames = []string{"misc", "test-failures", "bugs", "feature-requests", "proposals", "pr-reviews", "api-reviews"} - beginMarker = "" - endMarker = "" + readmeTemplate = "readme.tmpl" + listTemplate = "list.tmpl" + headerTemplate = "header.tmpl" + + sigsYamlFile = "sigs.yaml" + sigListOutput = "sig-list.md" + indexFilename = "README.md" + baseOutputDir = "generated" + + githubTeamNames = []string{"misc", "test-failures", "bugs", "feature-requests", "proposals", "pr-reviews", "api-reviews"} + beginMarker = "" + endMarker = "" ) +// Lead represents a lead engineer for a particular group. There are usually +// 2 per group. type Lead struct { Name string Company string GitHub string } +// Meeting represents a regular meeting for a group. type Meeting struct { Day string UTC string @@ -55,6 +60,7 @@ type Meeting struct { Frequency string } +// Contact represents the various contact points for a group. type Contact struct { Slack string MailingList string `yaml:"mailing_list"` @@ -63,7 +69,8 @@ type Contact struct { GithubTeamNames []string } -type Sig struct { +// Group represents either a Special Interest Group (SIG) or a Working Group (WG) +type Group struct { Name string Dir string MissionStatement string `yaml:"mission_statement"` @@ -74,28 +81,31 @@ type Sig struct { Contact Contact } -type Wg struct { - Name string - Dir string - MissionStatement string `yaml:"mission_statement"` - Organizers []Lead - Meetings []Meeting - MeetingURL string `yaml:"meeting_url"` - MeetingArchiveURL string `yaml:"meeting_archive_url"` - Contact Contact +// DirName returns the directory that a group's documentation will be +// generated into. It is composed of a prefix (sig for SIGs and wg for WGs), +// and a formatted version of the group's name (in kebab case). +func (e *Group) DirName(prefix string) string { + return fmt.Sprintf("%s-%s", prefix, strings.ToLower(strings.Replace(e.Name, " ", "-", -1))) } -type Context struct { - Sigs []Sig - WorkingGroups []Wg -} +// SetupGitHubTeams will iterate over all the possible teams available to a +// group (these are defined by the Kubernetes organisation) and populate a +// list using the group's prefix. +func (e *Group) SetupGitHubTeams(prefix string) { + ghPrefix := e.Contact.GithubTeamPrefix + if ghPrefix == "" { + ghPrefix = e.DirName(prefix) + } -type SigEntries struct { - Sigs []Sig + for _, gtn := range githubTeamNames { + e.Contact.GithubTeamNames = append(e.Contact.GithubTeamNames, fmt.Sprintf("%s-%s", ghPrefix, gtn)) + } } -type WgEntries struct { - WorkingGroups []Wg +// Context is the context for the sigs.yaml file. +type Context struct { + Sigs []Group + WorkingGroups []Group } func pathExists(path string) bool { @@ -105,8 +115,7 @@ func pathExists(path string) bool { func createDirIfNotExists(path string) error { if !pathExists(path) { - fmt.Printf("%s directory does not exist, creating\n", path) - return os.Mkdir(path, 0755) + return os.MkdirAll(path, 0755) } return nil } @@ -137,13 +146,7 @@ func getExistingContent(path string) (string, error) { return strings.Join(captured, "\n"), nil } -func writeTemplate(templateFilePath, outputPath string, data interface{}) error { - wd, err := os.Getwd() - if err != nil { - return err - } - templatePath := filepath.Join(wd, templateFilePath) - +func writeTemplate(templatePath, outputPath string, data interface{}) error { // set up template t, err := template.ParseFiles(templatePath, headerTemplate) if err != nil { @@ -152,7 +155,7 @@ func writeTemplate(templateFilePath, outputPath string, data interface{}) error // create if not exists if !pathExists(outputPath) { - _, err := os.Create(outputPath) + _, err = os.Create(outputPath) if err != nil { return err } @@ -183,7 +186,6 @@ func writeTemplate(templateFilePath, outputPath string, data interface{}) error // custom content block writeCustomContentBlock(f, content) - fmt.Printf("Generated %s\n", outputPath) return nil } @@ -194,61 +196,33 @@ func writeCustomContentBlock(f *os.File, content string) { } } -func createReadmeFiles(ctx Context) error { - var selectedSig *string - if sig, ok := os.LookupEnv("SIG"); ok { - selectedSig = &sig +func createGroupReadme(groups []Group, prefix string) error { + // figure out if the user wants to generate one group + var selectedGroupName *string + if envVal, ok := os.LookupEnv(strings.ToUpper(prefix)); ok { + selectedGroupName = &envVal } - for _, sig := range ctx.Sigs { - dirName := fmt.Sprintf("sig-%s", strings.ToLower(strings.Replace(sig.Name, " ", "-", -1))) - if selectedSig != nil && *selectedSig != dirName { - fmt.Printf("Skipping %s\n", dirName) + for _, group := range groups { + group.Dir = group.DirName(prefix) + // skip generation if the user specified only one group + if selectedGroupName != nil && *selectedGroupName != group.Dir { + fmt.Printf("Skipping %s/README.md\n", group.Dir) continue } - createDirIfNotExists(dirName) - - prefix := sig.Contact.GithubTeamPrefix - if prefix == "" { - prefix = dirName - } - - for _, gtn := range githubTeamNames { - sig.Contact.GithubTeamNames = append(sig.Contact.GithubTeamNames, fmt.Sprintf("%s-%s", prefix, gtn)) - } + fmt.Printf("Generating %s/README.md\n", group.Dir) - outputPath := fmt.Sprintf("%s/%s", dirName, sigIndexOutput) - if err := writeTemplate(sigIndexTemplate, outputPath, sig); err != nil { + outputDir := filepath.Join(baseOutputDir, group.Dir) + if err := createDirIfNotExists(outputDir); err != nil { return err } - } - - var selectedWg *string - if wg, ok := os.LookupEnv("WG"); ok { - selectedWg = &wg - } - for _, wg := range ctx.WorkingGroups { - dirName := fmt.Sprintf("wg-%s", strings.ToLower(strings.Replace(wg.Name, " ", "-", -1))) - - if selectedWg != nil && *selectedWg != dirName { - fmt.Printf("Skipping %s\n", dirName) - continue - } - - createDirIfNotExists(dirName) - - prefix := wg.Contact.GithubTeamPrefix - if prefix == "" { - prefix = dirName - } - for _, gtn := range githubTeamNames { - wg.Contact.GithubTeamNames = append(wg.Contact.GithubTeamNames, fmt.Sprintf("%s-%s", prefix, gtn)) - } + group.SetupGitHubTeams(prefix) - outputPath := fmt.Sprintf("%s/%s", dirName, sigIndexOutput) - if err := writeTemplate(wgIndexTemplate, outputPath, wg); err != nil { + outputPath := filepath.Join(outputDir, indexFilename) + readmePath := fmt.Sprintf("%s_%s", prefix, readmeTemplate) + if err := writeTemplate(readmePath, outputPath, group); err != nil { return err } } @@ -256,12 +230,8 @@ func createReadmeFiles(ctx Context) error { return nil } -func createListFile(ctx Context) error { - return writeTemplate(listTemplate, sigListOutput, ctx) -} - func main() { - yamlData, err := ioutil.ReadFile(sigsYamlFile) + yamlData, err := ioutil.ReadFile(filepath.Join(baseOutputDir, sigsYamlFile)) if err != nil { log.Fatal(err) } @@ -273,19 +243,26 @@ func main() { } sort.Slice(ctx.Sigs, func(i, j int) bool { - return ctx.Sigs[i].Name >= ctx.Sigs[j].Name + return ctx.Sigs[i].Name <= ctx.Sigs[j].Name }) sort.Slice(ctx.WorkingGroups, func(i, j int) bool { - return ctx.WorkingGroups[i].Name >= ctx.WorkingGroups[j].Name + return ctx.WorkingGroups[i].Name <= ctx.WorkingGroups[j].Name }) - err = createReadmeFiles(ctx) + err = createGroupReadme(ctx.Sigs, "sig") + if err != nil { + log.Fatal(err) + } + + err = createGroupReadme(ctx.WorkingGroups, "wg") if err != nil { log.Fatal(err) } - err = createListFile(ctx) + fmt.Println("Generating sig-list.md") + outputPath := filepath.Join(baseOutputDir, sigListOutput) + err = writeTemplate(listTemplate, outputPath, ctx) if err != nil { log.Fatal(err) } diff --git a/generator/app_test.go b/generator/app_test.go index 1ef3a574818..c71471c72c2 100644 --- a/generator/app_test.go +++ b/generator/app_test.go @@ -17,7 +17,11 @@ limitations under the License. package main import ( + "fmt" "io/ioutil" + "os" + "path/filepath" + "reflect" "strings" "testing" ) @@ -33,6 +37,14 @@ func TestNonExistantDirIsCreated(t *testing.T) { } } +func TestExistantDirNotCreated(t *testing.T) { + dir := "./testdata" + err := createDirIfNotExists(dir) + if err != nil { + t.Fatalf("Received error creating dir: %v", err) + } +} + func TestGetExistingData(t *testing.T) { cases := []struct { path string @@ -105,13 +117,6 @@ content! data: map[string]string{"Message": "Hello!"}, expected: customContent, }, - { - templatePath: "./testdata/example.tmpl", - outputPath: "/tmp/non_existing_path.md", - expectErr: false, - data: map[string]string{"Message": "Hello!"}, - expected: "Last generated: ", - }, } for _, c := range cases { @@ -134,3 +139,111 @@ content! } } } + +func TestGroupDirName(t *testing.T) { + group := Group{Name: "Foo Bar"} + if group.DirName("sig") != "sig-foo-bar" { + t.Fatal("DirName incorrect") + } +} + +func TestSetupGithubTeams(t *testing.T) { + group := Group{Name: "Foo Bar"} + group.SetupGitHubTeams("sig") + + var expected []string + for _, ght := range githubTeamNames { + expected = append(expected, fmt.Sprintf("sig-foo-bar-%s", ght)) + } + + if !reflect.DeepEqual(group.Contact.GithubTeamNames, expected) { + t.Fatalf("%v does not match %v", group.Contact.GithubTeamNames, expected) + } +} + +func TestCustomPrefixSetupGithubTeams(t *testing.T) { + group := Group{Contact: Contact{GithubTeamPrefix: "foo"}} + group.SetupGitHubTeams("") + + var expected []string + for _, ght := range githubTeamNames { + expected = append(expected, fmt.Sprintf("foo-%s", ght)) + } + + if !reflect.DeepEqual(group.Contact.GithubTeamNames, expected) { + t.Fatalf("%v does not match %v", group.Contact.GithubTeamNames, expected) + } +} + +func TestCreateGroupReadmes(t *testing.T) { + groups := []Group{ + Group{Name: "Foo"}, + Group{Name: "Bar"}, + } + + err := createGroupReadme(groups, "sig") + if err != nil { + t.Fatal(err) + } + + for _, group := range groups { + path := filepath.Join(baseOutputDir, group.DirName("sig"), "README.md") + if !pathExists(path) { + t.Fatalf("%s should exist", path) + } + } +} + +func TestReadmesAreSkipped(t *testing.T) { + os.Setenv("SIG", "sig-foo") + + groups := []Group{ + Group{Name: "Foo"}, + Group{Name: "Bar"}, + } + + err := createGroupReadme(groups, "sig") + if err != nil { + t.Fatal(err) + } + + for _, group := range groups[1:] { + path := filepath.Join(baseOutputDir, group.DirName("sig"), "README.md") + if !pathExists(path) { + t.Fatalf("%s should exist", path) + } + } + + os.Setenv("SIG", "") +} + +func copyFile(src, dst string) error { + // Read all content of src to data + data, err := ioutil.ReadFile(src) + if err != nil { + return err + } + // Write data to dst + err = ioutil.WriteFile(dst, data, 0644) + if err != nil { + return err + } + return nil +} + +func TestFullGeneration(t *testing.T) { + err := copyFile("testdata/sigs.yaml", "generated/sigs.yaml") + if err != nil { + t.Fatalf("Error received: %v", err) + } + + main() + + expectedDirs := []string{"sig-foo", "sig-bar", "wg-baz"} + for _, ed := range expectedDirs { + path := filepath.Join(baseOutputDir, ed, "README.md") + if !pathExists(path) { + t.Fatalf("%s should exist", path) + } + } +} diff --git a/generator/sig_list.tmpl b/generator/list.tmpl similarity index 75% rename from generator/sig_list.tmpl rename to generator/list.tmpl index 01c5782c5fd..f911529eba3 100644 --- a/generator/sig_list.tmpl +++ b/generator/list.tmpl @@ -24,5 +24,5 @@ When the need arises, a [new SIG can be created](sig-creation-procedure.md) | Name | Organizers | Contact | Meetings | |------|------------|---------|----------| {{- range .WorkingGroups}} -|[{{.Name}}]({{.Dir}}/README.md)|{{range .Organizers}}* [{{.Name}}](https://github.com/{{.GitHub}}){{if .Company}}, {{.Company}}{{end}}
{{end}}|* [Slack](https://kubernetes.slack.com/messages/{{.Contact.Slack}})
* [Mailing List]({{.Contact.MailingList}})|{{ $save := . }}{{range .Meetings}}* [{{.Day}}s at {{.UTC}} UTC ({{.Frequency}})]({{$save.MeetingURL}})
{{end}} +|[{{.Name}}]({{.Dir}}/README.md)|{{range .Leads}}* [{{.Name}}](https://github.com/{{.GitHub}}){{if .Company}}, {{.Company}}{{end}}
{{end}}|* [Slack](https://kubernetes.slack.com/messages/{{.Contact.Slack}})
* [Mailing List]({{.Contact.MailingList}})|{{ $save := . }}{{range .Meetings}}* [{{.Day}}s at {{.UTC}} UTC ({{.Frequency}})]({{$save.MeetingURL}})
{{end}} {{- end }} diff --git a/generator/sig_index.tmpl b/generator/sig_readme.tmpl similarity index 100% rename from generator/sig_index.tmpl rename to generator/sig_readme.tmpl diff --git a/generator/testdata/custom_content.md b/generator/testdata/custom_content.md index 501f56b408d..592088e3f5e 100644 --- a/generator/testdata/custom_content.md +++ b/generator/testdata/custom_content.md @@ -27,5 +27,3 @@ Meeting notes and Agenda can be found [here](https://docs.google.com/document/d/ FOO BAR BAZ - -Last generated: Wed Jun 7 2017 14:27:56 diff --git a/generator/testdata/example.md b/generator/testdata/example.md index 63ddbe7b335..9016ae8335e 100644 --- a/generator/testdata/example.md +++ b/generator/testdata/example.md @@ -5,5 +5,3 @@ custom content! - -Last generated: Thu Jun 15 2017 15:20:28 \ No newline at end of file diff --git a/generator/testdata/no_custom_content.md b/generator/testdata/no_custom_content.md index 737509fdb7d..342997890a7 100644 --- a/generator/testdata/no_custom_content.md +++ b/generator/testdata/no_custom_content.md @@ -27,5 +27,3 @@ Meeting notes and Agenda can be found [here](https://docs.google.com/document/d/ - -Last generated: Wed Jun 7 2017 14:27:56 diff --git a/generator/testdata/sigs.yaml b/generator/testdata/sigs.yaml new file mode 100644 index 00000000000..a4fe858c8d7 --- /dev/null +++ b/generator/testdata/sigs.yaml @@ -0,0 +1,5 @@ +sigs: + - name: Foo + - name: Bar +workinggroups: + - name: Baz diff --git a/generator/wg_index.tmpl b/generator/wg_readme.tmpl similarity index 96% rename from generator/wg_index.tmpl rename to generator/wg_readme.tmpl index ed8fc9c078f..9a0144a6ec8 100644 --- a/generator/wg_index.tmpl +++ b/generator/wg_readme.tmpl @@ -10,7 +10,7 @@ Meeting notes and Agenda can be found [here]({{.MeetingArchiveURL}}). ## Organizers -{{- range .Organizers }} +{{- range .Leads }} * [{{.Name}}](https://github.com/{{.GitHub}}){{if .Company}}, {{.Company}}{{end}} {{- end }} diff --git a/sigs.yaml b/sigs.yaml index af3b0185796..f3ab0518b10 100644 --- a/sigs.yaml +++ b/sigs.yaml @@ -627,7 +627,7 @@ workinggroups: dir: wg-resource-management mission_statement: > Designing and shepherding cross-cutting features around compute resource isolation and utilization. - organizers: + leads: - name: Vishnu Kannan github: vishh company: Google @@ -642,4 +642,4 @@ workinggroups: meeting_archive_url: https://www.youtube.com/playlist?list=PL69nYSiGNLP1wJPj5DYWXjiArF-MJ5fNG contact: slack: wg-resource-mgmt - mailing_list: https://groups.google.com/forum/#!forum/kubernetes-wg-resource-management \ No newline at end of file + mailing_list: https://groups.google.com/forum/#!forum/kubernetes-wg-resource-management