diff --git a/Makefile b/Makefile index 269708e6093..7406d2810cd 100644 --- a/Makefile +++ b/Makefile @@ -13,3 +13,7 @@ gen-doc: gen-docs: docker run -v $(shell pwd):/go/src/app sigdocs + +test: + docker build -t sigdocs-test -f generator/Dockerfile.test generator + docker run sigdocs-test diff --git a/generator/Dockerfile b/generator/Dockerfile index e07f42e4ce1..f1b21921793 100644 --- a/generator/Dockerfile +++ b/generator/Dockerfile @@ -1 +1 @@ -FROM golang:1.6-onbuild +FROM golang:1.8-onbuild diff --git a/generator/Dockerfile.test b/generator/Dockerfile.test new file mode 100644 index 00000000000..f88078313d8 --- /dev/null +++ b/generator/Dockerfile.test @@ -0,0 +1,8 @@ +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 c522ec972d7..3fd2230ebc5 100644 --- a/generator/README.md +++ b/generator/README.md @@ -1,14 +1,26 @@ # SIG Doc builder -This script will generate the following documentation files: +This folder contains scripts to automatically generate documentation about the +different Special Interest Groups (SIGs) of Kubernetes. The authoritative +source for SIG information is the `sigs.yaml` file in the project root. All +updates must be done there. + +When an update happens to the this file, the next step is generate the +accompanying documentation. This takes the format of two types of doc file: ``` -sig-*/README.md -wg-*/README.md -sig-list.md +./sig-/README.md +./wg-/README.md +./sig-list.md ``` -Based off the `sigs.yaml` metadata file. +For example, if a contributor has updated `sig-cluster-lifecycle`, the +following files will be generated: + +``` +./sig-cluster-lifecycle/README.md +./sig-list.md +``` ## How to use diff --git a/generator/app.go b/generator/app.go index 6826af4664b..9cfb45b84f2 100644 --- a/generator/app.go +++ b/generator/app.go @@ -31,11 +31,10 @@ import ( var ( sigsYamlFile = "sigs.yaml" - templateDir = "generator" - sigIndexTemplate = filepath.Join(templateDir, "sig_index.tmpl") - wgIndexTemplate = filepath.Join(templateDir, "wg_index.tmpl") - listTemplate = filepath.Join(templateDir, "sig_list.tmpl") - headerTemplate = filepath.Join(templateDir, "header.tmpl") + 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"} @@ -95,34 +94,10 @@ type SigEntries struct { Sigs []Sig } -func (slice SigEntries) Len() int { - return len(slice.Sigs) -} - -func (slice SigEntries) Less(i, j int) bool { - return slice.Sigs[i].Name < slice.Sigs[j].Name -} - -func (slice SigEntries) Swap(i, j int) { - slice.Sigs[i], slice.Sigs[j] = slice.Sigs[j], slice.Sigs[i] -} - type WgEntries struct { WorkingGroups []Wg } -func (slice WgEntries) Len() int { - return len(slice.WorkingGroups) -} - -func (slice WgEntries) Less(i, j int) bool { - return slice.WorkingGroups[i].Name < slice.WorkingGroups[j].Name -} - -func (slice WgEntries) Swap(i, j int) { - slice.WorkingGroups[i], slice.WorkingGroups[j] = slice.WorkingGroups[j], slice.WorkingGroups[i] -} - func pathExists(path string) bool { _, err := os.Stat(path) return err == nil @@ -162,7 +137,13 @@ func getExistingContent(path string) (string, error) { return strings.Join(captured, "\n"), nil } -func writeTemplate(templatePath, outputPath string, data interface{}) error { +func writeTemplate(templateFilePath, outputPath string, data interface{}) error { + wd, err := os.Getwd() + if err != nil { + return err + } + templatePath := filepath.Join(wd, templateFilePath) + // set up template t, err := template.ParseFiles(templatePath, headerTemplate) if err != nil { @@ -242,6 +223,7 @@ func createReadmeFiles(ctx Context) error { return err } } + var selectedWg *string if wg, ok := os.LookupEnv("WG"); ok { selectedWg = &wg @@ -289,12 +271,14 @@ func main() { if err != nil { log.Fatal(err) } - sigEntries := SigEntries{ctx.Sigs} - sort.Sort(sigEntries) - ctx.Sigs = sigEntries.Sigs - wgEntries := WgEntries{ctx.WorkingGroups} - sort.Sort(wgEntries) - ctx.WorkingGroups = wgEntries.WorkingGroups + + sort.Slice(ctx.Sigs, func(i, j int) bool { + 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 + }) err = createReadmeFiles(ctx) if err != nil { diff --git a/generator/app_test.go b/generator/app_test.go new file mode 100644 index 00000000000..1ef3a574818 --- /dev/null +++ b/generator/app_test.go @@ -0,0 +1,136 @@ +/* +Copyright 2017 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +import ( + "io/ioutil" + "strings" + "testing" +) + +func TestNonExistantDirIsCreated(t *testing.T) { + dir := "/tmp/nonexistent" + err := createDirIfNotExists(dir) + if err != nil { + t.Fatalf("Received error creating dir: %v", err) + } + if !pathExists(dir) { + t.Fatalf("%s should exist", dir) + } +} + +func TestGetExistingData(t *testing.T) { + cases := []struct { + path string + expected string + expectErr bool + }{ + { + path: "./testdata/custom_content.md", + expected: "FOO BAR BAZ", + expectErr: false, + }, + { + path: "./testdata/no_custom_content.md", + expected: "", + expectErr: false, + }, + { + path: "./testdata/foo.md", + expected: "", + expectErr: true, + }, + } + + for _, c := range cases { + content, err := getExistingContent(c.path) + if err != nil && c.expectErr == false { + t.Fatalf("Received unexpected error for %s: %v", c.path, err) + } + if err == nil && c.expectErr == true { + t.Fatalf("Expected error for %s but received none", c.path) + } + if content != c.expected { + t.Fatalf("Expected %s but received %s", c.expected, content) + } + } +} + +func TestWriteTemplate(t *testing.T) { + customContent := ` + +Example +custom +content! + + +` + + cases := []struct { + templatePath string + outputPath string + data map[string]string + expectErr bool + expected string + }{ + { + templatePath: "./testdata/non_existent_template.tmpl", + expectErr: true, + }, + { + templatePath: "./testdata/example.tmpl", + outputPath: "/tmp/non_existing_path.md", + expectErr: false, + data: map[string]string{"Message": "Hello!"}, + expected: "Hello!", + }, + { + templatePath: "./testdata/example.tmpl", + outputPath: "./testdata/example.md", + expectErr: false, + 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 { + err := writeTemplate(c.templatePath, c.outputPath, c.data) + if err != nil && c.expectErr == false { + t.Fatalf("Received unexpected error for %s: %v", c.templatePath, err) + } + if c.expectErr { + if err == nil { + t.Fatalf("Expected error for %s but received none", c.templatePath) + } + continue + } + content, err := ioutil.ReadFile(c.outputPath) + if err != nil { + t.Fatalf("%s should exist", c.outputPath) + } + if strings.Contains(string(content), c.expected) == false { + t.Fatalf("%s was not found in %s", c.expected, c.outputPath) + } + } +} diff --git a/generator/testdata/custom_content.md b/generator/testdata/custom_content.md new file mode 100644 index 00000000000..501f56b408d --- /dev/null +++ b/generator/testdata/custom_content.md @@ -0,0 +1,31 @@ + +# Auth SIG + +Covers improvements to Kubernetes authorization, authentication, and cluster security policy. + +## Meetings +* [Wednesdays at 18:00 UTC](https://zoom.us/my/k8s.sig.auth) (biweekly). [Convert to your timezone](http://www.thetimezoneconverter.com/?t=18:00&tz=UTC). + +Meeting notes and Agenda can be found [here](https://docs.google.com/document/d/1woLGRoONE3EBVx-wTb4pvp4CI7tmLZ6lS26VTbosLKM/edit#). + +## Leads +* [Eric Chiang](https://github.com/ericchiang), CoreOS +* [Jordan Liggitt](https://github.com/liggitt), Red Hat +* [David Eads](https://github.com/deads2k), Red Hat + +## Contact +* [Slack](https://kubernetes.slack.com/messages/sig-auth) +* [Mailing list](https://groups.google.com/forum/#!forum/kubernetes-sig-auth) + + +FOO BAR BAZ + + +Last generated: Wed Jun 7 2017 14:27:56 diff --git a/generator/testdata/example.md b/generator/testdata/example.md new file mode 100644 index 00000000000..63ddbe7b335 --- /dev/null +++ b/generator/testdata/example.md @@ -0,0 +1,9 @@ +Hello! + +Example +custom +content! + + + +Last generated: Thu Jun 15 2017 15:20:28 \ No newline at end of file diff --git a/generator/testdata/example.tmpl b/generator/testdata/example.tmpl new file mode 100644 index 00000000000..287ff1919c1 --- /dev/null +++ b/generator/testdata/example.tmpl @@ -0,0 +1 @@ +{{.Message}} diff --git a/generator/testdata/no_custom_content.md b/generator/testdata/no_custom_content.md new file mode 100644 index 00000000000..737509fdb7d --- /dev/null +++ b/generator/testdata/no_custom_content.md @@ -0,0 +1,31 @@ + +# Auth SIG + +Covers improvements to Kubernetes authorization, authentication, and cluster security policy. + +## Meetings +* [Wednesdays at 18:00 UTC](https://zoom.us/my/k8s.sig.auth) (biweekly). [Convert to your timezone](http://www.thetimezoneconverter.com/?t=18:00&tz=UTC). + +Meeting notes and Agenda can be found [here](https://docs.google.com/document/d/1woLGRoONE3EBVx-wTb4pvp4CI7tmLZ6lS26VTbosLKM/edit#). + +## Leads +* [Eric Chiang](https://github.com/ericchiang), CoreOS +* [Jordan Liggitt](https://github.com/liggitt), Red Hat +* [David Eads](https://github.com/deads2k), Red Hat + +## Contact +* [Slack](https://kubernetes.slack.com/messages/sig-auth) +* [Mailing list](https://groups.google.com/forum/#!forum/kubernetes-sig-auth) + + + + + +Last generated: Wed Jun 7 2017 14:27:56