diff --git a/azure-pipelines.yml b/azure-pipelines.yml index d254313e0985..e63506d2df7c 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -1,58 +1,67 @@ -pool: - vmImage: 'Ubuntu 16.04' +jobs: + - job: Build_Test + strategy: + matrix: + Linux_Go112: + vm.image: 'ubuntu-18.04' + go.version: '1.12' + GOROOT: '/usr/local/go$(go.version)' + Linux_Go113: + vm.image: 'ubuntu-18.04' + go.version: '1.13' + GOROOT: '/usr/local/go$(go.version)' -variables: - GOROOT: '/usr/local/go1.12' - GOPATH: '$(system.defaultWorkingDirectory)/work' - sdkPath: '$(GOPATH)/src/github.com/$(build.repository.name)' - IGNORE_BREAKING_CHANGES: true + pool: + vmImage: $(vm.image) -steps: -- script: | - set -e - mkdir -p '$(GOPATH)/bin' - mkdir -p '$(sdkPath)' - shopt -s dotglob extglob - mv !(work) '$(sdkPath)' - echo '##vso[task.prependpath]$(GOROOT)/bin' - echo '##vso[task.prependpath]$(GOPATH)/bin' - displayName: 'Create Go Workspace' -- script: | - set -e - curl -sSL https://raw.githubusercontent.com/golang/dep/master/install.sh | sh - dep ensure -v - go get -u golang.org/x/lint/golint - workingDirectory: '$(sdkPath)' - displayName: 'Install Dependencies' -- script: go vet $(go list ./... | grep -v vendor) - workingDirectory: '$(sdkPath)' - displayName: 'Vet' -- script: go build -v $(go list ./... | grep -v vendor) - workingDirectory: '$(sdkPath)' - displayName: 'Build' -- script: go test $(dirname $(find . -path ./vendor -prune -o -name '*_test.go' -print) | sort -u) - workingDirectory: '$(sdkPath)' - displayName: 'Run Tests' -- script: go run ./tools/apidiff/main.go packages ./services FETCH_HEAD~1 FETCH_HEAD --copyrepo --breakingchanges || $IGNORE_BREAKING_CHANGES - workingDirectory: '$(sdkPath)' - displayName: 'Display Breaking Changes' -- script: go run ./tools/pkgchk/main.go ./services --exceptions ./tools/pkgchk/exceptions.txt - workingDirectory: '$(sdkPath)' - displayName: 'Verify Package Directory' -- script: grep -L -r --include *.go --exclude-dir vendor -P "Copyright (\d{4}|\(c\)) Microsoft" ./ | tee >&2 - workingDirectory: '$(sdkPath)' - displayName: 'Copyright Header Check' - failOnStderr: true - condition: succeededOrFailed() -- script: gofmt -s -l -w $(find . -path ./vendor -prune -o -name '*.go' -print) >&2 - workingDirectory: '$(sdkPath)' - displayName: 'Format Check' - failOnStderr: true - condition: succeededOrFailed() -- script: | - golint ./storage/... >&2 - golint ./tools/... >&2 - workingDirectory: '$(sdkPath)' - displayName: 'Linter Check' - failOnStderr: true - condition: succeededOrFailed() + variables: + GOPATH: '$(system.defaultWorkingDirectory)/work' + sdkPath: '$(GOPATH)/src/github.com/Azure/azure-sdk-for-go' + + steps: + - script: | + set -e + mkdir -p '$(GOPATH)/bin' + mkdir -p '$(sdkPath)' + shopt -s dotglob extglob + mv !(work) '$(sdkPath)' + echo '##vso[task.prependpath]$(GOROOT)/bin' + echo '##vso[task.prependpath]$(GOPATH)/bin' + displayName: 'Create Go Workspace' + - script: | + set -e + go version + curl -sSL https://raw.githubusercontent.com/golang/dep/master/install.sh | sh + dep ensure -v + go get -u golang.org/x/lint/golint + workingDirectory: '$(sdkPath)' + displayName: 'Install Dependencies' + - script: go vet $(go list ./... | grep -v vendor) + workingDirectory: '$(sdkPath)' + displayName: 'Vet' + - script: go build -v $(go list ./... | grep -v vendor) + workingDirectory: '$(sdkPath)' + displayName: 'Build' + - script: go test $(dirname $(find . -path ./vendor -prune -o -name '*_test.go' -print) | sort -u) + workingDirectory: '$(sdkPath)' + displayName: 'Run Tests' + - script: go run ./tools/pkgchk/main.go ./services --exceptions ./tools/pkgchk/exceptions.txt + workingDirectory: '$(sdkPath)' + displayName: 'Verify Package Directory' + - script: grep -L -r --include *.go --exclude-dir vendor -P "Copyright (\d{4}|\(c\)) Microsoft" ./ | tee >&2 + workingDirectory: '$(sdkPath)' + displayName: 'Copyright Header Check' + failOnStderr: true + condition: succeededOrFailed() + - script: gofmt -s -l -w $(find . -path ./vendor -prune -o -name '*.go' -print) >&2 + workingDirectory: '$(sdkPath)' + displayName: 'Format Check' + failOnStderr: true + condition: succeededOrFailed() + - script: | + golint ./storage/... >&2 + golint ./tools/... >&2 + workingDirectory: '$(sdkPath)' + displayName: 'Linter Check' + failOnStderr: true + condition: succeededOrFailed() \ No newline at end of file diff --git a/moduler-pipelines.yml b/moduler-pipelines.yml new file mode 100644 index 000000000000..d08f1803cbc8 --- /dev/null +++ b/moduler-pipelines.yml @@ -0,0 +1,95 @@ +pool: + vmImage: 'Ubuntu 18.04' + +variables: + GOROOT: '/usr/local/go1.12' + GOPATH: '$(system.defaultWorkingDirectory)/work' + sdkPath: '$(GOPATH)/src/github.com/Azure/azure-sdk-for-go' + +trigger: + batch: true + branches: + include: + - master + +pr: none + +steps: + - script: | + # Enable bash verbose logging + set -x + mkdir -p '$(GOPATH)/bin' + mkdir -p '$(sdkPath)' + shopt -s dotglob extglob + mv !(work) '$(sdkPath)' + echo '##vso[task.prependpath]$(GOROOT)/bin' + echo '##vso[task.prependpath]$(GOPATH)/bin' + displayName: 'Create Go Workspace' + - script: | + set -x + go version + curl -sSL https://raw.githubusercontent.com/golang/dep/master/install.sh | sh + dep ensure -v + go get -u golang.org/x/lint/golint + workingDirectory: '$(sdkPath)' + displayName: 'Install Dependencies' + - script: | + # Echo error if github-pat is empty + [ -z $(github-pat) ] && echo "github-pat is empty!" && exit -1 + echo $(Build.SourceBranchName) + # Enable bash verbose logging + set -x + git checkout $(Build.SourceBranchName) + git config user.email "azuresdkci@microsoft.com" + git config user.name "azuresdkci" + git config credential.helper store + echo https://$(github-pat):x-oauth-basic@github.com > ~/.git-credentials + displayName: 'Setup github' + workingDirectory: '$(sdkPath)' + - script: | + go generate ./profiles + gofmt -s -w ./profiles + workingDirectory: '$(sdkPath)' + displayName: 'Generate profiles' + - script: go vet $(go list ./... | grep -v vendor) + workingDirectory: '$(sdkPath)' + displayName: 'Vet' + - script: go build -v $(go list ./... | grep -v vendor) + workingDirectory: '$(sdkPath)' + displayName: 'Build' + - script: go test $(dirname $(find . -path ./vendor -prune -o -name '*_test.go' -print) | sort -u) + workingDirectory: '$(sdkPath)' + displayName: 'Run Tests' + - script: go run ./tools/pkgchk/main.go ./services --exceptions ./tools/pkgchk/exceptions.txt + workingDirectory: '$(sdkPath)' + displayName: 'Verify Package Directory' + - script: grep -L -r --include *.go --exclude-dir vendor -P "Copyright (\d{4}|\(c\)) Microsoft" ./ | tee >&2 + workingDirectory: '$(sdkPath)' + displayName: 'Copyright Header Check' + failOnStderr: true + condition: succeededOrFailed() + - script: gofmt -s -l -w $(find . -path ./vendor -prune -o -name '*.go' -print) >&2 + workingDirectory: '$(sdkPath)' + displayName: 'Format Check' + failOnStderr: true + condition: succeededOrFailed() + - script: | + golint ./storage/... >&2 + golint ./tools/... >&2 + workingDirectory: '$(sdkPath)' + displayName: 'Linter Check' + failOnStderr: true + condition: succeededOrFailed() + - script: | + # test if there are new profiles + if [ -z "$(git status --porcelain)" ]; then + echo no new profiles generated. + else + git commit -a -m "regenerated profiles" + git push origin $(Build.SourceBranchName) + fi + workingDirectory: '$(sdkPath)' + displayName: 'Push profiles' + - script: go run ./tools/moduler/main.go ./services -v + displayName: 'Detecting new tag and push' + workingDirectory: '$(sdkPath)' diff --git a/services/containerservice/mgmt/2016-09-30/containerservice/CHANGELOG.md b/services/containerservice/mgmt/2016-09-30/containerservice/CHANGELOG.md new file mode 100644 index 000000000000..4b553421a78a --- /dev/null +++ b/services/containerservice/mgmt/2016-09-30/containerservice/CHANGELOG.md @@ -0,0 +1 @@ +No changes to exported content compared to the previous release. diff --git a/services/containerservice/mgmt/2016-09-30/containerservice/go.mod b/services/containerservice/mgmt/2016-09-30/containerservice/go.mod new file mode 100644 index 000000000000..45774c72c8c1 --- /dev/null +++ b/services/containerservice/mgmt/2016-09-30/containerservice/go.mod @@ -0,0 +1,3 @@ +module github.com/Azure/azure-sdk-for-go/services/containerservice/mgmt/2016-09-30/containerservice + +go 1.12 diff --git a/services/containerservice/mgmt/2016-09-30/containerservice/version.go b/services/containerservice/mgmt/2016-09-30/containerservice/version.go index 55e405750344..7d17fafe4929 100644 --- a/services/containerservice/mgmt/2016-09-30/containerservice/version.go +++ b/services/containerservice/mgmt/2016-09-30/containerservice/version.go @@ -1,7 +1,5 @@ package containerservice -import "github.com/Azure/azure-sdk-for-go/version" - // Copyright (c) Microsoft and contributors. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -21,10 +19,12 @@ import "github.com/Azure/azure-sdk-for-go/version" // UserAgent returns the UserAgent string to use when sending http.Requests. func UserAgent() string { - return "Azure-SDK-For-Go/" + version.Number + " containerservice/2016-09-30" + return "Azure-SDK-For-Go/1.0.0 containerservice/2016-09-30" } // Version returns the semantic version (see http://semver.org) of the client. func Version() string { - return version.Number + return "1.0.0" } + +// tag: services/containerservice/mgmt/2016-09-30/containerservice/v1.0.0 diff --git a/swagger_to_sdk_config.json b/swagger_to_sdk_config.json index cb18404b1b0b..18493e1db6be 100644 --- a/swagger_to_sdk_config.json +++ b/swagger_to_sdk_config.json @@ -1,10 +1,9 @@ { "$schema": "https://openapistorageprod.blob.core.windows.net/sdkautomation/prod/schemas/swagger_to_sdk_config.schema.json", "meta": { - "after_scripts": [ + "after_scripts_in_repo": [ "dep ensure", - "go generate ./profiles/generate.go", - "gofmt -w ./profiles/", + "go run ./tools/versioner/main.go ./services -v", "gofmt -w ./services/" ], "autorest_options": { @@ -13,8 +12,10 @@ "verbose": "", "sdkrel:go-sdk-folder": ".", "multiapi": "", - "use-onever": "", - "preview-chk": "" + "preview-chk": "", + "go.clear-output-folder": false, + "stage": "", + "gomod-root": "github.com/Azure/azure-sdk-for-go" }, "repotag": "azure-sdk-for-go", "envs": { @@ -27,4 +28,4 @@ }, "version": "0.2.0" } -} +} \ No newline at end of file diff --git a/tools/apidiff/repo/repo.go b/tools/apidiff/repo/repo.go index a0a6ba36a996..1733b0399f55 100644 --- a/tools/apidiff/repo/repo.go +++ b/tools/apidiff/repo/repo.go @@ -171,7 +171,7 @@ func (wt WorkingTree) CreateTag(name string) error { // ListTags calls "git tag -l " to obtain the list of tags. // If there are no tags the returned slice will have zero length. -// Tags are sorted in lexographic ascending order. +// Tags are sorted in lexicographic ascending order. func (wt WorkingTree) ListTags(pattern string) ([]string, error) { cmd := exec.Command("git", "tag", "-l", pattern) cmd.Dir = wt.dir diff --git a/tools/internal/pkgs/pkgs.go b/tools/internal/pkgs/pkgs.go new file mode 100644 index 000000000000..8114fad8d293 --- /dev/null +++ b/tools/internal/pkgs/pkgs.go @@ -0,0 +1,126 @@ +// Copyright 2018 Microsoft Corporation +// +// 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 pkgs + +import ( + "errors" + "fmt" + "go/ast" + "go/parser" + "go/token" + "io/ioutil" + "os" + "path/filepath" + "regexp" + "strings" +) + +// Pkg provides a package of a service +type Pkg struct { + // the directory where the package relative to the root dir + Dest string + // the ast of the package + Package *ast.Package +} + +// IsARMPkg returns if this package is ARM package (management plane) +func (p Pkg) IsARMPkg() bool { + return strings.Index(p.Dest, "/mgmt/") > -1 +} + +// GetAPIVersion returns the api version of this package +func (p Pkg) GetAPIVersion() (string, error) { + dest := p.Dest + if p.IsARMPkg() { + // management-plane + regex := regexp.MustCompile(`mgmt/(.+)/`) + groups := regex.FindStringSubmatch(dest) + if len(groups) < 2 { + return "", fmt.Errorf("cannot find api version in %s", dest) + } + versionString := groups[1] + return versionString, nil + } + // data-plane + regex := regexp.MustCompile(`/(\d{4}-\d{2}.*|v?\d+(\.\d+)?)/`) + groups := regex.FindStringSubmatch(dest) + if len(groups) < 2 { + return "", fmt.Errorf("cannot find api version in %s", dest) + } + versionString := groups[1] + if versionString == "" { + return "", fmt.Errorf("does not find api version in data plane package %s", dest) + } + return versionString, nil +} + +// GetPkgs returns every package under the rootDir. Package for interfaces will be ignored. +func GetPkgs(rootDir string) ([]Pkg, error) { + var pkgs []Pkg + err := filepath.Walk(rootDir, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if info.IsDir() { + // check if leaf dir + fi, err := ioutil.ReadDir(path) + if err != nil { + return err + } + hasSubDirs := false + interfacesDir := false + for _, f := range fi { + if f.IsDir() { + hasSubDirs = true + break + } + if f.Name() == "interfaces.go" { + interfacesDir = true + } + } + if !hasSubDirs { + fs := token.NewFileSet() + // with interfaces codegen the majority of leaf directories are now the + // *api packages. when this is the case parse from the parent directory. + if interfacesDir { + path = filepath.Dir(path) + } + packages, err := parser.ParseDir(fs, path, func(fileInfo os.FileInfo) bool { + return fileInfo.Name() != "interfaces.go" + }, parser.PackageClauseOnly) + if err != nil { + return nil + } + if len(packages) < 1 { + return errors.New("didn't find any packages which is unexpected") + } + if len(packages) > 1 { + return errors.New("found more than one package which is unexpected") + } + var p *ast.Package + for _, pkgs := range packages { + p = pkgs + } + // normalize directory separator to '/' character + pkgs = append(pkgs, Pkg{ + Dest: strings.ReplaceAll(path[len(rootDir):], "\\", "/"), + Package: p, + }) + } + } + return nil + }) + return pkgs, err +} diff --git a/tools/internal/pkgs/pkgs_test.go b/tools/internal/pkgs/pkgs_test.go new file mode 100644 index 000000000000..99a94848ddb1 --- /dev/null +++ b/tools/internal/pkgs/pkgs_test.go @@ -0,0 +1,106 @@ +// Copyright 2018 Microsoft Corporation +// +// 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 pkgs + +import ( + "path/filepath" + "strings" + "testing" +) + +const ( + testRoot = "../../testpkgs" +) + +var ( + expected = map[string]string{ + "/scenrioa/foo": "foo", + "/scenriob/foo": "foo", + "/scenriob/foo/v2": "foo", + "/scenrioc/mgmt/2019-10-11/foo": "foo", + "/scenriod/mgmt/2019-10-11/foo": "foo", + "/scenriod/mgmt/2019-10-11/foo/v2": "foo", + "/scenrioe/mgmt/2019-10-11/foo": "foo", + "/scenrioe/mgmt/2019-10-11/foo/v2": "foo", + "/scenrioe/mgmt/2019-10-11/foo/v3": "foo", + } +) + +func TestGetPkgs(t *testing.T) { + root, err := filepath.Abs(testRoot) + if err != nil { + t.Fatalf("failed to get absolute path: %+v", err) + } + pkgs, err := GetPkgs(root) + if err != nil { + t.Fatalf("failed to get packages: %+v", err) + } + if len(pkgs) != len(expected) { + t.Fatalf("expected %d packages, but got %d", len(expected), len(pkgs)) + } + for _, pkg := range pkgs { + if pkgName, ok := expected[pkg.Dest]; !ok { + t.Fatalf("got pkg path '%s', but not found in expected", pkg.Dest) + } else if !strings.EqualFold(pkgName, pkg.Package.Name) { + t.Fatalf("expected package of '%s' in path '%s', but got '%s'", pkgName, pkg.Dest, pkg.Package.Name) + } + } +} + +func TestPkg_GetApiVersion(t *testing.T) { + cases := []struct { + input string + expected string + }{ + { + input: "/netapp/mgmt/2019-07-01/netapp", + expected: "2019-07-01", + }, + { + input: "/preview/resources/mgmt/2017-08-31-preview/managementgroups", + expected: "2017-08-31-preview", + }, + { + input: "/batch/2018-12-01.8.0/batch", + expected: "2018-12-01.8.0", + }, + { + input: "/cognitiveservices/v1.0/imagesearch", + expected: "v1.0", + }, + { + input: "/servicefabric/6.3/servicefabric", + expected: "6.3", + }, + { + input: "/keyvault/2015-06-01/keyvault", + expected: "2015-06-01", + }, + { + input: "/preview/datalake/analytics/2017-09-01-preview/job", + expected: "2017-09-01-preview", + }, + } + for _, c := range cases { + p := Pkg{Dest: c.input} + api, err := p.GetAPIVersion() + if err != nil { + t.Fatalf("failed to get api version: %+v", err) + } + if !strings.EqualFold(api, c.expected) { + t.Fatalf("expected: %s, but got %s", c.expected, api) + } + } +} diff --git a/tools/moduler/cmd/common.go b/tools/moduler/cmd/common.go new file mode 100644 index 000000000000..ff5da22ef9f6 --- /dev/null +++ b/tools/moduler/cmd/common.go @@ -0,0 +1,37 @@ +// Copyright 2018 Microsoft Corporation +// +// 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 cmd + +import "fmt" + +func vprintf(format string, a ...interface{}) { + if verboseFlag { + fmt.Printf(format, a...) + } +} + +func vprintln(message string) { + if verboseFlag { + fmt.Println(message) + } +} + +func printf(format string, a ...interface{}) { + fmt.Printf(format, a...) +} + +func println(message string) { + fmt.Println(message) +} diff --git a/tools/moduler/cmd/root.go b/tools/moduler/cmd/root.go new file mode 100644 index 000000000000..109e0e5b6152 --- /dev/null +++ b/tools/moduler/cmd/root.go @@ -0,0 +1,203 @@ +// Copyright 2018 Microsoft Corporation +// +// 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 cmd + +import ( + "bufio" + "errors" + "fmt" + "os" + "os/exec" + "path/filepath" + "strings" + + "github.com/Azure/azure-sdk-for-go/tools/apidiff/repo" + "github.com/spf13/cobra" +) + +const ( + tagPrefix = "// tag: " + versionFile = "version.go" +) + +var rootCmd = &cobra.Command{ + Use: "moduler ", + Short: "Release a new module by pushing new tags to github", + Long: `This tool search the whole SDK folder for tags in version.go files +which are produced by the versioner tool, and push the new tags to github.`, + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + return theCommand(args) + }, +} + +var ( + getTagsHook func(string) ([]string, error) + dryRunFlag bool + verboseFlag bool +) + +func init() { + getTagsHook = getTags + rootCmd.PersistentFlags().BoolVarP(&dryRunFlag, "dry-run", "d", false, "dry run, only list the detected tags, do not add or push them") + rootCmd.PersistentFlags().BoolVarP(&verboseFlag, "verbose", "v", false, "verbose output") +} + +// Execute the tool +func Execute() { + if err := rootCmd.Execute(); err != nil { + os.Exit(1) + } +} + +func theCommand(args []string) error { + root, err := filepath.Abs(args[0]) + if err != nil { + return fmt.Errorf("failed to get absolute root: %v", err) + } + newTags, err := readNewTags(root) + if err != nil { + return err + } + println("Found new tags:") + println(strings.Join(newTags, "\n")) + if !dryRunFlag { + // push new tags + if err := pushNewTags(newTags); err != nil { + return fmt.Errorf("failed to push tags: %v", err) + } + } + return nil +} + +func readNewTags(root string) ([]string, error) { + // get list of all existing tags + tags, err := getTagsHook(root) + if err != nil { + return nil, fmt.Errorf("failed to list tags: %v", err) + } + printf("Get %d tags from remote\n", len(tags)) + vprintln(strings.Join(tags, "\n")) + // find all version.go files + files, err := findAllFiles(root, versionFile) + if err != nil { + return nil, fmt.Errorf("failed to find all version.go files: %v", err) + } + // read new tags in version.go + newTags, err := readTags(tags, files) + if err != nil { + return nil, fmt.Errorf("failed during reading new tags: %v", err) + } + return newTags, nil +} + +func getTags(repoPath string) ([]string, error) { + wt, err := repo.Get(repoPath) + if err != nil { + return nil, err + } + return wt.ListTags("*") +} + +func tagExists(tags []string, tag string) bool { + for _, item := range tags { + if item == tag { + return true + } + } + return false +} + +func findAllFiles(root, filename string) ([]string, error) { + // check if root exists + if _, err := os.Stat(root); os.IsNotExist(err) { + return nil, fmt.Errorf("the root path '%s' does not exist", root) + } + files := make([]string, 0) + err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error { + if err == nil && !info.IsDir() && info.Name() == filename { + files = append(files, path) + return nil + } + return nil + }) + return files, err +} + +func readTags(tags []string, files []string) ([]string, error) { + var newTags []string + for _, file := range files { + newTag, err := readNewTagInFile(file) + if err != nil { + return nil, fmt.Errorf("failed to read tag in file '%s': %v", file, err) + } + if newTag == "" { + printf("Found empty tag in file '%s'\n", file) + continue + } else if tagExists(tags, newTag) { + vprintf("Found existed tag '%s' in file '%s'\n", newTag, file) + continue + } + // add new tag to list + tags = append(tags, newTag) + newTags = append(newTags, newTag) + } + return newTags, nil +} + +func readNewTagInFile(path string) (string, error) { + file, err := os.OpenFile(path, os.O_RDWR, 0666) + defer file.Close() + if err != nil { + return "", err + } + scanner := bufio.NewScanner(file) + scanner.Split(bufio.ScanLines) + tag := "" + for scanner.Scan() { + line := scanner.Text() + if strings.HasPrefix(line, tagPrefix) { + tag = strings.ReplaceAll(line, tagPrefix, "") + break + } + } + return tag, nil +} + +func addNewTag(newTag string) error { + vprintf("Adding new tag '%s'\n", newTag) + if err := executeCommand("git", "tag", "-a", newTag, "-m", newTag); err != nil { + return err + } + return nil +} + +func pushNewTags(newTags []string) error { + vprintln("Pushing new tags") + for _, tag := range newTags { + if err := addNewTag(tag); err != nil { + return err + } + } + return executeCommand("git", "push", "origin", "--tags") +} + +func executeCommand(name string, args ...string) error { + cmd := exec.Command(name, args...) + if output, err := cmd.CombinedOutput(); err != nil { + return errors.New(string(output)) + } + return nil +} diff --git a/tools/moduler/cmd/root_test.go b/tools/moduler/cmd/root_test.go new file mode 100644 index 000000000000..84deddae4398 --- /dev/null +++ b/tools/moduler/cmd/root_test.go @@ -0,0 +1,188 @@ +// Copyright 2018 Microsoft Corporation +// +// 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 cmd + +import ( + "fmt" + "os/exec" + "path/filepath" + "strings" + "testing" + + "github.com/Azure/azure-sdk-for-go/tools/versioner/cmd" +) + +var ( + tagsFullList = []string{ + "tools/testdata/scenarioa/foo/v1.0.0", + "tools/testdata/scenariob/foo/v1.0.0", + "tools/testdata/scenariob/foo/v1.1.0", + "tools/testdata/scenarioc/foo/v1.0.0", + "tools/testdata/scenariod/foo/v1.0.0", + "tools/testdata/scenariod/foo/v1.0.1", + "tools/testdata/scenariod/foo/v1.1.0", + "tools/testdata/scenariod/foo/v1.2.0", + "tools/testdata/scenariod/foo/v2.0.0", + "tools/testdata/scenariod/foo/v2.1.0", + "tools/testdata/scenariod/foo/v2.1.1", + "tools/testdata/scenarioe/foo/v1.0.0", + "tools/testdata/scenarioe/foo/v1.1.0", + "tools/testdata/scenarioe/foo/v2.0.0", + "tools/testdata/scenarioe/foo/v2.1.0", + } +) + +func prepareTestData(root string, tagsHook cmd.TagsHookFunc) error { + if _, err := cmd.ExecuteVersioner(root, tagsHook); err != nil { + return fmt.Errorf("failed to prepare test data: %+v", err) + } + return nil +} + +func cleanTestData() { + c := exec.Command("git", "clean", "-xdf", "../../testdata") + if output, err := c.CombinedOutput(); err != nil { + panic(string(output)) + } + c = exec.Command("git", "checkout", "--", "../../testdata") + if output, err := c.CombinedOutput(); err != nil { + panic(string(output)) + } +} + +func Test_findAllVersionFiles(t *testing.T) { + cleanTestData() + if err := prepareTestData("../../testdata", func(repoPath string, tagPrefix string) ([]string, error) { + return tagsFullList, nil + }); err != nil { + t.Fatalf("failed to prepare test data: %+v", err) + } + defer cleanTestData() + expected := []string{ + "../../testdata/scenarioa/foo/version.go", + "../../testdata/scenariob/foo/version.go", + "../../testdata/scenariob/foo/v2/version.go", + "../../testdata/scenarioc/foo/version.go", + "../../testdata/scenariod/foo/version.go", + "../../testdata/scenariod/foo/v2/version.go", + "../../testdata/scenariod/foo/v3/version.go", + "../../testdata/scenarioe/foo/version.go", + "../../testdata/scenarioe/foo/v2/version.go", + "../../testdata/scenariof/foo/version.go", + "../../testdata/scenariog/foo/mgmt/2019-10-11/foo/version.go", + "../../testdata/scenariog/foo/mgmt/2019-10-11/foo/v2/version.go", + "../../testdata/scenarioh/foo/mgmt/2019-10-11/foo/version.go", + "../../testdata/scenarioh/foo/mgmt/2019-10-11/foo/v2/version.go", + } + root, err := filepath.Abs("../../testdata") + if err != nil { + t.Fatalf("failed to get absolute path of root: %+v", err) + } + files, err := findAllFiles(root, "version.go") + if len(expected) != len(files) { + t.Fatalf("expected %d version files, but got %d", len(expected), len(files)) + } + fileSet, err := makeSet(files) + if err != nil { + t.Fatalf("failed to make file set of version files: %+v", err) + } + for _, e := range expected { + absE, err := filepath.Abs(e) + if err != nil { + t.Fatalf("failed to get absolute path of expected file '%s': %+v", e, err) + } + if _, ok := fileSet[absE]; !ok { + t.Fatalf("expected folder '%s' not found", absE) + } + } +} + +func makeSet(array []string) (map[string]bool, error) { + result := make(map[string]bool, len(array)) + for _, item := range array { + if _, ok := result[item]; ok { + return nil, fmt.Errorf("failed to make set, contains duplicate items '%s'", item) + } + result[item] = true + } + return result, nil +} + +func Test_readNewTagInFile(t *testing.T) { + cleanTestData() + if err := prepareTestData("../../testdata/scenarioa", func(repoPath string, tagPrefix string) ([]string, error) { + return []string{ + "tools/testdata/scenarioa/foo/v1.0.0", + }, nil + }); err != nil { + t.Fatalf("failed to prepare test data: %+v", err) + } + defer cleanTestData() + expected := "tools/testdata/scenarioa/foo/v1.1.0" + p, err := filepath.Abs("../../testdata/scenarioa/foo/version.go") + if err != nil { + t.Fatalf("failed to get absolute path of version file: %+v", err) + } + tag, err := readNewTagInFile(p) + if !strings.EqualFold(expected, tag) { + t.Fatalf("expected tag '%s', but got '%s'", expected, tag) + } +} + +func Test_readNewTags(t *testing.T) { + cleanTestData() + if err := prepareTestData("../../testdata", func(repoPath string, tagPrefix string) ([]string, error) { + tags := make([]string, 0) + for _, tag := range tagsFullList { + if strings.HasPrefix(tag, tagPrefix) { + tags = append(tags, tag) + } + } + return tags, nil + }); err != nil { + t.Fatalf("failed to prepare test data: %+v", err) + } + defer cleanTestData() + expected := []string{ + "tools/testdata/scenarioa/foo/v1.1.0", + "tools/testdata/scenariob/foo/v2.0.0", + "tools/testdata/scenarioc/foo/v1.0.1", + "tools/testdata/scenariod/foo/v3.0.0", + "tools/testdata/scenarioe/foo/v2.2.0", + "tools/testdata/scenariof/foo/v1.0.0", + "tools/testdata/scenariog/foo/mgmt/2019-10-11/foo/v2.0.0", + "tools/testdata/scenarioh/foo/mgmt/2019-10-11/foo/v2.0.0", + } + root, err := filepath.Abs("../../testdata") + if err != nil { + t.Fatalf("failed to get absolute path of root: %+v", err) + } + newTags, err := readNewTags(root) + if err != nil { + t.Fatalf("failed to read new tags: %+v", err) + } + if len(expected) != len(newTags) { + t.Fatalf("expected %d new tags, but got %d", len(expected), len(newTags)) + } + tagSet, err := makeSet(newTags) + if err != nil { + t.Fatalf("failed to make file set of tags: %+v", err) + } + for _, tag := range expected { + if _, ok := tagSet[tag]; !ok { + t.Fatalf("expected tag '%s' not found", tag) + } + } +} diff --git a/tools/moduler/main.go b/tools/moduler/main.go new file mode 100644 index 000000000000..60f443b818f5 --- /dev/null +++ b/tools/moduler/main.go @@ -0,0 +1,21 @@ +// Copyright 2018 Microsoft Corporation +// +// 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 "github.com/Azure/azure-sdk-for-go/tools/moduler/cmd" + +func main() { + cmd.Execute() +} diff --git a/tools/pkgchk/cmd/root.go b/tools/pkgchk/cmd/root.go index 5d74563f35ce..30d52f08d8ee 100644 --- a/tools/pkgchk/cmd/root.go +++ b/tools/pkgchk/cmd/root.go @@ -17,16 +17,14 @@ package cmd import ( "bufio" "fmt" - "go/ast" - "go/parser" - "go/token" - "io/ioutil" "os" "path/filepath" "regexp" "strings" "unicode" + "github.com/Azure/azure-sdk-for-go/tools/internal/pkgs" + "github.com/pkg/errors" "github.com/spf13/cobra" ) @@ -64,7 +62,7 @@ func theCommand(args []string) error { if err != nil { return errors.Wrap(err, "failed to get absolute path") } - pkgs, err := getPkgs(rootDir) + ps, err := pkgs.GetPkgs(rootDir) if err != nil { return errors.Wrap(err, "failed to get packages") } @@ -74,7 +72,7 @@ func theCommand(args []string) error { } verifiers := getVerifiers() count := 0 - for _, pkg := range pkgs { + for _, pkg := range ps { for _, v := range verifiers { if err = v(pkg); err != nil && !contains(exceptions, err.Error()) { fmt.Fprintln(os.Stderr, err) @@ -122,79 +120,7 @@ func loadExceptions(exceptFile string) ([]string, error) { return exceps, nil } -type pkg struct { - // the directory where the package resides relative to the root dir - d string - - // the AST of the package - p *ast.Package -} - -// returns true if the package directory corresponds to an ARM package -func (p pkg) isARMPkg() bool { - return strings.Index(p.d, "/mgmt/") > -1 -} - -// walks the directory hierarchy from the specified root returning a slice of all the packages found -func getPkgs(rootDir string) ([]pkg, error) { - pkgs := make([]pkg, 0) - err := filepath.Walk(rootDir, func(path string, info os.FileInfo, err error) error { - if err != nil { - return err - } - if info.IsDir() { - // check if leaf dir - fi, err := ioutil.ReadDir(path) - if err != nil { - return err - } - hasSubDirs := false - interfacesDir := false - for _, f := range fi { - if f.IsDir() { - hasSubDirs = true - break - } - if f.Name() == "interfaces.go" { - interfacesDir = true - } - } - if !hasSubDirs { - fs := token.NewFileSet() - // with interfaces codegen the majority of leaf directories are now the - // *api packages. when this is the case parse from the parent directory. - if interfacesDir { - path = filepath.Dir(path) - } - packages, err := parser.ParseDir(fs, path, func(fi os.FileInfo) bool { - return fi.Name() != "interfaces.go" - }, parser.PackageClauseOnly) - if err != nil { - return err - } - if len(packages) < 1 { - return errors.New("didn't find any packages which is unexpected") - } - if len(packages) > 1 { - return errors.New("found more than one package which is unexpected") - } - var p *ast.Package - for _, pkgs := range packages { - p = pkgs - } - // normalize directory separator to '/' character - pkgs = append(pkgs, pkg{ - d: strings.Replace(path[len(rootDir):], "\\", "/", -1), - p: p, - }) - } - } - return nil - }) - return pkgs, err -} - -type verifier func(p pkg) error +type verifier func(p pkgs.Pkg) error // returns a list of verifiers to execute func getVerifiers() []verifier { @@ -207,10 +133,10 @@ func getVerifiers() []verifier { // ensures that the leaf directory name matches the package name // new to modules: if the last leaf is version suffix, find its parent as leaf folder -func verifyPkgMatchesDir(p pkg) error { - leaf := findPackageFolderInPath(p.d) - if !strings.EqualFold(leaf, p.p.Name) { - return fmt.Errorf("leaf directory of '%s' doesn't match package name '%s'", p.d, p.p.Name) +func verifyPkgMatchesDir(p pkgs.Pkg) error { + leaf := findPackageFolderInPath(p.Dest) + if !strings.EqualFold(leaf, p.Package.Name) { + return fmt.Errorf("leaf directory of '%s' doesn't match package name '%s'", p.Dest, p.Package.Name) } return nil } @@ -226,28 +152,28 @@ func findPackageFolderInPath(path string) string { } // ensures that there are no upper-case letters in a package's directory -func verifyLowerCase(p pkg) error { +func verifyLowerCase(p pkgs.Pkg) error { // walk the package directory looking for upper-case characters - for _, r := range p.d { + for _, r := range p.Dest { if r == '/' { continue } if unicode.IsUpper(r) { - return fmt.Errorf("found upper-case character in directory '%s'", p.d) + return fmt.Errorf("found upper-case character in directory '%s'", p.Dest) } } return nil } // ensures that the package's directory hierarchy is properly formed -func verifyDirectoryStructure(p pkg) error { +func verifyDirectoryStructure(p pkgs.Pkg) error { // for ARM the package directory structure is highly deterministic: // /redis/mgmt/2015-08-01/redis // /resources/mgmt/2017-06-01-preview/policy // /preview/signalr/mgmt/2018-03-01-preview/signalr // /preview/security/mgmt/v2.0/security (version scheme for composite packages) // /network/mgmt/2019-10-01/network/v2 (new with modules) - if !p.isARMPkg() { + if !p.IsARMPkg() { return nil } regexStr := strings.Join([]string{ @@ -259,8 +185,8 @@ func verifyDirectoryStructure(p pkg) error { }, "/") regexStr = regexStr + `(/v\d+)?$` regex := regexp.MustCompile(regexStr) - if !regex.MatchString(p.d) { - return fmt.Errorf("bad directory structure '%s'", p.d) + if !regex.MatchString(p.Dest) { + return fmt.Errorf("bad directory structure '%s'", p.Dest) } return nil } diff --git a/tools/pkgchk/cmd/root_test.go b/tools/pkgchk/cmd/root_test.go index 6dc4eacb5383..3c34e9f24b85 100644 --- a/tools/pkgchk/cmd/root_test.go +++ b/tools/pkgchk/cmd/root_test.go @@ -16,59 +16,25 @@ package cmd import ( "path/filepath" - "strings" "testing" + + "github.com/Azure/azure-sdk-for-go/tools/internal/pkgs" ) const ( testRoot = "../../testpkgs" ) -var ( - expected = map[string]string{ - "/scenrioa/foo": "foo", - "/scenriob/foo": "foo", - "/scenriob/foo/v2": "foo", - "/scenrioc/mgmt/2019-10-11/foo": "foo", - "/scenriod/mgmt/2019-10-11/foo": "foo", - "/scenriod/mgmt/2019-10-11/foo/v2": "foo", - "/scenrioe/mgmt/2019-10-11/foo": "foo", - "/scenrioe/mgmt/2019-10-11/foo/v2": "foo", - "/scenrioe/mgmt/2019-10-11/foo/v3": "foo", - } -) - -func Test_getPkgs(t *testing.T) { - rootDir, err := filepath.Abs(testRoot) - if err != nil { - t.Fatalf("failed to get absolute path: %+v", err) - } - pkgs, err := getPkgs(rootDir) - if err != nil { - t.Fatalf("failed to get packages: %+v", err) - } - if len(pkgs) != len(expected) { - t.Fatalf("expected %d packages, but got %d", len(expected), len(pkgs)) - } - for _, pkg := range pkgs { - if pkgName, ok := expected[pkg.d]; !ok { - t.Fatalf("got pkg path '%s', but not found in expected", pkg.d) - } else if !strings.EqualFold(pkgName, pkg.p.Name) { - t.Fatalf("expected package of '%s' in path '%s', but got '%s'", pkgName, pkg.d, pkg.p.Name) - } - } -} - func Test_verifyDirectoryStructure(t *testing.T) { rootDir, err := filepath.Abs(testRoot) if err != nil { t.Fatalf("failed to get absolute path: %+v", err) } - pkgs, err := getPkgs(rootDir) + ps, err := pkgs.GetPkgs(rootDir) if err != nil { t.Fatalf("failed to get packages: %+v", err) } - for _, pkg := range pkgs { + for _, pkg := range ps { if err := verifyDirectoryStructure(pkg); err != nil { t.Fatalf("failed to verify directory structure: %+v", err) } @@ -80,11 +46,11 @@ func Test_verifyPkgMatchesDir(t *testing.T) { if err != nil { t.Fatalf("failed to get absolute path: %+v", err) } - pkgs, err := getPkgs(rootDir) + ps, err := pkgs.GetPkgs(rootDir) if err != nil { t.Fatalf("failed to get packages: %+v", err) } - for _, pkg := range pkgs { + for _, pkg := range ps { if err := verifyPkgMatchesDir(pkg); err != nil { t.Fatalf("failed to verify package directory name: %+v", err) } diff --git a/tools/testdata/README.md b/tools/testdata/README.md new file mode 100644 index 000000000000..7f2ecab23088 --- /dev/null +++ b/tools/testdata/README.md @@ -0,0 +1,11 @@ +# Test cases for versioner tool + +| Test case | Additional changes | Breaking changes | Existing versions | go.mod file existence | +| :--- | :--- | :--- | :--- | :--- | +| a | Yes | No | v1 | Yes | +| b | Yes | Yes | v1 | Yes | +| c | No | No | v1 | Yes | +| d | Yes | Yes | v1, v2 | Yes | +| e | Yes | No | v1, v2 | Yes | +| f | Yes | No | None | Yes | +| g | Yes | Yes | v1 | Yes | \ No newline at end of file diff --git a/tools/testdata/scenarioa/foo/stage/version.go b/tools/testdata/scenarioa/foo/stage/version.go new file mode 100644 index 000000000000..b729a3cd2f00 --- /dev/null +++ b/tools/testdata/scenarioa/foo/stage/version.go @@ -0,0 +1,28 @@ +package foo + +// Copyright (c) Microsoft and contributors. All rights reserved. +// +// 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. +// +// Code generated by Microsoft (R) AutoRest Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +// UserAgent returns the UserAgent string to use when sending http.Requests. +func UserAgent() string { + return "Azure-SDK-For-Go/0.0.0 foo/2019-04-01" +} + +// Version returns the semantic version (see http://semver.org) of the client. +func Version() string { + return "0.0.0" +} diff --git a/tools/testdata/scenarioa/foo/version.go b/tools/testdata/scenarioa/foo/version.go new file mode 100644 index 000000000000..a2201e008c96 --- /dev/null +++ b/tools/testdata/scenarioa/foo/version.go @@ -0,0 +1,28 @@ +package foo + +// Copyright (c) Microsoft and contributors. All rights reserved. +// +// 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. +// +// Code generated by Microsoft (R) AutoRest Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +// UserAgent returns the UserAgent string to use when sending http.Requests. +func UserAgent() string { + return "Azure-SDK-For-Go/1.0.0 foo/2019-04-01" +} + +// Version returns the semantic version (see http://semver.org) of the client. +func Version() string { + return "1.0.0" +} diff --git a/tools/testdata/scenariob/foo/stage/version.go b/tools/testdata/scenariob/foo/stage/version.go new file mode 100644 index 000000000000..b729a3cd2f00 --- /dev/null +++ b/tools/testdata/scenariob/foo/stage/version.go @@ -0,0 +1,28 @@ +package foo + +// Copyright (c) Microsoft and contributors. All rights reserved. +// +// 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. +// +// Code generated by Microsoft (R) AutoRest Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +// UserAgent returns the UserAgent string to use when sending http.Requests. +func UserAgent() string { + return "Azure-SDK-For-Go/0.0.0 foo/2019-04-01" +} + +// Version returns the semantic version (see http://semver.org) of the client. +func Version() string { + return "0.0.0" +} diff --git a/tools/testdata/scenariob/foo/version.go b/tools/testdata/scenariob/foo/version.go new file mode 100644 index 000000000000..a2201e008c96 --- /dev/null +++ b/tools/testdata/scenariob/foo/version.go @@ -0,0 +1,28 @@ +package foo + +// Copyright (c) Microsoft and contributors. All rights reserved. +// +// 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. +// +// Code generated by Microsoft (R) AutoRest Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +// UserAgent returns the UserAgent string to use when sending http.Requests. +func UserAgent() string { + return "Azure-SDK-For-Go/1.0.0 foo/2019-04-01" +} + +// Version returns the semantic version (see http://semver.org) of the client. +func Version() string { + return "1.0.0" +} diff --git a/tools/testdata/scenarioc/foo/stage/version.go b/tools/testdata/scenarioc/foo/stage/version.go new file mode 100644 index 000000000000..b729a3cd2f00 --- /dev/null +++ b/tools/testdata/scenarioc/foo/stage/version.go @@ -0,0 +1,28 @@ +package foo + +// Copyright (c) Microsoft and contributors. All rights reserved. +// +// 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. +// +// Code generated by Microsoft (R) AutoRest Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +// UserAgent returns the UserAgent string to use when sending http.Requests. +func UserAgent() string { + return "Azure-SDK-For-Go/0.0.0 foo/2019-04-01" +} + +// Version returns the semantic version (see http://semver.org) of the client. +func Version() string { + return "0.0.0" +} diff --git a/tools/testdata/scenarioc/foo/version.go b/tools/testdata/scenarioc/foo/version.go new file mode 100644 index 000000000000..b729a3cd2f00 --- /dev/null +++ b/tools/testdata/scenarioc/foo/version.go @@ -0,0 +1,28 @@ +package foo + +// Copyright (c) Microsoft and contributors. All rights reserved. +// +// 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. +// +// Code generated by Microsoft (R) AutoRest Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +// UserAgent returns the UserAgent string to use when sending http.Requests. +func UserAgent() string { + return "Azure-SDK-For-Go/0.0.0 foo/2019-04-01" +} + +// Version returns the semantic version (see http://semver.org) of the client. +func Version() string { + return "0.0.0" +} diff --git a/tools/testdata/scenariod/foo/stage/version.go b/tools/testdata/scenariod/foo/stage/version.go new file mode 100644 index 000000000000..b729a3cd2f00 --- /dev/null +++ b/tools/testdata/scenariod/foo/stage/version.go @@ -0,0 +1,28 @@ +package foo + +// Copyright (c) Microsoft and contributors. All rights reserved. +// +// 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. +// +// Code generated by Microsoft (R) AutoRest Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +// UserAgent returns the UserAgent string to use when sending http.Requests. +func UserAgent() string { + return "Azure-SDK-For-Go/0.0.0 foo/2019-04-01" +} + +// Version returns the semantic version (see http://semver.org) of the client. +func Version() string { + return "0.0.0" +} diff --git a/tools/testdata/scenariod/foo/v2/version.go b/tools/testdata/scenariod/foo/v2/version.go new file mode 100644 index 000000000000..5fccf0de3cb4 --- /dev/null +++ b/tools/testdata/scenariod/foo/v2/version.go @@ -0,0 +1,28 @@ +package foo + +// Copyright (c) Microsoft and contributors. All rights reserved. +// +// 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. +// +// Code generated by Microsoft (R) AutoRest Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +// UserAgent returns the UserAgent string to use when sending http.Requests. +func UserAgent() string { + return "Azure-SDK-For-Go/2.0.0 foo/2019-04-01" +} + +// Version returns the semantic version (see http://semver.org) of the client. +func Version() string { + return "2.0.0" +} diff --git a/tools/testdata/scenariod/foo/version.go b/tools/testdata/scenariod/foo/version.go new file mode 100644 index 000000000000..a2201e008c96 --- /dev/null +++ b/tools/testdata/scenariod/foo/version.go @@ -0,0 +1,28 @@ +package foo + +// Copyright (c) Microsoft and contributors. All rights reserved. +// +// 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. +// +// Code generated by Microsoft (R) AutoRest Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +// UserAgent returns the UserAgent string to use when sending http.Requests. +func UserAgent() string { + return "Azure-SDK-For-Go/1.0.0 foo/2019-04-01" +} + +// Version returns the semantic version (see http://semver.org) of the client. +func Version() string { + return "1.0.0" +} diff --git a/tools/testdata/scenarioe/foo/stage/version.go b/tools/testdata/scenarioe/foo/stage/version.go new file mode 100644 index 000000000000..b729a3cd2f00 --- /dev/null +++ b/tools/testdata/scenarioe/foo/stage/version.go @@ -0,0 +1,28 @@ +package foo + +// Copyright (c) Microsoft and contributors. All rights reserved. +// +// 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. +// +// Code generated by Microsoft (R) AutoRest Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +// UserAgent returns the UserAgent string to use when sending http.Requests. +func UserAgent() string { + return "Azure-SDK-For-Go/0.0.0 foo/2019-04-01" +} + +// Version returns the semantic version (see http://semver.org) of the client. +func Version() string { + return "0.0.0" +} diff --git a/tools/testdata/scenarioe/foo/v2/version.go b/tools/testdata/scenarioe/foo/v2/version.go new file mode 100644 index 000000000000..5fccf0de3cb4 --- /dev/null +++ b/tools/testdata/scenarioe/foo/v2/version.go @@ -0,0 +1,28 @@ +package foo + +// Copyright (c) Microsoft and contributors. All rights reserved. +// +// 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. +// +// Code generated by Microsoft (R) AutoRest Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +// UserAgent returns the UserAgent string to use when sending http.Requests. +func UserAgent() string { + return "Azure-SDK-For-Go/2.0.0 foo/2019-04-01" +} + +// Version returns the semantic version (see http://semver.org) of the client. +func Version() string { + return "2.0.0" +} diff --git a/tools/testdata/scenarioe/foo/version.go b/tools/testdata/scenarioe/foo/version.go new file mode 100644 index 000000000000..a2201e008c96 --- /dev/null +++ b/tools/testdata/scenarioe/foo/version.go @@ -0,0 +1,28 @@ +package foo + +// Copyright (c) Microsoft and contributors. All rights reserved. +// +// 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. +// +// Code generated by Microsoft (R) AutoRest Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +// UserAgent returns the UserAgent string to use when sending http.Requests. +func UserAgent() string { + return "Azure-SDK-For-Go/1.0.0 foo/2019-04-01" +} + +// Version returns the semantic version (see http://semver.org) of the client. +func Version() string { + return "1.0.0" +} diff --git a/tools/testdata/scenariof/foo/stage/version.go b/tools/testdata/scenariof/foo/stage/version.go new file mode 100644 index 000000000000..b729a3cd2f00 --- /dev/null +++ b/tools/testdata/scenariof/foo/stage/version.go @@ -0,0 +1,28 @@ +package foo + +// Copyright (c) Microsoft and contributors. All rights reserved. +// +// 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. +// +// Code generated by Microsoft (R) AutoRest Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +// UserAgent returns the UserAgent string to use when sending http.Requests. +func UserAgent() string { + return "Azure-SDK-For-Go/0.0.0 foo/2019-04-01" +} + +// Version returns the semantic version (see http://semver.org) of the client. +func Version() string { + return "0.0.0" +} diff --git a/tools/testdata/scenariog/foo/mgmt/2019-10-11/foo/client.go b/tools/testdata/scenariog/foo/mgmt/2019-10-11/foo/client.go new file mode 100644 index 000000000000..6d99a4f8d6e2 --- /dev/null +++ b/tools/testdata/scenariog/foo/mgmt/2019-10-11/foo/client.go @@ -0,0 +1,43 @@ +// Copyright 2018 Microsoft Corporation +// +// 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 foo + +import "github.com/Azure/go-autorest/autorest" + +const ( + // DefaultBaseURI is the default URI used for the service Network + DefaultBaseURI = "https://management.azure.com" +) + +// BaseClient is the base client for Network. +type BaseClient struct { + autorest.Client + BaseURI string + SubscriptionID string +} + +// New creates an instance of the BaseClient client. +func New(subscriptionID string) BaseClient { + return NewWithBaseURI(DefaultBaseURI, subscriptionID) +} + +// NewWithBaseURI creates an instance of the BaseClient client. +func NewWithBaseURI(baseURI string, subscriptionID string) BaseClient { + return BaseClient{ + Client: autorest.NewClientWithUserAgent(UserAgent()), + BaseURI: baseURI, + SubscriptionID: subscriptionID, + } +} diff --git a/tools/testdata/scenariog/foo/mgmt/2019-10-11/foo/fooapi/interfaces.go b/tools/testdata/scenariog/foo/mgmt/2019-10-11/foo/fooapi/interfaces.go new file mode 100644 index 000000000000..9e78816309ed --- /dev/null +++ b/tools/testdata/scenariog/foo/mgmt/2019-10-11/foo/fooapi/interfaces.go @@ -0,0 +1,22 @@ +// Copyright 2018 Microsoft Corporation +// +// 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 fooapi + +import "github.com/Azure/azure-sdk-for-go/tools/testdata/scenariog/foo/mgmt/2019-10-11/foo" + +// GatewaysClientAPI ... +type GatewaysClientAPI interface { + CreateOrUpdate(resGroup string, parameters foo.Gateway) error +} diff --git a/tools/testdata/scenariog/foo/mgmt/2019-10-11/foo/gateway.go b/tools/testdata/scenariog/foo/mgmt/2019-10-11/foo/gateway.go new file mode 100644 index 000000000000..af4110e621ed --- /dev/null +++ b/tools/testdata/scenariog/foo/mgmt/2019-10-11/foo/gateway.go @@ -0,0 +1,34 @@ +// Copyright 2018 Microsoft Corporation +// +// 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 foo + +// NatGatewaysClient is the network Client +type GatewaysClient struct { + BaseClient +} + +// NewGatewaysClient creates an instance of the NatGatewaysClient client. +func NewGatewaysClient(subscriptionID string) GatewaysClient { + return NewGatewaysClientWithBaseURI(DefaultBaseURI, subscriptionID) +} + +// NewGatewaysClientWithBaseURI creates an instance of the NatGatewaysClient client. +func NewGatewaysClientWithBaseURI(baseURI string, subscriptionID string) GatewaysClient { + return GatewaysClient{NewWithBaseURI(baseURI, subscriptionID)} +} + +func (client GatewaysClient) CreateOrUpdate(resGroup string, parameters Gateway) error { + return nil +} diff --git a/tools/testdata/scenariog/foo/mgmt/2019-10-11/foo/go.mod b/tools/testdata/scenariog/foo/mgmt/2019-10-11/foo/go.mod new file mode 100644 index 000000000000..9ded283f48b3 --- /dev/null +++ b/tools/testdata/scenariog/foo/mgmt/2019-10-11/foo/go.mod @@ -0,0 +1,3 @@ +module github.com/Azure/azure-sdk-for-go/tools/testdata/scenariog/foo + +go 1.12 diff --git a/tools/testdata/scenariog/foo/mgmt/2019-10-11/foo/models.go b/tools/testdata/scenariog/foo/mgmt/2019-10-11/foo/models.go new file mode 100644 index 000000000000..e5c0128d31db --- /dev/null +++ b/tools/testdata/scenariog/foo/mgmt/2019-10-11/foo/models.go @@ -0,0 +1,23 @@ +// Copyright 2018 Microsoft Corporation +// +// 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 foo + +// Gateway ... +type Gateway struct { + // Name ... + Name *string + // ID ... + ID *int +} diff --git a/tools/testdata/scenariog/foo/mgmt/2019-10-11/foo/stage/client.go b/tools/testdata/scenariog/foo/mgmt/2019-10-11/foo/stage/client.go new file mode 100644 index 000000000000..6d99a4f8d6e2 --- /dev/null +++ b/tools/testdata/scenariog/foo/mgmt/2019-10-11/foo/stage/client.go @@ -0,0 +1,43 @@ +// Copyright 2018 Microsoft Corporation +// +// 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 foo + +import "github.com/Azure/go-autorest/autorest" + +const ( + // DefaultBaseURI is the default URI used for the service Network + DefaultBaseURI = "https://management.azure.com" +) + +// BaseClient is the base client for Network. +type BaseClient struct { + autorest.Client + BaseURI string + SubscriptionID string +} + +// New creates an instance of the BaseClient client. +func New(subscriptionID string) BaseClient { + return NewWithBaseURI(DefaultBaseURI, subscriptionID) +} + +// NewWithBaseURI creates an instance of the BaseClient client. +func NewWithBaseURI(baseURI string, subscriptionID string) BaseClient { + return BaseClient{ + Client: autorest.NewClientWithUserAgent(UserAgent()), + BaseURI: baseURI, + SubscriptionID: subscriptionID, + } +} diff --git a/tools/testdata/scenariog/foo/mgmt/2019-10-11/foo/stage/fooapi/interfaces.go b/tools/testdata/scenariog/foo/mgmt/2019-10-11/foo/stage/fooapi/interfaces.go new file mode 100644 index 000000000000..5f6ebea05fbf --- /dev/null +++ b/tools/testdata/scenariog/foo/mgmt/2019-10-11/foo/stage/fooapi/interfaces.go @@ -0,0 +1,27 @@ +// Copyright 2018 Microsoft Corporation +// +// 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 fooapi + +import ( + "context" + + "github.com/Azure/azure-sdk-for-go/tools/testdata/scenariog/foo/mgmt/2019-10-11/foo" +) + +// GatewaysClientAPI ... +type GatewaysClientAPI interface { + CreateOrUpdate(ctx context.Context, resGroup string, parameters foo.Gateway) error + DoSomething(ctx context.Context, something foo.Something) error +} diff --git a/tools/testdata/scenariog/foo/mgmt/2019-10-11/foo/stage/gateway.go b/tools/testdata/scenariog/foo/mgmt/2019-10-11/foo/stage/gateway.go new file mode 100644 index 000000000000..9f57a628ff29 --- /dev/null +++ b/tools/testdata/scenariog/foo/mgmt/2019-10-11/foo/stage/gateway.go @@ -0,0 +1,40 @@ +// Copyright 2018 Microsoft Corporation +// +// 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 foo + +import "context" + +// NatGatewaysClient is the network Client +type GatewaysClient struct { + BaseClient +} + +// NewGatewaysClient creates an instance of the NatGatewaysClient client. +func NewGatewaysClient(subscriptionID string) GatewaysClient { + return NewGatewaysClientWithBaseURI(DefaultBaseURI, subscriptionID) +} + +// NewGatewaysClientWithBaseURI creates an instance of the NatGatewaysClient client. +func NewGatewaysClientWithBaseURI(baseURI string, subscriptionID string) GatewaysClient { + return GatewaysClient{NewWithBaseURI(baseURI, subscriptionID)} +} + +func (client *GatewaysClient) CreateOrUpdate(ctx context.Context, resGroup string, parameters Gateway) error { + return nil +} + +func (client *GatewaysClient) DoSomething(ctx context.Context, something Something) error { + return nil +} diff --git a/tools/testdata/scenariog/foo/mgmt/2019-10-11/foo/stage/go.mod b/tools/testdata/scenariog/foo/mgmt/2019-10-11/foo/stage/go.mod new file mode 100644 index 000000000000..9ded283f48b3 --- /dev/null +++ b/tools/testdata/scenariog/foo/mgmt/2019-10-11/foo/stage/go.mod @@ -0,0 +1,3 @@ +module github.com/Azure/azure-sdk-for-go/tools/testdata/scenariog/foo + +go 1.12 diff --git a/tools/testdata/scenariog/foo/mgmt/2019-10-11/foo/stage/models.go b/tools/testdata/scenariog/foo/mgmt/2019-10-11/foo/stage/models.go new file mode 100644 index 000000000000..dc04ac4af46e --- /dev/null +++ b/tools/testdata/scenariog/foo/mgmt/2019-10-11/foo/stage/models.go @@ -0,0 +1,29 @@ +// Copyright 2018 Microsoft Corporation +// +// 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 foo + +// Gateway ... +type Gateway struct { + // Name ... + Name *string + // ID ... + ID *int +} + +// Something ... +type Something struct { + // Name ... + Name *string +} diff --git a/tools/testdata/scenariog/foo/mgmt/2019-10-11/foo/stage/version.go b/tools/testdata/scenariog/foo/mgmt/2019-10-11/foo/stage/version.go new file mode 100644 index 000000000000..b729a3cd2f00 --- /dev/null +++ b/tools/testdata/scenariog/foo/mgmt/2019-10-11/foo/stage/version.go @@ -0,0 +1,28 @@ +package foo + +// Copyright (c) Microsoft and contributors. All rights reserved. +// +// 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. +// +// Code generated by Microsoft (R) AutoRest Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +// UserAgent returns the UserAgent string to use when sending http.Requests. +func UserAgent() string { + return "Azure-SDK-For-Go/0.0.0 foo/2019-04-01" +} + +// Version returns the semantic version (see http://semver.org) of the client. +func Version() string { + return "0.0.0" +} diff --git a/tools/testdata/scenariog/foo/mgmt/2019-10-11/foo/version.go b/tools/testdata/scenariog/foo/mgmt/2019-10-11/foo/version.go new file mode 100644 index 000000000000..a2201e008c96 --- /dev/null +++ b/tools/testdata/scenariog/foo/mgmt/2019-10-11/foo/version.go @@ -0,0 +1,28 @@ +package foo + +// Copyright (c) Microsoft and contributors. All rights reserved. +// +// 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. +// +// Code generated by Microsoft (R) AutoRest Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +// UserAgent returns the UserAgent string to use when sending http.Requests. +func UserAgent() string { + return "Azure-SDK-For-Go/1.0.0 foo/2019-04-01" +} + +// Version returns the semantic version (see http://semver.org) of the client. +func Version() string { + return "1.0.0" +} diff --git a/tools/testdata/scenarioh/foo/mgmt/2019-10-11/foo/client.go b/tools/testdata/scenarioh/foo/mgmt/2019-10-11/foo/client.go new file mode 100644 index 000000000000..6d99a4f8d6e2 --- /dev/null +++ b/tools/testdata/scenarioh/foo/mgmt/2019-10-11/foo/client.go @@ -0,0 +1,43 @@ +// Copyright 2018 Microsoft Corporation +// +// 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 foo + +import "github.com/Azure/go-autorest/autorest" + +const ( + // DefaultBaseURI is the default URI used for the service Network + DefaultBaseURI = "https://management.azure.com" +) + +// BaseClient is the base client for Network. +type BaseClient struct { + autorest.Client + BaseURI string + SubscriptionID string +} + +// New creates an instance of the BaseClient client. +func New(subscriptionID string) BaseClient { + return NewWithBaseURI(DefaultBaseURI, subscriptionID) +} + +// NewWithBaseURI creates an instance of the BaseClient client. +func NewWithBaseURI(baseURI string, subscriptionID string) BaseClient { + return BaseClient{ + Client: autorest.NewClientWithUserAgent(UserAgent()), + BaseURI: baseURI, + SubscriptionID: subscriptionID, + } +} diff --git a/tools/testdata/scenarioh/foo/mgmt/2019-10-11/foo/fooapi/interfaces.go b/tools/testdata/scenarioh/foo/mgmt/2019-10-11/foo/fooapi/interfaces.go new file mode 100644 index 000000000000..e2f44a20e6d3 --- /dev/null +++ b/tools/testdata/scenarioh/foo/mgmt/2019-10-11/foo/fooapi/interfaces.go @@ -0,0 +1,22 @@ +// Copyright 2018 Microsoft Corporation +// +// 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 fooapi + +import "github.com/Azure/azure-sdk-for-go/tools/testdata/scenarioh/foo/mgmt/2019-10-11/foo" + +// GatewaysClientAPI ... +type GatewaysClientAPI interface { + CreateOrUpdate(resGroup string, parameters foo.Gateway) error +} diff --git a/tools/testdata/scenarioh/foo/mgmt/2019-10-11/foo/gateway.go b/tools/testdata/scenarioh/foo/mgmt/2019-10-11/foo/gateway.go new file mode 100644 index 000000000000..af4110e621ed --- /dev/null +++ b/tools/testdata/scenarioh/foo/mgmt/2019-10-11/foo/gateway.go @@ -0,0 +1,34 @@ +// Copyright 2018 Microsoft Corporation +// +// 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 foo + +// NatGatewaysClient is the network Client +type GatewaysClient struct { + BaseClient +} + +// NewGatewaysClient creates an instance of the NatGatewaysClient client. +func NewGatewaysClient(subscriptionID string) GatewaysClient { + return NewGatewaysClientWithBaseURI(DefaultBaseURI, subscriptionID) +} + +// NewGatewaysClientWithBaseURI creates an instance of the NatGatewaysClient client. +func NewGatewaysClientWithBaseURI(baseURI string, subscriptionID string) GatewaysClient { + return GatewaysClient{NewWithBaseURI(baseURI, subscriptionID)} +} + +func (client GatewaysClient) CreateOrUpdate(resGroup string, parameters Gateway) error { + return nil +} diff --git a/tools/testdata/scenarioh/foo/mgmt/2019-10-11/foo/go.mod b/tools/testdata/scenarioh/foo/mgmt/2019-10-11/foo/go.mod new file mode 100644 index 000000000000..4f5aef0031ea --- /dev/null +++ b/tools/testdata/scenarioh/foo/mgmt/2019-10-11/foo/go.mod @@ -0,0 +1,3 @@ +module github.com/Azure/azure-sdk-for-go/tools/testdata/scenarioh/foo + +go 1.12 diff --git a/tools/testdata/scenarioh/foo/mgmt/2019-10-11/foo/models.go b/tools/testdata/scenarioh/foo/mgmt/2019-10-11/foo/models.go new file mode 100644 index 000000000000..e5c0128d31db --- /dev/null +++ b/tools/testdata/scenarioh/foo/mgmt/2019-10-11/foo/models.go @@ -0,0 +1,23 @@ +// Copyright 2018 Microsoft Corporation +// +// 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 foo + +// Gateway ... +type Gateway struct { + // Name ... + Name *string + // ID ... + ID *int +} diff --git a/tools/testdata/scenarioh/foo/mgmt/2019-10-11/foo/stage/client.go b/tools/testdata/scenarioh/foo/mgmt/2019-10-11/foo/stage/client.go new file mode 100644 index 000000000000..6d99a4f8d6e2 --- /dev/null +++ b/tools/testdata/scenarioh/foo/mgmt/2019-10-11/foo/stage/client.go @@ -0,0 +1,43 @@ +// Copyright 2018 Microsoft Corporation +// +// 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 foo + +import "github.com/Azure/go-autorest/autorest" + +const ( + // DefaultBaseURI is the default URI used for the service Network + DefaultBaseURI = "https://management.azure.com" +) + +// BaseClient is the base client for Network. +type BaseClient struct { + autorest.Client + BaseURI string + SubscriptionID string +} + +// New creates an instance of the BaseClient client. +func New(subscriptionID string) BaseClient { + return NewWithBaseURI(DefaultBaseURI, subscriptionID) +} + +// NewWithBaseURI creates an instance of the BaseClient client. +func NewWithBaseURI(baseURI string, subscriptionID string) BaseClient { + return BaseClient{ + Client: autorest.NewClientWithUserAgent(UserAgent()), + BaseURI: baseURI, + SubscriptionID: subscriptionID, + } +} diff --git a/tools/testdata/scenarioh/foo/mgmt/2019-10-11/foo/stage/fooapi/interfaces.go b/tools/testdata/scenarioh/foo/mgmt/2019-10-11/foo/stage/fooapi/interfaces.go new file mode 100644 index 000000000000..5a6dea35e829 --- /dev/null +++ b/tools/testdata/scenarioh/foo/mgmt/2019-10-11/foo/stage/fooapi/interfaces.go @@ -0,0 +1,23 @@ +// Copyright 2018 Microsoft Corporation +// +// 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 fooapi + +import "github.com/Azure/azure-sdk-for-go/tools/testdata/scenarioh/foo/mgmt/2019-10-11/foo" + +// GatewaysClientAPI ... +type GatewaysClientAPI interface { + CreateOrUpdate(resGroup string, parameters foo.Gateway) error + DoSomething(something foo.Something) error +} diff --git a/tools/testdata/scenarioh/foo/mgmt/2019-10-11/foo/stage/gateway.go b/tools/testdata/scenarioh/foo/mgmt/2019-10-11/foo/stage/gateway.go new file mode 100644 index 000000000000..03b6ccc0a207 --- /dev/null +++ b/tools/testdata/scenarioh/foo/mgmt/2019-10-11/foo/stage/gateway.go @@ -0,0 +1,38 @@ +// Copyright 2018 Microsoft Corporation +// +// 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 foo + +// NatGatewaysClient is the network Client +type GatewaysClient struct { + BaseClient +} + +// NewGatewaysClient creates an instance of the NatGatewaysClient client. +func NewGatewaysClient(subscriptionID string) GatewaysClient { + return NewGatewaysClientWithBaseURI(DefaultBaseURI, subscriptionID) +} + +// NewGatewaysClientWithBaseURI creates an instance of the NatGatewaysClient client. +func NewGatewaysClientWithBaseURI(baseURI string, subscriptionID string) GatewaysClient { + return GatewaysClient{NewWithBaseURI(baseURI, subscriptionID)} +} + +func (client *GatewaysClient) CreateOrUpdate(resGroup string, parameters Gateway) error { + return nil +} + +func (client *GatewaysClient) DoSomething(something Something) error { + return nil +} diff --git a/tools/testdata/scenarioh/foo/mgmt/2019-10-11/foo/stage/go.mod b/tools/testdata/scenarioh/foo/mgmt/2019-10-11/foo/stage/go.mod new file mode 100644 index 000000000000..4f5aef0031ea --- /dev/null +++ b/tools/testdata/scenarioh/foo/mgmt/2019-10-11/foo/stage/go.mod @@ -0,0 +1,3 @@ +module github.com/Azure/azure-sdk-for-go/tools/testdata/scenarioh/foo + +go 1.12 diff --git a/tools/testdata/scenarioh/foo/mgmt/2019-10-11/foo/stage/models.go b/tools/testdata/scenarioh/foo/mgmt/2019-10-11/foo/stage/models.go new file mode 100644 index 000000000000..53fa6b3cec60 --- /dev/null +++ b/tools/testdata/scenarioh/foo/mgmt/2019-10-11/foo/stage/models.go @@ -0,0 +1,29 @@ +// Copyright 2018 Microsoft Corporation +// +// 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 foo + +// Gateway ... +type Gateway struct { + // Name ... + Name *string + // ID ... + ID *string +} + +// Something ... +type Something struct { + // Name ... + Name *string +} diff --git a/tools/testdata/scenarioh/foo/mgmt/2019-10-11/foo/stage/version.go b/tools/testdata/scenarioh/foo/mgmt/2019-10-11/foo/stage/version.go new file mode 100644 index 000000000000..b729a3cd2f00 --- /dev/null +++ b/tools/testdata/scenarioh/foo/mgmt/2019-10-11/foo/stage/version.go @@ -0,0 +1,28 @@ +package foo + +// Copyright (c) Microsoft and contributors. All rights reserved. +// +// 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. +// +// Code generated by Microsoft (R) AutoRest Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +// UserAgent returns the UserAgent string to use when sending http.Requests. +func UserAgent() string { + return "Azure-SDK-For-Go/0.0.0 foo/2019-04-01" +} + +// Version returns the semantic version (see http://semver.org) of the client. +func Version() string { + return "0.0.0" +} diff --git a/tools/testdata/scenarioh/foo/mgmt/2019-10-11/foo/version.go b/tools/testdata/scenarioh/foo/mgmt/2019-10-11/foo/version.go new file mode 100644 index 000000000000..a2201e008c96 --- /dev/null +++ b/tools/testdata/scenarioh/foo/mgmt/2019-10-11/foo/version.go @@ -0,0 +1,28 @@ +package foo + +// Copyright (c) Microsoft and contributors. All rights reserved. +// +// 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. +// +// Code generated by Microsoft (R) AutoRest Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +// UserAgent returns the UserAgent string to use when sending http.Requests. +func UserAgent() string { + return "Azure-SDK-For-Go/1.0.0 foo/2019-04-01" +} + +// Version returns the semantic version (see http://semver.org) of the client. +func Version() string { + return "1.0.0" +} diff --git a/tools/testpkgs/scenrioa/foo/fooapi/interfaces.go b/tools/testpkgs/scenrioa/foo/fooapi/interfaces.go index 979eaa56028c..7d4dab643968 100644 --- a/tools/testpkgs/scenrioa/foo/fooapi/interfaces.go +++ b/tools/testpkgs/scenrioa/foo/fooapi/interfaces.go @@ -2,6 +2,7 @@ package fooapi import ( "context" + "github.com/Azure/azure-sdk-for-go/tools/testpkgs/scenrioa/foo" ) diff --git a/tools/testpkgs/scenriob/foo/fooapi/interfaces.go b/tools/testpkgs/scenriob/foo/fooapi/interfaces.go index 979eaa56028c..7d4dab643968 100644 --- a/tools/testpkgs/scenriob/foo/fooapi/interfaces.go +++ b/tools/testpkgs/scenriob/foo/fooapi/interfaces.go @@ -2,6 +2,7 @@ package fooapi import ( "context" + "github.com/Azure/azure-sdk-for-go/tools/testpkgs/scenrioa/foo" ) diff --git a/tools/testpkgs/scenriob/foo/v2/fooapi/interfaces.go b/tools/testpkgs/scenriob/foo/v2/fooapi/interfaces.go index 979eaa56028c..7d4dab643968 100644 --- a/tools/testpkgs/scenriob/foo/v2/fooapi/interfaces.go +++ b/tools/testpkgs/scenriob/foo/v2/fooapi/interfaces.go @@ -2,6 +2,7 @@ package fooapi import ( "context" + "github.com/Azure/azure-sdk-for-go/tools/testpkgs/scenrioa/foo" ) diff --git a/tools/testpkgs/scenrioc/mgmt/2019-10-11/foo/fooapi/interfaces.go b/tools/testpkgs/scenrioc/mgmt/2019-10-11/foo/fooapi/interfaces.go index 979eaa56028c..7d4dab643968 100644 --- a/tools/testpkgs/scenrioc/mgmt/2019-10-11/foo/fooapi/interfaces.go +++ b/tools/testpkgs/scenrioc/mgmt/2019-10-11/foo/fooapi/interfaces.go @@ -2,6 +2,7 @@ package fooapi import ( "context" + "github.com/Azure/azure-sdk-for-go/tools/testpkgs/scenrioa/foo" ) diff --git a/tools/testpkgs/scenriod/mgmt/2019-10-11/foo/fooapi/interfaces.go b/tools/testpkgs/scenriod/mgmt/2019-10-11/foo/fooapi/interfaces.go index 979eaa56028c..7d4dab643968 100644 --- a/tools/testpkgs/scenriod/mgmt/2019-10-11/foo/fooapi/interfaces.go +++ b/tools/testpkgs/scenriod/mgmt/2019-10-11/foo/fooapi/interfaces.go @@ -2,6 +2,7 @@ package fooapi import ( "context" + "github.com/Azure/azure-sdk-for-go/tools/testpkgs/scenrioa/foo" ) diff --git a/tools/testpkgs/scenriod/mgmt/2019-10-11/foo/v2/fooapi/interfaces.go b/tools/testpkgs/scenriod/mgmt/2019-10-11/foo/v2/fooapi/interfaces.go index 979eaa56028c..7d4dab643968 100644 --- a/tools/testpkgs/scenriod/mgmt/2019-10-11/foo/v2/fooapi/interfaces.go +++ b/tools/testpkgs/scenriod/mgmt/2019-10-11/foo/v2/fooapi/interfaces.go @@ -2,6 +2,7 @@ package fooapi import ( "context" + "github.com/Azure/azure-sdk-for-go/tools/testpkgs/scenrioa/foo" ) diff --git a/tools/testpkgs/scenrioe/mgmt/2019-10-11/foo/fooapi/interfaces.go b/tools/testpkgs/scenrioe/mgmt/2019-10-11/foo/fooapi/interfaces.go index 979eaa56028c..7d4dab643968 100644 --- a/tools/testpkgs/scenrioe/mgmt/2019-10-11/foo/fooapi/interfaces.go +++ b/tools/testpkgs/scenrioe/mgmt/2019-10-11/foo/fooapi/interfaces.go @@ -2,6 +2,7 @@ package fooapi import ( "context" + "github.com/Azure/azure-sdk-for-go/tools/testpkgs/scenrioa/foo" ) diff --git a/tools/testpkgs/scenrioe/mgmt/2019-10-11/foo/v2/fooapi/interfaces.go b/tools/testpkgs/scenrioe/mgmt/2019-10-11/foo/v2/fooapi/interfaces.go index 979eaa56028c..7d4dab643968 100644 --- a/tools/testpkgs/scenrioe/mgmt/2019-10-11/foo/v2/fooapi/interfaces.go +++ b/tools/testpkgs/scenrioe/mgmt/2019-10-11/foo/v2/fooapi/interfaces.go @@ -2,6 +2,7 @@ package fooapi import ( "context" + "github.com/Azure/azure-sdk-for-go/tools/testpkgs/scenrioa/foo" ) diff --git a/tools/testpkgs/scenrioe/mgmt/2019-10-11/foo/v3/fooapi/interfaces.go b/tools/testpkgs/scenrioe/mgmt/2019-10-11/foo/v3/fooapi/interfaces.go index 979eaa56028c..7d4dab643968 100644 --- a/tools/testpkgs/scenrioe/mgmt/2019-10-11/foo/v3/fooapi/interfaces.go +++ b/tools/testpkgs/scenrioe/mgmt/2019-10-11/foo/v3/fooapi/interfaces.go @@ -2,6 +2,7 @@ package fooapi import ( "context" + "github.com/Azure/azure-sdk-for-go/tools/testpkgs/scenrioa/foo" ) diff --git a/tools/versioner/classical_packages.txt b/tools/versioner/classical_packages.txt new file mode 100644 index 000000000000..87c2094c7fba --- /dev/null +++ b/tools/versioner/classical_packages.txt @@ -0,0 +1,15 @@ +/classic/management/affinitygroup +/classic/management/hostedservice +/classic/management/location +/classic/management/networksecuritygroup +/classic/management/osimage +/classic/management/sql +/classic/management/storageservice +/classic/management/testutils +/classic/management/virtualmachine +/classic/management/virtualmachinedisk +/classic/management/virtualmachineimage +/classic/management/virtualnetwork +/classic/management/vmutils +/keyvault/auth +/cosmos-db/mongodb \ No newline at end of file diff --git a/tools/versioner/cmd/common.go b/tools/versioner/cmd/common.go new file mode 100644 index 000000000000..99b6b52278e5 --- /dev/null +++ b/tools/versioner/cmd/common.go @@ -0,0 +1,43 @@ +// Copyright 2018 Microsoft Corporation +// +// 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 cmd + +import ( + "fmt" +) + +func printf(format string, a ...interface{}) { + if !quietFlag { + fmt.Printf(format, a...) + } +} + +func println(a ...interface{}) { + if !quietFlag { + fmt.Println(a...) + } +} + +func vprintf(format string, a ...interface{}) { + if verboseFlag { + printf(format, a...) + } +} + +func vprintln(a ...interface{}) { + if verboseFlag { + println(a...) + } +} diff --git a/tools/versioner/cmd/init.go b/tools/versioner/cmd/init.go new file mode 100644 index 000000000000..eddfd193e4ff --- /dev/null +++ b/tools/versioner/cmd/init.go @@ -0,0 +1,207 @@ +// Copyright 2018 Microsoft Corporation +// +// 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 cmd + +import ( + "bufio" + "fmt" + "io/ioutil" + "os" + "path/filepath" + "strings" + + "github.com/Azure/azure-sdk-for-go/tools/internal/modinfo" + "github.com/Azure/azure-sdk-for-go/tools/internal/pkgs" + "github.com/spf13/cobra" +) + +var initCmd = &cobra.Command{ + Use: "init [initial module version]", + Short: "Initialize a package into go module with initial version", + Long: `This tool will detect every possible service under the searching directory, +and make them as module with initial version. The default version for new modules is v1.0.0 +or the value specified for [initial module version]. +NOTE: This command is only used on local and only for initial release. +`, + Args: cobra.RangeArgs(1, 2), + RunE: func(cmd *cobra.Command, args []string) error { + return theInitCommand(args) + }, +} + +const ( + initialVerGo = `package %s + +// Copyright (c) Microsoft and contributors. All rights reserved. +// +// 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. +// +// Code generated by Microsoft (R) AutoRest Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +// UserAgent returns the UserAgent string to use when sending http.Requests. +func UserAgent() string { + return "Azure-SDK-For-Go/%s %s/%s" +} + +// Version returns the semantic version (see http://semver.org) of the client. +func Version() string { + return "%s" +} + +// tag: %s +` + + initialGoMod = `module %s + +%s +` + + goVersion = `go 1.12` +) + +var exceptFile string + +func init() { + rootCmd.AddCommand(initCmd) + initCmd.PersistentFlags().StringVarP(&exceptFile, "exceptions", "e", "", "file for exception package list") +} + +func theInitCommand(args []string) error { + root, err := filepath.Abs(args[0]) + if err != nil { + return fmt.Errorf("failed to get absolute path from '%s': %+v", args[0], err) + } + if len(args) == 2 { + if !modinfo.IsValidModuleVersion(args[1]) { + return fmt.Errorf("the string '%s' is not a valid module version", args[1]) + } + startingModVer = args[1] + } + exceptions, err := loadExceptions(exceptFile) + if err != nil { + return fmt.Errorf("failed to load exceptions: %+v", err) + } + ps, err := pkgs.GetPkgs(root) + if err != nil { + return fmt.Errorf("failed to get packages: %+v", err) + } + var errs []error + for _, p := range ps { + if _, ok := exceptions[p.Dest]; ok { + vprintf("Classical package %s, skip\n", p.Dest) + continue + } + path := filepath.Join(root, p.Dest) + tagPrefix, err := getTagPrefix(path) + if err != nil { + return fmt.Errorf("failed to get tag prefix: %+v", err) + } + vprintf("TagPrefix: %s\n", tagPrefix) + if err := createVersionFile(root, p, tagPrefix); err != nil { + errs = append(errs, err) + } + if err := createGoModFile(root, p); err != nil { + errs = append(errs, err) + } + if err := createChangeLogFile(root, p); err != nil { + errs = append(errs, err) + } + } + // handle errors + if len(errs) == 0 { + return nil + } + for _, err := range errs { + println(err.Error()) + } + return fmt.Errorf("execution failed with %d errors", len(errs)) +} + +func createVersionFile(root string, p pkgs.Pkg, tagPrefix string) error { + verFilePath := filepath.Join(root, p.Dest, "version.go") + apiVersion, err := p.GetAPIVersion() + if err != nil { + return fmt.Errorf("failed to get api version of package %s: %+v", p.Dest, err) + } + tag := tagPrefix + "/" + startingModVer + ver := versionGoRegex.FindString(startingModVer) + content := fmt.Sprintf(initialVerGo, p.Package.Name, ver, p.Package.Name, apiVersion, ver, tag) + err = ioutil.WriteFile(verFilePath, []byte(content), 0755) + if err != nil { + return fmt.Errorf("failed to write file %s: %+v", verFilePath, err) + } + return nil +} + +func createGoModFile(root string, p pkgs.Pkg) error { + modFilePath := filepath.Join(root, p.Dest, "go.mod") + fullPath := filepath.Join(root, p.Dest) + index := strings.Index(fullPath, "github.com") + if index < 0 { + return fmt.Errorf("failed to find github.com in filepath %s", fullPath) + } + importPath := strings.ReplaceAll(fullPath[index:], "\\", "/") + content := fmt.Sprintf(initialGoMod, importPath, goVersion) + err := ioutil.WriteFile(modFilePath, []byte(content), 0755) + if err != nil { + return fmt.Errorf("failed to write file %s: %+v", modFilePath, err) + } + return nil +} + +func createChangeLogFile(root string, p pkgs.Pkg) error { + logFilePath := filepath.Join(root, p.Dest, changeLogName) + log, err := os.Create(logFilePath) + if err != nil { + return fmt.Errorf("failed to create %s: %v", changeLogName, err) + } + defer log.Close() + _, err = log.WriteString("No changes to exported content compared to the previous release.\n") + return err +} + +func loadExceptions(exceptFile string) (map[string]bool, error) { + result := make(map[string]bool) + if exceptFile == "" { + return result, nil + } + abs, err := filepath.Abs(exceptFile) + if err != nil { + return nil, err + } + file, err := os.Open(abs) + defer file.Close() + if err != nil { + return nil, err + } + scanner := bufio.NewScanner(file) + scanner.Split(bufio.ScanLines) + for scanner.Scan() { + line := scanner.Text() + result[line] = true + } + return result, nil +} diff --git a/tools/versioner/cmd/root.go b/tools/versioner/cmd/root.go index 6c254e97c4a7..4ca88a5c8ac3 100644 --- a/tools/versioner/cmd/root.go +++ b/tools/versioner/cmd/root.go @@ -15,24 +15,16 @@ package cmd import ( - "bufio" - "errors" "fmt" - "io" "os" "path/filepath" - "regexp" - "sort" "strings" - "github.com/Azure/azure-sdk-for-go/tools/apidiff/repo" - "github.com/Azure/azure-sdk-for-go/tools/internal/modinfo" - "github.com/Masterminds/semver" "github.com/spf13/cobra" ) var rootCmd = &cobra.Command{ - Use: "versioner [initial module version]", + Use: "versioner [initial module version]", Short: "Creates or updates the latest major version for a package from staged content.", Long: `This tool will compare a staged package against its latest major version to detect breaking changes. If there are no breaking changes the latest major version is updated @@ -40,32 +32,26 @@ with the staged content. If there are breaking changes the staged content becom next latest major vesion and the go.mod file is updated. The default version for new modules is v1.0.0 or the value specified for [initial module version]. `, - Args: func(cmd *cobra.Command, args []string) error { - if err := cobra.MinimumNArgs(1)(cmd, args); err != nil { - return err - } - if err := cobra.MaximumNArgs(2)(cmd, args); err != nil { - return err - } - return nil - }, + Args: cobra.RangeArgs(1, 2), RunE: func(cmd *cobra.Command, args []string) error { cmd.SilenceUsage = true - return theCommand(args) + _, err := theCommand(args) + return err }, } var ( - semverRegex = regexp.MustCompile(`v\d+\.\d+\.\d+$`) - // this is used so tests can hook getTags() to return whatever tags - getTagsHook func(string, string) ([]string, error) - // default version to start a module at if not specified - startingModVer = "v1.0.0" + quietFlag bool + verboseFlag bool +) + +const ( + stageName = "stage" ) func init() { - // default to the real version - getTagsHook = getTags + rootCmd.PersistentFlags().BoolVarP(&verboseFlag, "verbose", "v", false, "verbose output") + rootCmd.PersistentFlags().BoolVarP(&quietFlag, "quiet", "q", false, "quiet output") } // Execute executes the specified command. @@ -75,244 +61,47 @@ func Execute() { } } -// wrapper for cobra, prints tag to stdout -func theCommand(args []string) error { - tag, err := theCommandImpl(args) - if err == nil { - fmt.Printf("tag: %s\n", tag) - } - return err -} - -// does the actual work -func theCommandImpl(args []string) (string, error) { - stage := filepath.Clean(args[0]) - if len(args) == 2 { - if !modinfo.IsValidModuleVersion(args[1]) { - return "", fmt.Errorf("the string '%s' is not a valid module version", args[1]) - } - startingModVer = args[1] - } - lmv, err := findLatestMajorVersion(stage) - if err != nil { - return "", fmt.Errorf("failed to find latest major version: %v", err) - } - mod, err := modinfo.GetModuleInfo(lmv, stage) - if err != nil { - return "", fmt.Errorf("failed to create module info: %v", err) - } - if err = writeChangelog(stage, mod); err != nil { - return "", fmt.Errorf("failed to write changelog: %v", err) - } - var tag string - if mod.BreakingChanges() { - tag, err = forSideBySideRelease(stage, mod) - } else { - tag, err = forInplaceUpdate(lmv, stage, mod) +// ExecuteVersioner is used for programmatically call in other tools +func ExecuteVersioner(root string, tagsHook TagsHookFunc) ([]string, error) { + if tagsHook != nil { + getTagsHook = tagsHook } - return tag, err + return theCommand([]string{root}) } -// releases the module as a new side-by-side major version -func forSideBySideRelease(stage string, mod modinfo.Provider) (string, error) { - // update the go.mod file with the new major version - goMod := filepath.Join(stage, "go.mod") - file, err := os.OpenFile(goMod, os.O_RDWR, 0666) - if err != nil { - return "", fmt.Errorf("failed to open for read '%s': %v", goMod, err) - } - ver := modinfo.FindVersionSuffix(mod.DestDir()) - if err = updateGoModVer(file, ver); err != nil { - file.Close() - return "", fmt.Errorf("failed to update go.mod file: %v", err) - } - // must close file before renaming directory - file.Close() - // move staging to new LMV directory - if err = os.Rename(stage, mod.DestDir()); err != nil { - return "", fmt.Errorf("failed to rename '%s' to '%s': %v", stage, mod.DestDir(), err) - } - var tag string - if tag, err = calculateModuleTag(nil, mod); err != nil { - return "", fmt.Errorf("failed to calculate module tag: %v", err) - } - return tag, nil -} - -// releases the module as an in-place update (minor or patch) -func forInplaceUpdate(lmv, stage string, mod modinfo.Provider) (string, error) { - // find existing tags for this module and create a new one - prefix, err := getTagPrefix(lmv) - if err != nil { - return "", fmt.Errorf("failed to get tag prefix: %v", err) - } - tags, err := getTagsHook(lmv, prefix) +// wrapper for cobra, prints tag to stdout +func theCommand(args []string) ([]string, error) { + root, err := filepath.Abs(args[0]) if err != nil { - return "", fmt.Errorf("failed to retrieve tags: %v", err) - } - var tag string - if tag, err = calculateModuleTag(tags, mod); err != nil { - return "", fmt.Errorf("failed to calculate module tag: %v", err) - } - // move staging directory over the LMV by first deleting LMV then renaming stage - if modinfo.HasVersionSuffix(lmv) { - if err := os.RemoveAll(lmv); err != nil { - return "", fmt.Errorf("failed to delete '%s': %v", lmv, err) - } - if err := os.Rename(stage, mod.DestDir()); err != nil { - return "", fmt.Errorf("failed to rename '%s' toi '%s': %v", stage, lmv, err) + return nil, fmt.Errorf("failed to get absolute path from '%s': %v", args[0], err) + } + stages, err := findAllSubDirectories(root, stageName) + printf("Found %d stage folder(s)", len(stages)) + vprintf("Stage folders: \n%s\n", strings.Join(stages, "\n")) + tags := make([]string, 0) + for _, stage := range stages { + args[0] = stage + tag, err := theUnstageCommand(args) + if err != nil { + return tags, fmt.Errorf("failed to get tag in stage folder '%s': %v", stage, err) } - return tag, nil + tags = append(tags, tag) } - // for v1 it's a bit more complicated since stage is a subdir of LMV. - // first move stage to a temp dir outside of LMV, then remove LMV, then move temp to LMV - dest := filepath.Dir(stage) - temp := dest + "1temp" - if err := os.Rename(stage, temp); err != nil { - return "", fmt.Errorf("failed to rename '%s' to '%s': %v", stage, temp, err) - } - if err := os.RemoveAll(dest); err != nil { - return "", fmt.Errorf("failed to delete '%s': %v", dest, err) - } - if err := os.Rename(temp, dest); err != nil { - return "", fmt.Errorf("failed to rename '%s' to '%s': %v", temp, dest, err) - } - return tag, nil + return tags, nil } -// returns the absolute path to the latest major version based on the provided staging directory. -// it's assumed that the staging directory is a subdirectory of the actual package directory. -func findLatestMajorVersion(stage string) (string, error) { - // example input: - // ~/work/src/github.com/Azure/azure-sdk-for-go/services/redis/mgmt/2018-03-01/redis/stage - // finds: - // ~/work/src/github.com/Azure/azure-sdk-for-go/services/redis/mgmt/2018-03-01/redis - // ~/work/src/github.com/Azure/azure-sdk-for-go/services/redis/mgmt/2018-03-01/redis/v2 - // returns: - // ~/work/src/github.com/Azure/azure-sdk-for-go/services/redis/mgmt/2018-03-01/redis/v2 - parent := filepath.Dir(stage) - dirs, err := modinfo.GetModuleSubdirs(parent) - if err != nil { - return "", fmt.Errorf("failed to get module subdirs '%s': %v", parent, err) +func findAllSubDirectories(root, target string) ([]string, error) { + // check if root exists + if _, err := os.Stat(root); os.IsNotExist(err) { + return nil, fmt.Errorf("the root path '%s' does not exist", root) } - // no dirs means this is a v1 package - if len(dirs) == 0 { - return parent, nil - } - sort.Strings(dirs) - // last one in the slice is the largest - return filepath.Join(parent, dirs[len(dirs)-1]), nil -} - -// updates the module version inside the go.mod file -func updateGoModVer(goMod io.ReadWriteSeeker, newVer string) error { - scanner := bufio.NewScanner(goMod) - scanner.Split(bufio.ScanLines) - lines := []string{} - for scanner.Scan() { - lines = append(lines, scanner.Text()) - } - _, err := goMod.Seek(0, io.SeekStart) - if err != nil { - return fmt.Errorf("failed to seek to start: %v", err) - } - for _, line := range lines { - if strings.Index(line, "module") > -1 { - if modinfo.HasVersionSuffix(line) { - line = strings.Replace(line, "/"+modinfo.FindVersionSuffix(line), "/"+newVer, 1) - } else { - line = line + "/" + newVer - } + stages := make([]string, 0) + err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error { + if info.IsDir() && info.Name() == target { + stages = append(stages, path) + return nil } - fmt.Fprintln(goMod, line) - } - return nil -} - -func writeChangelog(stage string, mod modinfo.Provider) error { - // don't write a changelog for a new module - if mod.NewModule() { return nil - } - const changeLogName = "CHANGELOG.md" - rpt := mod.GenerateReport() - log, err := os.Create(filepath.Join(stage, changeLogName)) - if err != nil { - return fmt.Errorf("failed to create %s: %v", changeLogName, err) - } - defer log.Close() - if rpt.IsEmpty() { - _, err = log.WriteString("No changes to exported content compared to the previous release.\n") - return err - } - _, err = log.WriteString(rpt.ToMarkdown()) - return err -} - -// returns a slice of tags for the specified repo and tag prefix -func getTags(repoPath, tagPrefix string) ([]string, error) { - wt, err := repo.Get(repoPath) - if err != nil { - return nil, err - } - return wt.ListTags(tagPrefix + "*") -} - -// returns the tag prefix for the specified package. -// assumes repo root of github.com/Azure/azure-sdk-for-go/ -func getTagPrefix(pkgDir string) (string, error) { - // e.g. /work/src/github.com/Azure/azure-sdk-for-go/services/redis/mgmt/2018-03-01/redis/v2 - // would return services/redis/mgmt/2018-03-01/redis/v2.0.0 - repoRoot := filepath.Join("github.com", "Azure", "azure-sdk-for-go") - i := strings.Index(pkgDir, repoRoot) - if i < 0 { - return "", fmt.Errorf("didn't find '%s' in '%s'", repoRoot, pkgDir) - } - return strings.Replace(pkgDir[i+len(repoRoot)+1:], "\\", "/", -1), nil -} - -// returns the appropriate module tag based on the package version info -// tags - list of all current tags for the module -func calculateModuleTag(tags []string, mod modinfo.Provider) (string, error) { - if mod.BreakingChanges() && !mod.VersionSuffix() { - return "", errors.New("package has breaking changes but directory has no version suffix") - } - tagPrefix, err := getTagPrefix(mod.DestDir()) - if err != nil { - return "", err - } - // if this has breaking changes then it's simply the prefix as a new major version - if mod.BreakingChanges() { - return tagPrefix + ".0.0", nil - } - if len(tags) == 0 { - if mod.VersionSuffix() { - panic("module contains a version suffix but no tags were found") - } - // this is the first module version - return tagPrefix + "/" + startingModVer, nil - } - if !mod.VersionSuffix() { - tagPrefix = tagPrefix + "/v1" - } - tag := tags[len(tags)-1] - v := semverRegex.FindString(tag) - if v == "" { - return "", fmt.Errorf("didn't find semver in tag '%s'", tag) - } - sv, err := semver.NewVersion(v) - if err != nil { - return "", fmt.Errorf("failed to parse semver: %v", err) - } - // for non-breaking changes determine if this is a minor or patch update. - if mod.NewExports() { - // new exports, this is a minor update so bump minor version - n := sv.IncMinor() - sv = &n - } else { - // no new exports, this is a patch update - n := sv.IncPatch() - sv = &n - } - return strings.Replace(tag, v, "v"+sv.String(), 1), nil + }) + return stages, err } diff --git a/tools/versioner/cmd/root_test.go b/tools/versioner/cmd/root_test.go index 5be1d7cae459..a524ad98c580 100644 --- a/tools/versioner/cmd/root_test.go +++ b/tools/versioner/cmd/root_test.go @@ -15,266 +15,12 @@ package cmd import ( - "bytes" - "fmt" - "io/ioutil" - "os" "os/exec" "path/filepath" - "runtime" "strings" "testing" - - "github.com/Azure/azure-sdk-for-go/tools/apidiff/report" - "github.com/Azure/azure-sdk-for-go/tools/internal/modinfo" ) -var newline = "\n" - -func init() { - if runtime.GOOS == "windows" { - newline = "\r\n" - } - -} -func Test_getTags(t *testing.T) { - if os.Getenv("TRAVIS") == "true" { - // travis does a shallow clone so tag count is not consistent - t.SkipNow() - } - cwd, err := os.Getwd() - if err != nil { - t.Fatalf("failed to get cwd: %v", err) - } - tags, err := getTags(cwd, "v10") - if err != nil { - t.Fatalf("failed to get tags: %v", err) - } - if l := len(tags); l != 11 { - t.Fatalf("expected 11 tags, got %d", l) - } - found := false - for _, tag := range tags { - if tag == "v10.1.0-beta" { - found = true - break - } - } - if !found { - t.Fatal("didn't find tag v10.1.0-beta") - } -} - -func Test_getTagPrefix(t *testing.T) { - p, err := getTagPrefix(filepath.Join("work", "src", "github.com", "Azure", "azure-sdk-for-go", "services", "redis", "mgmt", "2018-03-01", "redis")) - if err != nil { - t.Fatal("failed to get tag prefix") - } - if p != "services/redis/mgmt/2018-03-01/redis" { - t.Fatalf("wrong value '%s' for tag prefix", p) - } - p, err = getTagPrefix("/work/src/github.com/something/else") - if err == nil { - t.Fatal("unexpected nil error") - } - if p != "" { - t.Fatalf("unexpected tag '%s'", p) - } -} - -type mockModInfo struct { - dir string - exports bool - breaks bool -} - -func (mock mockModInfo) DestDir() string { - return mock.dir -} - -func (mock mockModInfo) NewExports() bool { - return mock.exports -} - -func (mock mockModInfo) BreakingChanges() bool { - return mock.breaks -} - -func (mock mockModInfo) VersionSuffix() bool { - return modinfo.HasVersionSuffix(mock.dir) -} - -func (mock mockModInfo) NewModule() bool { - // not needed by tests - return false -} - -func (mock mockModInfo) GenerateReport() report.Package { - // not needed by tests - return report.Package{} -} - -func Test_calculateModuleTagMajorV1(t *testing.T) { - pkg := mockModInfo{ - dir: filepath.Join("work", "src", "github.com", "Azure", "azure-sdk-for-go", "services", "foo"), - } - tag, err := calculateModuleTag([]string{}, pkg) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if tag != "services/foo/v1.0.0" { - t.Fatalf("bad tag '%s", tag) - } -} - -func Test_calculateModuleTagMajorV2(t *testing.T) { - tags := []string{ - "services/foo/v1.0.0", - "services/foo/v1.1.0", - } - pkg := mockModInfo{ - dir: filepath.Join("work", "src", "github.com", "Azure", "azure-sdk-for-go", "services", "foo", "v2"), - breaks: true, - } - tag, err := calculateModuleTag(tags, pkg) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if tag != "services/foo/v2.0.0" { - t.Fatalf("bad tag '%s", tag) - } -} - -func Test_calculateModuleTagMajorV2Invalid(t *testing.T) { - tags := []string{ - "services/foo/v1.0.0", - "services/foo/v1.1.0", - } - pkg := mockModInfo{ - dir: filepath.Join("work", "src", "github.com", "Azure", "azure-sdk-for-go", "services", "foo"), // missing /v2 suffix - breaks: true, - } - tag, err := calculateModuleTag(tags, pkg) - if err == nil { - t.Fatal("expected non-nil error") - } - if tag != "" { - t.Fatal("expected no tag") - } -} - -func Test_calculateModuleTagMinorV1(t *testing.T) { - tags := []string{ - "services/foo/v1.0.0", - "services/foo/v1.0.1", - } - pkg := mockModInfo{ - dir: filepath.Join("work", "src", "github.com", "Azure", "azure-sdk-for-go", "services", "foo"), - exports: true, - } - tag, err := calculateModuleTag(tags, pkg) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if tag != "services/foo/v1.1.0" { - t.Fatalf("bad tag '%s", tag) - } -} - -func Test_calculateModuleTagMinorV2(t *testing.T) { - tags := []string{ - "services/foo/v1.0.0", - "services/foo/v1.0.1", - "services/foo/v2.0.0", - } - pkg := mockModInfo{ - dir: filepath.Join("work", "src", "github.com", "Azure", "azure-sdk-for-go", "services", "foo", "v2"), - } - tag, err := calculateModuleTag(tags, pkg) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if tag != "services/foo/v2.0.1" { - t.Fatalf("bad tag '%s", tag) - } -} - -func Test_findLatestMajorVersion(t *testing.T) { - ver, err := findLatestMajorVersion("../../testdata/scenarioa/foo/stage") - if err != nil { - t.Fatalf("failed to find LMV: %v", err) - } - if ver != filepath.Join("..", "..", "testdata", "scenarioa", "foo") { - t.Fatalf("bad LMV %s", ver) - } - ver, err = findLatestMajorVersion("../../testdata/scenariod/foo/stage") - if err != nil { - t.Fatalf("failed to find LMV: %v", err) - } - if ver != filepath.Join("..", "..", "testdata", "scenariod", "foo", "v2") { - t.Fatalf("bad LMV %s", ver) - } - ver, err = findLatestMajorVersion("../../testdata/scenariof/foo/stage") - if err != nil { - t.Fatalf("failed to find LMV: %v", err) - } - if ver != filepath.Join("..", "..", "testdata", "scenariof", "foo") { - t.Fatalf("bad LMV %s", ver) - } -} - -type byteBufferSeeker struct { - buf *bytes.Buffer -} - -func (b byteBufferSeeker) Read(p []byte) (int, error) { - return b.buf.Read(p) -} - -func (b byteBufferSeeker) Write(p []byte) (int, error) { - return b.buf.Write(p) -} - -func (b byteBufferSeeker) Seek(offset int64, whence int) (int64, error) { - if offset != 0 && whence != 0 { - panic("seek only supports 0, 0") - } - b.buf.Reset() - return 0, nil -} - -func Test_updateGoModVerA(t *testing.T) { - // updates from v1 to v2 - const before = "module github.com/Azure/azure-sdk-for-go/services/foo/mgmt/2019-05-01/foo\n\ngo 1.12\n" - buf := byteBufferSeeker{ - buf: bytes.NewBuffer([]byte(before)), - } - err := updateGoModVer(buf, "v2") - if err != nil { - t.Fatalf("updateGoModVerA failed: %v", err) - } - const after = "module github.com/Azure/azure-sdk-for-go/services/foo/mgmt/2019-05-01/foo/v2\n\ngo 1.12\n" - if !strings.EqualFold(buf.buf.String(), after) { - t.Fatalf("bad go.mod update, epected %s got %s", after, buf.buf.String()) - } -} - -func Test_updateGoModVerB(t *testing.T) { - // updates from v2 to v3 - const before = "module github.com/Azure/azure-sdk-for-go/services/foo/mgmt/2019-05-01/foo/v2\n\ngo 1.12\n" - buf := byteBufferSeeker{ - buf: bytes.NewBuffer([]byte(before)), - } - err := updateGoModVer(buf, "v3") - if err != nil { - t.Fatalf("updateGoModVerA failed: %v", err) - } - const after = "module github.com/Azure/azure-sdk-for-go/services/foo/mgmt/2019-05-01/foo/v3\n\ngo 1.12\n" - if !strings.EqualFold(buf.buf.String(), after) { - t.Fatalf("bad go.mod update, epected %s got %s", after, buf.buf.String()) - } -} - func cleanTestData() { cmd := exec.Command("git", "clean", "-xdf", "../../testdata") output, err := cmd.CombinedOutput() @@ -288,159 +34,37 @@ func cleanTestData() { } } -func fileExists(path string) bool { - _, err := os.Stat(path) - if err == nil { - return true - } else if os.IsNotExist(err) { - return false - } - panic(err) -} - -func Test_theCommandImplMajor(t *testing.T) { - cleanTestData() - defer cleanTestData() - getTagsHook = func(root, prefix string) ([]string, error) { - // root doesn't matter - if !strings.HasSuffix(prefix, "/testdata/scenariob/foo") { - return nil, fmt.Errorf("bad prefix '%s'", prefix) - } - return []string{ - "tools/testdata/scenariob/foo/v1.0.0", - "tools/testdata/scenariob/foo/v1.1.0", - }, nil - } - pkg, err := filepath.Abs("../../testdata/scenariob/foo/stage") - if err != nil { - t.Fatalf("failed to get absolute path: %v", err) - } - tag, err := theCommandImpl([]string{pkg}) - if err != nil { - t.Fatalf("failed: %v", err) - } - const expected = "tools/testdata/scenariob/foo/v2.0.0" - if tag != expected { - t.Fatalf("bad tag, expected '%s' got '%s'", expected, tag) - } - b, err := ioutil.ReadFile("../../testdata/scenariob/foo/v2/go.mod") - if err != nil { - t.Fatalf("failed to read go.mod file: %v", err) - } - const after = "module github.com/Azure/azure-sdk-for-go/tools/testdata/scenariob/foo/v2\n\ngo 1.12\n" - if !strings.EqualFold(string(b), after) { - t.Fatalf("bad go.mod file, expected '%s' got '%s'", after, string(b)) - } - if !fileExists(filepath.Join("../../testdata/scenariob/foo/v2", "CHANGELOG.md")) { - t.Fatal("expected changelog in scenariob") - } -} - -func Test_theCommandImplMinor(t *testing.T) { +func Test_findAllSubDirectories(t *testing.T) { cleanTestData() defer cleanTestData() - getTagsHook = func(root, prefix string) ([]string, error) { - // root doesn't matter - if !strings.HasSuffix(prefix, "/testdata/scenarioa/foo") { - return nil, fmt.Errorf("bad prefix '%s'", prefix) - } - return []string{ - "tools/testdata/scenarioa/foo/v1.0.0", - }, nil + expected := []string{ + "../../testdata/scenarioa/foo/stage", + "../../testdata/scenariob/foo/stage", + "../../testdata/scenarioc/foo/stage", + "../../testdata/scenariod/foo/stage", + "../../testdata/scenarioe/foo/stage", + "../../testdata/scenariof/foo/stage", + "../../testdata/scenariog/foo/mgmt/2019-10-11/foo/stage", + "../../testdata/scenarioh/foo/mgmt/2019-10-11/foo/stage", } - pkg, err := filepath.Abs("../../testdata/scenarioa/foo/stage") + root, err := filepath.Abs("../../testdata") if err != nil { - t.Fatalf("failed to get absolute path: %v", err) - } - tag, err := theCommandImpl([]string{pkg}) - if err != nil { - t.Fatalf("failed: %v", err) - } - const expected = "tools/testdata/scenarioa/foo/v1.1.0" - if tag != expected { - t.Fatalf("bad tag, expected '%s' got '%s'", expected, tag) + t.Fatalf("error when get absolute path of root: %+v", err) } - b, err := ioutil.ReadFile("../../testdata/scenarioa/foo/go.mod") + stages, err := findAllSubDirectories(root, "stage") if err != nil { - t.Fatalf("failed to read go.mod file: %v", err) + t.Fatalf("error when listing all stage folders: %+v", err) } - after := "module github.com/Azure/azure-sdk-for-go/tools/testdata/scenarioa/foo" + newline + newline + "go 1.12" + newline - if !strings.EqualFold(string(b), after) { - t.Fatalf("bad go.mod file, expected '%s' got '%s'", after, string(b)) + if len(stages) != len(expected) { + t.Fatalf("expected %d stages folders, but got %d", len(expected), len(stages)) } - if !fileExists(filepath.Join("../../testdata/scenarioa/foo", "CHANGELOG.md")) { - t.Fatal("expected changelog in scenarioa") - } -} - -func Test_theCommandImplPatch(t *testing.T) { - cleanTestData() - defer cleanTestData() - getTagsHook = func(root, prefix string) ([]string, error) { - // root doesn't matter - if !strings.HasSuffix(prefix, "/testdata/scenarioc/foo") { - return nil, fmt.Errorf("bad prefix '%s'", prefix) + for i, stage := range stages { + e, err := filepath.Abs(expected[i]) + if err != nil { + t.Fatalf("error when parsing expected results '%s'(%d)", expected[i], i) } - return []string{ - "tools/testdata/scenarioc/foo/v1.0.0", - }, nil - } - pkg, err := filepath.Abs("../../testdata/scenarioc/foo/stage") - if err != nil { - t.Fatalf("failed to get absolute path: %v", err) - } - tag, err := theCommandImpl([]string{pkg}) - if err != nil { - t.Fatalf("failed: %v", err) - } - const expected = "tools/testdata/scenarioc/foo/v1.0.1" - if tag != expected { - t.Fatalf("bad tag, expected '%s' got '%s'", expected, tag) - } - b, err := ioutil.ReadFile("../../testdata/scenarioc/foo/go.mod") - if err != nil { - t.Fatalf("failed to read go.mod file: %v", err) - } - after := "module github.com/Azure/azure-sdk-for-go/tools/testdata/scenarioc/foo" + newline + newline + "go 1.12" + newline - if !strings.EqualFold(string(b), after) { - t.Fatalf("bad go.mod file, expected '%s' got '%s'", after, string(b)) - } - if !fileExists(filepath.Join("../../testdata/scenarioc/foo", "CHANGELOG.md")) { - t.Fatal("expected changelog in scenarioc") - } -} - -func Test_theCommandImplNewMod(t *testing.T) { - cleanTestData() - defer cleanTestData() - getTagsHook = func(root, prefix string) ([]string, error) { - // root doesn't matter - if !strings.HasSuffix(prefix, "/testdata/scenariof/foo") { - return nil, fmt.Errorf("bad prefix '%s'", prefix) + if !strings.EqualFold(stage, e) { + t.Fatalf("expected folder '%s', but got '%s'", e, stage) } - return []string{}, nil - } - pkg, err := filepath.Abs("../../testdata/scenariof/foo/stage") - if err != nil { - t.Fatalf("failed to get absolute path: %v", err) - } - tag, err := theCommandImpl([]string{pkg}) - if err != nil { - t.Fatalf("failed: %v", err) - } - const expected = "tools/testdata/scenariof/foo/v1.0.0" - if tag != expected { - t.Fatalf("bad tag, expected '%s' got '%s'", expected, tag) - } - b, err := ioutil.ReadFile("../../testdata/scenariof/foo/go.mod") - if err != nil { - t.Fatalf("failed to read go.mod file: %v", err) - } - after := "module github.com/Azure/azure-sdk-for-go/tools/testdata/scenariof/foo" + newline + newline + "go 1.12" + newline - if !strings.EqualFold(string(b), after) { - t.Fatalf("bad go.mod file, expected '%s' got '%s'", after, string(b)) - } - if fileExists(filepath.Join("../../testdata/scenariof/foo", "CHANGELOG.md")) { - t.Fatal("unexpected changelog in scenariof") } } diff --git a/tools/versioner/cmd/unstage.go b/tools/versioner/cmd/unstage.go new file mode 100644 index 000000000000..43dc027e5e5e --- /dev/null +++ b/tools/versioner/cmd/unstage.go @@ -0,0 +1,493 @@ +// Copyright 2018 Microsoft Corporation +// +// 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 cmd + +import ( + "bufio" + "errors" + "fmt" + "io" + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "regexp" + "sort" + "strings" + + "github.com/Azure/azure-sdk-for-go/tools/apidiff/repo" + "github.com/Azure/azure-sdk-for-go/tools/internal/modinfo" + "github.com/Masterminds/semver" + "github.com/spf13/cobra" +) + +var unstageCmd = &cobra.Command{ + Use: "unstage [initial module version]", + Short: "Creates or updates the latest major version for a package from staged content.", + Long: `This tool will compare a staged package against its latest major version to detect +breaking changes. If there are no breaking changes the latest major version is updated +with the staged content. If there are breaking changes the staged content becomes the +next latest major version and the go.mod file is updated. +The default version for new modules is v1.0.0 or the value specified for [initial module version]. +`, + Args: cobra.RangeArgs(1, 2), + RunE: func(cmd *cobra.Command, args []string) error { + _, err := theUnstageCommand(args) + return err + }, +} + +var ( + semverRegex = regexp.MustCompile(`v\d+\.\d+\.\d+$`) + versionGoRegex = regexp.MustCompile(`\d+\.\d+\.\d+`) + // default version to start a module at if not specified + startingModVer = "v1.0.0" + // this is used so tests can hook getTags() to return whatever tags + getTagsHook TagsHookFunc +) + +const changeLogName = "CHANGELOG.md" + +// TagsHookFunc is a func used for get tags from remote +type TagsHookFunc func(string, string) ([]string, error) + +func init() { + // default to the real version + getTagsHook = getTags + rootCmd.AddCommand(unstageCmd) +} + +// ExecuteUnstageCommand is used for programmatically call in other tools +func ExecuteUnstageCommand(stage string, tagsHook TagsHookFunc) (string, error) { + if tagsHook != nil { + getTagsHook = tagsHook + } + return theUnstageCommand([]string{stage}) +} + +func theUnstageCommand(args []string) (string, error) { + stage, err := filepath.Abs(args[0]) + if err != nil { + return "", fmt.Errorf("failed to get absolute path from '%s': %v", args[0], err) + } + if len(args) == 2 { + if !modinfo.IsValidModuleVersion(args[1]) { + return "", fmt.Errorf("the string '%s' is not a valid module version", args[1]) + } + startingModVer = args[1] + } + // format stage folder first to avoid unnecessary changes detected by apidiff + if err := formatCode(stage); err != nil { + return "", fmt.Errorf("failed to format stage folder: %v", err) + } + lmv, err := findLatestMajorVersion(stage) + if err != nil { + return "", fmt.Errorf("failed to find latest major version in '%s': %v", stage, err) + } + vprintf("Latest major version path: %v\n", lmv) + mod, err := modinfo.GetModuleInfo(lmv, stage) + if err != nil { + return "", fmt.Errorf("failed to create module info: %v", err) + } + if err := writeChangelog(stage, mod); err != nil { + return "", fmt.Errorf("failed to write changelog: %v", err) + } + var tag string + if mod.BreakingChanges() { + tag, err = forSideBySideRelease(stage, mod) + } else { + tag, err = forInplaceUpdate(lmv, stage, mod) + } + return tag, err +} + +// releases the module as a new side-by-side major version +func forSideBySideRelease(stage string, mod modinfo.Provider) (string, error) { + vprintln("This is a side by side update") + // update the go.mod file with the new major version + goMod := filepath.Join(stage, "go.mod") + file, err := os.OpenFile(goMod, os.O_RDWR, 0666) + if err != nil { + return "", fmt.Errorf("failed to open for read '%s': %v", goMod, err) + } + dest := mod.DestDir() + ver := modinfo.FindVersionSuffix(dest) + if err = updateGoModVer(file, ver); err != nil { + file.Close() + return "", fmt.Errorf("failed to update go.mod file: %v", err) + } + // must close file before renaming directory + file.Close() + // calculate tags + var tag string + if tag, err = calculateModuleTag(nil, mod); err != nil { + return "", fmt.Errorf("failed to calculate module tag: %v", err) + } + // update import statement + if err := updateImportStatement(stage, dest, ver); err != nil { + return "", fmt.Errorf("failed to update import statements: %v", err) + } + // change version.go file + if err := updateVersion(stage, tag); err != nil { + return "", fmt.Errorf("failed to update version.go: %v", err) + } + // move staging to new LMV directory + if err = os.Rename(stage, mod.DestDir()); err != nil { + return "", fmt.Errorf("failed to rename '%s' to '%s': %v", stage, mod.DestDir(), err) + } + return tag, nil +} + +// releases the module as an in-place update (minor or patch) +func forInplaceUpdate(lmv, stage string, mod modinfo.Provider) (string, error) { + vprintln("This is a inplace update") + goMod := filepath.Join(stage, "go.mod") + file, err := os.OpenFile(goMod, os.O_RDWR, 0666) + if err != nil { + return "", fmt.Errorf("failed to open for read '%s': %v", goMod, err) + } + ver := modinfo.FindVersionSuffix(mod.DestDir()) + if err = updateGoModVer(file, ver); err != nil { + file.Close() + return "", fmt.Errorf("failed to update go.mod file: %v", err) + } + file.Close() + // find existing tags for this module and create a new one + prefix, err := getTagPrefix(lmv) + if err != nil { + return "", fmt.Errorf("failed to get tag prefix: %v", err) + } + tags, err := getTagsHook(lmv, prefix) + if err != nil { + return "", fmt.Errorf("failed to retrieve tags: %v", err) + } + var tag string + if tag, err = calculateModuleTag(tags, mod); err != nil { + return "", fmt.Errorf("failed to calculate module tag: %v", err) + } + if err := updateVersion(stage, tag); err != nil { + return "", fmt.Errorf("failed to update version.go: %v", err) + } + // move staging directory over the LMV by first deleting LMV then renaming stage + if modinfo.HasVersionSuffix(lmv) { + if err := os.RemoveAll(lmv); err != nil { + return "", fmt.Errorf("failed to delete '%s': %v", lmv, err) + } + if err := os.Rename(stage, mod.DestDir()); err != nil { + return "", fmt.Errorf("failed to rename '%s' toi '%s': %v", stage, lmv, err) + } + return tag, nil + } + // for v1 it's a bit more complicated since stage is a subdir of LMV. + // first move stage to a temp dir outside of LMV, then remove LMV, then move temp to LMV + dest := filepath.Dir(stage) + temp := dest + "1temp" + if err := os.Rename(stage, temp); err != nil { + return "", fmt.Errorf("failed to rename '%s' to '%s': %v", stage, temp, err) + } + if err := os.RemoveAll(dest); err != nil { + return "", fmt.Errorf("failed to delete '%s': %v", dest, err) + } + if err := os.Rename(temp, dest); err != nil { + return "", fmt.Errorf("failed to rename '%s' to '%s': %v", temp, dest, err) + } + return tag, nil +} + +func updateVersion(path, tag string) error { + version := semverRegex.FindString(tag) + vprintf("Updating version.go file in %s with version %s\n", path, version) + v, err := semver.NewVersion(version) + if err != nil { + return err + } + version = v.String() + // version.go file must exists + file := filepath.Join(path, "version.go") + if _, err := os.Stat(file); os.IsNotExist(err) { + return errors.New("version.go file does not exist") + } + verFile, err := os.OpenFile(file, os.O_RDWR, 0666) + defer verFile.Close() + if err != nil { + return err + } + scanner := bufio.NewScanner(verFile) + scanner.Split(bufio.ScanLines) + lines := make([]string, 0) + for scanner.Scan() { + lines = append(lines, scanner.Text()) + } + _, err = verFile.Seek(0, io.SeekStart) // set pointer to start of the file + if err != nil { + return fmt.Errorf("failed to seek to start: %v", err) + } + hasTag := false + for _, line := range lines { + if !strings.HasPrefix(line, "// ") && versionGoRegex.MatchString(line) { + // line = strings.ReplaceAll(line, defaultVersion, version) + line = versionGoRegex.ReplaceAllString(line, version) + } + if strings.HasPrefix(line, "// tag: ") { + line = fmt.Sprintf("// tag: %s", tag) + hasTag = true + } + fmt.Fprintln(verFile, line) + } + if !hasTag { + fmt.Fprintf(verFile, "\n// tag: %s\n", tag) + } + return nil +} + +// returns the absolute path to the latest major version based on the provided staging directory. +// it's assumed that the staging directory is a subdirectory of the actual package directory. +func findLatestMajorVersion(stage string) (string, error) { + // example input: + // ~/work/src/github.com/Azure/azure-sdk-for-go/services/redis/mgmt/2018-03-01/redis/stage + // finds: + // ~/work/src/github.com/Azure/azure-sdk-for-go/services/redis/mgmt/2018-03-01/redis + // ~/work/src/github.com/Azure/azure-sdk-for-go/services/redis/mgmt/2018-03-01/redis/v2 + // returns: + // ~/work/src/github.com/Azure/azure-sdk-for-go/services/redis/mgmt/2018-03-01/redis/v2 + parent := filepath.Dir(stage) + dirs, err := modinfo.GetModuleSubdirs(parent) + if err != nil { + return "", fmt.Errorf("failed to get module subdirs '%s': %v", parent, err) + } + // no dirs means this is a v1 package + if len(dirs) == 0 { + return parent, nil + } + sort.Strings(dirs) + // last one in the slice is the largest + v := filepath.Join(parent, dirs[len(dirs)-1]) + vprintf("Latest major version: %v\n", v) + return v, nil +} + +// updates the module version inside the go.mod file +func updateGoModVer(goMod io.ReadWriteSeeker, newVer string) error { + if newVer == "" { + return nil + } + scanner := bufio.NewScanner(goMod) + scanner.Split(bufio.ScanLines) + lines := make([]string, 0) + for scanner.Scan() { + lines = append(lines, scanner.Text()) + } + _, err := goMod.Seek(0, io.SeekStart) + if err != nil { + return fmt.Errorf("failed to seek to start: %v", err) + } + for _, line := range lines { + if strings.Index(line, "module") > -1 { + if modinfo.HasVersionSuffix(line) { + line = strings.Replace(line, "/"+modinfo.FindVersionSuffix(line), "/"+newVer, 1) + } else { + line = line + "/" + newVer + } + } + fmt.Fprintln(goMod, line) + } + return nil +} + +// traversal all go source files in the stage folder, and replace the import statement with new ones +func updateImportStatement(stage, currentPath, ver string) error { + index := strings.Index(currentPath, "github.com") + if index < 0 { + return fmt.Errorf("github.com does not find in path '%s', this should never happen", currentPath) + } + newImport := strings.ReplaceAll(currentPath[index:], "\\", "/") + oldImport := newImport[:strings.LastIndex(newImport, "/"+ver)] + printf("Attempting to replace import statement from '%s' to '%s'\n", oldImport, newImport) + files, err := findAllFilesContainImportStatement(stage, oldImport) + if err != nil { + return err + } + printf("Found %d files with import statement of '%s'\n", len(files), oldImport) + vprintf("Files: \n%s", strings.Join(files, "\n")) + err = replaceImportStatement(files, oldImport, newImport) + if err != nil { + return err + } + return nil +} + +func findAllFilesContainImportStatement(path, importStatement string) ([]string, error) { + files := make([]string, 0) // files stores filenames for those content contained the given import statements + err := filepath.Walk(path, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if !info.IsDir() && strings.HasSuffix(info.Name(), ".go") { + // read every line of this file + file, err := os.Open(path) + defer file.Close() + if err != nil { + return fmt.Errorf("failed to open file '%s': %v", path, err) + } + scanner := bufio.NewScanner(file) + scanner.Split(bufio.ScanLines) + for scanner.Scan() { + line := scanner.Text() + if strings.Index(line, importStatement) > -1 { + files = append(files, path) + } + } + return nil + } + return nil + }) + return files, err +} + +func replaceImportStatement(files []string, oldImport, newImport string) error { + for _, file := range files { + err := replaceImportInFile(file, oldImport, newImport) + if err != nil { + return fmt.Errorf("failed to preform replace in file '%s'", file) + } + } + return nil +} + +func replaceImportInFile(filepath, oldContent, newContent string) error { + bytes, err := ioutil.ReadFile(filepath) + if err != nil { + return fmt.Errorf("failed to open file '%s': %v", filepath, err) + } + content := string(bytes) + importStatements, err := findImportStatements(content) + if err != nil { + return err + } + newImportStatements := strings.ReplaceAll(importStatements, oldContent, newContent) + newFileContent := strings.ReplaceAll(content, importStatements, newImportStatements) + if err := ioutil.WriteFile(filepath, []byte(newFileContent), 0755); err != nil { + return err + } + return nil +} + +func findImportStatements(content string) (string, error) { + oneLineImport := regexp.MustCompile(`import ".*"`) + if oneLineImport.MatchString(content) { + return oneLineImport.FindString(content), nil + } + multiLineRegex := `import \(\r?\n(\s*\".*\"\r?\n)+\s*\)` + multiLineImport := regexp.MustCompile(multiLineRegex) + if multiLineImport.MatchString(content) { + return multiLineImport.FindString(content), nil + } + return "", fmt.Errorf("failed to match import statement") +} + +func writeChangelog(stage string, mod modinfo.Provider) error { + // don't write a changelog for a new module + if mod.NewModule() { + return nil + } + rpt := mod.GenerateReport() + log, err := os.Create(filepath.Join(stage, changeLogName)) + if err != nil { + return fmt.Errorf("failed to create %s: %v", changeLogName, err) + } + defer log.Close() + if rpt.IsEmpty() { + _, err = log.WriteString("No changes to exported content compared to the previous release.\n") + return err + } + _, err = log.WriteString(rpt.ToMarkdown()) + return err +} + +func formatCode(folder string) error { + c := exec.Command("gofmt", "-w", folder) + if output, err := c.CombinedOutput(); err != nil { + return errors.New(string(output)) + } + return nil +} + +// returns a slice of tags for the specified repo and tag prefix +func getTags(repoPath, tagPrefix string) ([]string, error) { + wt, err := repo.Get(repoPath) + if err != nil { + return nil, err + } + return wt.ListTags(tagPrefix + "*") +} + +// returns the tag prefix for the specified package. +// assumes repo root of github.com/Azure/azure-sdk-for-go/ +func getTagPrefix(pkgDir string) (string, error) { + // e.g. /work/src/github.com/Azure/azure-sdk-for-go/services/redis/mgmt/2018-03-01/redis/v2 + // would return services/redis/mgmt/2018-03-01/redis/v2.0.0 + repoRoot := filepath.Join("github.com", "Azure", "azure-sdk-for-go") + i := strings.Index(pkgDir, repoRoot) + if i < 0 { + return "", fmt.Errorf("didn't find '%s' in '%s'", repoRoot, pkgDir) + } + return strings.Replace(pkgDir[i+len(repoRoot)+1:], "\\", "/", -1), nil +} + +// returns the appropriate module tag based on the package version info +// tags - list of all current tags for the module +func calculateModuleTag(tags []string, mod modinfo.Provider) (string, error) { + if mod.BreakingChanges() && !mod.VersionSuffix() { + return "", errors.New("package has breaking changes but directory has no version suffix") + } + tagPrefix, err := getTagPrefix(mod.DestDir()) + if err != nil { + return "", err + } + // if this has breaking changes then it's simply the prefix as a new major version + if mod.BreakingChanges() { + return tagPrefix + ".0.0", nil + } + if len(tags) == 0 { + if mod.VersionSuffix() { + panic("module contains a version suffix but no tags were found") + } + // this is the first module version + return tagPrefix + "/" + startingModVer, nil + } + if !mod.VersionSuffix() { + tagPrefix = tagPrefix + "/v1" + } + tag := tags[len(tags)-1] + v := semverRegex.FindString(tag) + if v == "" { + return "", fmt.Errorf("didn't find semver in tag '%s'", tag) + } + sv, err := semver.NewVersion(v) + if err != nil { + return "", fmt.Errorf("failed to parse semver: %v", err) + } + // for non-breaking changes determine if this is a minor or patch update. + if mod.NewExports() { + // new exports, this is a minor update so bump minor version + n := sv.IncMinor() + sv = &n + } else { + // no new exports, this is a patch update + n := sv.IncPatch() + sv = &n + } + return strings.Replace(tag, v, "v"+sv.String(), 1), nil +} diff --git a/tools/versioner/cmd/unstage_test.go b/tools/versioner/cmd/unstage_test.go new file mode 100644 index 000000000000..5a364001e073 --- /dev/null +++ b/tools/versioner/cmd/unstage_test.go @@ -0,0 +1,638 @@ +// Copyright 2018 Microsoft Corporation +// +// 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 cmd + +import ( + "bytes" + "fmt" + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "strings" + "testing" + + "github.com/Azure/azure-sdk-for-go/tools/apidiff/report" + "github.com/Azure/azure-sdk-for-go/tools/internal/modinfo" +) + +const versionGoFormat = `package foo + +// Copyright (c) Microsoft and contributors. All rights reserved. +// +// 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. +// +// Code generated by Microsoft (R) AutoRest Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +// UserAgent returns the UserAgent string to use when sending http.Requests. +func UserAgent() string { + return "Azure-SDK-For-Go/%s foo/2019-04-01" +} + +// Version returns the semantic version (see http://semver.org) of the client. +func Version() string { + return "%s" +} + +// tag: %s +` + +func Test_getTags(t *testing.T) { + if os.Getenv("TRAVIS") == "true" { + // travis does a shallow clone so tag count is not consistent + t.SkipNow() + } + cwd, err := os.Getwd() + if err != nil { + t.Fatalf("failed to get cwd: %v", err) + } + tags, err := getTags(cwd, "v10") + if err != nil { + t.Fatalf("failed to get tags: %v", err) + } + if l := len(tags); l != 11 { + t.Fatalf("expected 11 tags, got %d", l) + } + found := false + for _, tag := range tags { + if tag == "v10.1.0-beta" { + found = true + break + } + } + if !found { + t.Fatal("didn't find tag v10.1.0-beta") + } +} + +func Test_getTagPrefix(t *testing.T) { + p, err := getTagPrefix(filepath.Join("work", "src", "github.com", "Azure", "azure-sdk-for-go", "services", "redis", "mgmt", "2018-03-01", "redis")) + if err != nil { + t.Fatal("failed to get tag prefix") + } + if p != "services/redis/mgmt/2018-03-01/redis" { + t.Fatalf("wrong value '%s' for tag prefix", p) + } + p, err = getTagPrefix("/work/src/github.com/something/else") + if err == nil { + t.Fatal("unexpected nil error") + } + if p != "" { + t.Fatalf("unexpected tag '%s'", p) + } +} + +type mockModInfo struct { + dir string + exports bool + breaks bool +} + +func (mock mockModInfo) DestDir() string { + return mock.dir +} + +func (mock mockModInfo) NewExports() bool { + return mock.exports +} + +func (mock mockModInfo) BreakingChanges() bool { + return mock.breaks +} + +func (mock mockModInfo) VersionSuffix() bool { + return modinfo.HasVersionSuffix(mock.dir) +} + +func (mock mockModInfo) NewModule() bool { + // not needed by tests + return false +} + +func (mock mockModInfo) GenerateReport() report.Package { + // not needed by tests + return report.Package{} +} + +func Test_calculateModuleTagMajorV1(t *testing.T) { + pkg := mockModInfo{ + dir: filepath.Join("work", "src", "github.com", "Azure", "azure-sdk-for-go", "services", "foo"), + } + tag, err := calculateModuleTag([]string{}, pkg) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if tag != "services/foo/v1.0.0" { + t.Fatalf("bad tag '%s", tag) + } +} + +func Test_calculateModuleTagMajorV2(t *testing.T) { + tags := []string{ + "services/foo/v1.0.0", + "services/foo/v1.1.0", + } + pkg := mockModInfo{ + dir: filepath.Join("work", "src", "github.com", "Azure", "azure-sdk-for-go", "services", "foo", "v2"), + breaks: true, + } + tag, err := calculateModuleTag(tags, pkg) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if tag != "services/foo/v2.0.0" { + t.Fatalf("bad tag '%s", tag) + } +} + +func Test_calculateModuleTagMajorV2Invalid(t *testing.T) { + tags := []string{ + "services/foo/v1.0.0", + "services/foo/v1.1.0", + } + pkg := mockModInfo{ + dir: filepath.Join("work", "src", "github.com", "Azure", "azure-sdk-for-go", "services", "foo"), // missing /v2 suffix + breaks: true, + } + tag, err := calculateModuleTag(tags, pkg) + if err == nil { + t.Fatal("expected non-nil error") + } + if tag != "" { + t.Fatal("expected no tag") + } +} + +func Test_calculateModuleTagMinorV1(t *testing.T) { + tags := []string{ + "services/foo/v1.0.0", + "services/foo/v1.0.1", + } + pkg := mockModInfo{ + dir: filepath.Join("work", "src", "github.com", "Azure", "azure-sdk-for-go", "services", "foo"), + exports: true, + } + tag, err := calculateModuleTag(tags, pkg) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if tag != "services/foo/v1.1.0" { + t.Fatalf("bad tag '%s", tag) + } +} + +func Test_calculateModuleTagMinorV2(t *testing.T) { + tags := []string{ + "services/foo/v1.0.0", + "services/foo/v1.0.1", + "services/foo/v2.0.0", + } + pkg := mockModInfo{ + dir: filepath.Join("work", "src", "github.com", "Azure", "azure-sdk-for-go", "services", "foo", "v2"), + } + tag, err := calculateModuleTag(tags, pkg) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if tag != "services/foo/v2.0.1" { + t.Fatalf("bad tag '%s", tag) + } +} + +func Test_findLatestMajorVersion(t *testing.T) { + ver, err := findLatestMajorVersion("../../testdata/scenarioa/foo/stage") + if err != nil { + t.Fatalf("failed to find LMV: %v", err) + } + if ver != filepath.Join("..", "..", "testdata", "scenarioa", "foo") { + t.Fatalf("bad LMV %s", ver) + } + ver, err = findLatestMajorVersion("../../testdata/scenariod/foo/stage") + if err != nil { + t.Fatalf("failed to find LMV: %v", err) + } + if ver != filepath.Join("..", "..", "testdata", "scenariod", "foo", "v2") { + t.Fatalf("bad LMV %s", ver) + } + ver, err = findLatestMajorVersion("../../testdata/scenariof/foo/stage") + if err != nil { + t.Fatalf("failed to find LMV: %v", err) + } + if ver != filepath.Join("..", "..", "testdata", "scenariof", "foo") { + t.Fatalf("bad LMV %s", ver) + } +} + +type byteBufferSeeker struct { + buf *bytes.Buffer +} + +func (b byteBufferSeeker) Read(p []byte) (int, error) { + return b.buf.Read(p) +} + +func (b byteBufferSeeker) Write(p []byte) (int, error) { + return b.buf.Write(p) +} + +func (b byteBufferSeeker) Seek(offset int64, whence int) (int64, error) { + if offset != 0 && whence != 0 { + panic("seek only supports 0, 0") + } + b.buf.Reset() + return 0, nil +} + +func Test_updateGoModVerA(t *testing.T) { + // updates from v1 to v2 + before := fmt.Sprintf("module github.com/Azure/azure-sdk-for-go/services/foo/mgmt/2019-05-01/foo\n\n%s\n", goVersion) + buf := byteBufferSeeker{ + buf: bytes.NewBuffer([]byte(before)), + } + err := updateGoModVer(buf, "v2") + if err != nil { + t.Fatalf("updateGoModVerA failed: %v", err) + } + after := fmt.Sprintf("module github.com/Azure/azure-sdk-for-go/services/foo/mgmt/2019-05-01/foo/v2\n\n%s\n", goVersion) + if !strings.EqualFold(buf.buf.String(), after) { + t.Fatalf("bad go.mod update, epected %s got %s", after, buf.buf.String()) + } +} + +func Test_updateGoModVerB(t *testing.T) { + // updates from v2 to v3 + before := fmt.Sprintf("module github.com/Azure/azure-sdk-for-go/services/foo/mgmt/2019-05-01/foo/v2\n\n%s\n", goVersion) + buf := byteBufferSeeker{ + buf: bytes.NewBuffer([]byte(before)), + } + err := updateGoModVer(buf, "v3") + if err != nil { + t.Fatalf("updateGoModVerA failed: %v", err) + } + after := fmt.Sprintf("module github.com/Azure/azure-sdk-for-go/services/foo/mgmt/2019-05-01/foo/v3\n\n%s\n", goVersion) + if !strings.EqualFold(buf.buf.String(), after) { + t.Fatalf("bad go.mod update, epected %s got %s", after, buf.buf.String()) + } +} + +func fileExists(path string) bool { + _, err := os.Stat(path) + if err == nil { + return true + } else if os.IsNotExist(err) { + return false + } + panic(err) +} + +func verifyVersion(t *testing.T, path, version, tag string) { + b, err := ioutil.ReadFile(filepath.Join(path, "version.go")) + if err != nil { + t.Fatalf("failed to read version.go file: %v", err) + } + expected := fmt.Sprintf(versionGoFormat, version, version, tag) + if !fileContentEquals(expected, string(b)) { + t.Fatalf("bad version.go file, expected '%s' got '%s'", expected, string(b)) + } +} + +func verifyGoMod(t *testing.T, path, expected string) { + b, err := ioutil.ReadFile(filepath.Join(path, "go.mod")) + if err != nil { + t.Fatalf("failed to read go.mod file: %v", err) + } + if !fileContentEquals(expected, string(b)) { + t.Fatalf("bad go.mod file, expected '%s' got '%s'", expected, string(b)) + } +} + +func verifyChangelog(t *testing.T, path string) { + if !fileExists(filepath.Join(path, "CHANGELOG.md")) { + t.Fatalf("expected changelog in %s", path) + } +} + +func verifyNoChangelog(t *testing.T, path string) { + if fileExists(filepath.Join(path, "CHANGELOG.md")) { + t.Fatalf("unexpected changelog in %s", path) + } +} + +func verifyGoVet(t *testing.T, root string) { + root, err := filepath.Abs(root) + if err != nil { + t.Fatalf("failed to get absolute path: %v", err) + } + folders := make([]string, 0) + err = filepath.Walk(root, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if info.IsDir() { + pkg := path[strings.Index(path, "github.com"):] + folders = append(folders, pkg) + return nil + } + return nil + }) + if err != nil { + t.Fatalf("failed to list all sub-folders in root '%s': %v", root, err) + } + for _, folder := range folders { + c := exec.Command("go", "vet", folder) + if output, err := c.CombinedOutput(); err != nil { + t.Fatalf("vet failed: %s", string(output)) + } + } +} + +func fileContentEquals(expected, content string) bool { + replacedContent := strings.ReplaceAll(content, "\r\n", "\n") + return strings.EqualFold(replacedContent, expected) +} + +// scenariob +func Test_theCommandImplMajor(t *testing.T) { + cleanTestData() + defer cleanTestData() + getTagsHook = func(root, prefix string) ([]string, error) { + // root doesn't matter + if !strings.HasSuffix(prefix, "/testdata/scenariob/foo") { + return nil, fmt.Errorf("bad prefix '%s'", prefix) + } + return []string{ + "tools/testdata/scenariob/foo/v1.0.0", + "tools/testdata/scenariob/foo/v1.1.0", + }, nil + } + pkg, err := filepath.Abs("../../testdata/scenariob/foo/stage") + if err != nil { + t.Fatalf("failed to get absolute path: %v", err) + } + tag, err := theUnstageCommand([]string{pkg}) + if err != nil { + t.Fatalf("failed: %v", err) + } + const expectedTag = "tools/testdata/scenariob/foo/v2.0.0" + if tag != expectedTag { + t.Fatalf("bad tag, expected '%s' got '%s'", expectedTag, tag) + } + expectedMod := fmt.Sprintf("module github.com/Azure/azure-sdk-for-go/tools/testdata/scenariob/foo/v2\n\n%s\n", goVersion) + verifyGoMod(t, "../../testdata/scenariob/foo/v2", expectedMod) + verifyVersion(t, "../../testdata/scenariob/foo/v2", "2.0.0", tag) + verifyChangelog(t, "../../testdata/scenariob/foo/v2") + verifyGoVet(t, "../../testdata/scenariob/foo") +} + +// scenarioa +func Test_theCommandImplMinor(t *testing.T) { + cleanTestData() + defer cleanTestData() + getTagsHook = func(root, prefix string) ([]string, error) { + // root doesn't matter + if !strings.HasSuffix(prefix, "/testdata/scenarioa/foo") { + return nil, fmt.Errorf("bad prefix '%s'", prefix) + } + return []string{ + "tools/testdata/scenarioa/foo/v1.0.0", + }, nil + } + pkg, err := filepath.Abs("../../testdata/scenarioa/foo/stage") + if err != nil { + t.Fatalf("failed to get absolute path: %v", err) + } + tag, err := theUnstageCommand([]string{pkg}) + if err != nil { + t.Fatalf("failed: %v", err) + } + const expectedTag = "tools/testdata/scenarioa/foo/v1.1.0" + if tag != expectedTag { + t.Fatalf("bad tag, expected '%s' got '%s'", expectedTag, tag) + } + expectedMod := fmt.Sprintf("module github.com/Azure/azure-sdk-for-go/tools/testdata/scenarioa/foo\n\n%s\n", goVersion) + verifyGoMod(t, "../../testdata/scenarioa/foo", expectedMod) + verifyVersion(t, "../../testdata/scenarioa/foo", "1.1.0", tag) + verifyChangelog(t, "../../testdata/scenarioa/foo") + verifyGoVet(t, "../../testdata/scenarioa/foo") +} + +// scenarioc +func Test_theCommandImplPatch(t *testing.T) { + cleanTestData() + defer cleanTestData() + getTagsHook = func(root, prefix string) ([]string, error) { + // root doesn't matter + if !strings.HasSuffix(prefix, "/testdata/scenarioc/foo") { + return nil, fmt.Errorf("bad prefix '%s'", prefix) + } + return []string{ + "tools/testdata/scenarioc/foo/v1.0.0", + }, nil + } + pkg, err := filepath.Abs("../../testdata/scenarioc/foo/stage") + if err != nil { + t.Fatalf("failed to get absolute path: %v", err) + } + tag, err := theUnstageCommand([]string{pkg}) + if err != nil { + t.Fatalf("failed: %v", err) + } + const expected = "tools/testdata/scenarioc/foo/v1.0.1" + if tag != expected { + t.Fatalf("bad tag, expected '%s' got '%s'", expected, tag) + } + expectedMod := fmt.Sprintf("module github.com/Azure/azure-sdk-for-go/tools/testdata/scenarioc/foo\n\n%s\n", goVersion) + verifyGoMod(t, "../../testdata/scenarioc/foo", expectedMod) + verifyVersion(t, "../../testdata/scenarioc/foo", "1.0.1", tag) + verifyChangelog(t, "../../testdata/scenarioc/foo") + verifyGoVet(t, "../../testdata/scenarioc/foo") +} + +// scenariod +func Test_theCommandImplMajorV3(t *testing.T) { + cleanTestData() + defer cleanTestData() + getTagsHook = func(root, prefix string) ([]string, error) { + if !strings.HasSuffix(prefix, "/testdata/scenariod/foo/v2") { + return nil, fmt.Errorf("bad prefix '%s'", prefix) + } + return []string{ + "tools/testdata/scenariod/foo/v1.0.0", + "tools/testdata/scenariod/foo/v1.0.1", + "tools/testdata/scenariod/foo/v1.1.0", + "tools/testdata/scenariod/foo/v1.2.0", + "tools/testdata/scenariod/foo/v2.0.0", + "tools/testdata/scenariod/foo/v2.1.0", + "tools/testdata/scenariod/foo/v2.1.1", + }, nil + } + pkg, err := filepath.Abs("../../testdata/scenariod/foo/stage") + if err != nil { + t.Fatalf("failed to get absolute path: %v", err) + } + tag, err := theUnstageCommand([]string{pkg}) + if err != nil { + t.Fatalf("failed: %v", err) + } + const expectedTag = "tools/testdata/scenariod/foo/v3.0.0" + if tag != expectedTag { + t.Fatalf("bad tag, expected '%s' got '%s'", expectedTag, tag) + } + expectedMod := fmt.Sprintf("module github.com/Azure/azure-sdk-for-go/tools/testdata/scenariod/foo/v3\n\n%s\n", goVersion) + verifyGoMod(t, "../../testdata/scenariod/foo/v3", expectedMod) + verifyVersion(t, "../../testdata/scenariod/foo/v3", "3.0.0", tag) + verifyChangelog(t, "../../testdata/scenariod/foo/v3") + verifyGoVet(t, "../../testdata/scenariod/foo") +} + +// scenarioe +func Test_theCommandImplMajorMinor(t *testing.T) { + cleanTestData() + defer cleanTestData() + getTagsHook = func(root, prefix string) ([]string, error) { + // root doesn't matter + if !strings.HasSuffix(prefix, "/testdata/scenarioe/foo/v2") { + return nil, fmt.Errorf("bad prefix '%s'", prefix) + } + return []string{ + "tools/testdata/scenarioe/foo/v1.0.0", + "tools/testdata/scenarioe/foo/v1.1.0", + "tools/testdata/scenarioe/foo/v2.0.0", + "tools/testdata/scenarioe/foo/v2.1.0", + }, nil + } + pkg, err := filepath.Abs("../../testdata/scenarioe/foo/stage") + if err != nil { + t.Fatalf("failed to get absolute path: %v", err) + } + tag, err := theUnstageCommand([]string{pkg}) + if err != nil { + t.Fatalf("failed: %v", err) + } + const expectedTag = "tools/testdata/scenarioe/foo/v2.2.0" + if tag != expectedTag { + t.Fatalf("bad tag, expected '%s' got '%s'", expectedTag, tag) + } + expectedMod := fmt.Sprintf("module github.com/Azure/azure-sdk-for-go/tools/testdata/scenarioe/foo/v2\n\n%s\n", goVersion) + verifyGoMod(t, "../../testdata/scenarioe/foo/v2", expectedMod) + verifyVersion(t, "../../testdata/scenarioe/foo/v2", "2.2.0", tag) + verifyChangelog(t, "../../testdata/scenarioe/foo/v2") + verifyGoVet(t, "../../testdata/scenarioe/foo") +} + +// scenariof +func Test_theCommandImplNewMod(t *testing.T) { + cleanTestData() + defer cleanTestData() + getTagsHook = func(root, prefix string) ([]string, error) { + // root doesn't matter + if !strings.HasSuffix(prefix, "/testdata/scenariof/foo") { + return nil, fmt.Errorf("bad prefix '%s'", prefix) + } + return []string{}, nil + } + pkg, err := filepath.Abs("../../testdata/scenariof/foo/stage") + if err != nil { + t.Fatalf("failed to get absolute path: %v", err) + } + tag, err := theUnstageCommand([]string{pkg}) + if err != nil { + t.Fatalf("failed: %v", err) + } + const expectedTag = "tools/testdata/scenariof/foo/v1.0.0" + if tag != expectedTag { + t.Fatalf("bad tag, expected '%s' got '%s'", expectedTag, tag) + } + expectedMod := fmt.Sprintf("module github.com/Azure/azure-sdk-for-go/tools/testdata/scenariof/foo\n\n%s\n", goVersion) + verifyGoMod(t, "../../testdata/scenariof/foo", expectedMod) + verifyVersion(t, "../../testdata/scenariof/foo", "1.0.0", tag) + verifyNoChangelog(t, "../../testdata/scenariof/foo") + verifyGoVet(t, "../../testdata/scenariof/foo") +} + +// scenariog +func Test_theCommandNewMgmtMajorV2(t *testing.T) { + cleanTestData() + defer cleanTestData() + getTagsHook = func(root string, prefix string) ([]string, error) { + if !strings.HasPrefix(prefix, "/testdata/scenariog/foo") { + return nil, fmt.Errorf("bad prefix '%s'", prefix) + } + return []string{ + "/testdata/scenariog/foo/mgmt/2019-10-11/foo/v1.0.0", + }, nil + } + stage, err := filepath.Abs("../../testdata/scenariog/foo/mgmt/2019-10-11/foo/stage") + if err != nil { + t.Fatalf("failed: %v", err) + } + tag, err := theUnstageCommand([]string{stage}) + if err != nil { + t.Fatalf("failed: %v", err) + } + const expectedTag = "tools/testdata/scenariog/foo/mgmt/2019-10-11/foo/v2.0.0" + if tag != expectedTag { + t.Fatalf("bad tag, expected '%s' got '%s'", expectedTag, tag) + } + expectedMod := fmt.Sprintf("module github.com/Azure/azure-sdk-for-go/tools/testdata/scenariog/foo/v2\n\n%s\n", goVersion) + verifyGoMod(t, "../../testdata/scenariog/foo/mgmt/2019-10-11/foo/v2", expectedMod) + verifyVersion(t, "../../testdata/scenariog/foo/mgmt/2019-10-11/foo/v2", "2.0.0", tag) + verifyChangelog(t, "../../testdata/scenariog/foo/mgmt/2019-10-11/foo/v2") + verifyGoVet(t, "../../testdata/scenariog/foo/mgmt/2019-10-11/foo") +} + +// scenarioh +func Test_theCommandNewMgmtMajorV2WithOneLineImport(t *testing.T) { + cleanTestData() + defer cleanTestData() + getTagsHook = func(root string, prefix string) ([]string, error) { + if !strings.HasPrefix(prefix, "/testdata/scenarioh/foo") { + return nil, fmt.Errorf("bad prefix '%s'", prefix) + } + return []string{ + "/testdata/scenarioh/foo/mgmt/2019-10-11/foo/v1.0.0", + "/testdata/scenarioh/foo/mgmt/2019-10-11/foo/v1.1.0", + "/testdata/scenarioh/foo/mgmt/2019-10-11/foo/v1.2.0", + }, nil + } + stage, err := filepath.Abs("../../testdata/scenarioh/foo/mgmt/2019-10-11/foo/stage") + if err != nil { + t.Fatalf("failed: %v", err) + } + tag, err := theUnstageCommand([]string{stage}) + if err != nil { + t.Fatalf("failed: %v", err) + } + const expectedTag = "tools/testdata/scenarioh/foo/mgmt/2019-10-11/foo/v2.0.0" + if tag != expectedTag { + t.Fatalf("bad tag, expected '%s' got '%s'", expectedTag, tag) + } + expectedMod := fmt.Sprintf("module github.com/Azure/azure-sdk-for-go/tools/testdata/scenarioh/foo/v2\n\n%s\n", goVersion) + verifyGoMod(t, "../../testdata/scenarioh/foo/mgmt/2019-10-11/foo/v2", expectedMod) + verifyVersion(t, "../../testdata/scenarioh/foo/mgmt/2019-10-11/foo/v2", "2.0.0", tag) + verifyChangelog(t, "../../testdata/scenarioh/foo/mgmt/2019-10-11/foo/v2") + verifyGoVet(t, "../../testdata/scenarioh/foo/mgmt/2019-10-11/foo") +}