Skip to content

Commit

Permalink
Refactor versioner (Azure#1461)
Browse files Browse the repository at this point in the history
* Ensures go.mod exists before doing actual work

* Fix wrong module name

* Add a new test scenario

* Some refinement on testcases for versioner

* Update version in version.go

* Only keep one line of tag in version.go

* Minor fix

* No need to print tag to stdout

* New test scenario

* Fix linter problem

* Return error from filepath.Abs

* Add test for update version.go file

* Remove method that ensures existence of go.mod file, and some redaudent
test cases

* Formatting code after versioning done

* Refine tests

* Fix a bug that when update applied to v2, the go.mod file does not
update as v2

* Clean up

* Purely file rename, nothing else changed

* Add functionality for run tool in root folder

* Add root command again

* Fix format

* Typo fix

* Add test for list all stage folders

* Add two functions for programmatically call

* Fix CI failure

* Add a new test case for more realistic

* Fix errors in the new test case

* Fix gofmt issues

* Add a new struct to cover this scenario

* Add test for go vet

* Add new test case for one line import

* Format stage folder first to avoid un-expected changes in code

* Fix a bug in regex used for multiline import statement

* Move getPkgs method to an internal package and exported

* Fix broken tests

* Add comment to silence golint error

* Init command

* Format change

* More minor tweak to satisfy golint
  • Loading branch information
ArcturusZhang authored Oct 18, 2019
1 parent fbbae7a commit 6e9e666
Show file tree
Hide file tree
Showing 58 changed files with 2,716 additions and 783 deletions.
118 changes: 118 additions & 0 deletions tools/internal/pkgs/pkgs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
// 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/(.+)/`)
versionString := regex.FindStringSubmatch(dest)[1]
return versionString, nil
}
// data-plane
regex := regexp.MustCompile(`/(\d{4}-\d{2}.*|v?\d+(\.\d+)?)/`)
versionString := regex.FindStringSubmatch(dest)[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
}
106 changes: 106 additions & 0 deletions tools/internal/pkgs/pkgs_test.go
Original file line number Diff line number Diff line change
@@ -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)
}
}
}
Loading

0 comments on commit 6e9e666

Please sign in to comment.