diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4a8a528 --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib +/tools/*/bin/* +/tools/*/dist/* + +# We don't want to commit env variables +*.env + +# Tool generated files +*.idea +*.vscode diff --git a/tools/.tool-versions b/tools/.tool-versions new file mode 100644 index 0000000..6bc74c5 --- /dev/null +++ b/tools/.tool-versions @@ -0,0 +1 @@ +golang 1.22.1 diff --git a/tools/cli/Makefile b/tools/cli/Makefile new file mode 100644 index 0000000..c1e8608 --- /dev/null +++ b/tools/cli/Makefile @@ -0,0 +1,51 @@ +# A Self-Documenting Makefile: http://marmelab.com/blog/2016/02/29/auto-documented-makefile.html + +SOURCE_FILES?=./cmd +BINARY_NAME=openapicli +VERSION=v0.0.1 +GIT_SHA?=$(shell git rev-parse HEAD) +DESTINATION=./bin/$(BINARY_NAME) + +LINKER_GH_SHA_FLAG=-s -w -X github.com/mongodb/openapi/tools/cli/internal/version.GitCommit=${GIT_SHA} +LINKER_FLAGS=${LINKER_GH_SHA_FLAG} -X github.com/mongodb/openapi/tools/cli/internal/version.Version=${VERSION} + +DEBUG_FLAGS=all=-N -l + +export TERM := linux-m +export GO111MODULE := on + +.PHONY: deps +deps: ## Download go module dependencies + @echo "==> Installing go.mod dependencies..." + go mod download + go mod tidy + +.PHONY: setup +setup: deps ## Set up dev env + +.PHONY: fmt +fmt: ### Format all go files with goimports and gofmt + find . -name "*.go" -exec gofmt -w "{}" \; + find . -name "*.go" -exec goimports -l -w "{}" \; + +.PHONY: build +build: + @echo "==> Building openapicli binary" + go build -ldflags "$(LINKER_FLAGS)" -o $(DESTINATION) $(SOURCE_FILES) + + +.PHONY: build-debug +build-debug: + @echo "==> Building openapicli binary for debugging" + go build -gcflags="$(DEBUG_FLAGS)" -ldflags "$(LINKER_FLAGS)" -o $(DESTINATION) $(SOURCE_FILES) + + +.PHONY: list +list: ## List all make targets + @${MAKE} -pRrn : -f $(MAKEFILE_LIST) 2>/dev/null | awk -v RS= -F: '/^# File/,/^# Finished Make data base/ {if ($$1 !~ "^[#.]") {print $$1}}' | egrep -v -e '^[^[:alnum:]]' -e '^$@$$' | sort + + +.PHONY: help +.DEFAULT_GOAL := help +help: + @grep -h -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' diff --git a/tools/cli/cmd/openapicli.go b/tools/cli/cmd/openapicli.go new file mode 100644 index 0000000..83b2c59 --- /dev/null +++ b/tools/cli/cmd/openapicli.go @@ -0,0 +1,34 @@ +// Copyright 2024 MongoDB Inc +// +// 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 ( + "context" + "mongodb/openapi/tools/cli/internal/cli/root/openapi" + "os" +) + +// Execute adds all child commands to the root command and sets flags appropriately. +// This is called by main.main(). It only needs to happen once to the rootCmd. +func Execute(ctx context.Context) { + rootCmd := openapi.Builder() + if err := rootCmd.ExecuteContext(ctx); err != nil { + os.Exit(1) + } +} + +func main() { + Execute(context.Background()) +} diff --git a/tools/cli/go.mod b/tools/cli/go.mod new file mode 100644 index 0000000..6ff94b0 --- /dev/null +++ b/tools/cli/go.mod @@ -0,0 +1,23 @@ +module mongodb/openapi/tools/cli + +go 1.22.1 + +require ( + github.com/spf13/cobra v1.8.0 + github.com/tufin/oasdiff v1.9.5 +) + +require ( + github.com/getkin/kin-openapi v0.120.0 // indirect + github.com/go-openapi/jsonpointer v0.20.0 // indirect + github.com/go-openapi/swag v0.22.4 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/invopop/yaml v0.2.0 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/mailru/easyjson v0.7.7 // indirect + github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect + github.com/perimeterx/marshmallow v1.1.5 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/yargevad/filepathx v1.0.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/tools/cli/go.sum b/tools/cli/go.sum new file mode 100644 index 0000000..d89a2c1 --- /dev/null +++ b/tools/cli/go.sum @@ -0,0 +1,50 @@ +github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/getkin/kin-openapi v0.120.0 h1:MqJcNJFrMDFNc07iwE8iFC5eT2k/NPUFDIpNeiZv8Jg= +github.com/getkin/kin-openapi v0.120.0/go.mod h1:PCWw/lfBrJY4HcdqE3jj+QFkaFK8ABoqo7PvqVhXXqw= +github.com/go-openapi/jsonpointer v0.20.0 h1:ESKJdU9ASRfaPNOPRx12IUyA1vn3R9GiE3KYD14BXdQ= +github.com/go-openapi/jsonpointer v0.20.0/go.mod h1:6PGzBjjIIumbLYysB73Klnms1mwnU4G3YHOECG3CedA= +github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU= +github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM= +github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/invopop/yaml v0.2.0 h1:7zky/qH+O0DwAyoobXUqvVBwgBFRxKoQ/3FjcVpjTMY= +github.com/invopop/yaml v0.2.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdiBN3Q= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= +github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= +github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s= +github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= +github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= +github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/tufin/oasdiff v1.9.5 h1:xXzfLj8ezA14E6miyPQyNnO+c0qvefJazirMij8WyMQ= +github.com/tufin/oasdiff v1.9.5/go.mod h1:E1XiSLK/vnxZDmkB8pUYsiYUpdMjCKj47tNUmVnSIzA= +github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0= +github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= +github.com/yargevad/filepathx v1.0.0 h1:SYcT+N3tYGi+NvazubCNlvgIPbzAk7i7y2dwg3I5FYc= +github.com/yargevad/filepathx v1.0.0/go.mod h1:BprfX/gpYNJHJfc35GjRRpVcwWXS89gGulUIU5tK3tA= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/tools/cli/internal/cli/merge/merge.go b/tools/cli/internal/cli/merge/merge.go new file mode 100644 index 0000000..249bcde --- /dev/null +++ b/tools/cli/internal/cli/merge/merge.go @@ -0,0 +1,62 @@ +// Copyright 2024 MongoDB Inc +// +// 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 merge + +import ( + "github.com/spf13/cobra" + "github.com/tufin/oasdiff/load" +) + +type Opts struct { + Base *load.SpecInfo + outputPath string +} + +func (o *Opts) Run(args []string) error { + // To add in follow up PR: CLOUDP-225849 + return nil +} + +func (o *Opts) removeExternalReferences(paths []string, federated *load.SpecInfo) ([]byte, error) { + // To add in follow up PR: CLOUDP-225849 + return nil, nil + +} +func (o *Opts) saveFile(data []byte) error { + return nil +} + +func (o *Opts) PreRunE(args []string) error { + // To Add in follow up PR: CLOUDP-225849 + return nil +} + +func Builder() *cobra.Command { + opts := &Opts{} + + cmd := &cobra.Command{ + Use: "merge [base-spec] [spec-1] [spec-2] [spec-3] ... [spec-n]", + Short: "Merge Open API specifications into a base spec.", + Args: cobra.MinimumNArgs(2), + PreRunE: func(cmd *cobra.Command, args []string) error { + return opts.PreRunE(args) + }, + RunE: func(cmd *cobra.Command, args []string) error { + return opts.Run(args) + }, + } + + return cmd +} diff --git a/tools/cli/internal/cli/root/openapi/builder.go b/tools/cli/internal/cli/root/openapi/builder.go new file mode 100644 index 0000000..24ba2c4 --- /dev/null +++ b/tools/cli/internal/cli/root/openapi/builder.go @@ -0,0 +1,66 @@ +// Copyright 2024 MongoDB Inc +// +// 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 openapi + +import ( + "fmt" + "mongodb/openapi/tools/cli/internal/cli/merge" + "mongodb/openapi/tools/cli/internal/version" + "runtime" + + "github.com/spf13/cobra" +) + +const ( + ToolName = "openapicli" + verTemplate = `openapicli version: %s +git version: %s +Go version: %s + os: %s + arch: %s + compiler: %s +` +) + +// Builder conditionally adds children commands as needed. +// This is important in particular for Atlas as it dynamically sets flags for cluster creation and +// this can be slow to timeout on environments with limited internet access (Ops Manager). +func Builder() *cobra.Command { + rootCmd := &cobra.Command{ + Version: version.Version, + Use: ToolName, + Short: "CLI tool to validate and merge your Open API Specifications", + Example: ` # Display the help menu for the merge command: + openapicli merge --help +`, + SilenceUsage: true, + } + + rootCmd.SetVersionTemplate(formattedVersion()) + rootCmd.AddCommand( + merge.Builder(), + ) + return rootCmd +} + +func formattedVersion() string { + return fmt.Sprintf(verTemplate, + version.Version, + version.GitCommit, + runtime.Version(), + runtime.GOOS, + runtime.GOARCH, + runtime.Compiler) +} diff --git a/tools/cli/internal/version/version.go b/tools/cli/internal/version/version.go new file mode 100644 index 0000000..2a9abf7 --- /dev/null +++ b/tools/cli/internal/version/version.go @@ -0,0 +1,21 @@ +// Copyright 2024 MongoDB Inc +// +// 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 version + +// These variables are set at compilation time in the Makefile +var ( + Version = "next" // Version for CLI. + GitCommit = "next" // GitCommit git sha of the build. +)