diff --git a/hack/release.sh b/hack/release.sh index 5f83b2cbea5..4ff5c486f3b 100755 --- a/hack/release.sh +++ b/hack/release.sh @@ -24,6 +24,8 @@ go run ${DIR}/release_notes/listpullreqs.go # sync files from integration examples to examples/ rm -rf ${EXAMPLES_DIR} && rm -rf ${INTEGRATION_EXAMPLES_DIR}/bazel/bazel-* && cp -r ${INTEGRATION_EXAMPLES_DIR} ${EXAMPLES_DIR} && rm -rf ${EXAMPLES_DIR}/test-* +go run hack/versions/cmd/mark_latest_released/main.go + echo echo "Huge thanks goes out to all of our contributors for this release:" echo diff --git a/hack/versions/cmd/mark_latest_released/main.go b/hack/versions/cmd/mark_latest_released/main.go new file mode 100644 index 00000000000..d58c97d3677 --- /dev/null +++ b/hack/versions/cmd/mark_latest_released/main.go @@ -0,0 +1,28 @@ +/* +Copyright 2019 The Skaffold 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 ( + "path" + + "github.com/GoogleContainerTools/skaffold/hack/versions/pkg/schema" +) + +func main() { + latest := path.Join("pkg", "skaffold", "schema", "latest", "config.go") + schema.UpdateVersionComment(latest, true) +} diff --git a/hack/versions/cmd/new/version.go b/hack/versions/cmd/new/version.go index 0669d84b231..62f386420af 100644 --- a/hack/versions/cmd/new/version.go +++ b/hack/versions/cmd/new/version.go @@ -24,6 +24,8 @@ import ( "regexp" "strings" + hackschema "github.com/GoogleContainerTools/skaffold/hack/versions/pkg/schema" + "github.com/GoogleContainerTools/skaffold/hack/versions/pkg/version" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/color" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema" @@ -71,6 +73,8 @@ func main() { // Latest uses the new version sed(path("latest", "config.go"), current, next) + hackschema.UpdateVersionComment(path(current, "config.go"), true) + // Update skaffold.yaml in integration tests walk("integration", func(path string, info os.FileInfo) { if info.Name() == "skaffold.yaml" { @@ -91,6 +95,7 @@ func main() { // Update the docs with the new version sed("docs/config.toml", current, next) + } func makeSchemaDir(new string) { diff --git a/hack/versions/cmd/update_comments/main.go b/hack/versions/cmd/update_comments/main.go new file mode 100644 index 00000000000..f680f3eb4ab --- /dev/null +++ b/hack/versions/cmd/update_comments/main.go @@ -0,0 +1,45 @@ +/* +Copyright 2019 The Skaffold 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 ( + "os" + "path" + "path/filepath" + "strings" + + "github.com/GoogleContainerTools/skaffold/hack/versions/pkg/version" + + "github.com/sirupsen/logrus" + + "github.com/GoogleContainerTools/skaffold/hack/versions/pkg/schema" +) + +func main() { + schemaDir := path.Join("pkg", "skaffold", "schema") + _, isReleased := version.GetLatestVersion() + if err := filepath.Walk(schemaDir, func(path string, info os.FileInfo, err error) error { + if info.Name() == "config.go" { + released := !strings.Contains(path, "latest") || isReleased + return schema.UpdateVersionComment(path, released) + } + return nil + }); err != nil { + logrus.Fatalf("%s", err) + } + +} diff --git a/hack/versions/pkg/schema/comment.go b/hack/versions/pkg/schema/comment.go new file mode 100644 index 00000000000..1d0bdf61949 --- /dev/null +++ b/hack/versions/pkg/schema/comment.go @@ -0,0 +1,137 @@ +/* +Copyright 2019 The Skaffold 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 schema + +import ( + "bytes" + "go/ast" + "go/parser" + "go/printer" + "go/token" + "io/ioutil" + "os" + "os/exec" +) + +const releasedComment = `// !!! WARNING !!! This config version is already released, please DO NOT MODIFY the structs in this file.` +const unreleasedComment = `// This config version is not yet released, it is SAFE TO MODIFY the structs in this file.` + +// recognizedComments is used to recognize whether an existing comment is a "release comment" or not. +// If you want to change releasedComment (or unreleasedComment) historically on all files, then: +// 1.) add the old version to `recognizedComments` +// 2.) change the text of `releasedComment` (or `unreleasedComment`) +// 3.) run `go run hack/versions/cmd/update_comments/main.go` +// 4.) remove the old version from `recognizedComments` +var recognizedComments = []string{ + releasedComment, + unreleasedComment, +} + +func UpdateVersionComment(origFile string, released bool) error { + info, err := os.Stat(origFile) + if err != nil { + return err + } + content, err := updateVersionComment(origFile, released) + if err != nil { + return err + } + + if err := ioutil.WriteFile(origFile, content, info.Mode()); err != nil { + return err + } + + cmd := exec.Command("go", "fmt", origFile) + return cmd.Run() +} + +func updateVersionComment(origFile string, released bool) ([]byte, error) { + fset := token.NewFileSet() + + var commentString string + if released { + commentString = releasedComment + } else { + commentString = unreleasedComment + } + + astA, err := parser.ParseFile(fset, origFile, nil, parser.ParseComments) + if err != nil { + return nil, err + } + + firstComment := astA.Comments[1].List[0] + + if firstComment.Text == commentString { + return printAst(fset, astA) + } + + if isRecognizedComment(firstComment) { + firstComment.Text = commentString + return printAst(fset, astA) + } + + addFirstCommentOnVersion(astA, commentString) + + return printAst(fset, astA) +} + +func isRecognizedComment(firstComment *ast.Comment) bool { + for _, comment := range recognizedComments { + if comment == firstComment.Text { + return true + } + } + return false +} + +func printAst(fset *token.FileSet, ast *ast.File) ([]byte, error) { + var buf bytes.Buffer + + if err := printer.Fprint(&buf, fset, ast); err != nil { + return nil, err + } + return buf.Bytes(), nil +} + +func addFirstCommentOnVersion(astA *ast.File, commentString string) { + ast.Inspect(astA, func(node ast.Node) bool { + + if decl, ok := node.(*ast.GenDecl); ok && + decl.Tok.String() == "const" && + len(decl.Specs) == 1 { + sp := decl.Specs[0] + if t, ok := sp.(*ast.ValueSpec); ok { + if len(t.Names) == 1 && t.Names[0].Name == "Version" { + comment := ast.Comment{ + Slash: decl.TokPos - 1, + Text: commentString + "\n", + } + comments := []*ast.Comment{ + &comment, + } + + cg := &ast.CommentGroup{List: comments} + astA.Comments = append([]*ast.CommentGroup{astA.Comments[0], cg}, astA.Comments[1:]...) + decl.Doc = cg + return false + } + } + } + return true + }) +} diff --git a/hack/versions/pkg/schema/comment_test.go b/hack/versions/pkg/schema/comment_test.go new file mode 100644 index 00000000000..af77eaaabc9 --- /dev/null +++ b/hack/versions/pkg/schema/comment_test.go @@ -0,0 +1,118 @@ +/* +Copyright 2019 The Skaffold 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 schema + +import ( + "fmt" + "io/ioutil" + "testing" + + "github.com/GoogleContainerTools/skaffold/testutil" +) + +const configFileTemplate = `/* +Copyright 2019 The Skaffold 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 v1beta12 + +import ( + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/util" +) + +%sconst Version string = "skaffold/v1beta12" + +// NewSkaffoldConfig creates a SkaffoldConfig +func NewSkaffoldConfig() util.VersionedConfig { + return new(SkaffoldConfig) +} +` + +var configWithNoComment = fmt.Sprintf(configFileTemplate, "") +var configWithReleasedComment = fmt.Sprintf(configFileTemplate, releasedComment+"\n") +var configWithUnreleasedComment = fmt.Sprintf(configFileTemplate, unreleasedComment+"\n") + +func TestUpdateComments(t *testing.T) { + + tcs := []struct { + name string + orig string + expected string + released bool + }{ + { + name: "unreleased comment added on file", + released: true, + orig: configWithNoComment, + expected: configWithReleasedComment, + }, + { + name: "released comment added on file", + released: false, + orig: configWithNoComment, + expected: configWithUnreleasedComment, + }, + { + name: "released -> released", + released: true, + orig: configWithReleasedComment, + expected: configWithReleasedComment, + }, + { + name: "unreleased -> unreleased", + released: false, + orig: configWithUnreleasedComment, + expected: configWithUnreleasedComment, + }, + { + name: "released -> unreleased", + released: false, + orig: configWithReleasedComment, + expected: configWithUnreleasedComment, + }, + { + name: "unreleased -> released", + released: true, + orig: configWithUnreleasedComment, + expected: configWithReleasedComment, + }, + } + + for _, tc := range tcs { + testutil.Run(t, tc.name, func(t *testutil.T) { + + dir := t.NewTempDir() + aFile := dir.Path("a.go") + t.CheckNoError(ioutil.WriteFile(aFile, []byte(tc.orig), 0666)) + modified, err := updateVersionComment(aFile, tc.released) + t.CheckErrorAndDeepEqual(false, err, tc.expected, string(modified)) + }) + } + +}