Remove seemingly obsolete binaries
This commit is contained in:
@@ -137,7 +137,6 @@ filegroup(
|
|||||||
"//cmd/genyaml",
|
"//cmd/genyaml",
|
||||||
"//cmd/kubemark", # TODO: server platforms only
|
"//cmd/kubemark", # TODO: server platforms only
|
||||||
"//cmd/linkcheck",
|
"//cmd/linkcheck",
|
||||||
"//cmd/mungedocs",
|
|
||||||
"//federation/cmd/genfeddocs",
|
"//federation/cmd/genfeddocs",
|
||||||
"//test/e2e:e2e.test",
|
"//test/e2e:e2e.test",
|
||||||
"//test/e2e_node:e2e_node.test", # TODO: server platforms only
|
"//test/e2e_node:e2e_node.test", # TODO: server platforms only
|
||||||
|
|||||||
@@ -30,7 +30,6 @@ package_group(
|
|||||||
packages = [
|
packages = [
|
||||||
"//cmd/gendocs",
|
"//cmd/gendocs",
|
||||||
"//cmd/genman",
|
"//cmd/genman",
|
||||||
"//cmd/genslateyaml",
|
|
||||||
"//cmd/genyaml",
|
"//cmd/genyaml",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ filegroup(
|
|||||||
"//cmd/gendocs:all-srcs",
|
"//cmd/gendocs:all-srcs",
|
||||||
"//cmd/genkubedocs:all-srcs",
|
"//cmd/genkubedocs:all-srcs",
|
||||||
"//cmd/genman:all-srcs",
|
"//cmd/genman:all-srcs",
|
||||||
"//cmd/genslateyaml:all-srcs",
|
|
||||||
"//cmd/genswaggertypedocs:all-srcs",
|
"//cmd/genswaggertypedocs:all-srcs",
|
||||||
"//cmd/genutils:all-srcs",
|
"//cmd/genutils:all-srcs",
|
||||||
"//cmd/genyaml:all-srcs",
|
"//cmd/genyaml:all-srcs",
|
||||||
@@ -31,7 +30,6 @@ filegroup(
|
|||||||
"//cmd/kubelet:all-srcs",
|
"//cmd/kubelet:all-srcs",
|
||||||
"//cmd/kubemark:all-srcs",
|
"//cmd/kubemark:all-srcs",
|
||||||
"//cmd/linkcheck:all-srcs",
|
"//cmd/linkcheck:all-srcs",
|
||||||
"//cmd/mungedocs:all-srcs",
|
|
||||||
],
|
],
|
||||||
tags = ["automanaged"],
|
tags = ["automanaged"],
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,37 +0,0 @@
|
|||||||
package(default_visibility = ["//visibility:public"])
|
|
||||||
|
|
||||||
load(
|
|
||||||
"@io_bazel_rules_go//go:def.bzl",
|
|
||||||
"go_binary",
|
|
||||||
"go_library",
|
|
||||||
)
|
|
||||||
|
|
||||||
go_binary(
|
|
||||||
name = "genslateyaml",
|
|
||||||
library = ":go_default_library",
|
|
||||||
)
|
|
||||||
|
|
||||||
go_library(
|
|
||||||
name = "go_default_library",
|
|
||||||
srcs = ["gen_slate_yaml.go"],
|
|
||||||
deps = [
|
|
||||||
"//pkg/kubectl/cmd:go_default_library",
|
|
||||||
"//pkg/kubectl/cmd/util:go_default_library",
|
|
||||||
"//vendor/github.com/spf13/cobra:go_default_library",
|
|
||||||
"//vendor/github.com/spf13/pflag:go_default_library",
|
|
||||||
"//vendor/gopkg.in/yaml.v2:go_default_library",
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
filegroup(
|
|
||||||
name = "package-srcs",
|
|
||||||
srcs = glob(["**"]),
|
|
||||||
tags = ["automanaged"],
|
|
||||||
visibility = ["//visibility:private"],
|
|
||||||
)
|
|
||||||
|
|
||||||
filegroup(
|
|
||||||
name = "all-srcs",
|
|
||||||
srcs = [":package-srcs"],
|
|
||||||
tags = ["automanaged"],
|
|
||||||
)
|
|
||||||
@@ -1,197 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2016 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 (
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"sort"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"github.com/spf13/pflag"
|
|
||||||
"gopkg.in/yaml.v2"
|
|
||||||
|
|
||||||
"k8s.io/kubernetes/pkg/kubectl/cmd"
|
|
||||||
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
|
||||||
)
|
|
||||||
|
|
||||||
// gen_slate_yaml creates a yaml representation of the kubectl help commands. This is to be consumed
|
|
||||||
// by tools to generate documentation.
|
|
||||||
|
|
||||||
var outputFile = flag.String("output", "", "Destination for kubectl yaml representation.")
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
flag.Parse()
|
|
||||||
|
|
||||||
if len(*outputFile) < 1 {
|
|
||||||
fmt.Printf("Must specify --output.\n")
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize a kubectl command that we can use to get the help documentation
|
|
||||||
kubectl := cmd.NewKubectlCommand(cmdutil.NewFactory(nil), os.Stdin, ioutil.Discard, ioutil.Discard)
|
|
||||||
|
|
||||||
// Create the structural representation
|
|
||||||
spec := NewKubectlSpec(kubectl)
|
|
||||||
|
|
||||||
// Write the spec to a file as yaml
|
|
||||||
WriteFile(spec)
|
|
||||||
}
|
|
||||||
|
|
||||||
func WriteFile(spec KubectlSpec) {
|
|
||||||
// Marshall the yaml
|
|
||||||
final, err := yaml.Marshal(&spec)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create the file
|
|
||||||
outFile, err := os.Create(*outputFile)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
defer outFile.Close()
|
|
||||||
|
|
||||||
// Write the file
|
|
||||||
_, err = outFile.Write(final)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewKubectlSpec(c *cobra.Command) KubectlSpec {
|
|
||||||
return KubectlSpec{
|
|
||||||
TopLevelCommandGroups: []TopLevelCommands{NewTopLevelCommands(c.Commands())},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewTopLevelCommands(cs []*cobra.Command) TopLevelCommands {
|
|
||||||
tlc := TopLevelCommands{}
|
|
||||||
for _, c := range cs {
|
|
||||||
tlc.Commands = append(tlc.Commands, NewTopLevelCommand(c))
|
|
||||||
}
|
|
||||||
sort.Sort(tlc)
|
|
||||||
return tlc
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewTopLevelCommand(c *cobra.Command) TopLevelCommand {
|
|
||||||
result := TopLevelCommand{
|
|
||||||
MainCommand: NewCommand(c, ""),
|
|
||||||
}
|
|
||||||
for _, sub := range c.Commands() {
|
|
||||||
result.SubCommands = append(result.SubCommands, NewSubCommands(sub, "")...)
|
|
||||||
}
|
|
||||||
sort.Sort(result.SubCommands)
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse the Options
|
|
||||||
func NewOptions(flags *pflag.FlagSet) Options {
|
|
||||||
result := Options{}
|
|
||||||
flags.VisitAll(func(flag *pflag.Flag) {
|
|
||||||
opt := &Option{
|
|
||||||
Name: flag.Name,
|
|
||||||
Shorthand: flag.Shorthand,
|
|
||||||
DefaultValue: flag.DefValue,
|
|
||||||
Usage: flag.Usage,
|
|
||||||
}
|
|
||||||
result = append(result, opt)
|
|
||||||
})
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse the Commands
|
|
||||||
func NewSubCommands(c *cobra.Command, path string) Commands {
|
|
||||||
subCommands := Commands{NewCommand(c, path+c.Name())}
|
|
||||||
for _, subCommand := range c.Commands() {
|
|
||||||
subCommands = append(subCommands, NewSubCommands(subCommand, path+c.Name()+" ")...)
|
|
||||||
}
|
|
||||||
return subCommands
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewCommand(c *cobra.Command, path string) *Command {
|
|
||||||
return &Command{
|
|
||||||
Name: c.Name(),
|
|
||||||
Path: path,
|
|
||||||
Description: c.Long,
|
|
||||||
Synopsis: c.Short,
|
|
||||||
Example: c.Example,
|
|
||||||
Options: NewOptions(c.NonInheritedFlags()),
|
|
||||||
InheritedOptions: NewOptions(c.InheritedFlags()),
|
|
||||||
Usage: c.Use,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////////
|
|
||||||
// Types
|
|
||||||
//////////////////////////
|
|
||||||
|
|
||||||
type KubectlSpec struct {
|
|
||||||
TopLevelCommandGroups []TopLevelCommands `yaml:",omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type TopLevelCommands struct {
|
|
||||||
Commands []TopLevelCommand `yaml:",omitempty"`
|
|
||||||
}
|
|
||||||
type TopLevelCommand struct {
|
|
||||||
MainCommand *Command `yaml:",omitempty"`
|
|
||||||
SubCommands Commands `yaml:",omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type Options []*Option
|
|
||||||
type Option struct {
|
|
||||||
Name string `yaml:",omitempty"`
|
|
||||||
Shorthand string `yaml:",omitempty"`
|
|
||||||
DefaultValue string `yaml:"default_value,omitempty"`
|
|
||||||
Usage string `yaml:",omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type Commands []*Command
|
|
||||||
type Command struct {
|
|
||||||
Name string `yaml:",omitempty"`
|
|
||||||
Path string `yaml:",omitempty"`
|
|
||||||
Synopsis string `yaml:",omitempty"`
|
|
||||||
Description string `yaml:",omitempty"`
|
|
||||||
Options Options `yaml:",omitempty"`
|
|
||||||
InheritedOptions Options `yaml:"inherited_options,omitempty"`
|
|
||||||
Example string `yaml:",omitempty"`
|
|
||||||
SeeAlso []string `yaml:"see_also,omitempty"`
|
|
||||||
Usage string `yaml:",omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a Options) Len() int { return len(a) }
|
|
||||||
func (a Options) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
|
||||||
func (a Options) Less(i, j int) bool {
|
|
||||||
return a[i].Name < a[j].Name
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a TopLevelCommands) Len() int { return len(a.Commands) }
|
|
||||||
func (a TopLevelCommands) Swap(i, j int) { a.Commands[i], a.Commands[j] = a.Commands[j], a.Commands[i] }
|
|
||||||
func (a TopLevelCommands) Less(i, j int) bool {
|
|
||||||
return a.Commands[i].MainCommand.Path < a.Commands[j].MainCommand.Path
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a Commands) Len() int { return len(a) }
|
|
||||||
func (a Commands) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
|
||||||
func (a Commands) Less(i, j int) bool {
|
|
||||||
return a[i].Path < a[j].Path
|
|
||||||
}
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
package(default_visibility = ["//visibility:public"])
|
|
||||||
|
|
||||||
load(
|
|
||||||
"@io_bazel_rules_go//go:def.bzl",
|
|
||||||
"go_binary",
|
|
||||||
"go_library",
|
|
||||||
"go_test",
|
|
||||||
)
|
|
||||||
|
|
||||||
go_binary(
|
|
||||||
name = "mungedocs",
|
|
||||||
library = ":go_default_library",
|
|
||||||
)
|
|
||||||
|
|
||||||
go_test(
|
|
||||||
name = "go_default_test",
|
|
||||||
srcs = [
|
|
||||||
"analytics_test.go",
|
|
||||||
"example_syncer_test.go",
|
|
||||||
"headers_test.go",
|
|
||||||
"kubectl_dash_f_test.go",
|
|
||||||
"links_test.go",
|
|
||||||
"preformatted_test.go",
|
|
||||||
"toc_test.go",
|
|
||||||
"util_test.go",
|
|
||||||
"whitespace_test.go",
|
|
||||||
],
|
|
||||||
data = [
|
|
||||||
"mungedocs.go",
|
|
||||||
":testdata",
|
|
||||||
"//docs:srcs",
|
|
||||||
],
|
|
||||||
library = ":go_default_library",
|
|
||||||
deps = ["//vendor/github.com/stretchr/testify/assert:go_default_library"],
|
|
||||||
)
|
|
||||||
|
|
||||||
go_library(
|
|
||||||
name = "go_default_library",
|
|
||||||
srcs = [
|
|
||||||
"analytics.go",
|
|
||||||
"example_syncer.go",
|
|
||||||
"headers.go",
|
|
||||||
"kubectl_dash_f.go",
|
|
||||||
"links.go",
|
|
||||||
"mungedocs.go",
|
|
||||||
"preformatted.go",
|
|
||||||
"toc.go",
|
|
||||||
"util.go",
|
|
||||||
"whitespace.go",
|
|
||||||
],
|
|
||||||
deps = ["//vendor/github.com/spf13/pflag:go_default_library"],
|
|
||||||
)
|
|
||||||
|
|
||||||
filegroup(
|
|
||||||
name = "testdata",
|
|
||||||
srcs = glob(["testdata/*"]),
|
|
||||||
)
|
|
||||||
|
|
||||||
filegroup(
|
|
||||||
name = "package-srcs",
|
|
||||||
srcs = glob(["**"]),
|
|
||||||
tags = ["automanaged"],
|
|
||||||
visibility = ["//visibility:private"],
|
|
||||||
)
|
|
||||||
|
|
||||||
filegroup(
|
|
||||||
name = "all-srcs",
|
|
||||||
srcs = [":package-srcs"],
|
|
||||||
tags = ["automanaged"],
|
|
||||||
)
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
# Documentation Mungers
|
|
||||||
|
|
||||||
Basically this is like lint/gofmt for md docs.
|
|
||||||
|
|
||||||
It basically does the following:
|
|
||||||
- iterate over all files in the given doc root.
|
|
||||||
- for each file split it into a slice (mungeLines) of lines (mungeLine)
|
|
||||||
- a mungeline has metadata about each line typically determined by a 'fast' regex.
|
|
||||||
- metadata contains things like 'is inside a preformatted block'
|
|
||||||
- contains a markdown header
|
|
||||||
- has a link to another file
|
|
||||||
- etc..
|
|
||||||
- if you have a really slow regex with a lot of backtracking you might want to write a fast one to limit how often you run the slow one.
|
|
||||||
- each munger is then called in turn
|
|
||||||
- they are given the mungeLines
|
|
||||||
- they create an entirely new set of mungeLines with their modifications
|
|
||||||
- the new set is returned
|
|
||||||
- the new set is then fed into the next munger.
|
|
||||||
- in the end we might commit the end mungeLines to the file or not (--verify)
|
|
||||||
|
|
||||||
|
|
||||||
[]()
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2015 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 (
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
const analyticsMungeTag = "GENERATED_ANALYTICS"
|
|
||||||
const analyticsLinePrefix = "[ (mungeLines, error) {
|
|
||||||
var out mungeLines
|
|
||||||
fileName, err := makeRepoRelative(fileName, fileName)
|
|
||||||
if err != nil {
|
|
||||||
return mlines, err
|
|
||||||
}
|
|
||||||
|
|
||||||
link := fmt.Sprintf(analyticsLinePrefix+"%s?pixel)]()", fileName)
|
|
||||||
insertLines := getMungeLines(link)
|
|
||||||
mlines, err = removeMacroBlock(analyticsMungeTag, mlines)
|
|
||||||
if err != nil {
|
|
||||||
return mlines, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove floating analytics links not surrounded by the munge tags.
|
|
||||||
for _, mline := range mlines {
|
|
||||||
if mline.preformatted || mline.header || mline.beginTag || mline.endTag {
|
|
||||||
out = append(out, mline)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if strings.HasPrefix(mline.data, analyticsLinePrefix) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
out = append(out, mline)
|
|
||||||
}
|
|
||||||
out = appendMacroBlock(out, analyticsMungeTag)
|
|
||||||
out, err = updateMacroBlock(out, analyticsMungeTag, insertLines)
|
|
||||||
if err != nil {
|
|
||||||
return mlines, err
|
|
||||||
}
|
|
||||||
return out, nil
|
|
||||||
}
|
|
||||||
@@ -1,93 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2015 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 (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestAnalytics(t *testing.T) {
|
|
||||||
b := beginMungeTag("GENERATED_ANALYTICS")
|
|
||||||
e := endMungeTag("GENERATED_ANALYTICS")
|
|
||||||
var cases = []struct {
|
|
||||||
in string
|
|
||||||
expected string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
"aoeu",
|
|
||||||
"aoeu" + "\n" + "\n" +
|
|
||||||
b + "\n" +
|
|
||||||
"[]()" + "\n" +
|
|
||||||
e + "\n"},
|
|
||||||
{
|
|
||||||
"aoeu" + "\n" + "\n" + "\n" +
|
|
||||||
"[]()",
|
|
||||||
"aoeu" + "\n" + "\n" + "\n" +
|
|
||||||
b + "\n" +
|
|
||||||
"[]()" + "\n" +
|
|
||||||
e + "\n"},
|
|
||||||
{
|
|
||||||
"aoeu" + "\n" +
|
|
||||||
b + "\n" +
|
|
||||||
"[]()" + "\n" +
|
|
||||||
e + "\n",
|
|
||||||
"aoeu" + "\n" + "\n" +
|
|
||||||
b + "\n" +
|
|
||||||
"[]()" + "\n" +
|
|
||||||
e + "\n"},
|
|
||||||
{
|
|
||||||
"aoeu" + "\n" + "\n" +
|
|
||||||
"[]()" + "\n" + "\n" + "\n" +
|
|
||||||
b + "\n" +
|
|
||||||
"[]()" + "\n" +
|
|
||||||
e + "\n",
|
|
||||||
"aoeu" + "\n" + "\n" + "\n" + "\n" +
|
|
||||||
b + "\n" +
|
|
||||||
"[]()" + "\n" +
|
|
||||||
e + "\n"},
|
|
||||||
{
|
|
||||||
"prefix" + "\n" +
|
|
||||||
b + "\n" +
|
|
||||||
"[]()" + "\n" +
|
|
||||||
e +
|
|
||||||
"\n" + "suffix",
|
|
||||||
"prefix" + "\n" + "suffix" + "\n" + "\n" +
|
|
||||||
b + "\n" +
|
|
||||||
"[]()" + "\n" +
|
|
||||||
e + "\n"},
|
|
||||||
{
|
|
||||||
"aoeu" + "\n" + "\n" + "\n" +
|
|
||||||
b + "\n" +
|
|
||||||
"[]()" + "\n" +
|
|
||||||
e + "\n",
|
|
||||||
"aoeu" + "\n" + "\n" + "\n" +
|
|
||||||
b + "\n" +
|
|
||||||
"[]()" + "\n" +
|
|
||||||
e + "\n"},
|
|
||||||
}
|
|
||||||
for i, c := range cases {
|
|
||||||
in := getMungeLines(c.in)
|
|
||||||
expected := getMungeLines(c.expected)
|
|
||||||
out, err := updateAnalytics("path/to/file-name.md", in)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
if !expected.Equal(out) {
|
|
||||||
t.Errorf("Case %d Expected \n\n%v\n\n but got \n\n%v\n\n", i, expected.String(), out.String())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,121 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2015 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 (
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"regexp"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
const exampleToken = "EXAMPLE"
|
|
||||||
|
|
||||||
const exampleLineStart = "<!-- BEGIN MUNGE: EXAMPLE"
|
|
||||||
|
|
||||||
var exampleMungeTagRE = regexp.MustCompile(beginMungeTag(fmt.Sprintf("%s %s", exampleToken, `(([^ ])*[.]([^.]*))`)))
|
|
||||||
|
|
||||||
// syncExamples updates all examples in markdown file.
|
|
||||||
//
|
|
||||||
// Finds the magic macro block tags, find the link to the example
|
|
||||||
// specified in the tags, and replaces anything between those with
|
|
||||||
// the content of the example, thereby syncing it.
|
|
||||||
//
|
|
||||||
// For example,
|
|
||||||
// <!-- BEGIN MUNGE: EXAMPLE ../../examples/guestbook/frontend-service.yaml -->
|
|
||||||
//
|
|
||||||
// ```yaml
|
|
||||||
// foo:
|
|
||||||
// bar:
|
|
||||||
// ```
|
|
||||||
//
|
|
||||||
// [Download example](../../examples/guestbook/frontend-service.yaml?raw=true)
|
|
||||||
// <!-- END MUNGE: EXAMPLE -->
|
|
||||||
func syncExamples(filePath string, mlines mungeLines) (mungeLines, error) {
|
|
||||||
var err error
|
|
||||||
type exampleTag struct {
|
|
||||||
token string
|
|
||||||
linkText string
|
|
||||||
fileType string
|
|
||||||
}
|
|
||||||
exampleTags := []exampleTag{}
|
|
||||||
|
|
||||||
// collect all example Tags
|
|
||||||
for _, mline := range mlines {
|
|
||||||
if mline.preformatted || !mline.beginTag {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
line := mline.data
|
|
||||||
if !strings.HasPrefix(line, exampleLineStart) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
match := exampleMungeTagRE.FindStringSubmatch(line)
|
|
||||||
if len(match) < 4 {
|
|
||||||
err = fmt.Errorf("Found unparsable EXAMPLE munge line %v", line)
|
|
||||||
return mlines, err
|
|
||||||
}
|
|
||||||
tag := exampleTag{
|
|
||||||
token: exampleToken + " " + match[1],
|
|
||||||
linkText: match[1],
|
|
||||||
fileType: match[3],
|
|
||||||
}
|
|
||||||
exampleTags = append(exampleTags, tag)
|
|
||||||
}
|
|
||||||
// update all example Tags
|
|
||||||
for _, tag := range exampleTags {
|
|
||||||
ft := ""
|
|
||||||
if tag.fileType == "json" {
|
|
||||||
ft = "json"
|
|
||||||
}
|
|
||||||
if tag.fileType == "yaml" {
|
|
||||||
ft = "yaml"
|
|
||||||
}
|
|
||||||
example, err := exampleContent(filePath, tag.linkText, ft)
|
|
||||||
if err != nil {
|
|
||||||
return mlines, err
|
|
||||||
}
|
|
||||||
mlines, err = updateMacroBlock(mlines, tag.token, example)
|
|
||||||
if err != nil {
|
|
||||||
return mlines, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return mlines, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// exampleContent retrieves the content of the file at linkPath
|
|
||||||
func exampleContent(filePath, linkPath, fileType string) (mungeLines, error) {
|
|
||||||
repoRel, err := makeRepoRelative(linkPath, filePath)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
fileRel, err := makeFileRelative(linkPath, filePath)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
dat, err := ioutil.ReadFile(repoRel)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// remove leading and trailing spaces and newlines
|
|
||||||
trimmedFileContent := strings.TrimSpace(string(dat))
|
|
||||||
content := fmt.Sprintf("\n```%s\n%s\n```\n\n[Download example](%s?raw=true)", fileType, trimmedFileContent, fileRel)
|
|
||||||
out := getMungeLines(content)
|
|
||||||
return out, nil
|
|
||||||
}
|
|
||||||
@@ -1,67 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2015 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 (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Test_syncExamples(t *testing.T) {
|
|
||||||
var podExample = `apiVersion: v1
|
|
||||||
kind: Pod
|
|
||||||
metadata:
|
|
||||||
name: nginx
|
|
||||||
spec:
|
|
||||||
containers:
|
|
||||||
- name: nginx
|
|
||||||
image: nginx
|
|
||||||
ports:
|
|
||||||
- containerPort: 80
|
|
||||||
`
|
|
||||||
var textExample = `some text
|
|
||||||
`
|
|
||||||
var cases = []struct {
|
|
||||||
in string
|
|
||||||
expected string
|
|
||||||
}{
|
|
||||||
{"", ""},
|
|
||||||
{
|
|
||||||
"<!-- BEGIN MUNGE: EXAMPLE testdata/pod.yaml -->\n<!-- END MUNGE: EXAMPLE testdata/pod.yaml -->\n",
|
|
||||||
"<!-- BEGIN MUNGE: EXAMPLE testdata/pod.yaml -->\n\n```yaml\n" + podExample + "```\n\n[Download example](testdata/pod.yaml?raw=true)\n<!-- END MUNGE: EXAMPLE testdata/pod.yaml -->\n",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"<!-- BEGIN MUNGE: EXAMPLE ../mungedocs/testdata/pod.yaml -->\n<!-- END MUNGE: EXAMPLE ../mungedocs/testdata/pod.yaml -->\n",
|
|
||||||
"<!-- BEGIN MUNGE: EXAMPLE ../mungedocs/testdata/pod.yaml -->\n\n```yaml\n" + podExample + "```\n\n[Download example](../mungedocs/testdata/pod.yaml?raw=true)\n<!-- END MUNGE: EXAMPLE ../mungedocs/testdata/pod.yaml -->\n",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"<!-- BEGIN MUNGE: EXAMPLE testdata/example.txt -->\n<!-- END MUNGE: EXAMPLE testdata/example.txt -->\n",
|
|
||||||
"<!-- BEGIN MUNGE: EXAMPLE testdata/example.txt -->\n\n```\n" + textExample + "```\n\n[Download example](testdata/example.txt?raw=true)\n<!-- END MUNGE: EXAMPLE testdata/example.txt -->\n",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
repoRoot = ""
|
|
||||||
for _, c := range cases {
|
|
||||||
in := getMungeLines(c.in)
|
|
||||||
expected := getMungeLines(c.expected)
|
|
||||||
actual, err := syncExamples("filename.md", in)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
if !expected.Equal(actual) {
|
|
||||||
t.Errorf("Expected example \n'%q' but got \n'%q'", expected.String(), actual.String())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,74 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2015 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 (
|
|
||||||
"fmt"
|
|
||||||
"regexp"
|
|
||||||
)
|
|
||||||
|
|
||||||
var headerRegex = regexp.MustCompile(`^(#+)\s*(.*)$`)
|
|
||||||
|
|
||||||
func fixHeaderLine(mlines mungeLines, newlines mungeLines, linenum int) mungeLines {
|
|
||||||
var out mungeLines
|
|
||||||
|
|
||||||
mline := mlines[linenum]
|
|
||||||
line := mlines[linenum].data
|
|
||||||
|
|
||||||
matches := headerRegex.FindStringSubmatch(line)
|
|
||||||
if matches == nil {
|
|
||||||
out = append(out, mline)
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
// There must be a blank line before the # (unless first line in file)
|
|
||||||
if linenum != 0 {
|
|
||||||
newlen := len(newlines)
|
|
||||||
if newlines[newlen-1].data != "" {
|
|
||||||
out = append(out, blankMungeLine)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// There must be a space AFTER the ##'s
|
|
||||||
newline := fmt.Sprintf("%s %s", matches[1], matches[2])
|
|
||||||
newmline := newMungeLine(newline)
|
|
||||||
out = append(out, newmline)
|
|
||||||
|
|
||||||
// The next line needs to be a blank line (unless last line in file)
|
|
||||||
if len(mlines) > linenum+1 && mlines[linenum+1].data != "" {
|
|
||||||
out = append(out, blankMungeLine)
|
|
||||||
}
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
// Header lines need whitespace around them and after the #s.
|
|
||||||
func updateHeaderLines(filePath string, mlines mungeLines) (mungeLines, error) {
|
|
||||||
var out mungeLines
|
|
||||||
for i, mline := range mlines {
|
|
||||||
if mline.preformatted {
|
|
||||||
out = append(out, mline)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if !mline.header {
|
|
||||||
out = append(out, mline)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
newLines := fixHeaderLine(mlines, out, i)
|
|
||||||
out = append(out, newLines...)
|
|
||||||
}
|
|
||||||
return out, nil
|
|
||||||
}
|
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2015 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 (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestHeaderLines(t *testing.T) {
|
|
||||||
var cases = []struct {
|
|
||||||
in string
|
|
||||||
expected string
|
|
||||||
}{
|
|
||||||
{"", ""},
|
|
||||||
{
|
|
||||||
"# ok",
|
|
||||||
"# ok",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"## ok",
|
|
||||||
"## ok",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"##### ok",
|
|
||||||
"##### ok",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"##fix",
|
|
||||||
"## fix",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"foo\n\n##fix\n\nbar",
|
|
||||||
"foo\n\n## fix\n\nbar",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"foo\n##fix\nbar",
|
|
||||||
"foo\n\n## fix\n\nbar",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"foo\n```\n##fix\n```\nbar",
|
|
||||||
"foo\n```\n##fix\n```\nbar",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"foo\n#fix1\n##fix2\nbar",
|
|
||||||
"foo\n\n# fix1\n\n## fix2\n\nbar",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for i, c := range cases {
|
|
||||||
in := getMungeLines(c.in)
|
|
||||||
expected := getMungeLines(c.expected)
|
|
||||||
actual, err := updateHeaderLines("filename.md", in)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
if !actual.Equal(expected) {
|
|
||||||
t.Errorf("case[%d]: expected %q got %q", i, c.expected, actual.String())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,125 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2015 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 (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Looks for lines that have kubectl commands with -f flags and files that
|
|
||||||
// don't exist.
|
|
||||||
func updateKubectlFileTargets(file string, mlines mungeLines) (mungeLines, error) {
|
|
||||||
var errors []string
|
|
||||||
for i, mline := range mlines {
|
|
||||||
if !mline.preformatted {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if err := lookForKubectl(mline.data, i); err != nil {
|
|
||||||
errors = append(errors, err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
err := error(nil)
|
|
||||||
if len(errors) != 0 {
|
|
||||||
err = fmt.Errorf("%s", strings.Join(errors, "\n"))
|
|
||||||
}
|
|
||||||
return mlines, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func lookForKubectl(line string, lineNum int) error {
|
|
||||||
fields := strings.Fields(line)
|
|
||||||
for i := range fields {
|
|
||||||
if fields[i] == "kubectl" {
|
|
||||||
return gotKubectl(lineNum, fields, i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func gotKubectl(lineNum int, fields []string, fieldNum int) error {
|
|
||||||
for i := fieldNum + 1; i < len(fields); i++ {
|
|
||||||
switch fields[i] {
|
|
||||||
case "create", "update", "replace", "delete":
|
|
||||||
return gotCommand(lineNum, fields, i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func gotCommand(lineNum int, fields []string, fieldNum int) error {
|
|
||||||
for i := fieldNum + 1; i < len(fields); i++ {
|
|
||||||
if strings.HasPrefix(fields[i], "-f") {
|
|
||||||
return gotDashF(lineNum, fields, i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func gotDashF(lineNum int, fields []string, fieldNum int) error {
|
|
||||||
target := ""
|
|
||||||
if fields[fieldNum] == "-f" {
|
|
||||||
if fieldNum+1 == len(fields) {
|
|
||||||
return fmt.Errorf("ran out of fields after '-f'")
|
|
||||||
}
|
|
||||||
target = fields[fieldNum+1]
|
|
||||||
} else {
|
|
||||||
target = fields[fieldNum][2:]
|
|
||||||
}
|
|
||||||
// Turn dirs into file-like names.
|
|
||||||
target = strings.TrimRight(target, "/")
|
|
||||||
|
|
||||||
// Now exclude special-cases
|
|
||||||
|
|
||||||
if target == "-" || target == "FILENAME" {
|
|
||||||
// stdin and "FILENAME" are OK
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if strings.HasPrefix(target, "http://") || strings.HasPrefix(target, "https://") {
|
|
||||||
// URLs are ok
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if strings.HasPrefix(target, "./") {
|
|
||||||
// Same-dir files are usually created in the same example
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if strings.HasPrefix(target, "~/") {
|
|
||||||
// Home directory may also be created by the same example
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if strings.HasPrefix(target, "/") {
|
|
||||||
// Absolute paths tend to be /tmp/* and created in the same example.
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if strings.HasPrefix(target, "$") {
|
|
||||||
// Allow the start of the target to be an environment
|
|
||||||
// variable that points to the root of the kubernetes
|
|
||||||
// repo.
|
|
||||||
split := strings.SplitN(target, "/", 2)
|
|
||||||
if len(split) == 2 {
|
|
||||||
target = split[1]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we got here we expect the file to exist.
|
|
||||||
_, err := os.Stat(path.Join(repoRoot, target))
|
|
||||||
if os.IsNotExist(err) {
|
|
||||||
return fmt.Errorf("%d: target file %q does not exist", lineNum, target)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
@@ -1,143 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2015 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 "testing"
|
|
||||||
|
|
||||||
func TestKubectlDashF(t *testing.T) {
|
|
||||||
var cases = []struct {
|
|
||||||
in string
|
|
||||||
ok bool
|
|
||||||
}{
|
|
||||||
// No match
|
|
||||||
{"", true},
|
|
||||||
{
|
|
||||||
"Foo\nBar\n",
|
|
||||||
true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Foo\nkubectl blah blech\nBar",
|
|
||||||
true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Foo\n```shell\nkubectl blah blech\n```\nBar",
|
|
||||||
true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Foo\n```\nkubectl -blah create blech\n```\nBar",
|
|
||||||
true,
|
|
||||||
},
|
|
||||||
// Special cases
|
|
||||||
{
|
|
||||||
"Foo\n```\nkubectl -blah create -f -\n```\nBar",
|
|
||||||
true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Foo\n```\nkubectl -blah create -f-\n```\nBar",
|
|
||||||
true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Foo\n```\nkubectl -blah create -f FILENAME\n```\nBar",
|
|
||||||
true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Foo\n```\nkubectl -blah create -fFILENAME\n```\nBar",
|
|
||||||
true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Foo\n```\nkubectl -blah create -f http://google.com/foobar\n```\nBar",
|
|
||||||
true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Foo\n```\nkubectl -blah create -fhttp://google.com/foobar\n```\nBar",
|
|
||||||
true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Foo\n```\nkubectl -blah create -f ./foobar\n```\nBar",
|
|
||||||
true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Foo\n```\nkubectl -blah create -f./foobar\n```\nBar",
|
|
||||||
true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Foo\n```\nkubectl -blah create -f /foobar\n```\nBar",
|
|
||||||
true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Foo\n```\nkubectl -blah create -f/foobar\n```\nBar",
|
|
||||||
true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Foo\n```\nkubectl -blah create -f~/foobar\n```\nBar",
|
|
||||||
true,
|
|
||||||
},
|
|
||||||
// Real checks
|
|
||||||
{
|
|
||||||
"Foo\n```\nkubectl -blah create -f mungedocs.go\n```\nBar",
|
|
||||||
true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Foo\n```\nkubectl -blah create -fmungedocs.go\n```\nBar",
|
|
||||||
true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Foo\n```\nkubectl -blah update -f mungedocs.go\n```\nBar",
|
|
||||||
true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Foo\n```\nkubectl -blah update -fmungedocs.go\n```\nBar",
|
|
||||||
true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Foo\n```\nkubectl -blah replace -f mungedocs.go\n```\nBar",
|
|
||||||
true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Foo\n```\nkubectl -blah replace -fmungedocs.go\n```\nBar",
|
|
||||||
true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Foo\n```\nkubectl -blah delete -f mungedocs.go\n```\nBar",
|
|
||||||
true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Foo\n```\nkubectl -blah delete -fmungedocs.go\n```\nBar",
|
|
||||||
true,
|
|
||||||
},
|
|
||||||
// Failures
|
|
||||||
{
|
|
||||||
"Foo\n```\nkubectl -blah delete -f does_not_exist\n```\nBar",
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Foo\n```\nkubectl -blah delete -fdoes_not_exist\n```\nBar",
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for i, c := range cases {
|
|
||||||
repoRoot = ""
|
|
||||||
in := getMungeLines(c.in)
|
|
||||||
_, err := updateKubectlFileTargets("filename.md", in)
|
|
||||||
if err != nil && c.ok {
|
|
||||||
t.Errorf("case[%d]: expected success, got %v", i, err)
|
|
||||||
}
|
|
||||||
if err == nil && !c.ok {
|
|
||||||
t.Errorf("case[%d]: unexpected success", i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,238 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2015 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 (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"net/url"
|
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
"regexp"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
// Finds markdown links of the form [foo](bar "alt-text").
|
|
||||||
linkRE = regexp.MustCompile(`\[([^]]*)\]\(([^)]*)\)`)
|
|
||||||
// Finds markdown link typos of the form (foo)[bar]
|
|
||||||
badLinkRE = regexp.MustCompile(`\([^]()]*\)\[[^]()]*\]`)
|
|
||||||
// Splits the link target into link target and alt-text.
|
|
||||||
altTextRE = regexp.MustCompile(`([^)]*)( ".*")`)
|
|
||||||
)
|
|
||||||
|
|
||||||
func processLink(in string, filePath string) (string, error) {
|
|
||||||
var errs []string
|
|
||||||
out := linkRE.ReplaceAllStringFunc(in, func(in string) string {
|
|
||||||
var err error
|
|
||||||
match := linkRE.FindStringSubmatch(in)
|
|
||||||
if match == nil {
|
|
||||||
errs = append(errs, fmt.Sprintf("Detected this line had a link, but unable to parse, %v", in))
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
// match[0] is the entire expression;
|
|
||||||
visibleText := match[1]
|
|
||||||
linkText := match[2]
|
|
||||||
altText := ""
|
|
||||||
if parts := altTextRE.FindStringSubmatch(linkText); parts != nil {
|
|
||||||
linkText = parts[1]
|
|
||||||
altText = parts[2]
|
|
||||||
}
|
|
||||||
|
|
||||||
// clean up some random garbage I found in our docs.
|
|
||||||
linkText = strings.Trim(linkText, " ")
|
|
||||||
linkText = strings.Trim(linkText, "\n")
|
|
||||||
linkText = strings.Trim(linkText, " ")
|
|
||||||
|
|
||||||
u, terr := url.Parse(linkText)
|
|
||||||
if terr != nil {
|
|
||||||
errs = append(errs, fmt.Sprintf("link %q is unparsable: %v", linkText, terr))
|
|
||||||
return in
|
|
||||||
}
|
|
||||||
|
|
||||||
if u.Host != "" && u.Host != "github.com" {
|
|
||||||
// We only care about relative links and links within github.
|
|
||||||
return in
|
|
||||||
}
|
|
||||||
|
|
||||||
suggestedVisibleText := visibleText
|
|
||||||
if u.Path != "" && !strings.HasPrefix(linkText, "TODO:") {
|
|
||||||
newPath, targetExists := checkPath(filePath, path.Clean(u.Path))
|
|
||||||
if !targetExists {
|
|
||||||
errs = append(errs, fmt.Sprintf("%q: target not found", linkText))
|
|
||||||
return in
|
|
||||||
}
|
|
||||||
u.Path = newPath
|
|
||||||
if strings.HasPrefix(u.Path, "/") {
|
|
||||||
u.Host = "github.com"
|
|
||||||
u.Scheme = "https"
|
|
||||||
} else {
|
|
||||||
// Remove host and scheme from relative paths
|
|
||||||
u.Host = ""
|
|
||||||
u.Scheme = ""
|
|
||||||
}
|
|
||||||
// Make the visible text show the absolute path if it's
|
|
||||||
// not nested in or beneath the current directory.
|
|
||||||
if strings.HasPrefix(u.Path, "..") {
|
|
||||||
dir := path.Dir(filePath)
|
|
||||||
suggestedVisibleText, err = makeRepoRelative(path.Join(dir, u.Path), filePath)
|
|
||||||
if err != nil {
|
|
||||||
errs = append(errs, fmt.Sprintf("%q: unable to make path relative", filePath))
|
|
||||||
return in
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
suggestedVisibleText = u.Path
|
|
||||||
}
|
|
||||||
var unescaped string
|
|
||||||
if unescaped, err = url.QueryUnescape(u.String()); err != nil {
|
|
||||||
// Remove %28 type stuff, be nice to humans.
|
|
||||||
// And don't fight with the toc generator.
|
|
||||||
linkText = unescaped
|
|
||||||
} else {
|
|
||||||
linkText = u.String()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// If the current visible text is trying to be a file name, use
|
|
||||||
// the correct file name.
|
|
||||||
if strings.HasSuffix(visibleText, ".md") && !strings.ContainsAny(visibleText, ` '"`+"`") {
|
|
||||||
visibleText = suggestedVisibleText
|
|
||||||
}
|
|
||||||
|
|
||||||
return fmt.Sprintf("[%s](%s)", visibleText, linkText+altText)
|
|
||||||
})
|
|
||||||
if len(errs) != 0 {
|
|
||||||
return "", errors.New(strings.Join(errs, ","))
|
|
||||||
}
|
|
||||||
return out, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// updateLinks assumes lines has links in markdown syntax, and verifies that
|
|
||||||
// any relative links actually point to files that exist.
|
|
||||||
func updateLinks(filePath string, mlines mungeLines) (mungeLines, error) {
|
|
||||||
var out mungeLines
|
|
||||||
allErrs := []string{}
|
|
||||||
|
|
||||||
for lineNum, mline := range mlines {
|
|
||||||
if mline.preformatted {
|
|
||||||
out = append(out, mline)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if badMatch := badLinkRE.FindString(mline.data); badMatch != "" {
|
|
||||||
allErrs = append(allErrs,
|
|
||||||
fmt.Sprintf("On line %d: found backwards markdown link %q", lineNum, badMatch))
|
|
||||||
}
|
|
||||||
if !mline.link {
|
|
||||||
out = append(out, mline)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
line, err := processLink(mline.data, filePath)
|
|
||||||
if err != nil {
|
|
||||||
var s = fmt.Sprintf("On line %d: %s", lineNum, err.Error())
|
|
||||||
err := errors.New(s)
|
|
||||||
allErrs = append(allErrs, err.Error())
|
|
||||||
}
|
|
||||||
ml := newMungeLine(line)
|
|
||||||
out = append(out, ml)
|
|
||||||
}
|
|
||||||
err := error(nil)
|
|
||||||
if len(allErrs) != 0 {
|
|
||||||
err = fmt.Errorf("%s", strings.Join(allErrs, "\n"))
|
|
||||||
}
|
|
||||||
return out, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// We have to append together before path.Clean will be able to tell that stuff
|
|
||||||
// like ../docs isn't needed.
|
|
||||||
func cleanPath(dirPath, linkPath string) string {
|
|
||||||
clean := path.Clean(path.Join(dirPath, linkPath))
|
|
||||||
if strings.HasPrefix(clean, dirPath+"/") {
|
|
||||||
out := strings.TrimPrefix(clean, dirPath+"/")
|
|
||||||
if out != linkPath {
|
|
||||||
fmt.Printf("%s -> %s\n", linkPath, out)
|
|
||||||
}
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
return linkPath
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkPath(filePath, linkPath string) (newPath string, ok bool) {
|
|
||||||
dir := path.Dir(filePath)
|
|
||||||
absFilePrefixes := []string{
|
|
||||||
"/kubernetes/kubernetes/blob/master/",
|
|
||||||
"/kubernetes/kubernetes/tree/master/",
|
|
||||||
}
|
|
||||||
for _, prefix := range absFilePrefixes {
|
|
||||||
if strings.HasPrefix(linkPath, prefix) {
|
|
||||||
linkPath = strings.TrimPrefix(linkPath, prefix)
|
|
||||||
// Now linkPath is relative to the root of the repo. The below
|
|
||||||
// loop that adds ../ at the beginning of the path should find
|
|
||||||
// the right path.
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if strings.HasPrefix(linkPath, "/") {
|
|
||||||
// These links might go to e.g. the github issues page, or a
|
|
||||||
// file at a particular revision, or another github project
|
|
||||||
// entirely.
|
|
||||||
return linkPath, true
|
|
||||||
}
|
|
||||||
linkPath = cleanPath(dir, linkPath)
|
|
||||||
|
|
||||||
// Fast exit if the link is already correct.
|
|
||||||
if info, err := os.Stat(path.Join(dir, linkPath)); err == nil {
|
|
||||||
if info.IsDir() {
|
|
||||||
return linkPath + "/", true
|
|
||||||
}
|
|
||||||
return linkPath, true
|
|
||||||
}
|
|
||||||
|
|
||||||
for strings.HasPrefix(linkPath, "../") {
|
|
||||||
linkPath = strings.TrimPrefix(linkPath, "../")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fix - vs _ automatically
|
|
||||||
nameMungers := []func(string) string{
|
|
||||||
func(s string) string { return s },
|
|
||||||
func(s string) string { return strings.Replace(s, "-", "_", -1) },
|
|
||||||
func(s string) string { return strings.Replace(s, "_", "-", -1) },
|
|
||||||
}
|
|
||||||
// Fix being moved into/out of admin (replace "admin" with directory
|
|
||||||
// you're doing mass movements to/from).
|
|
||||||
pathMungers := []func(string) string{
|
|
||||||
func(s string) string { return s },
|
|
||||||
func(s string) string { return path.Join("admin", s) },
|
|
||||||
func(s string) string { return strings.TrimPrefix(s, "admin/") },
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, namer := range nameMungers {
|
|
||||||
for _, pather := range pathMungers {
|
|
||||||
newPath = pather(namer(linkPath))
|
|
||||||
for i := 0; i < 7; i++ {
|
|
||||||
// The file must exist.
|
|
||||||
target := path.Join(dir, newPath)
|
|
||||||
if info, err := os.Stat(target); err == nil {
|
|
||||||
if info.IsDir() {
|
|
||||||
return newPath + "/", true
|
|
||||||
}
|
|
||||||
return cleanPath(dir, newPath), true
|
|
||||||
}
|
|
||||||
newPath = path.Join("..", newPath)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return linkPath, false
|
|
||||||
}
|
|
||||||
@@ -1,72 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2015 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 (
|
|
||||||
"fmt"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
var _ = fmt.Printf
|
|
||||||
|
|
||||||
func TestBadLinks(t *testing.T) {
|
|
||||||
var cases = []struct {
|
|
||||||
in string
|
|
||||||
}{
|
|
||||||
{"[NOTREADME](https://github.com/kubernetes/kubernetes/tree/master/NOTREADME.md)"},
|
|
||||||
{"[NOTREADME](https://github.com/kubernetes/kubernetes/tree/master/docs/NOTREADME.md)"},
|
|
||||||
{"[NOTREADME](../NOTREADME.md)"},
|
|
||||||
}
|
|
||||||
for _, c := range cases {
|
|
||||||
in := getMungeLines(c.in)
|
|
||||||
_, err := updateLinks("filename.md", in)
|
|
||||||
assert.Error(t, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
func TestGoodLinks(t *testing.T) {
|
|
||||||
var cases = []struct {
|
|
||||||
in string
|
|
||||||
expected string
|
|
||||||
}{
|
|
||||||
{"", ""},
|
|
||||||
{"[README](https://lwn.net)",
|
|
||||||
"[README](https://lwn.net)"},
|
|
||||||
// _ to -
|
|
||||||
{"[README](https://github.com/kubernetes/kubernetes/tree/master/cmd/mungedocs/testdata/test_dashes.md)",
|
|
||||||
"[README](../../cmd/mungedocs/testdata/test-dashes.md)"},
|
|
||||||
// - to _
|
|
||||||
{"[README](../../cmd/mungedocs/testdata/test-underscores.md)",
|
|
||||||
"[README](../../cmd/mungedocs/testdata/test_underscores.md)"},
|
|
||||||
|
|
||||||
// Does this even make sense? i dunno
|
|
||||||
{"[README](/docs/README.md)",
|
|
||||||
"[README](https://github.com/docs/README.md)"},
|
|
||||||
{"[README](/kubernetes/kubernetes/tree/master/cmd/mungedocs/testdata/README.md)",
|
|
||||||
"[README](../../cmd/mungedocs/testdata/README.md)"},
|
|
||||||
}
|
|
||||||
for i, c := range cases {
|
|
||||||
in := getMungeLines(c.in)
|
|
||||||
expected := getMungeLines(c.expected)
|
|
||||||
actual, err := updateLinks("filename.md", in)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
if !actual.Equal(expected) {
|
|
||||||
t.Errorf("case[%d]: expected %q got %q", i, c.expected, actual.String())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,239 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2015 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 (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
flag "github.com/spf13/pflag"
|
|
||||||
)
|
|
||||||
|
|
||||||
// This needs to be updated when we cut a new release series.
|
|
||||||
const latestReleaseBranch = "release-1.5"
|
|
||||||
|
|
||||||
var (
|
|
||||||
verbose = flag.Bool("verbose", false, "On verification failure, emit pre-munge and post-munge versions.")
|
|
||||||
verify = flag.Bool("verify", false, "Exit with status 1 if files would have needed changes but do not change.")
|
|
||||||
norecurse = flag.Bool("norecurse", false, "Only process the files of --root-dir.")
|
|
||||||
upstream = flag.String("upstream", "upstream", "The name of the upstream Git remote to pull from")
|
|
||||||
rootDir = flag.String("root-dir", "", "Root directory containing documents to be processed.")
|
|
||||||
// "repo-root" seems like a dumb name, this is the relative path (from rootDir) to get to the repoRoot
|
|
||||||
relRoot = flag.String("repo-root", "..", `Appended to --root-dir to get the repository root.
|
|
||||||
It's done this way so that generally you just have to set --root-dir.
|
|
||||||
Examples:
|
|
||||||
* --root-dir=docs/ --repo-root=.. means the repository root is ./
|
|
||||||
* --root-dir=/usr/local/long/path/repo/docs/ --repo-root=.. means the repository root is /usr/local/long/path/repo/
|
|
||||||
* --root-dir=/usr/local/long/path/repo/docs/admin --repo-root=../.. means the repository root is /usr/local/long/path/repo/`)
|
|
||||||
skipMunges = flag.String("skip-munges", "", "Comma-separated list of munges to *not* run. Available munges are: "+availableMungeList)
|
|
||||||
repoRoot string
|
|
||||||
|
|
||||||
ErrChangesNeeded = errors.New("mungedocs: changes required")
|
|
||||||
|
|
||||||
// All of the munge operations to perform.
|
|
||||||
// TODO: allow selection from command line. (e.g., just check links in the examples directory.)
|
|
||||||
allMunges = []munge{
|
|
||||||
// Simple "check something" functions must run first.
|
|
||||||
{"preformat-balance", checkPreformatBalance},
|
|
||||||
// Functions which modify state.
|
|
||||||
{"remove-whitespace", updateWhitespace},
|
|
||||||
{"table-of-contents", updateTOC},
|
|
||||||
{"md-links", updateLinks},
|
|
||||||
{"blank-lines-surround-preformatted", updatePreformatted},
|
|
||||||
{"header-lines", updateHeaderLines},
|
|
||||||
{"analytics", updateAnalytics},
|
|
||||||
{"kubectl-dash-f", updateKubectlFileTargets},
|
|
||||||
{"sync-examples", syncExamples},
|
|
||||||
}
|
|
||||||
availableMungeList = func() string {
|
|
||||||
names := []string{}
|
|
||||||
for _, m := range allMunges {
|
|
||||||
names = append(names, m.name)
|
|
||||||
}
|
|
||||||
return strings.Join(names, ",")
|
|
||||||
}()
|
|
||||||
)
|
|
||||||
|
|
||||||
// a munge processes a document, returning an updated document xor an error.
|
|
||||||
// The fn is NOT allowed to mutate 'before', if changes are needed it must copy
|
|
||||||
// data into a new byte array and return that.
|
|
||||||
type munge struct {
|
|
||||||
name string
|
|
||||||
fn func(filePath string, mlines mungeLines) (after mungeLines, err error)
|
|
||||||
}
|
|
||||||
|
|
||||||
type fileProcessor struct {
|
|
||||||
// Which munge functions should we call?
|
|
||||||
munges []munge
|
|
||||||
|
|
||||||
// Are we allowed to make changes?
|
|
||||||
verifyOnly bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// Either change a file or verify that it needs no changes (according to modify argument)
|
|
||||||
func (f fileProcessor) visit(path string) error {
|
|
||||||
if !strings.HasSuffix(path, ".md") {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
fileBytes, err := ioutil.ReadFile(path)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
mungeLines := getMungeLines(string(fileBytes))
|
|
||||||
|
|
||||||
modificationsMade := false
|
|
||||||
errFound := false
|
|
||||||
filePrinted := false
|
|
||||||
for _, munge := range f.munges {
|
|
||||||
after, err := munge.fn(path, mungeLines)
|
|
||||||
if err != nil || !after.Equal(mungeLines) {
|
|
||||||
if !filePrinted {
|
|
||||||
fmt.Printf("%s\n----\n", path)
|
|
||||||
filePrinted = true
|
|
||||||
}
|
|
||||||
fmt.Printf("%s:\n", munge.name)
|
|
||||||
if *verbose {
|
|
||||||
if len(mungeLines) <= 20 {
|
|
||||||
fmt.Printf("INPUT: <<<%v>>>\n", mungeLines)
|
|
||||||
fmt.Printf("MUNGED: <<<%v>>>\n", after)
|
|
||||||
} else {
|
|
||||||
fmt.Printf("not printing failed chunk: too many lines\n")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
errFound = true
|
|
||||||
} else {
|
|
||||||
fmt.Println("contents were modified")
|
|
||||||
modificationsMade = true
|
|
||||||
}
|
|
||||||
fmt.Println("")
|
|
||||||
}
|
|
||||||
mungeLines = after
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write out new file with any changes.
|
|
||||||
if modificationsMade {
|
|
||||||
if f.verifyOnly {
|
|
||||||
// We're not allowed to make changes.
|
|
||||||
return ErrChangesNeeded
|
|
||||||
}
|
|
||||||
ioutil.WriteFile(path, mungeLines.Bytes(), 0644)
|
|
||||||
}
|
|
||||||
if errFound {
|
|
||||||
return ErrChangesNeeded
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func newWalkFunc(fp *fileProcessor, changesNeeded *bool) filepath.WalkFunc {
|
|
||||||
return func(path string, info os.FileInfo, err error) error {
|
|
||||||
stat, err := os.Stat(path)
|
|
||||||
if err != nil {
|
|
||||||
if os.IsNotExist(err) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if path != *rootDir && stat.IsDir() && *norecurse {
|
|
||||||
return filepath.SkipDir
|
|
||||||
}
|
|
||||||
if err := fp.visit(path); err != nil {
|
|
||||||
*changesNeeded = true
|
|
||||||
if err != ErrChangesNeeded {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func wantedMunges() (filtered []munge) {
|
|
||||||
skipList := strings.Split(*skipMunges, ",")
|
|
||||||
skipped := map[string]bool{}
|
|
||||||
for _, m := range skipList {
|
|
||||||
if len(m) > 0 {
|
|
||||||
skipped[m] = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, m := range allMunges {
|
|
||||||
if !skipped[m.name] {
|
|
||||||
filtered = append(filtered, m)
|
|
||||||
} else {
|
|
||||||
// Remove from the map so we can verify that everything
|
|
||||||
// requested was in fact valid.
|
|
||||||
delete(skipped, m.name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(skipped) != 0 {
|
|
||||||
fmt.Fprintf(os.Stderr, "ERROR: requested to skip %v, but these are not valid munges. (valid: %v)\n", skipped, availableMungeList)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
return filtered
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
var err error
|
|
||||||
flag.Parse()
|
|
||||||
|
|
||||||
if *rootDir == "" {
|
|
||||||
fmt.Fprintf(os.Stderr, "usage: %s [--help] [--verify] [--norecurse] --root-dir [--skip-munges=<skip list>] [--upstream=<git remote>] <docs root>\n", flag.Arg(0))
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
repoRoot = path.Join(*rootDir, *relRoot)
|
|
||||||
repoRoot, err = filepath.Abs(repoRoot)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintf(os.Stderr, "ERROR: %v\n", err)
|
|
||||||
os.Exit(2)
|
|
||||||
}
|
|
||||||
|
|
||||||
fp := fileProcessor{
|
|
||||||
munges: wantedMunges(),
|
|
||||||
verifyOnly: *verify,
|
|
||||||
}
|
|
||||||
|
|
||||||
// For each markdown file under source docs root, process the doc.
|
|
||||||
// - If any error occurs: exit with failure (exit >1).
|
|
||||||
// - If verify is true: exit 0 if no changes needed, exit 1 if changes
|
|
||||||
// needed.
|
|
||||||
// - If verify is false: exit 0 if changes successfully made or no
|
|
||||||
// changes needed, exit 1 if manual changes are needed.
|
|
||||||
var changesNeeded bool
|
|
||||||
|
|
||||||
err = filepath.Walk(*rootDir, newWalkFunc(&fp, &changesNeeded))
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintf(os.Stderr, "ERROR: %v\n", err)
|
|
||||||
os.Exit(2)
|
|
||||||
}
|
|
||||||
if changesNeeded {
|
|
||||||
if *verify {
|
|
||||||
fmt.Fprintf(os.Stderr, "FAIL: changes needed but not made due to --verify\n")
|
|
||||||
} else {
|
|
||||||
fmt.Fprintf(os.Stderr, "FAIL: some manual changes are still required.\n")
|
|
||||||
}
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2015 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 "fmt"
|
|
||||||
|
|
||||||
// Blocks of ``` need to have blank lines on both sides or they don't look
|
|
||||||
// right in HTML.
|
|
||||||
func updatePreformatted(filePath string, mlines mungeLines) (mungeLines, error) {
|
|
||||||
var out mungeLines
|
|
||||||
inpreformat := false
|
|
||||||
for i, mline := range mlines {
|
|
||||||
if !inpreformat && mline.preformatted {
|
|
||||||
if i == 0 || out[len(out)-1].data != "" {
|
|
||||||
out = append(out, blankMungeLine)
|
|
||||||
}
|
|
||||||
// start of a preformat block
|
|
||||||
inpreformat = true
|
|
||||||
}
|
|
||||||
out = append(out, mline)
|
|
||||||
if inpreformat && !mline.preformatted {
|
|
||||||
if i >= len(mlines)-2 || mlines[i+1].data != "" {
|
|
||||||
out = append(out, blankMungeLine)
|
|
||||||
}
|
|
||||||
inpreformat = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return out, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the file ends on a preformatted line, there must have been an imbalance.
|
|
||||||
func checkPreformatBalance(filePath string, mlines mungeLines) (mungeLines, error) {
|
|
||||||
if len(mlines) > 0 && mlines[len(mlines)-1].preformatted {
|
|
||||||
return mlines, fmt.Errorf("unbalanced triple backtick delimiters")
|
|
||||||
}
|
|
||||||
return mlines, nil
|
|
||||||
}
|
|
||||||
@@ -1,97 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2015 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 (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestPreformatted(t *testing.T) {
|
|
||||||
var cases = []struct {
|
|
||||||
in string
|
|
||||||
expected string
|
|
||||||
}{
|
|
||||||
{"", ""},
|
|
||||||
{
|
|
||||||
"```\nbob\n```",
|
|
||||||
"\n```\nbob\n```\n\n",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"```\nbob\n```\n```\nnotbob\n```\n",
|
|
||||||
"\n```\nbob\n```\n\n```\nnotbob\n```\n\n",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"```bob```\n",
|
|
||||||
"```bob```\n",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
" ```\n bob\n ```",
|
|
||||||
"\n ```\n bob\n ```\n\n",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for i, c := range cases {
|
|
||||||
in := getMungeLines(c.in)
|
|
||||||
expected := getMungeLines(c.expected)
|
|
||||||
actual, err := updatePreformatted("filename.md", in)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
if !actual.Equal(expected) {
|
|
||||||
t.Errorf("case[%d]: expected %q got %q", i, c.expected, actual.String())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPreformattedImbalance(t *testing.T) {
|
|
||||||
var cases = []struct {
|
|
||||||
in string
|
|
||||||
ok bool
|
|
||||||
}{
|
|
||||||
{"", true},
|
|
||||||
{"```\nin\n```", true},
|
|
||||||
{"```\nin\n```\nout", true},
|
|
||||||
{"```", false},
|
|
||||||
{"```\nin\n```\nout\n```", false},
|
|
||||||
}
|
|
||||||
for i, c := range cases {
|
|
||||||
in := getMungeLines(c.in)
|
|
||||||
out, err := checkPreformatBalance("filename.md", in)
|
|
||||||
if err != nil && c.ok {
|
|
||||||
t.Errorf("case[%d]: expected success", i)
|
|
||||||
}
|
|
||||||
if err == nil && !c.ok {
|
|
||||||
t.Errorf("case[%d]: expected failure", i)
|
|
||||||
}
|
|
||||||
// Even in case of misformat, return all the text,
|
|
||||||
// so that the user's work is not lost.
|
|
||||||
if !equalMungeLines(out, in) {
|
|
||||||
t.Errorf("case[%d]: expected munged text to be identical to input text", i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func equalMungeLines(a, b mungeLines) bool {
|
|
||||||
if len(a) != len(b) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
for i := range a {
|
|
||||||
if a[i] != b[i] {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
1
cmd/mungedocs/testdata/README.md
vendored
1
cmd/mungedocs/testdata/README.md
vendored
@@ -1 +0,0 @@
|
|||||||
some text
|
|
||||||
1
cmd/mungedocs/testdata/example.txt
vendored
1
cmd/mungedocs/testdata/example.txt
vendored
@@ -1 +0,0 @@
|
|||||||
some text
|
|
||||||
10
cmd/mungedocs/testdata/pod.yaml
vendored
10
cmd/mungedocs/testdata/pod.yaml
vendored
@@ -1,10 +0,0 @@
|
|||||||
apiVersion: v1
|
|
||||||
kind: Pod
|
|
||||||
metadata:
|
|
||||||
name: nginx
|
|
||||||
spec:
|
|
||||||
containers:
|
|
||||||
- name: nginx
|
|
||||||
image: nginx
|
|
||||||
ports:
|
|
||||||
- containerPort: 80
|
|
||||||
1
cmd/mungedocs/testdata/test-dashes.md
vendored
1
cmd/mungedocs/testdata/test-dashes.md
vendored
@@ -1 +0,0 @@
|
|||||||
some text
|
|
||||||
1
cmd/mungedocs/testdata/test_underscores.md
vendored
1
cmd/mungedocs/testdata/test_underscores.md
vendored
@@ -1 +0,0 @@
|
|||||||
some text
|
|
||||||
@@ -1,89 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2015 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 (
|
|
||||||
"fmt"
|
|
||||||
"regexp"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
const tocMungeTag = "GENERATED_TOC"
|
|
||||||
|
|
||||||
var r = regexp.MustCompile("[^A-Za-z0-9-]")
|
|
||||||
|
|
||||||
// inserts/updates a table of contents in markdown file.
|
|
||||||
//
|
|
||||||
// First, builds a ToC.
|
|
||||||
// Then, finds the magic macro block tags and replaces anything between those with
|
|
||||||
// the ToC, thereby updating any previously inserted ToC.
|
|
||||||
//
|
|
||||||
// TODO(erictune): put this in own package with tests
|
|
||||||
func updateTOC(filePath string, mlines mungeLines) (mungeLines, error) {
|
|
||||||
toc := buildTOC(mlines)
|
|
||||||
updatedMarkdown, err := updateMacroBlock(mlines, tocMungeTag, toc)
|
|
||||||
if err != nil {
|
|
||||||
return mlines, err
|
|
||||||
}
|
|
||||||
return updatedMarkdown, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// builds table of contents for markdown file
|
|
||||||
//
|
|
||||||
// First scans for all section headers (lines that begin with "#" but not within code quotes)
|
|
||||||
// and builds a table of contents from those. Assumes bookmarks for those will be
|
|
||||||
// like #each-word-in-heading-in-lowercases-with-dashes-instead-of-spaces.
|
|
||||||
// builds the ToC.
|
|
||||||
|
|
||||||
func buildTOC(mlines mungeLines) mungeLines {
|
|
||||||
var out mungeLines
|
|
||||||
bookmarks := map[string]int{}
|
|
||||||
|
|
||||||
for _, mline := range mlines {
|
|
||||||
if mline.preformatted || !mline.header {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// Add a blank line after the munge start tag
|
|
||||||
if len(out) == 0 {
|
|
||||||
out = append(out, blankMungeLine)
|
|
||||||
}
|
|
||||||
line := mline.data
|
|
||||||
noSharps := strings.TrimLeft(line, "#")
|
|
||||||
numSharps := len(line) - len(noSharps)
|
|
||||||
heading := strings.Trim(noSharps, " \n")
|
|
||||||
if numSharps > 0 {
|
|
||||||
indent := strings.Repeat(" ", numSharps-1)
|
|
||||||
bookmark := strings.Replace(strings.ToLower(heading), " ", "-", -1)
|
|
||||||
// remove symbols (except for -) in bookmarks
|
|
||||||
bookmark = r.ReplaceAllString(bookmark, "")
|
|
||||||
// Incremental counter for duplicate bookmarks
|
|
||||||
next := bookmarks[bookmark]
|
|
||||||
bookmarks[bookmark] = next + 1
|
|
||||||
if next > 0 {
|
|
||||||
bookmark = fmt.Sprintf("%s-%d", bookmark, next)
|
|
||||||
}
|
|
||||||
tocLine := fmt.Sprintf("%s- [%s](#%s)", indent, heading, bookmark)
|
|
||||||
out = append(out, newMungeLine(tocLine))
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
// Add a blank line before the munge end tag
|
|
||||||
if len(out) != 0 {
|
|
||||||
out = append(out, blankMungeLine)
|
|
||||||
}
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
@@ -1,79 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2015 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 (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Test_buildTOC(t *testing.T) {
|
|
||||||
var cases = []struct {
|
|
||||||
in string
|
|
||||||
expected string
|
|
||||||
}{
|
|
||||||
{"", ""},
|
|
||||||
{"Lorem ipsum\ndolor sit amet\n", ""},
|
|
||||||
{
|
|
||||||
"# Title\nLorem ipsum \n## Section Heading\ndolor sit amet\n",
|
|
||||||
"\n- [Title](#title)\n - [Section Heading](#section-heading)\n\n",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"# Title\nLorem ipsum \n## Section Heading\ndolor sit amet\n```bash\n#!/bin/sh\n```",
|
|
||||||
"\n- [Title](#title)\n - [Section Heading](#section-heading)\n\n",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"# Title\nLorem ipsum \n## Section Heading\n### Ok, why doesn't this work? ...add 4 *more* `symbols`!\ndolor sit amet\n",
|
|
||||||
"\n- [Title](#title)\n - [Section Heading](#section-heading)\n - [Ok, why doesn't this work? ...add 4 *more* `symbols`!](#ok-why-doesnt-this-work-add-4-more-symbols)\n\n",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for i, c := range cases {
|
|
||||||
in := getMungeLines(c.in)
|
|
||||||
expected := getMungeLines(c.expected)
|
|
||||||
actual := buildTOC(in)
|
|
||||||
if !expected.Equal(actual) {
|
|
||||||
t.Errorf("Case[%d] Expected TOC '%v' but got '%v'", i, expected.String(), actual.String())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_updateTOC(t *testing.T) {
|
|
||||||
var cases = []struct {
|
|
||||||
in string
|
|
||||||
expected string
|
|
||||||
}{
|
|
||||||
{"", ""},
|
|
||||||
{
|
|
||||||
"Lorem ipsum\ndolor sit amet\n",
|
|
||||||
"Lorem ipsum\ndolor sit amet\n",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"# Title\nLorem ipsum \n**table of contents**\n<!-- BEGIN MUNGE: GENERATED_TOC -->\nold cruft\n<!-- END MUNGE: GENERATED_TOC -->\n## Section Heading\ndolor sit amet\n",
|
|
||||||
"# Title\nLorem ipsum \n**table of contents**\n<!-- BEGIN MUNGE: GENERATED_TOC -->\n\n- [Title](#title)\n - [Section Heading](#section-heading)\n\n<!-- END MUNGE: GENERATED_TOC -->\n## Section Heading\ndolor sit amet\n",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, c := range cases {
|
|
||||||
in := getMungeLines(c.in)
|
|
||||||
expected := getMungeLines(c.expected)
|
|
||||||
actual, err := updateTOC("filename.md", in)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
if !expected.Equal(actual) {
|
|
||||||
t.Errorf("Expected TOC '%v' but got '%v'", expected.String(), actual.String())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,291 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2015 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 (
|
|
||||||
"fmt"
|
|
||||||
"path"
|
|
||||||
"path/filepath"
|
|
||||||
"regexp"
|
|
||||||
"strings"
|
|
||||||
"unicode"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Replaces the text between matching "beginMark" and "endMark" within the
|
|
||||||
// document represented by "lines" with "insertThis".
|
|
||||||
//
|
|
||||||
// Delimiters should occupy own line.
|
|
||||||
// Returns copy of document with modifications.
|
|
||||||
func updateMacroBlock(mlines mungeLines, token string, insertThis mungeLines) (mungeLines, error) {
|
|
||||||
beginMark := beginMungeTag(token)
|
|
||||||
endMark := endMungeTag(token)
|
|
||||||
var out mungeLines
|
|
||||||
betweenBeginAndEnd := false
|
|
||||||
for _, mline := range mlines {
|
|
||||||
if mline.preformatted && !betweenBeginAndEnd {
|
|
||||||
out = append(out, mline)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
line := mline.data
|
|
||||||
if mline.beginTag && line == beginMark {
|
|
||||||
if betweenBeginAndEnd {
|
|
||||||
return nil, fmt.Errorf("found second begin mark while updating macro blocks")
|
|
||||||
}
|
|
||||||
betweenBeginAndEnd = true
|
|
||||||
out = append(out, mline)
|
|
||||||
} else if mline.endTag && line == endMark {
|
|
||||||
if !betweenBeginAndEnd {
|
|
||||||
return nil, fmt.Errorf("found end mark without begin mark while updating macro blocks")
|
|
||||||
}
|
|
||||||
betweenBeginAndEnd = false
|
|
||||||
out = append(out, insertThis...)
|
|
||||||
out = append(out, mline)
|
|
||||||
} else {
|
|
||||||
if !betweenBeginAndEnd {
|
|
||||||
out = append(out, mline)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if betweenBeginAndEnd {
|
|
||||||
return nil, fmt.Errorf("never found closing end mark while updating macro blocks")
|
|
||||||
}
|
|
||||||
return out, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tests that a document, represented as a slice of lines, has a line. Ignores
|
|
||||||
// leading and trailing space.
|
|
||||||
func hasLine(lines mungeLines, needle string) bool {
|
|
||||||
for _, mline := range lines {
|
|
||||||
haystack := strings.TrimSpace(mline.data)
|
|
||||||
if haystack == needle {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func removeMacroBlock(token string, mlines mungeLines) (mungeLines, error) {
|
|
||||||
beginMark := beginMungeTag(token)
|
|
||||||
endMark := endMungeTag(token)
|
|
||||||
var out mungeLines
|
|
||||||
betweenBeginAndEnd := false
|
|
||||||
for _, mline := range mlines {
|
|
||||||
if mline.preformatted {
|
|
||||||
out = append(out, mline)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
line := mline.data
|
|
||||||
if mline.beginTag && line == beginMark {
|
|
||||||
if betweenBeginAndEnd {
|
|
||||||
return nil, fmt.Errorf("found second begin mark while updating macro blocks")
|
|
||||||
}
|
|
||||||
betweenBeginAndEnd = true
|
|
||||||
} else if mline.endTag && line == endMark {
|
|
||||||
if !betweenBeginAndEnd {
|
|
||||||
return nil, fmt.Errorf("found end mark without begin mark while updating macro blocks")
|
|
||||||
}
|
|
||||||
betweenBeginAndEnd = false
|
|
||||||
} else {
|
|
||||||
if !betweenBeginAndEnd {
|
|
||||||
out = append(out, mline)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if betweenBeginAndEnd {
|
|
||||||
return nil, fmt.Errorf("never found closing end mark while updating macro blocks")
|
|
||||||
}
|
|
||||||
return out, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add a macro block to the beginning of a set of lines
|
|
||||||
func prependMacroBlock(token string, mlines mungeLines) mungeLines {
|
|
||||||
beginLine := newMungeLine(beginMungeTag(token))
|
|
||||||
endLine := newMungeLine(endMungeTag(token))
|
|
||||||
out := mungeLines{beginLine, endLine}
|
|
||||||
if len(mlines) > 0 && mlines[0].data != "" {
|
|
||||||
out = append(out, blankMungeLine)
|
|
||||||
}
|
|
||||||
return append(out, mlines...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add a macro block to the end of a set of lines
|
|
||||||
func appendMacroBlock(mlines mungeLines, token string) mungeLines {
|
|
||||||
beginLine := newMungeLine(beginMungeTag(token))
|
|
||||||
endLine := newMungeLine(endMungeTag(token))
|
|
||||||
out := mlines
|
|
||||||
if len(mlines) > 0 && mlines[len(mlines)-1].data != "" {
|
|
||||||
out = append(out, blankMungeLine)
|
|
||||||
}
|
|
||||||
return append(out, beginLine, endLine)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tests that a document, represented as a slice of lines, has a macro block.
|
|
||||||
func hasMacroBlock(lines mungeLines, token string) bool {
|
|
||||||
beginMark := beginMungeTag(token)
|
|
||||||
endMark := endMungeTag(token)
|
|
||||||
|
|
||||||
foundBegin := false
|
|
||||||
for _, mline := range lines {
|
|
||||||
if mline.preformatted {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if !mline.beginTag && !mline.endTag {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
line := mline.data
|
|
||||||
switch {
|
|
||||||
case !foundBegin && line == beginMark:
|
|
||||||
foundBegin = true
|
|
||||||
case foundBegin && line == endMark:
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the canonical begin-tag for a given description. This does not
|
|
||||||
// include the trailing newline.
|
|
||||||
func beginMungeTag(desc string) string {
|
|
||||||
return fmt.Sprintf("<!-- BEGIN MUNGE: %s -->", desc)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the canonical end-tag for a given description. This does not
|
|
||||||
// include the trailing newline.
|
|
||||||
func endMungeTag(desc string) string {
|
|
||||||
return fmt.Sprintf("<!-- END MUNGE: %s -->", desc)
|
|
||||||
}
|
|
||||||
|
|
||||||
type mungeLine struct {
|
|
||||||
data string
|
|
||||||
preformatted bool
|
|
||||||
header bool
|
|
||||||
link bool
|
|
||||||
beginTag bool
|
|
||||||
endTag bool
|
|
||||||
}
|
|
||||||
|
|
||||||
type mungeLines []mungeLine
|
|
||||||
|
|
||||||
func (m1 mungeLines) Equal(m2 mungeLines) bool {
|
|
||||||
if len(m1) != len(m2) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
for i := range m1 {
|
|
||||||
if m1[i].data != m2[i].data {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mlines mungeLines) String() string {
|
|
||||||
slice := []string{}
|
|
||||||
for _, mline := range mlines {
|
|
||||||
slice = append(slice, mline.data)
|
|
||||||
}
|
|
||||||
s := strings.Join(slice, "\n")
|
|
||||||
// We need to tack on an extra newline at the end of the file
|
|
||||||
return s + "\n"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mlines mungeLines) Bytes() []byte {
|
|
||||||
return []byte(mlines.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
// Finds all preformatted block start/stops.
|
|
||||||
preformatRE = regexp.MustCompile("^\\s*```")
|
|
||||||
notPreformatRE = regexp.MustCompile("^\\s*```.*```")
|
|
||||||
// Is this line a header?
|
|
||||||
mlHeaderRE = regexp.MustCompile(`^#`)
|
|
||||||
// Is there a link on this line?
|
|
||||||
mlLinkRE = regexp.MustCompile(`\[[^]]*\]\([^)]*\)`)
|
|
||||||
beginTagRE = regexp.MustCompile(`<!-- BEGIN MUNGE:`)
|
|
||||||
endTagRE = regexp.MustCompile(`<!-- END MUNGE:`)
|
|
||||||
|
|
||||||
blankMungeLine = newMungeLine("")
|
|
||||||
)
|
|
||||||
|
|
||||||
// Does not set 'preformatted'
|
|
||||||
func newMungeLine(line string) mungeLine {
|
|
||||||
return mungeLine{
|
|
||||||
data: line,
|
|
||||||
header: mlHeaderRE.MatchString(line),
|
|
||||||
link: mlLinkRE.MatchString(line),
|
|
||||||
beginTag: beginTagRE.MatchString(line),
|
|
||||||
endTag: endTagRE.MatchString(line),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func trimRightSpace(in string) string {
|
|
||||||
return strings.TrimRightFunc(in, unicode.IsSpace)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Splits a document up into a slice of lines.
|
|
||||||
func splitLines(document string) []string {
|
|
||||||
lines := strings.Split(document, "\n")
|
|
||||||
// Skip trailing empty string from Split-ing
|
|
||||||
if len(lines) > 0 && lines[len(lines)-1] == "" {
|
|
||||||
lines = lines[:len(lines)-1]
|
|
||||||
}
|
|
||||||
return lines
|
|
||||||
}
|
|
||||||
|
|
||||||
func getMungeLines(in string) mungeLines {
|
|
||||||
var out mungeLines
|
|
||||||
preformatted := false
|
|
||||||
|
|
||||||
lines := splitLines(in)
|
|
||||||
// We indicate if any given line is inside a preformatted block or
|
|
||||||
// outside a preformatted block
|
|
||||||
for _, line := range lines {
|
|
||||||
if !preformatted {
|
|
||||||
if preformatRE.MatchString(line) && !notPreformatRE.MatchString(line) {
|
|
||||||
preformatted = true
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if preformatRE.MatchString(line) {
|
|
||||||
preformatted = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ml := newMungeLine(line)
|
|
||||||
ml.preformatted = preformatted
|
|
||||||
out = append(out, ml)
|
|
||||||
}
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
// filePath is the file we are looking for
|
|
||||||
// inFile is the file where we found the link. So if we are processing
|
|
||||||
// /path/to/repoRoot/docs/admin/README.md and are looking for
|
|
||||||
// ../../file.json we can find that location.
|
|
||||||
// In many cases filePath and processingFile may be the same
|
|
||||||
func makeRepoRelative(filePath string, processingFile string) (string, error) {
|
|
||||||
if filePath, err := filepath.Rel(repoRoot, filePath); err == nil {
|
|
||||||
return filePath, nil
|
|
||||||
}
|
|
||||||
cwd := path.Dir(processingFile)
|
|
||||||
return filepath.Rel(repoRoot, path.Join(cwd, filePath))
|
|
||||||
}
|
|
||||||
|
|
||||||
func makeFileRelative(filePath string, processingFile string) (string, error) {
|
|
||||||
cwd := path.Dir(processingFile)
|
|
||||||
if filePath, err := filepath.Rel(cwd, filePath); err == nil {
|
|
||||||
return filePath, nil
|
|
||||||
}
|
|
||||||
return filepath.Rel(cwd, path.Join(cwd, filePath))
|
|
||||||
}
|
|
||||||
@@ -1,169 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2015 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 (
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Test_updateMacroBlock(t *testing.T) {
|
|
||||||
token := "TOKEN"
|
|
||||||
BEGIN := beginMungeTag(token)
|
|
||||||
END := endMungeTag(token)
|
|
||||||
|
|
||||||
var cases = []struct {
|
|
||||||
in string
|
|
||||||
out string
|
|
||||||
}{
|
|
||||||
{"", ""},
|
|
||||||
{"Lorem ipsum\ndolor sit amet\n",
|
|
||||||
"Lorem ipsum\ndolor sit amet\n"},
|
|
||||||
{"Lorem ipsum \n" + BEGIN + "\ndolor\n" + END + "\nsit amet\n",
|
|
||||||
"Lorem ipsum \n" + BEGIN + "\nfoo\n" + END + "\nsit amet\n"},
|
|
||||||
}
|
|
||||||
for _, c := range cases {
|
|
||||||
in := getMungeLines(c.in)
|
|
||||||
expected := getMungeLines(c.out)
|
|
||||||
actual, err := updateMacroBlock(in, token, getMungeLines("foo"))
|
|
||||||
assert.NoError(t, err)
|
|
||||||
if !expected.Equal(actual) {
|
|
||||||
t.Errorf("Expected '%v' but got '%v'", expected.String(), expected.String())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_updateMacroBlock_errors(t *testing.T) {
|
|
||||||
token := "TOKEN"
|
|
||||||
b := beginMungeTag(token)
|
|
||||||
e := endMungeTag(token)
|
|
||||||
|
|
||||||
var cases = []struct {
|
|
||||||
in string
|
|
||||||
}{
|
|
||||||
{b + "\n"},
|
|
||||||
{"blah\n" + b + "\nblah"},
|
|
||||||
{e + "\n"},
|
|
||||||
{"blah\n" + e + "\nblah\n"},
|
|
||||||
{e + "\n" + b},
|
|
||||||
{b + "\n" + e + "\n" + e},
|
|
||||||
{b + "\n" + b + "\n" + e},
|
|
||||||
{b + "\n" + b + "\n" + e + "\n" + e},
|
|
||||||
}
|
|
||||||
for _, c := range cases {
|
|
||||||
in := getMungeLines(c.in)
|
|
||||||
_, err := updateMacroBlock(in, token, getMungeLines("foo"))
|
|
||||||
assert.Error(t, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestHasLine(t *testing.T) {
|
|
||||||
cases := []struct {
|
|
||||||
haystack string
|
|
||||||
needle string
|
|
||||||
expected bool
|
|
||||||
}{
|
|
||||||
{"abc\ndef\nghi", "abc", true},
|
|
||||||
{" abc\ndef\nghi", "abc", true},
|
|
||||||
{"abc \ndef\nghi", "abc", true},
|
|
||||||
{"\n abc\ndef\nghi", "abc", true},
|
|
||||||
{"abc \n\ndef\nghi", "abc", true},
|
|
||||||
{"abc\ndef\nghi", "def", true},
|
|
||||||
{"abc\ndef\nghi", "ghi", true},
|
|
||||||
{"abc\ndef\nghi", "xyz", false},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, c := range cases {
|
|
||||||
in := getMungeLines(c.haystack)
|
|
||||||
if hasLine(in, c.needle) != c.expected {
|
|
||||||
t.Errorf("case[%d]: %q, expected %t, got %t", i, c.needle, c.expected, !c.expected)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestHasMacroBlock(t *testing.T) {
|
|
||||||
token := "<<<"
|
|
||||||
b := beginMungeTag(token)
|
|
||||||
e := endMungeTag(token)
|
|
||||||
cases := []struct {
|
|
||||||
lines []string
|
|
||||||
expected bool
|
|
||||||
}{
|
|
||||||
{[]string{b, e}, true},
|
|
||||||
{[]string{b, "abc", e}, true},
|
|
||||||
{[]string{b, b, "abc", e}, true},
|
|
||||||
{[]string{b, "abc", e, e}, true},
|
|
||||||
{[]string{b, e, b, e}, true},
|
|
||||||
{[]string{b}, false},
|
|
||||||
{[]string{e}, false},
|
|
||||||
{[]string{b, "abc"}, false},
|
|
||||||
{[]string{"abc", e}, false},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, c := range cases {
|
|
||||||
in := getMungeLines(strings.Join(c.lines, "\n"))
|
|
||||||
if hasMacroBlock(in, token) != c.expected {
|
|
||||||
t.Errorf("case[%d]: expected %t, got %t", i, c.expected, !c.expected)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAppendMacroBlock(t *testing.T) {
|
|
||||||
token := "<<<"
|
|
||||||
b := beginMungeTag(token)
|
|
||||||
e := endMungeTag(token)
|
|
||||||
cases := []struct {
|
|
||||||
in []string
|
|
||||||
expected []string
|
|
||||||
}{
|
|
||||||
{[]string{}, []string{b, e}},
|
|
||||||
{[]string{"bob"}, []string{"bob", "", b, e}},
|
|
||||||
{[]string{b, e}, []string{b, e, "", b, e}},
|
|
||||||
}
|
|
||||||
for i, c := range cases {
|
|
||||||
in := getMungeLines(strings.Join(c.in, "\n"))
|
|
||||||
expected := getMungeLines(strings.Join(c.expected, "\n"))
|
|
||||||
out := appendMacroBlock(in, token)
|
|
||||||
if !out.Equal(expected) {
|
|
||||||
t.Errorf("Case[%d]: expected '%q' but got '%q'", i, expected.String(), out.String())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPrependMacroBlock(t *testing.T) {
|
|
||||||
token := "<<<"
|
|
||||||
b := beginMungeTag(token)
|
|
||||||
e := endMungeTag(token)
|
|
||||||
cases := []struct {
|
|
||||||
in []string
|
|
||||||
expected []string
|
|
||||||
}{
|
|
||||||
{[]string{}, []string{b, e}},
|
|
||||||
{[]string{"bob"}, []string{b, e, "", "bob"}},
|
|
||||||
{[]string{b, e}, []string{b, e, "", b, e}},
|
|
||||||
}
|
|
||||||
for i, c := range cases {
|
|
||||||
in := getMungeLines(strings.Join(c.in, "\n"))
|
|
||||||
expected := getMungeLines(strings.Join(c.expected, "\n"))
|
|
||||||
out := prependMacroBlock(token, in)
|
|
||||||
if !out.Equal(expected) {
|
|
||||||
t.Errorf("Case[%d]: expected '%q' but got '%q'", i, expected.String(), out.String())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2015 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
|
|
||||||
|
|
||||||
// Remove all trailing whitespace
|
|
||||||
func updateWhitespace(file string, mlines mungeLines) (mungeLines, error) {
|
|
||||||
var out mungeLines
|
|
||||||
for _, mline := range mlines {
|
|
||||||
if mline.preformatted {
|
|
||||||
out = append(out, mline)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
newline := trimRightSpace(mline.data)
|
|
||||||
out = append(out, newMungeLine(newline))
|
|
||||||
}
|
|
||||||
return out, nil
|
|
||||||
}
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2015 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 (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Test_updateWhiteSpace(t *testing.T) {
|
|
||||||
var cases = []struct {
|
|
||||||
in string
|
|
||||||
expected string
|
|
||||||
}{
|
|
||||||
{"", ""},
|
|
||||||
{"\n", "\n"},
|
|
||||||
{" \t \t \n", "\n"},
|
|
||||||
{"bob \t", "bob"},
|
|
||||||
{"```\n \n```\n", "```\n \n```\n"},
|
|
||||||
}
|
|
||||||
for i, c := range cases {
|
|
||||||
in := getMungeLines(c.in)
|
|
||||||
expected := getMungeLines(c.expected)
|
|
||||||
actual, err := updateWhitespace("filename.md", in)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
if !expected.Equal(actual) {
|
|
||||||
t.Errorf("Case[%d] Expected Whitespace '%v' but got '%v'", i, string(expected.Bytes()), string(actual.Bytes()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
cluster/images/etcd-version-monitor
|
cluster/images/etcd-version-monitor
|
||||||
cmd/cloud-controller-manager/app
|
cmd/cloud-controller-manager/app
|
||||||
cmd/cloud-controller-manager/app/options
|
cmd/cloud-controller-manager/app/options
|
||||||
cmd/genslateyaml
|
|
||||||
cmd/genutils
|
cmd/genutils
|
||||||
cmd/gke-certificates-controller/app
|
cmd/gke-certificates-controller/app
|
||||||
cmd/hyperkube
|
cmd/hyperkube
|
||||||
@@ -29,7 +28,6 @@ cmd/kubectl/app
|
|||||||
cmd/kubelet/app
|
cmd/kubelet/app
|
||||||
cmd/kubelet/app/options
|
cmd/kubelet/app/options
|
||||||
cmd/kubemark
|
cmd/kubemark
|
||||||
cmd/mungedocs
|
|
||||||
examples/guestbook-go
|
examples/guestbook-go
|
||||||
federation/apis/core
|
federation/apis/core
|
||||||
federation/apis/core/v1
|
federation/apis/core/v1
|
||||||
|
|||||||
@@ -134,7 +134,6 @@ kube::golang::test_targets() {
|
|||||||
cmd/genkubedocs
|
cmd/genkubedocs
|
||||||
cmd/genman
|
cmd/genman
|
||||||
cmd/genyaml
|
cmd/genyaml
|
||||||
cmd/mungedocs
|
|
||||||
cmd/genswaggertypedocs
|
cmd/genswaggertypedocs
|
||||||
cmd/linkcheck
|
cmd/linkcheck
|
||||||
federation/cmd/genfeddocs
|
federation/cmd/genfeddocs
|
||||||
|
|||||||
@@ -583,7 +583,9 @@ k8s.io/kubernetes/cmd/kubeadm/app/util/kubeconfig,apelisse,1,
|
|||||||
k8s.io/kubernetes/cmd/kubeadm/app/util/token,sttts,1,
|
k8s.io/kubernetes/cmd/kubeadm/app/util/token,sttts,1,
|
||||||
k8s.io/kubernetes/cmd/kubeadm/test/cmd,krousey,0,
|
k8s.io/kubernetes/cmd/kubeadm/test/cmd,krousey,0,
|
||||||
k8s.io/kubernetes/cmd/kubelet/app,derekwaynecarr,0,
|
k8s.io/kubernetes/cmd/kubelet/app,derekwaynecarr,0,
|
||||||
k8s.io/kubernetes/cmd/mungedocs,mwielgus,1,
|
k8s.io/kubernetes/cmd/libs/go2idl/client-gen/types,caesarxuchao,0,
|
||||||
|
k8s.io/kubernetes/cmd/libs/go2idl/go-to-protobuf/protobuf,smarterclayton,0,
|
||||||
|
k8s.io/kubernetes/cmd/libs/go2idl/openapi-gen/generators,davidopp,1,
|
||||||
k8s.io/kubernetes/examples,Random-Liu,0,
|
k8s.io/kubernetes/examples,Random-Liu,0,
|
||||||
k8s.io/kubernetes/federation/apis/federation/install,nikhiljindal,0,
|
k8s.io/kubernetes/federation/apis/federation/install,nikhiljindal,0,
|
||||||
k8s.io/kubernetes/federation/apis/federation/validation,nikhiljindal,0,
|
k8s.io/kubernetes/federation/apis/federation/validation,nikhiljindal,0,
|
||||||
|
|||||||
|
Reference in New Issue
Block a user