From 2209e537dc8b9406bdbbc879c1b81b0d7341c0c4 Mon Sep 17 00:00:00 2001 From: Oleg <97077423+RobotSail@users.noreply.github.com> Date: Fri, 15 Jul 2022 13:34:10 -0400 Subject: [PATCH 01/14] add ginkgo tests Signed-off-by: Oleg <97077423+RobotSail@users.noreply.github.com> --- Makefile | 28 ++--- pkg/cmd/cmd.go | 2 - pkg/cmd/cmd_suite_test.go | 13 +++ pkg/cmd/cmd_test.go | 17 +++ pkg/cmd/config.go | 2 + pkg/cmd/config_test.go | 42 +++++++ pkg/cmd/edit_test.go | 14 +++ pkg/cmd/generate_test.go | 12 ++ pkg/cmd/utils.go | 6 +- pkg/cmd/utils_test.go | 12 ++ pkg/filemap/filemap.go | 15 ++- pkg/filemap/filemap_suite_test.go | 13 +++ pkg/filemap/filemap_test.go | 186 ++++++++++++++++++++++++++++++ pkg/tests/cmd_test.go | 88 +++++++------- 14 files changed, 384 insertions(+), 66 deletions(-) create mode 100644 pkg/cmd/cmd_suite_test.go create mode 100644 pkg/cmd/cmd_test.go create mode 100644 pkg/cmd/config_test.go create mode 100644 pkg/cmd/edit_test.go create mode 100644 pkg/cmd/generate_test.go create mode 100644 pkg/cmd/utils_test.go create mode 100644 pkg/filemap/filemap_suite_test.go create mode 100644 pkg/filemap/filemap_test.go diff --git a/Makefile b/Makefile index 2133fd1..763774a 100644 --- a/Makefile +++ b/Makefile @@ -5,16 +5,6 @@ build: @ echo ./copilot-ops -h "# run me!" .PHONY: build -test: build lint - @ echo ▶️ go test - go clean -testcache ./... - go test -v ./... - @ echo ✅ go test - @ echo ▶️ go vet - go vet ./... - @ echo ✅ go vet -.PHONY: test - ##@ Development @@ -24,8 +14,11 @@ lint: golangci-lint ## Lint source code $(GOLANGCILINT) run ./... @ echo "✅ golangci-lint run" -# .PHONY: test -# test: lint ginkgo ## Run tests. +.PHONY: test +test: lint ginkgo ## Run tests. + @ echo "▶️ ginkgo test" + $(GINKGO) --coverprofile "cover.out" ./... + @ echo "✅ ginkgo test" ##@ Build Dependencies @@ -42,5 +35,14 @@ GOLANGCILINT := $(LOCALBIN)/golangci-lint GOLANGCI_URL := https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh golangci-lint: $(GOLANGCILINT) ## Download golangci-lint $(GOLANGCILINT): $(LOCALBIN) + @ echo "▶️ Downloading golangci-lint" curl -sSfL $(GOLANGCI_URL) | sh -s -- -b $(LOCALBIN) $(GOLANGCI_VERSION) - + @ echo "✅ Downloading golangci-lint" + +.PHONY: ginkgo +GINKGO := $(LOCALBIN)/ginkgo +ginkgo: $(GINKGO) ## Download ginkgo +$(GINKGO): $(LOCALBIN) + @ echo "▶️ Downloading ginkgo@v2" + GOBIN=$(LOCALBIN) go install -mod=mod github.com/onsi/ginkgo/v2/ginkgo + @ echo "✅ Downloaded ginkgo" \ No newline at end of file diff --git a/pkg/cmd/cmd.go b/pkg/cmd/cmd.go index 40f5f1f..e692310 100644 --- a/pkg/cmd/cmd.go +++ b/pkg/cmd/cmd.go @@ -22,12 +22,10 @@ func NewRootCmd() *cobra.Command { // the root command shows the available subcommands cmd := &cobra.Command{ Use: "copilot-ops", - Long: `copilot-ops is a workflow automation tool that proposes an intelligent patches on a repo, using natural language AI engines (openai.com codex bring-your-own-token), and can be used to implement github bots, editor extensions, and more. `, - Example: ` copilot-ops generate --help`, // Usage on every error is too noisy and makes it harder diff --git a/pkg/cmd/cmd_suite_test.go b/pkg/cmd/cmd_suite_test.go new file mode 100644 index 0000000..0278cc0 --- /dev/null +++ b/pkg/cmd/cmd_suite_test.go @@ -0,0 +1,13 @@ +package cmd_test + +import ( + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +func TestCmd(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Cmd Suite") +} diff --git a/pkg/cmd/cmd_test.go b/pkg/cmd/cmd_test.go new file mode 100644 index 0000000..43b9b49 --- /dev/null +++ b/pkg/cmd/cmd_test.go @@ -0,0 +1,17 @@ +package cmd_test + +import ( + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + cmd "github.com/redhat-et/copilot-ops/pkg/cmd" +) + +var _ = Describe("Root command", func() { + When("root command is created", func() { + It("contains edit and generate", func() { + rootCmd := cmd.NewRootCmd() + Expect(rootCmd).NotTo(BeNil()) + }) + }) +}) diff --git a/pkg/cmd/config.go b/pkg/cmd/config.go index 67a58d1..581ad3f 100644 --- a/pkg/cmd/config.go +++ b/pkg/cmd/config.go @@ -75,6 +75,8 @@ func (c *Config) Load() error { return nil } +// FindFileset Returns a fileset with the matching name, +// or nil if none exists. func (c *Config) FindFileset(name string) *ConfigFilesets { for _, fileset := range c.Filesets { if fileset.Name == name { diff --git a/pkg/cmd/config_test.go b/pkg/cmd/config_test.go new file mode 100644 index 0000000..193e76a --- /dev/null +++ b/pkg/cmd/config_test.go @@ -0,0 +1,42 @@ +package cmd_test + +import ( + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + cmd "github.com/redhat-et/copilot-ops/pkg/cmd" +) + +var _ = Describe("Config", func() { + var config *cmd.Config + When("config is being loaded", func() { + BeforeEach(func() { + // generate an empty config + config = &cmd.Config{} + }) + When("filesets are provided", func() { + BeforeEach(func() { + config.Filesets = []cmd.ConfigFilesets{ + { + Name: "test", + Files: []string{"test.txt"}, + }, + } + }) + + It("finds the correct filesets", func() { + // config should find a fileset named "test" + Expect(config.FindFileset("test")).NotTo(BeNil()) + // config should not find a fileset named "test2" + Expect(config.FindFileset("test2")).To(BeNil()) + }) + + It("is case sensitive", func() { + // config should not find a fileset named "test" + Expect(config.FindFileset("test")).NotTo(BeNil()) + // config should find a fileset named "TEST" + Expect(config.FindFileset("TEST")).To(BeNil()) + }) + }) + }) +}) diff --git a/pkg/cmd/edit_test.go b/pkg/cmd/edit_test.go new file mode 100644 index 0000000..98b2792 --- /dev/null +++ b/pkg/cmd/edit_test.go @@ -0,0 +1,14 @@ +package cmd_test + +import ( + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + "github.com/redhat-et/copilot-ops/pkg/cmd" +) + +var _ = Describe("Edit", func() { + It("no-op", func() { + Expect(cmd.BuildOpenAIClient(cmd.Config{}, 0, 0, "")).NotTo(BeNil()) + }) +}) diff --git a/pkg/cmd/generate_test.go b/pkg/cmd/generate_test.go new file mode 100644 index 0000000..0f5685d --- /dev/null +++ b/pkg/cmd/generate_test.go @@ -0,0 +1,12 @@ +package cmd_test + +// import ( +// . "github.com/onsi/ginkgo/v2" +// . "github.com/onsi/gomega" + +// "github.com/redhat-et/copilot-ops/pkg/cmd" +// ) + +// var _ = Describe("Generate", func() { + +// }) diff --git a/pkg/cmd/utils.go b/pkg/cmd/utils.go index 6ea5f81..17fdef9 100644 --- a/pkg/cmd/utils.go +++ b/pkg/cmd/utils.go @@ -101,11 +101,7 @@ func PrepareRequest(cmd *cobra.Command, engine string) (*Request, error) { } fm.LogDump() - - filemapText, err := fm.EncodeToInputText() - if err != nil { - return nil, err - } + filemapText := fm.EncodeToInputText() // create OpenAI client openAIClient := BuildOpenAIClient(conf, nTokens, nCompletions, engine) diff --git a/pkg/cmd/utils_test.go b/pkg/cmd/utils_test.go new file mode 100644 index 0000000..220a8d2 --- /dev/null +++ b/pkg/cmd/utils_test.go @@ -0,0 +1,12 @@ +package cmd_test + +// import ( +// . "github.com/onsi/ginkgo/v2" +// . "github.com/onsi/gomega" + +// "github.com/redhat-et/copilot-ops/pkg/cmd" +// ) + +// var _ = Describe("Utils", func() { + +// }) diff --git a/pkg/filemap/filemap.go b/pkg/filemap/filemap.go index 1d9d113..65cf788 100644 --- a/pkg/filemap/filemap.go +++ b/pkg/filemap/filemap.go @@ -43,11 +43,14 @@ type Filemap struct { Files map[string]File `json:"files"` } +// NewFilemap Builds and returns a new filemap. func NewFilemap() *Filemap { return &Filemap{ Files: make(map[string]File), } } + +// LogDump Displays the contents of the filemap to the log. func (fm *Filemap) LogDump() { maxShown := 30 log.Printf("filemap: len %d\n", len(fm.Files)) @@ -136,7 +139,7 @@ func (fm *Filemap) WriteUpdatesToFiles() error { // EncodeToInputText Encodes the filemap into a string which can be used as input to the OpenAI CLI. // If there was some issue or problem encoding the filemap, an error will be returned. -func (fm *Filemap) EncodeToInputText() (string, error) { +func (fm *Filemap) EncodeToInputText() string { /* This function will encode the file contents as a string, with each file prepended by a hashtag, followed by its tagname. @@ -171,7 +174,7 @@ func (fm *Filemap) EncodeToInputText() (string, error) { } i++ } - return input, nil + return input } // EncodeToInputTextFullPaths Encodes the filemap into a string using each file's full path as its tagname. @@ -243,6 +246,9 @@ func extractTagName(content string) (string, int32, error) { // ConcatenateAfterLineNum Concatenates all of the content following the given lineNum. // If the lineNum exceeds the number of lines in the content, an error will be returned. +// +// The line numbers are zero-indexed, so passing -1 will concatenate all of the content, +// whereas 0 will exclude the first line. func ConcatenateAfterLineNum(content string, lineNum int32) (string, error) { lines := strings.Split(content, "\n") if lineNum >= int32(len(lines)) { @@ -301,6 +307,11 @@ func (fm *Filemap) DecodeFromOutput(content string) error { if err != nil { return err } + // ignore empty files + if strings.TrimSpace(concatenatedContent) == "" { + continue + } + // add to the filemap fm.AddContentByTag(tagName, concatenatedContent) } diff --git a/pkg/filemap/filemap_suite_test.go b/pkg/filemap/filemap_suite_test.go new file mode 100644 index 0000000..9fae84a --- /dev/null +++ b/pkg/filemap/filemap_suite_test.go @@ -0,0 +1,13 @@ +package filemap_test + +import ( + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +func TestFilemap(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Filemap Suite") +} diff --git a/pkg/filemap/filemap_test.go b/pkg/filemap/filemap_test.go new file mode 100644 index 0000000..4fbe919 --- /dev/null +++ b/pkg/filemap/filemap_test.go @@ -0,0 +1,186 @@ +package filemap_test + +import ( + "fmt" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + . "github.com/redhat-et/copilot-ops/pkg/filemap" +) + +var _ = Describe("Filemap", func() { + var filemap *Filemap + BeforeEach(func() { + // create the filemap object + filemap = NewFilemap() + Expect(filemap).NotTo(BeNil()) + }) + + When("multiple files are loaded", func() { + const ( + content1 = `--- +kind: FortniteVod +` + content2 = `--- +kind: CatVideos +` + ) + + BeforeEach(func() { + filemap.Files = map[string]File{ + "fortnite_vods": { + Path: "./testdata/fortnite_vods", + Content: content1, + Tag: "fortnite_vods", + }, + "cat_videos": { + Path: "./testdata/cat_videos", + Content: content2, + Tag: "cat_videos", + }, + } + }) + + It("contains the encoded files", func() { + // make sure both files are contained in the filemap + encoding := filemap.EncodeToInputText() + Expect(encoding).To(ContainSubstring("kind: FortniteVod")) + Expect(encoding).To(ContainSubstring("kind: CatVideos")) + + // files should be delimited by a delimiting string + Expect(encoding).To(ContainSubstring(FileDelimeter)) + }) + + It("adds new files", func() { + // make sure the new content is added and is encoded + filemap.AddContentByTag("new_tag", "new_content") + encoding := filemap.EncodeToInputText() + Expect(encoding).To(ContainSubstring("new_content")) + Expect(encoding).To(ContainSubstring(FileDelimeter)) + }) + + It("updates existing files by their tagname", func() { + // update the fortnite vods file with content + filemap.AddContentByTag("fortnite_vods", "new-fortnite-content") + Expect(filemap.Files["fortnite_vods"].Content).To(ContainSubstring("new-fortnite-content")) + + // make sure that content with new tags are simply appended to the filemap + filemap.AddContentByTag("new_tag", "new_content") + Expect(filemap.Files["new_tag"].Content).To(ContainSubstring("new_content")) + }) + + When("the filemap is encoded to output text", func() { + It("encodes files using their full paths", func() { + output, err := filemap.EncodeToInputTextFullPaths(OutputPlain) + Expect(err).NotTo(HaveOccurred()) + Expect(output).To(ContainSubstring("# @./testdata/fortnite_vods")) + + output, err = filemap.EncodeToInputTextFullPaths(OutputJSON) + Expect(err).NotTo(HaveOccurred()) + Expect(output).To(ContainSubstring("\"path\": \"./testdata/fortnite_vods\"")) + Expect(output).To(ContainSubstring("\"path\": \"./testdata/cat_videos\"")) + Expect(output).To(ContainSubstring("kind: FortniteVod")) + Expect(output).To(ContainSubstring("kind: CatVideos")) + }) + + It("encodes all formats except unknown", func() { + // create an anonymous struct & iterate through to make sure that only + // known formats are encoded + var formats = []struct { + Format string `json:"format"` + ShouldEncode bool `json:"should_encode"` + }{ + {Format: OutputPlain, ShouldEncode: true}, + {Format: OutputJSON, ShouldEncode: true}, + {Format: "unknown format", ShouldEncode: false}, + } + for _, format := range formats { + _, err := filemap.EncodeToInputTextFullPaths(format.Format) + if format.ShouldEncode { + Expect(err).NotTo(HaveOccurred()) + } else { + Expect(err).To(HaveOccurred()) + } + } + }) + }) + }) + + It("concatenates after a line number", func() { + const content = `1 +2 +3 +4 +5` + // check that concatenate after line number 0 includes the whole file + cat, err := ConcatenateAfterLineNum(content, -1) + Expect(err).NotTo(HaveOccurred()) + Expect(cat).To(ContainSubstring(content)) + + // after lin num should exclude it. + cat, err = ConcatenateAfterLineNum(content, 0) + Expect(err).NotTo(HaveOccurred()) + Expect(cat).To(ContainSubstring(content[2:])) + + // line 5 should return an error + _, err = ConcatenateAfterLineNum(content, 5) + Expect(err).To(HaveOccurred()) + }) + + When("decoding from an output", func() { + const responseTemplate = ` +# %sfortnite-stats +--- +kind: Deployment +metadata: + name: fortnite-stats +%s +--- +# %sviva-pinata-server +kind: Deployment +apiVersion: apps/v1 +metadata: + name: viva-pinata-server +` + + It("updates the filemap from the given yamls", func() { + var response = fmt.Sprintf(responseTemplate, FileTagPrefix, FileDelimeter, FileTagPrefix) + err := filemap.DecodeFromOutput(response) + Expect(err).NotTo(HaveOccurred()) + Expect(filemap.Files).To(HaveLen(2)) + Expect(filemap.Files["fortnite-stats"].Content).To(ContainSubstring("kind: Deployment")) + Expect(filemap.Files["viva-pinata-server"].Content).To(ContainSubstring("kind: Deployment")) + // delimeter should not be included in the content + Expect(filemap.Files["fortnite-stats"].Content).NotTo(ContainSubstring(FileDelimeter)) + Expect(filemap.Files["viva-pinata-server"].Content).NotTo(ContainSubstring(FileDelimeter)) + }) + + It("doesn't decode without tagname", func() { + var response = fmt.Sprintf(responseTemplate, "", FileDelimeter, FileTagPrefix) + err := filemap.DecodeFromOutput(response) + Expect(err).To(HaveOccurred()) + + response = fmt.Sprintf(responseTemplate, FileTagPrefix, FileDelimeter, "") + err = filemap.DecodeFromOutput(response) + Expect(err).To(HaveOccurred()) + }) + + It("doesn't add empty outputs", func() { + response := fmt.Sprintf(`# %semtpy-file +%s +# %snot-empty +kind: NotEmtpy`, FileTagPrefix, FileDelimeter, FileTagPrefix) + err := filemap.DecodeFromOutput(response) + Expect(err).NotTo(HaveOccurred()) + Expect(filemap.Files).To(HaveLen(1)) + // empty-file should not be in filemap + _, ok := filemap.Files["empty-file"] + Expect(ok).To(BeFalse()) + // not-empty should be in filemap + listing, ok := filemap.Files["not-empty"] + Expect(ok).To(BeTrue()) + Expect(listing.Content).To(ContainSubstring("kind: NotEmtpy")) + }) + }) +}) diff --git a/pkg/tests/cmd_test.go b/pkg/tests/cmd_test.go index 030a53a..857cfc4 100644 --- a/pkg/tests/cmd_test.go +++ b/pkg/tests/cmd_test.go @@ -1,51 +1,51 @@ package test_test -import ( - "bytes" - "io/ioutil" - "os" - "testing" +// import ( +// "bytes" +// "io/ioutil" +// "os" +// "testing" - "github.com/redhat-et/copilot-ops/pkg/cmd" -) +// "github.com/redhat-et/copilot-ops/pkg/cmd" +// ) -func TestGeneratePodForPVC(t *testing.T) { - t.Log(os.Getwd()) - Run(t, []string{ - "generate", - "--file", - "examples/app1/mysql-pvc.yaml", - "--request", - "Generate a pod that mounts the PVC. Set the pod resources requests and limits to 4 cpus and 5 Gig of memory.", - }) -} +// func TestGeneratePodForPVC(t *testing.T) { +// t.Log(os.Getwd()) +// Run(t, []string{ +// "generate", +// "--file", +// "examples/app1/mysql-pvc.yaml", +// "--request", +// "Generate a pod that mounts the PVC. Set the pod resources requests and limits to 4 cpus and 5 Gig of memory.", +// }) +// } -// TestEditPVCSize Tests that the edit command can successfully change the size of a PVC YAML -// to be 100Gi. -func TestEditPVCSize(t *testing.T) { - Run(t, []string{ - "edit", - "--file", - "examples/app1/mysql-pvc.yaml", - "--request", - "Increase the size of the PVC to 100Gi.", - }) -} +// // TestEditPVCSize Tests that the edit command can successfully change the size of a PVC YAML +// // to be 100Gi. +// func TestEditPVCSize(t *testing.T) { +// Run(t, []string{ +// "edit", +// "--file", +// "examples/app1/mysql-pvc.yaml", +// "--request", +// "Increase the size of the PVC to 100Gi.", +// }) +// } -func Run(t *testing.T, args []string) string { - cmd := cmd.NewRootCmd() - buf := bytes.NewBufferString("") - cmd.SetOut(buf) - cmd.SetArgs(args) - if err := cmd.Execute(); err != nil { - t.Fatal(err) - } +// func Run(t *testing.T, args []string) string { +// cmd := cmd.NewRootCmd() +// buf := bytes.NewBufferString("") +// cmd.SetOut(buf) +// cmd.SetArgs(args) +// if err := cmd.Execute(); err != nil { +// t.Fatal(err) +// } - bytes, err := ioutil.ReadAll(buf) - if err != nil { - t.Fatal(err) - } - out := string(bytes) - t.Logf("out: %+v\n", out) - return out -} +// bytes, err := ioutil.ReadAll(buf) +// if err != nil { +// t.Fatal(err) +// } +// out := string(bytes) +// t.Logf("out: %+v\n", out) +// return out +// } From d978ebe98beeec8d37ad769ee41ac81e9fdc8734 Mon Sep 17 00:00:00 2001 From: Oleg <97077423+RobotSail@users.noreply.github.com> Date: Fri, 15 Jul 2022 13:42:00 -0400 Subject: [PATCH 02/14] add ginkgo & gomega to go.mod Signed-off-by: Oleg <97077423+RobotSail@users.noreply.github.com> --- .gitignore | 5 ++++- go.mod | 3 +++ go.sum | 8 ++++++++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 47780b3..da6acea 100644 --- a/.gitignore +++ b/.gitignore @@ -13,4 +13,7 @@ last-processed.json .copilot-ops.local.yaml # development files -bin/ \ No newline at end of file +bin/ + +# coverage files should be generated within CI +cover.out \ No newline at end of file diff --git a/go.mod b/go.mod index 911fc5d..3f9aa28 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,8 @@ module github.com/redhat-et/copilot-ops go 1.18 require ( + github.com/onsi/ginkgo/v2 v2.1.4 + github.com/onsi/gomega v1.19.0 github.com/spf13/cobra v1.4.0 github.com/spf13/viper v1.11.0 ) @@ -20,6 +22,7 @@ require ( github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/subosito/gotenv v1.2.0 // indirect + golang.org/x/net v0.0.0-20220412020605-290c469a71a5 // indirect golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6 // indirect golang.org/x/text v0.3.7 // indirect gopkg.in/ini.v1 v1.66.4 // indirect diff --git a/go.sum b/go.sum index ef693a8..f8119a5 100644 --- a/go.sum +++ b/go.sum @@ -86,6 +86,7 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= @@ -136,6 +137,10 @@ github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamh github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/onsi/ginkgo/v2 v2.1.4 h1:GNapqRSid3zijZ9H77KrgVG4/8KqiyRsxcSxe+7ApXY= +github.com/onsi/ginkgo/v2 v2.1.4/go.mod h1:um6tUpWM/cxCK3/FK8BXqEiUMUwRgSM4JXG47RKZmLU= +github.com/onsi/gomega v1.19.0 h1:4ieX6qQjPP/BfC3mpsAtIGGlxTWPeA3Inl/7DtXw1tw= +github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml/v2 v2.0.1 h1:8e3L2cCQzLFi2CR4g7vGFuFxX7Jl1kKX8gW+iV0GUKU= @@ -249,6 +254,8 @@ golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220412020605-290c469a71a5 h1:bRb386wvrE+oBNdF1d/Xh9mQrfQ4ecYhW5qJ5GvTGT4= +golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -456,6 +463,7 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2 google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= From f55d4b6582a58b5d09d11097d1434b98d9e88018 Mon Sep 17 00:00:00 2001 From: Oleg <97077423+RobotSail@users.noreply.github.com> Date: Fri, 15 Jul 2022 13:44:02 -0400 Subject: [PATCH 03/14] remove old tests Signed-off-by: Oleg <97077423+RobotSail@users.noreply.github.com> --- pkg/tests/cmd_test.go | 51 ------------------------------------------- 1 file changed, 51 deletions(-) delete mode 100644 pkg/tests/cmd_test.go diff --git a/pkg/tests/cmd_test.go b/pkg/tests/cmd_test.go deleted file mode 100644 index 857cfc4..0000000 --- a/pkg/tests/cmd_test.go +++ /dev/null @@ -1,51 +0,0 @@ -package test_test - -// import ( -// "bytes" -// "io/ioutil" -// "os" -// "testing" - -// "github.com/redhat-et/copilot-ops/pkg/cmd" -// ) - -// func TestGeneratePodForPVC(t *testing.T) { -// t.Log(os.Getwd()) -// Run(t, []string{ -// "generate", -// "--file", -// "examples/app1/mysql-pvc.yaml", -// "--request", -// "Generate a pod that mounts the PVC. Set the pod resources requests and limits to 4 cpus and 5 Gig of memory.", -// }) -// } - -// // TestEditPVCSize Tests that the edit command can successfully change the size of a PVC YAML -// // to be 100Gi. -// func TestEditPVCSize(t *testing.T) { -// Run(t, []string{ -// "edit", -// "--file", -// "examples/app1/mysql-pvc.yaml", -// "--request", -// "Increase the size of the PVC to 100Gi.", -// }) -// } - -// func Run(t *testing.T, args []string) string { -// cmd := cmd.NewRootCmd() -// buf := bytes.NewBufferString("") -// cmd.SetOut(buf) -// cmd.SetArgs(args) -// if err := cmd.Execute(); err != nil { -// t.Fatal(err) -// } - -// bytes, err := ioutil.ReadAll(buf) -// if err != nil { -// t.Fatal(err) -// } -// out := string(bytes) -// t.Logf("out: %+v\n", out) -// return out -// } From 10fe3c77463ef076799240a04a180a582cecdf0d Mon Sep 17 00:00:00 2001 From: Oleg <97077423+RobotSail@users.noreply.github.com> Date: Mon, 25 Jul 2022 18:48:21 -0400 Subject: [PATCH 04/14] add tests Signed-off-by: Oleg <97077423+RobotSail@users.noreply.github.com> --- pkg/cmd/config.go | 8 +--- pkg/cmd/generate.go | 18 ++++----- pkg/cmd/utils.go | 31 +++++--------- pkg/filemap/filemap.go | 38 ++--------------- pkg/filemap/filemap_test.go | 2 - pkg/filemap/utils.go | 81 +++++++++++++++++++++++++++++++++++++ 6 files changed, 106 insertions(+), 72 deletions(-) create mode 100644 pkg/filemap/utils.go diff --git a/pkg/cmd/config.go b/pkg/cmd/config.go index 581ad3f..e627349 100644 --- a/pkg/cmd/config.go +++ b/pkg/cmd/config.go @@ -59,13 +59,7 @@ func (c *Config) Load() error { // optionally look for local (gitignored) config file and merge it in viper.SetConfigName(".copilot-ops.local") - if err := viper.MergeInConfig(); err != nil { - var configFileNotFound *viper.ConfigFileNotFoundError - if ok := errors.As(err, &configFileNotFound); !ok { - return err // allow no config file - } - } - + _ = viper.MergeInConfig() log.Printf("viper: %+v\n", viper.ConfigFileUsed()) if err := viper.Unmarshal(c); err != nil { diff --git a/pkg/cmd/generate.go b/pkg/cmd/generate.go index 8ae5260..1ba2b1b 100644 --- a/pkg/cmd/generate.go +++ b/pkg/cmd/generate.go @@ -68,9 +68,10 @@ func RunGenerate(cmd *cobra.Command, args []string) error { // generate a response from OpenAI output, err := r.OpenAI.GenerateCode(input) if err != nil { - return err + return fmt.Errorf("got error from OpenAI: %w", err) } + // decode the response r.Filemap = filemap.NewFilemap() log.Printf("decoding output") for _, s := range output { @@ -78,16 +79,15 @@ func RunGenerate(cmd *cobra.Command, args []string) error { err = r.Filemap.DecodeFromOutput(s) } } + if err == nil { return PrintOrWriteOut(r) } // HACK: try other way to decode the output to a fileset log.Printf("decoding failed, got error: %s", err) - log.Printf("trying fallback") - // fallback - generate new files and put the content inside - r = generateNewFiles(r, output) + generateNewFiles(r, output) return PrintOrWriteOut(r) } @@ -178,23 +178,23 @@ func callToActionSequence(request string, encodedFiles string) string { return prompt } -// Create a new file for every requested completion and store them in the "generated-by-copilot-ops" directory. -func generateNewFiles(req *Request, sepOutput []string) *Request { +// generateNewFiles Creates a new file for every requested completion, +// and stores them in the "generated-by-copilot-ops" directory. +func generateNewFiles(req *Request, sepOutput []string) { var i int32 newMap := make(map[string]filemap.File) for i = 0; i < req.OpenAI.NCompletions; i++ { + // set file name + path here newFileName := "generated-by-copilot-ops" + fmt.Sprint(i+1) + ".yaml" newFilePath := path.Join("generated-by-copilot-ops", newFileName) + var newFile filemap.File newFile.Content = sepOutput[i] newFile.Path = newFilePath - newFile.Tag = newFilePath newFile.Name = newFileName newMap[newFilePath] = newFile } req.Filemap.Files = newMap - - return req } diff --git a/pkg/cmd/utils.go b/pkg/cmd/utils.go index 17fdef9..10b0aac 100644 --- a/pkg/cmd/utils.go +++ b/pkg/cmd/utils.go @@ -67,10 +67,7 @@ func PrepareRequest(cmd *cobra.Command, engine string) (*Request, error) { // we'll just use the defaults and continue without error. // Errors here might return if the file exists but is invalid. conf := Config{} - err := conf.Load() - if err != nil { - return nil, err - } + _ = conf.Load() // WARNING we should not consider printing conf with its secret keys log.Printf("Filesets: %+v\n", conf.Filesets) @@ -123,29 +120,23 @@ func PrepareRequest(cmd *cobra.Command, engine string) (*Request, error) { // PrintOrWriteOut Accepts a request object and writes the contents of the filemap // to the disk if specified, otherwise it prints to STDOUT. func PrintOrWriteOut(r *Request) error { - // dump the state of the FileMap - r.Filemap.LogDump() - if r.IsWrite { - log.Printf("updating files ...\n") err := r.Filemap.WriteUpdatesToFiles() if err != nil { return err } - } else { - // TODO: Add output formatting control - // just encode the output and print it to stdout - // TODO: print as redirectable / pipeable write stream - fmOutput, err := r.Filemap.EncodeToInputTextFullPaths(r.OutputType) - if err != nil { - return err - } - - stringOut := strings.ReplaceAll(fmOutput, "\\n", "\n") + return nil + } - log.Printf("\n%s\n", stringOut) - log.Printf("use --write to actually update files\n") + // TODO: Add output formatting control + // just encode the output and print it to stdout + // TODO: print as redirectable / pipeable write stream + fmOutput, err := r.Filemap.EncodeToInputTextFullPaths(r.OutputType) + if err != nil { + return err } + stringOut := strings.ReplaceAll(fmOutput, "\\n", "\n") + log.Printf("\n%s\n", stringOut) return nil } diff --git a/pkg/filemap/filemap.go b/pkg/filemap/filemap.go index 65cf788..39fd741 100644 --- a/pkg/filemap/filemap.go +++ b/pkg/filemap/filemap.go @@ -8,7 +8,6 @@ import ( "log" "os" "path/filepath" - "regexp" "strings" ) @@ -32,8 +31,6 @@ type File struct { Name string `json:"name"` // Path is the path to the file. Path string `json:"path"` - // Tag is the tagname of the file. - Tag string `json:"tag"` // Content is the content of the file. Content string `json:"content"` } @@ -54,13 +51,13 @@ func NewFilemap() *Filemap { func (fm *Filemap) LogDump() { maxShown := 30 log.Printf("filemap: len %d\n", len(fm.Files)) - for _, f := range fm.Files { + for tag, f := range fm.Files { l := len(f.Content) if l > maxShown { l = maxShown } short := strings.ReplaceAll(f.Content[:l], "\n", " ") - log.Printf(" - tag: %-10q: path: %-20q [%s ...] len %d\n", f.Tag, f.Path, short, len(f.Content)) + log.Printf(" - tag: %-10q: path: %-20q [%s ...] len %d\n", tag, f.Path, short, len(f.Content)) } } @@ -80,7 +77,6 @@ func (fm *Filemap) LoadFile(path string) error { fm.Files[tag] = File{ Path: path, Content: string(bytes), - Tag: tag, } return nil } @@ -103,14 +99,14 @@ func (fm *Filemap) LoadFilesFromGlob(glob string) error { // WriteUpdatesToFiles Writes the updated contents of each file to the directory. func (fm *Filemap) WriteUpdatesToFiles() error { - for _, file := range fm.Files { + for tag, file := range fm.Files { // add extension if necessary, assume this is YAML for the time being // HACK: classify the relevant extension (e.g. .yaml, .yml, .json) // fileName := file.Tag // if len(strings.Split(file.Tag, ".")) == 1 { // fileName += ".yaml" // } - log.Printf("path: %q, tag: %q\n", file.Path, file.Tag) + log.Printf("path: %q, tag: %q\n", file.Path, tag) // locate the base directory of filePath dirPath := filepath.Dir(file.Path) // create the directory if it does not exist @@ -219,31 +215,6 @@ func GenerateJSON(input []File) (string, error) { return string(jsonOutput), err } -// extractTagName Extracts the tagname from the given content, -// providing its line number in the content, or an error if it doesn't exist. -func extractTagName(content string) (string, int32, error) { - // The tagname would be on a line in the format of: "# {FILE_TAG_PREFIX}tagname\n" - // We can split the line by the '#' character and then trim the leading and trailing whitespace. - lines := strings.Split(content, "\n") - - // search content for regex of the following pattern: /#\s*\{FILE_TAG_PREFIX}(.+)/g - // if found, return the tagname - // if not found, return an error - pattern := fmt.Sprintf(`#\s*\%s(.+)`, FileTagPrefix) - regexPattern, err := regexp.Compile(pattern) - if err != nil { - return "", 0, err - } - - for i, line := range lines { - // find the first line that matches the pattern - if match := regexPattern.FindStringSubmatch(line); match != nil { - return strings.TrimSpace(match[1]), int32(i), nil - } - } - return "", -1, fmt.Errorf("no tagname found in content") -} - // ConcatenateAfterLineNum Concatenates all of the content following the given lineNum. // If the lineNum exceeds the number of lines in the content, an error will be returned. // @@ -275,7 +246,6 @@ func (fm *Filemap) AddContentByTag(tagname string, content string) { // TODO: infer path Path: "", Content: content, - Tag: tagname, } } } diff --git a/pkg/filemap/filemap_test.go b/pkg/filemap/filemap_test.go index 4fbe919..f1fb260 100644 --- a/pkg/filemap/filemap_test.go +++ b/pkg/filemap/filemap_test.go @@ -32,12 +32,10 @@ kind: CatVideos "fortnite_vods": { Path: "./testdata/fortnite_vods", Content: content1, - Tag: "fortnite_vods", }, "cat_videos": { Path: "./testdata/cat_videos", Content: content2, - Tag: "cat_videos", }, } }) diff --git a/pkg/filemap/utils.go b/pkg/filemap/utils.go new file mode 100644 index 0000000..8da71cd --- /dev/null +++ b/pkg/filemap/utils.go @@ -0,0 +1,81 @@ +package filemap + +import ( + "fmt" + "regexp" + "strings" +) + +// extractTagName Extracts the tagname from the given content, +// providing its line number in the content, or an error if it doesn't exist. +func extractTagName(content string) (string, int32, error) { + // The tagname would be on a line in the format of: "# {FILE_TAG_PREFIX}tagname\n" + // We can split the line by the '#' character and then trim the leading and trailing whitespace. + lines := strings.Split(content, "\n") + + // search content for regex of the following pattern: /#\s*\{FILE_TAG_PREFIX}(.+)/g + // if found, return the tagname + // if not found, return an error + pattern := fmt.Sprintf(`#\s*\%s(.+)`, FileTagPrefix) + regexPattern, err := regexp.Compile(pattern) + if err != nil { + return "", 0, err + } + + for i, line := range lines { + // find the first line that matches the pattern + if match := regexPattern.FindStringSubmatch(line); match != nil { + return strings.TrimSpace(match[1]), int32(i), nil + } + } + return "", -1, fmt.Errorf("no tagname found in content") +} + +// // outputToFile Converts a given output into a file object. +// func outputToFile(output string) (File, error) { +// // Trim the leading and trailing whitespace +// part := strings.TrimSpace(output) +// // If the part is empty, skip it +// if part == "" { +// return File{}, fmt.Errorf("part is empty") +// } +// // The tagName should be the first line +// tagName, lineNum, err := extractTagName(part) +// if err != nil { +// return File{}, err +// } + +// // grab the content following the lineNum +// concatenatedContent, err := ConcatenateAfterLineNum(part, lineNum) +// if err != nil { +// return nil, err +// } +// // ignore empty files +// if strings.TrimSpace(concatenatedContent) == "" { +// continue +// } + +// } + +// // OutputToFile Converts a given string consisting of YAML files and returns a list of files. +// func OutputToFile(output string) ([]File, error) { +// // To decode from the output, we have to split the content up by the defined file delimeter. +// // Then we use RegEx to extract the tagname which we can use to look up the file and update its content. +// // If the tagname is not found, we assume that the file is new and we will create a new file with the tagname. +// files := make([]File, 0) + +// // Split the content by the file delimeter +// parts := strings.Split(output, FileDelimeter) +// for _, part := range parts { + +// // add to the filemap +// file := File{ +// Name: tagName, +// Path: "generated-by-copilot-ops/", +// Content: concatenatedContent, +// } +// files = append(files, file) +// } + +// return files, nil +// } From 48aa63ff2027757a05d6a768b03fb699be50d352 Mon Sep 17 00:00:00 2001 From: Oleg <97077423+RobotSail@users.noreply.github.com> Date: Mon, 25 Jul 2022 18:52:17 -0400 Subject: [PATCH 05/14] add go report card Signed-off-by: Oleg <97077423+RobotSail@users.noreply.github.com> --- README.md | 2 ++ pkg/cmd/generate.go | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 79c2b4e..798a0d8 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # copilot-ops +[![Go Report Card](https://goreportcard.com/badge/github.com/redhat-et/copilot-ops)](https://goreportcard.com/report/github.com/redhat-et/copilot-ops) + `copilot-ops` is a CLI tool that boosts up any "devops repo" to a ninja level of *Artificially Intelligent Ops Repo*. ## Requirements diff --git a/pkg/cmd/generate.go b/pkg/cmd/generate.go index 1ba2b1b..685bbd9 100644 --- a/pkg/cmd/generate.go +++ b/pkg/cmd/generate.go @@ -61,7 +61,6 @@ func RunGenerate(cmd *cobra.Command, args []string) error { if err != nil { return err } - input := PrepareGenerateInput(r.UserRequest, r.FilemapText) log.Printf("requesting generate from OpenAI: %s", input) From 48aacc4e1c698a8354740141057303c412de781e Mon Sep 17 00:00:00 2001 From: Oleg <97077423+RobotSail@users.noreply.github.com> Date: Mon, 25 Jul 2022 19:10:37 -0400 Subject: [PATCH 06/14] remove todo Signed-off-by: Oleg <97077423+RobotSail@users.noreply.github.com> --- pkg/cmd/utils.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/pkg/cmd/utils.go b/pkg/cmd/utils.go index 10b0aac..84e5bc2 100644 --- a/pkg/cmd/utils.go +++ b/pkg/cmd/utils.go @@ -128,8 +128,6 @@ func PrintOrWriteOut(r *Request) error { return nil } - // TODO: Add output formatting control - // just encode the output and print it to stdout // TODO: print as redirectable / pipeable write stream fmOutput, err := r.Filemap.EncodeToInputTextFullPaths(r.OutputType) if err != nil { From d8325b4ad019d6caf55107d47487af5d9240cecd Mon Sep 17 00:00:00 2001 From: Oleg <97077423+RobotSail@users.noreply.github.com> Date: Tue, 26 Jul 2022 13:56:33 -0400 Subject: [PATCH 07/14] add constants for flags Signed-off-by: Oleg <97077423+RobotSail@users.noreply.github.com> --- pkg/cmd/constants.go | 24 ++++++++++++++++-------- pkg/cmd/edit.go | 2 +- pkg/cmd/generate.go | 10 +++++----- pkg/cmd/utils.go | 42 +++++++++++++++++++++--------------------- 4 files changed, 43 insertions(+), 35 deletions(-) diff --git a/pkg/cmd/constants.go b/pkg/cmd/constants.go index 8d0b458..f16b2ed 100644 --- a/pkg/cmd/constants.go +++ b/pkg/cmd/constants.go @@ -2,14 +2,22 @@ package cmd // Define the names of flags used in commands. const ( - FlagRequest = "request" - FlagWrite = "write" - FlagPath = "path" - FlagFiles = "file" - FlagFilesets = "fileset" - FlagNTokens = "ntokens" - FlagNCompletions = "ncompletions" - FlagOutputType = "output" + FlagRequestFull = "request" + FlagRequestShort = "r" + FlagWriteFull = "write" + FlagWriteShort = "w" + FlagPathFull = "path" + FlagPathShort = "p" + FlagFilesFull = "file" + FlagFilesShort = "f" + FlagFilesetsFull = "fileset" + FlagFilesetsShort = "s" + FlagNTokensFull = "ntokens" + FlagNTokensShort = "n" + FlagNCompletionsFull = "ncompletions" + FlagNCompletionsShort = "c" + FlagOutputTypeFull = "output" + FlagOutputTypeShort = "o" ) // COMMAND Constants which define the names of commands used in the CLI. diff --git a/pkg/cmd/edit.go b/pkg/cmd/edit.go index b7edad5..57f3888 100644 --- a/pkg/cmd/edit.go +++ b/pkg/cmd/edit.go @@ -28,7 +28,7 @@ func NewEditCmd() *cobra.Command { // flag to add a file cmd.Flags().StringP( - FlagFiles, "f", "", + FlagFilesFull, FlagFilesShort, "", "File path to the document which should be edited.", ) diff --git a/pkg/cmd/generate.go b/pkg/cmd/generate.go index 685bbd9..0f333e7 100644 --- a/pkg/cmd/generate.go +++ b/pkg/cmd/generate.go @@ -33,29 +33,29 @@ func NewGenerateCmd() *cobra.Command { // generate-specific flags cmd.Flags().StringArrayP( - FlagFiles, "f", []string{}, + FlagFilesFull, FlagFilesShort, []string{}, "File paths (glob) to be considered for the patch (can be specified multiple times)", ) cmd.Flags().StringArrayP( - FlagFilesets, "s", []string{}, + FlagFilesetsFull, FlagFilesShort, []string{}, "Fileset names (defined in "+ConfigFile+") to be considered for the patch (can be specified multiple times)", ) cmd.Flags().Int32P( - FlagNTokens, "n", DefaultTokens, + FlagNTokensFull, FlagNTokensShort, DefaultTokens, "Max number of tokens to generate", ) cmd.Flags().Int32P( - FlagNCompletions, "c", DefaultCompletions, + FlagNCompletionsFull, FlagNCompletionsShort, DefaultCompletions, "Number of completions to generate", ) return cmd } -// RunGenerate is the implementation of the `copilot-ops patch` command. +// RunGenerate is the implementation of the `copilot-ops generate` command. func RunGenerate(cmd *cobra.Command, args []string) error { r, err := PrepareRequest(cmd, openai.OpenAICodeDavinciV2) if err != nil { diff --git a/pkg/cmd/utils.go b/pkg/cmd/utils.go index 84e5bc2..6fa0291 100644 --- a/pkg/cmd/utils.go +++ b/pkg/cmd/utils.go @@ -32,28 +32,28 @@ func BuildOpenAIClient(conf Config, nTokens int32, nCompletions int32, engine st } func PrepareRequest(cmd *cobra.Command, engine string) (*Request, error) { - request, _ := cmd.Flags().GetString(FlagRequest) - write, _ := cmd.Flags().GetBool(FlagWrite) - path, _ := cmd.Flags().GetString(FlagPath) - files, _ := cmd.Flags().GetStringArray(FlagFiles) + request, _ := cmd.Flags().GetString(FlagRequestFull) + write, _ := cmd.Flags().GetBool(FlagWriteFull) + path, _ := cmd.Flags().GetString(FlagPathFull) + files, _ := cmd.Flags().GetStringArray(FlagFilesFull) if cmd.Name() == CommandEdit { - file, _ := cmd.Flags().GetString(FlagFiles) + file, _ := cmd.Flags().GetString(FlagFilesFull) files = append(files, file) } - filesets, _ := cmd.Flags().GetStringArray(FlagFilesets) - nTokens, _ := cmd.Flags().GetInt32(FlagNTokens) - nCompletions, _ := cmd.Flags().GetInt32(FlagNCompletions) - outputType, _ := cmd.Flags().GetString(FlagOutputType) + filesets, _ := cmd.Flags().GetStringArray(FlagFilesetsFull) + nTokens, _ := cmd.Flags().GetInt32(FlagNTokensFull) + nCompletions, _ := cmd.Flags().GetInt32(FlagNCompletionsFull) + outputType, _ := cmd.Flags().GetString(FlagOutputTypeFull) log.Printf("flags:\n") - log.Printf(" - %-8s: %v\n", FlagRequest, request) - log.Printf(" - %-8s: %v\n", FlagWrite, write) - log.Printf(" - %-8s: %v\n", FlagPath, path) - log.Printf(" - %-8s: %v\n", FlagFiles, files) - log.Printf(" - %-8s: %v\n", FlagFilesets, filesets) - log.Printf(" - %-8s: %v\n", FlagNTokens, nTokens) - log.Printf(" - %-8s: %v\n", FlagNCompletions, nCompletions) - log.Printf(" - %-8s: %v\n", FlagOutputType, outputType) + log.Printf(" - %-8s: %v\n", FlagRequestFull, request) + log.Printf(" - %-8s: %v\n", FlagWriteFull, write) + log.Printf(" - %-8s: %v\n", FlagPathFull, path) + log.Printf(" - %-8s: %v\n", FlagFilesFull, files) + log.Printf(" - %-8s: %v\n", FlagFilesetsFull, filesets) + log.Printf(" - %-8s: %v\n", FlagNTokensFull, nTokens) + log.Printf(" - %-8s: %v\n", FlagNCompletionsFull, nCompletions) + log.Printf(" - %-8s: %v\n", FlagOutputTypeFull, outputType) // Handle --path by changing the working directory // so that every file name we refer to is relative to path @@ -142,22 +142,22 @@ func PrintOrWriteOut(r *Request) error { // AddRequestFlags Appends flags to the given command which are then used at the command-line. func AddRequestFlags(cmd *cobra.Command) { cmd.Flags().StringP( - FlagRequest, "r", "", + FlagRequestFull, FlagRequestShort, "", "Requested changes in natural language (empty request will surprise you!)", ) cmd.Flags().BoolP( - FlagWrite, "w", false, + FlagWriteFull, FlagWriteShort, false, "Write changes to the repo files (if not set the patch is printed to stdout)", ) cmd.Flags().StringP( - FlagPath, "p", ".", + FlagPathFull, FlagPathShort, ".", "Path to the root of the repo", ) cmd.Flags().StringP( - FlagOutputType, "o", "json", + FlagOutputTypeFull, FlagOutputTypeShort, "json", "How to format output", ) } From d83ae4d1fc9f0d13e528cae15ffdb037d548d5bb Mon Sep 17 00:00:00 2001 From: Oleg <97077423+RobotSail@users.noreply.github.com> Date: Thu, 28 Jul 2022 17:27:07 -0400 Subject: [PATCH 08/14] mocks openai api server Signed-off-by: Oleg <97077423+RobotSail@users.noreply.github.com> --- pkg/cmd/cmd_suite_test.go | 55 +++++++++++++++++++++++++++++++++++ pkg/cmd/constants.go | 2 ++ pkg/cmd/edit_test.go | 35 ++++++++++++++++++++-- pkg/cmd/generate.go | 2 +- pkg/cmd/generate_test.go | 61 ++++++++++++++++++++++++++++++++++----- pkg/cmd/utils.go | 20 +++++++++++-- pkg/openai/constants.go | 10 ++++--- pkg/openai/openai.go | 6 +--- pkg/openai/types.go | 17 +++++++---- 9 files changed, 179 insertions(+), 29 deletions(-) diff --git a/pkg/cmd/cmd_suite_test.go b/pkg/cmd/cmd_suite_test.go index 0278cc0..2f3788e 100644 --- a/pkg/cmd/cmd_suite_test.go +++ b/pkg/cmd/cmd_suite_test.go @@ -1,13 +1,68 @@ package cmd_test import ( + "encoding/json" + "fmt" + "net/http" + "net/http/httptest" "testing" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" + "github.com/redhat-et/copilot-ops/pkg/openai" ) func TestCmd(t *testing.T) { RegisterFailHandler(Fail) RunSpecs(t, "Cmd Suite") } + +// OpenAITestServer Creates a mocked OpenAI server which can pretend to handle requests during testing. +func OpenAITestServer() *httptest.Server { + return httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + var resBytes []byte + switch r.URL.Path { + case "/v1/edits": + res := openai.EditResponse{ + Response: openai.Response{ + Choices: []openai.Choice{ + { + Text: ` +# @path/to/kubernetes.yaml +apiVersion: v1 +kind: Pod +metadata: + name: cute-cats +spec: + priority: high +`, + Index: 0, + }, + }, + }, + } + resBytes, _ = json.Marshal(res) + fmt.Fprint(w, string(resBytes)) + return + case "/v1/completions": + res := openai.CompletionResponse{ + ID: "completion id", + Response: openai.Response{ + Choices: []openai.Choice{ + { + Text: "choice 1", + Index: 0, + }, + }, + }, + } + resBytes, _ = json.Marshal(res) + fmt.Fprintln(w, string(resBytes)) + return + default: + // the endpoint doesn't exist + http.Error(w, "the resource path doesn't exist", http.StatusNotFound) + return + } + })) +} diff --git a/pkg/cmd/constants.go b/pkg/cmd/constants.go index f16b2ed..6d6ed8b 100644 --- a/pkg/cmd/constants.go +++ b/pkg/cmd/constants.go @@ -16,6 +16,8 @@ const ( FlagNTokensShort = "n" FlagNCompletionsFull = "ncompletions" FlagNCompletionsShort = "c" + FlagOpenAIURLFull = "openai-url" + FlagOpenAIURLShort = "d" FlagOutputTypeFull = "output" FlagOutputTypeShort = "o" ) diff --git a/pkg/cmd/edit_test.go b/pkg/cmd/edit_test.go index 98b2792..153cc40 100644 --- a/pkg/cmd/edit_test.go +++ b/pkg/cmd/edit_test.go @@ -1,14 +1,43 @@ package cmd_test import ( + "net/http/httptest" + . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" - "github.com/redhat-et/copilot-ops/pkg/cmd" + "github.com/spf13/cobra" ) var _ = Describe("Edit", func() { - It("no-op", func() { - Expect(cmd.BuildOpenAIClient(cmd.Config{}, 0, 0, "")).NotTo(BeNil()) + var c *cobra.Command + + BeforeEach(func() { + // create command + c = cmd.NewEditCmd() + Expect(c).NotTo(BeNil()) + }) + + When("OpenAI server exists", func() { + var ts *httptest.Server + BeforeEach(func() { + ts = OpenAITestServer() + }) + + JustBeforeEach(func() { + ts.Start() + err := c.Flags().Set(cmd.FlagOpenAIURLFull, ts.URL) + Expect(err).To(BeNil()) + }) + + AfterEach(func() { + defer ts.Close() + }) + + It("works", func() { + err := cmd.RunEdit(c, []string{}) + Expect(err).To(BeNil()) + }) + }) }) diff --git a/pkg/cmd/generate.go b/pkg/cmd/generate.go index 0f333e7..3a2a8b8 100644 --- a/pkg/cmd/generate.go +++ b/pkg/cmd/generate.go @@ -38,7 +38,7 @@ func NewGenerateCmd() *cobra.Command { ) cmd.Flags().StringArrayP( - FlagFilesetsFull, FlagFilesShort, []string{}, + FlagFilesetsFull, FlagFilesetsShort, []string{}, "Fileset names (defined in "+ConfigFile+") to be considered for the patch (can be specified multiple times)", ) diff --git a/pkg/cmd/generate_test.go b/pkg/cmd/generate_test.go index 0f5685d..38b5000 100644 --- a/pkg/cmd/generate_test.go +++ b/pkg/cmd/generate_test.go @@ -1,12 +1,59 @@ package cmd_test -// import ( -// . "github.com/onsi/ginkgo/v2" -// . "github.com/onsi/gomega" +import ( + "net/http/httptest" -// "github.com/redhat-et/copilot-ops/pkg/cmd" -// ) + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "github.com/spf13/cobra" -// var _ = Describe("Generate", func() { + "github.com/redhat-et/copilot-ops/pkg/cmd" +) -// }) +var _ = Describe("Generate command", func() { + var c *cobra.Command + var ts *httptest.Server + + BeforeEach(func() { + c = cmd.NewGenerateCmd() + }) + + When("server is created", func() { + BeforeEach(func() { + ts = OpenAITestServer() + + Expect(c).NotTo(BeNil()) + err := c.Flags().Set(cmd.FlagNTokensFull, "1") + Expect(err).To(BeNil()) + }) + + JustBeforeEach(func() { + ts.Start() + err := c.Flags().Set(cmd.FlagOpenAIURLFull, ts.URL) + Expect(err).To(BeNil()) + }) + AfterEach(func() { + defer ts.Close() + }) + + It("executes properly", func() { + err := cmd.RunGenerate(c, []string{}) + // use the minimum amount of tokens from OpenAI + Expect(err).To(BeNil()) + }) + // TODO: add more tests for expected success + }) + + When("OpenAI server is down", func() { + BeforeEach(func() { + // set a port that isn't taken + err := c.Flags().Set(cmd.FlagOpenAIURLFull, "http://localhost:23423") + Expect(err).To(BeNil()) + }) + It("fails", func() { + err := cmd.RunGenerate(c, []string{}) + Expect(err).To(HaveOccurred()) + }) + // TODO: add more cases that should fail + }) +}) diff --git a/pkg/cmd/utils.go b/pkg/cmd/utils.go index 6fa0291..bff7b49 100644 --- a/pkg/cmd/utils.go +++ b/pkg/cmd/utils.go @@ -20,17 +20,23 @@ type Request struct { IsWrite bool OpenAI *openai.Client OutputType string + OpenAIURL string } -func BuildOpenAIClient(conf Config, nTokens int32, nCompletions int32, engine string) *openai.Client { +// BuildOpenAIClient Creates and configures an OpenAI client based on the given parameters. +func BuildOpenAIClient(conf Config, nTokens int32, nCompletions int32, engine string, openAIURL string) *openai.Client { // create OpenAI client - openAIClient := openai.CreateOpenAIClient(conf.OpenAI.APIKey, conf.OpenAI.OrgID, engine) + openAIClient := openai.CreateOpenAIClient() openAIClient.NTokens = nTokens openAIClient.NCompletions = nCompletions openAIClient.Engine = engine + // allow override of OpenAI URL for testing + openAIClient.APIUrl = openAIURL + openai.OpenAIEndpointV1 return openAIClient } +// PrepareRequest Processes the user input along with provided environment variables, +// creating a Request object which is used for context in further requests. func PrepareRequest(cmd *cobra.Command, engine string) (*Request, error) { request, _ := cmd.Flags().GetString(FlagRequestFull) write, _ := cmd.Flags().GetBool(FlagWriteFull) @@ -44,6 +50,7 @@ func PrepareRequest(cmd *cobra.Command, engine string) (*Request, error) { nTokens, _ := cmd.Flags().GetInt32(FlagNTokensFull) nCompletions, _ := cmd.Flags().GetInt32(FlagNCompletionsFull) outputType, _ := cmd.Flags().GetString(FlagOutputTypeFull) + openAIURL, _ := cmd.Flags().GetString(FlagOpenAIURLFull) log.Printf("flags:\n") log.Printf(" - %-8s: %v\n", FlagRequestFull, request) @@ -101,7 +108,7 @@ func PrepareRequest(cmd *cobra.Command, engine string) (*Request, error) { filemapText := fm.EncodeToInputText() // create OpenAI client - openAIClient := BuildOpenAIClient(conf, nTokens, nCompletions, engine) + openAIClient := BuildOpenAIClient(conf, nTokens, nCompletions, engine, openAIURL) log.Printf("Model in use: " + openAIClient.Engine) r := Request{ @@ -160,4 +167,11 @@ func AddRequestFlags(cmd *cobra.Command) { FlagOutputTypeFull, FlagOutputTypeShort, "json", "How to format output", ) + + _ = cmd.Flags().StringP( + FlagOpenAIURLFull, + FlagOpenAIURLShort, + openai.OpenAIURL, + "Domain of the OpenAI API", + ) } diff --git a/pkg/openai/constants.go b/pkg/openai/constants.go index dcd587c..76bc690 100644 --- a/pkg/openai/constants.go +++ b/pkg/openai/constants.go @@ -1,10 +1,12 @@ package openai const ( - EditEndpoint string = "edits" - CompletionEndpoint string = "completions" - SearchEndpoint string = "search" - OpenAIEndpointV1 string = "https://api.openai.com/v1" + EditEndpoint string = "edits" + CompletionEndpoint string = "completions" + SearchEndpoint string = "search" + OpenAIURL string = "https://api.openai.com" + // Maybe the OpenAIEndpoint should be a part of the URL string? + OpenAIEndpointV1 string = "/v1" OpenAICodeDavinciEditV1 string = "code-davinci-edit-001" OpenAICodeDavinciV2 string = "code-davinci-002" CompletionEndOfSequence string = "EOF" diff --git a/pkg/openai/openai.go b/pkg/openai/openai.go index 5ba0770..3060dba 100644 --- a/pkg/openai/openai.go +++ b/pkg/openai/openai.go @@ -13,15 +13,11 @@ import ( // CreateOpenAIClient Creates a client to perform requests to OpenAI based on the provided // API token and organization ID. -func CreateOpenAIClient(authToken string, organizationID string, engine string) *Client { +func CreateOpenAIClient() *Client { return &Client{ Client: &http.Client{ Timeout: time.Minute, }, - APIUrl: OpenAIEndpointV1, - Engine: engine, - AuthToken: authToken, - OrganizationID: organizationID, } } diff --git a/pkg/openai/types.go b/pkg/openai/types.go index daaba0b..ad95ed1 100644 --- a/pkg/openai/types.go +++ b/pkg/openai/types.go @@ -8,15 +8,20 @@ type EditResponse struct { Response } +// Choice is a single choice returned by OpenAI. +type Choice struct { + Text string `json:"text"` + Index int `json:"index"` +} + +// Response Defines a response object from OpenAI. type Response struct { - Object string `json:"object"` - Created uint64 `json:"created"` - Choices []struct { - Text string `json:"text"` - Index int `json:"index"` - } `json:"choices"` + Object string `json:"object"` + Created uint64 `json:"created"` + Choices []Choice `json:"choices"` } +// BodyParameters Define a set of parameters for the body of OpenAI requests. type BodyParameters struct { // Model is the model used by OpenAI to generate completions Model string `json:"model"` From 70cbd61a64cb005d1cbe6d4438d106f6d58adb2a Mon Sep 17 00:00:00 2001 From: Oleg <97077423+RobotSail@users.noreply.github.com> Date: Fri, 29 Jul 2022 09:32:43 -0400 Subject: [PATCH 09/14] remove unused code Signed-off-by: Oleg <97077423+RobotSail@users.noreply.github.com> --- pkg/filemap/utils.go | 49 -------------------------------------------- 1 file changed, 49 deletions(-) diff --git a/pkg/filemap/utils.go b/pkg/filemap/utils.go index 8da71cd..73fd39a 100644 --- a/pkg/filemap/utils.go +++ b/pkg/filemap/utils.go @@ -30,52 +30,3 @@ func extractTagName(content string) (string, int32, error) { } return "", -1, fmt.Errorf("no tagname found in content") } - -// // outputToFile Converts a given output into a file object. -// func outputToFile(output string) (File, error) { -// // Trim the leading and trailing whitespace -// part := strings.TrimSpace(output) -// // If the part is empty, skip it -// if part == "" { -// return File{}, fmt.Errorf("part is empty") -// } -// // The tagName should be the first line -// tagName, lineNum, err := extractTagName(part) -// if err != nil { -// return File{}, err -// } - -// // grab the content following the lineNum -// concatenatedContent, err := ConcatenateAfterLineNum(part, lineNum) -// if err != nil { -// return nil, err -// } -// // ignore empty files -// if strings.TrimSpace(concatenatedContent) == "" { -// continue -// } - -// } - -// // OutputToFile Converts a given string consisting of YAML files and returns a list of files. -// func OutputToFile(output string) ([]File, error) { -// // To decode from the output, we have to split the content up by the defined file delimeter. -// // Then we use RegEx to extract the tagname which we can use to look up the file and update its content. -// // If the tagname is not found, we assume that the file is new and we will create a new file with the tagname. -// files := make([]File, 0) - -// // Split the content by the file delimeter -// parts := strings.Split(output, FileDelimeter) -// for _, part := range parts { - -// // add to the filemap -// file := File{ -// Name: tagName, -// Path: "generated-by-copilot-ops/", -// Content: concatenatedContent, -// } -// files = append(files, file) -// } - -// return files, nil -// } From 69e388e73aca766b54f70a197659998366417674 Mon Sep 17 00:00:00 2001 From: Oleg <97077423+RobotSail@users.noreply.github.com> Date: Fri, 29 Jul 2022 10:03:37 -0400 Subject: [PATCH 10/14] publish coverage report to codecov Signed-off-by: Oleg <97077423+RobotSail@users.noreply.github.com> --- .github/workflows/ci.yaml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 2e329ec..0c7fa37 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -30,11 +30,12 @@ jobs: with: go-version: 1.18 - run: make test - name: Run Tests + name: Run tests and generate coverage report shell: bash - env: - OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} - OPENAI_ORG_ID: ${{ secrets.OPENAI_ORG_ID }} + - uses: codecov/codecov-action@v3 + with: + files: ./cover.out + fail_ci_if_error: true - run: go vet -v ./... build-all: runs-on: ubuntu-latest From 940e44d5edc5a90d9d1088894b3526570b8fc356 Mon Sep 17 00:00:00 2001 From: Oleg <97077423+RobotSail@users.noreply.github.com> Date: Mon, 1 Aug 2022 14:28:39 -0400 Subject: [PATCH 11/14] add localbin target & check conf load err Signed-off-by: Oleg <97077423+RobotSail@users.noreply.github.com> --- Makefile | 10 +++++++++- pkg/cmd/utils.go | 4 +++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 763774a..637af39 100644 --- a/Makefile +++ b/Makefile @@ -45,4 +45,12 @@ ginkgo: $(GINKGO) ## Download ginkgo $(GINKGO): $(LOCALBIN) @ echo "▶️ Downloading ginkgo@v2" GOBIN=$(LOCALBIN) go install -mod=mod github.com/onsi/ginkgo/v2/ginkgo - @ echo "✅ Downloaded ginkgo" \ No newline at end of file + @ echo "✅ Downloaded ginkgo" + + +##@ Build Dependencies +LOCALBIN ?= $(shell pwd)/bin +$(LOCALBIN): + @ echo "▶️ Local binary directory not present, creating..." + mkdir -p $(LOCALBIN) + @ echo "✅ Local binary directory created" \ No newline at end of file diff --git a/pkg/cmd/utils.go b/pkg/cmd/utils.go index bff7b49..1b4ef42 100644 --- a/pkg/cmd/utils.go +++ b/pkg/cmd/utils.go @@ -74,7 +74,9 @@ func PrepareRequest(cmd *cobra.Command, engine string) (*Request, error) { // we'll just use the defaults and continue without error. // Errors here might return if the file exists but is invalid. conf := Config{} - _ = conf.Load() + if err := conf.Load(); err != nil { + return nil, err + } // WARNING we should not consider printing conf with its secret keys log.Printf("Filesets: %+v\n", conf.Filesets) From 73a7c124aff503007931eb59b6efdf920e9796d1 Mon Sep 17 00:00:00 2001 From: Oleg <97077423+RobotSail@users.noreply.github.com> Date: Mon, 1 Aug 2022 14:39:10 -0400 Subject: [PATCH 12/14] fix issue with error casting Signed-off-by: Oleg <97077423+RobotSail@users.noreply.github.com> --- pkg/cmd/config.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/pkg/cmd/config.go b/pkg/cmd/config.go index e627349..e510c24 100644 --- a/pkg/cmd/config.go +++ b/pkg/cmd/config.go @@ -48,7 +48,7 @@ func (c *Config) Load() error { viper.SetConfigName(".copilot-ops") // name of config file (without extension) if err := viper.MergeInConfig(); err != nil { - var configFileNotFound *viper.ConfigFileNotFoundError + var configFileNotFound viper.ConfigFileNotFoundError if ok := errors.As(err, &configFileNotFound); !ok { return err // allow no config file } @@ -59,7 +59,13 @@ func (c *Config) Load() error { // optionally look for local (gitignored) config file and merge it in viper.SetConfigName(".copilot-ops.local") - _ = viper.MergeInConfig() + if err := viper.MergeInConfig(); err != nil { + var configFileNotFound viper.ConfigFileNotFoundError + if ok := errors.As(err, &configFileNotFound); !ok { + return err // allow no config file + } + } + log.Printf("viper: %+v\n", viper.ConfigFileUsed()) if err := viper.Unmarshal(c); err != nil { From a2978d3a2778fb641a94aeadfdb73aaad79cd2ee Mon Sep 17 00:00:00 2001 From: Oleg <97077423+RobotSail@users.noreply.github.com> Date: Mon, 1 Aug 2022 14:49:01 -0400 Subject: [PATCH 13/14] remove defer Signed-off-by: Oleg <97077423+RobotSail@users.noreply.github.com> --- pkg/cmd/cmd_test.go | 7 +++++++ pkg/cmd/generate_test.go | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/pkg/cmd/cmd_test.go b/pkg/cmd/cmd_test.go index 43b9b49..7acb9b6 100644 --- a/pkg/cmd/cmd_test.go +++ b/pkg/cmd/cmd_test.go @@ -1,6 +1,8 @@ package cmd_test import ( + "log" + . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" @@ -14,4 +16,9 @@ var _ = Describe("Root command", func() { Expect(rootCmd).NotTo(BeNil()) }) }) + Describe("Edit command", func() { + It("hello world", func() { + log.Println("hello") + }) + }) }) diff --git a/pkg/cmd/generate_test.go b/pkg/cmd/generate_test.go index 38b5000..a04b12a 100644 --- a/pkg/cmd/generate_test.go +++ b/pkg/cmd/generate_test.go @@ -33,7 +33,7 @@ var _ = Describe("Generate command", func() { Expect(err).To(BeNil()) }) AfterEach(func() { - defer ts.Close() + ts.Close() }) It("executes properly", func() { From 992fcc2cd7c16bf249b7ec41224086b9f34622f2 Mon Sep 17 00:00:00 2001 From: Oleg <97077423+RobotSail@users.noreply.github.com> Date: Mon, 1 Aug 2022 14:55:33 -0400 Subject: [PATCH 14/14] tag -> name Signed-off-by: Oleg <97077423+RobotSail@users.noreply.github.com> --- pkg/filemap/filemap.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/filemap/filemap.go b/pkg/filemap/filemap.go index 39fd741..9be86fe 100644 --- a/pkg/filemap/filemap.go +++ b/pkg/filemap/filemap.go @@ -51,13 +51,13 @@ func NewFilemap() *Filemap { func (fm *Filemap) LogDump() { maxShown := 30 log.Printf("filemap: len %d\n", len(fm.Files)) - for tag, f := range fm.Files { + for name, f := range fm.Files { l := len(f.Content) if l > maxShown { l = maxShown } short := strings.ReplaceAll(f.Content[:l], "\n", " ") - log.Printf(" - tag: %-10q: path: %-20q [%s ...] len %d\n", tag, f.Path, short, len(f.Content)) + log.Printf(" - tag: %-10q: path: %-20q [%s ...] len %d\n", name, f.Path, short, len(f.Content)) } } @@ -99,14 +99,14 @@ func (fm *Filemap) LoadFilesFromGlob(glob string) error { // WriteUpdatesToFiles Writes the updated contents of each file to the directory. func (fm *Filemap) WriteUpdatesToFiles() error { - for tag, file := range fm.Files { + for name, file := range fm.Files { // add extension if necessary, assume this is YAML for the time being // HACK: classify the relevant extension (e.g. .yaml, .yml, .json) // fileName := file.Tag // if len(strings.Split(file.Tag, ".")) == 1 { // fileName += ".yaml" // } - log.Printf("path: %q, tag: %q\n", file.Path, tag) + log.Printf("path: %q, tag: %q\n", file.Path, name) // locate the base directory of filePath dirPath := filepath.Dir(file.Path) // create the directory if it does not exist