diff --git a/CHANGELOG.md b/CHANGELOG.md index 810d2381e0..99400a0df9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # v0.3.3 (Unreleased) NEW FEATURES: +* Add support for importing from [glock](https://github.com/robfig/glock) based projects (#1422). * Add support for importing from [govendor](https://github.com/kardianos/govendor) based projects (#815). diff --git a/cmd/dep/doc.go b/cmd/dep/doc.go index 7d7e508872..7d5afdcea1 100644 --- a/cmd/dep/doc.go +++ b/cmd/dep/doc.go @@ -38,7 +38,7 @@ // When configuration for another dependency management tool is detected, it is // imported into the initial manifest and lock. Use the -skip-tools flag to // disable this behavior. The following external tools are supported: -// glide, godep, vndr, govend, gb, gvt. +// glide, godep, vndr, govend, gb, gvt, glock. // // Any dependencies that are not constrained by external configuration use the // GOPATH analysis below. diff --git a/cmd/dep/init.go b/cmd/dep/init.go index 205ce83f4e..e09c1f20b0 100644 --- a/cmd/dep/init.go +++ b/cmd/dep/init.go @@ -30,7 +30,7 @@ specified, use the current directory. When configuration for another dependency management tool is detected, it is imported into the initial manifest and lock. Use the -skip-tools flag to disable this behavior. The following external tools are supported: -glide, godep, vndr, govend, gb, gvt, govendor. +glide, godep, vndr, govend, gb, gvt, govendor, glock. Any dependencies that are not constrained by external configuration use the GOPATH analysis below. diff --git a/cmd/dep/testdata/harness_tests/init/glock/case1/final/Gopkg.lock b/cmd/dep/testdata/harness_tests/init/glock/case1/final/Gopkg.lock new file mode 100644 index 0000000000..ac445c05d2 --- /dev/null +++ b/cmd/dep/testdata/harness_tests/init/glock/case1/final/Gopkg.lock @@ -0,0 +1,21 @@ +# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. + + +[[projects]] + name = "github.com/sdboyer/deptest" + packages = ["."] + revision = "3f4c3bea144e112a69bbe5d8d01c1b09a544253f" + version = "v0.8.1" + +[[projects]] + name = "github.com/sdboyer/deptestdos" + packages = ["."] + revision = "5c607206be5decd28e6263ffffdcee067266015e" + version = "v2.0.0" + +[solve-meta] + analyzer-name = "dep" + analyzer-version = 1 + inputs-digest = "1ed417a0bec57ffe988fae1cba8f3d49994fb893394d61844e0b3c96d69573fe" + solver-name = "gps-cdcl" + solver-version = 1 diff --git a/cmd/dep/testdata/harness_tests/init/glock/case1/final/Gopkg.toml b/cmd/dep/testdata/harness_tests/init/glock/case1/final/Gopkg.toml new file mode 100644 index 0000000000..aaf78303fa --- /dev/null +++ b/cmd/dep/testdata/harness_tests/init/glock/case1/final/Gopkg.toml @@ -0,0 +1,4 @@ + +[[constraint]] + name = "github.com/sdboyer/deptestdos" + version = "2.0.0" diff --git a/cmd/dep/testdata/harness_tests/init/glock/case1/initial/GLOCKFILE b/cmd/dep/testdata/harness_tests/init/glock/case1/initial/GLOCKFILE new file mode 100644 index 0000000000..27f499a427 --- /dev/null +++ b/cmd/dep/testdata/harness_tests/init/glock/case1/initial/GLOCKFILE @@ -0,0 +1,3 @@ +cmd github.com/golang/lint +github.com/sdboyer/deptest 3f4c3bea144e112a69bbe5d8d01c1b09a544253f +github.com/sdboyer/deptestdos 5c607206be5decd28e6263ffffdcee067266015e diff --git a/cmd/dep/testdata/harness_tests/init/glock/case1/initial/main.go b/cmd/dep/testdata/harness_tests/init/glock/case1/initial/main.go new file mode 100644 index 0000000000..2b2c7c396e --- /dev/null +++ b/cmd/dep/testdata/harness_tests/init/glock/case1/initial/main.go @@ -0,0 +1,16 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "fmt" + + "github.com/sdboyer/deptestdos" +) + +func main() { + var x deptestdos.Bar + fmt.Println(x) +} diff --git a/cmd/dep/testdata/harness_tests/init/glock/case1/testcase.json b/cmd/dep/testdata/harness_tests/init/glock/case1/testcase.json new file mode 100644 index 0000000000..017dc4cd55 --- /dev/null +++ b/cmd/dep/testdata/harness_tests/init/glock/case1/testcase.json @@ -0,0 +1,13 @@ +{ + "commands": [ + ["init", "-no-examples"] + ], + "error-expected": "", + "gopath-initial": { + "github.com/sdboyer/deptest": "3f4c3bea144e112a69bbe5d8d01c1b09a544253f" + }, + "vendor-final": [ + "github.com/sdboyer/deptest", + "github.com/sdboyer/deptestdos" + ] +} diff --git a/docs/FAQ.md b/docs/FAQ.md index 69aa21154a..2da12a9602 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -217,7 +217,7 @@ about what's going on. During `dep init` configuration from other dependency managers is detected and imported, unless `-skip-tools` is specified. -The following tools are supported: `glide`, `godep`, `vndr`, `govend`, `gb`, `gvt` and `govendor`. +The following tools are supported: `glide`, `godep`, `vndr`, `govend`, `gb`, `gvt`, `govendor` and `glock`. See [#186](https://github.com/golang/dep/issues/186#issuecomment-306363441) for how to add support for another tool. diff --git a/internal/importers/glock/importer.go b/internal/importers/glock/importer.go new file mode 100644 index 0000000000..a1d1cb8672 --- /dev/null +++ b/internal/importers/glock/importer.go @@ -0,0 +1,139 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package glock + +import ( + "bufio" + "fmt" + "log" + "os" + "path/filepath" + "strings" + + "github.com/golang/dep" + "github.com/golang/dep/gps" + "github.com/golang/dep/internal/importers/base" + "github.com/pkg/errors" +) + +const glockfile = "GLOCKFILE" + +// Importer imports glock configuration into the dep configuration format. +type Importer struct { + *base.Importer + + packages []glockPackage +} + +// NewImporter for glock. +func NewImporter(logger *log.Logger, verbose bool, sm gps.SourceManager) *Importer { + return &Importer{Importer: base.NewImporter(logger, verbose, sm)} +} + +// Name of the importer. +func (g *Importer) Name() string { + return "glock" +} + +// HasDepMetadata checks if a directory contains config that the importer can handle. +func (g *Importer) HasDepMetadata(dir string) bool { + path := filepath.Join(dir, glockfile) + if _, err := os.Stat(path); err != nil { + return false + } + + return true +} + +// Import the config found in the directory. +func (g *Importer) Import(dir string, pr gps.ProjectRoot) (*dep.Manifest, *dep.Lock, error) { + err := g.load(dir) + if err != nil { + return nil, nil, err + } + + return g.convert(pr) +} + +type glockPackage struct { + importPath string + revision string +} + +func (g *Importer) load(projectDir string) error { + g.Logger.Println("Detected glock configuration files...") + path := filepath.Join(projectDir, glockfile) + if g.Verbose { + g.Logger.Printf(" Loading %s", path) + } + + f, err := os.Open(path) + if err != nil { + return errors.Wrapf(err, "unable to open %s", path) + } + defer f.Close() + + scanner := bufio.NewScanner(f) + for scanner.Scan() { + pkg, err := parseGlockLine(scanner.Text()) + if err != nil { + return err + } + if pkg == nil { + continue + } + g.packages = append(g.packages, *pkg) + } + + return nil +} + +func parseGlockLine(line string) (*glockPackage, error) { + fields := strings.Fields(line) + switch len(fields) { + case 2: // Valid. + case 0: // Skip empty lines. + return nil, nil + default: + return nil, fmt.Errorf("invalid glock configuration: %s", line) + } + + // Skip commands. + if fields[0] == "cmd" { + return nil, nil + } + return &glockPackage{ + importPath: fields[0], + revision: fields[1], + }, nil +} + +func (g *Importer) convert(pr gps.ProjectRoot) (*dep.Manifest, *dep.Lock, error) { + g.Logger.Println("Converting from GLOCKFILE ...") + + packages := make([]base.ImportedPackage, 0, len(g.packages)) + for _, pkg := range g.packages { + // Validate + if pkg.importPath == "" { + return nil, nil, errors.New("invalid glock configuration, import path is required") + } + + if pkg.revision == "" { + return nil, nil, errors.New("invalid glock configuration, revision is required") + } + + packages = append(packages, base.ImportedPackage{ + Name: pkg.importPath, + LockHint: pkg.revision, + }) + } + + err := g.ImportPackages(packages, true) + if err != nil { + return nil, nil, err + } + + return g.Manifest, g.Lock, nil +} diff --git a/internal/importers/glock/importer_test.go b/internal/importers/glock/importer_test.go new file mode 100644 index 0000000000..cc38c7b8c2 --- /dev/null +++ b/internal/importers/glock/importer_test.go @@ -0,0 +1,133 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package glock + +import ( + "bytes" + "fmt" + "log" + "path/filepath" + "testing" + + "github.com/golang/dep" + "github.com/golang/dep/gps" + "github.com/golang/dep/internal/importers/importertest" + "github.com/golang/dep/internal/test" + "github.com/pkg/errors" +) + +func TestGlockConfig_Convert(t *testing.T) { + testCases := map[string]struct { + importertest.TestCase + packages []glockPackage + }{ + "package": { + importertest.TestCase{ + WantConstraint: importertest.V1Constraint, + WantRevision: importertest.V1Rev, + WantVersion: importertest.V1Tag, + }, + []glockPackage{ + { + importPath: importertest.Project, + revision: importertest.V1Rev, + }, + }, + }, + "missing package name": { + importertest.TestCase{ + WantConvertErr: true, + }, + []glockPackage{{importPath: ""}}, + }, + "missing revision": { + importertest.TestCase{ + WantConvertErr: true, + }, + []glockPackage{{importPath: importertest.Project}}, + }, + } + + for name, testCase := range testCases { + name := name + testCase := testCase + t.Run(name, func(t *testing.T) { + err := testCase.Execute(t, func(logger *log.Logger, sm gps.SourceManager) (*dep.Manifest, *dep.Lock, error) { + g := NewImporter(logger, true, sm) + g.packages = testCase.packages + return g.convert(importertest.RootProject) + }) + if err != nil { + t.Fatalf("%#v", err) + } + }) + } +} + +func TestGlockConfig_LoadInvalid(t *testing.T) { + const testLine = "github.com/sdboyer/deptest 3f4c3bea144e112a69bbe5d8d01c1b09a544253f invalid" + _, err := parseGlockLine(testLine) + expected := fmt.Errorf("invalid glock configuration: %s", testLine) + if err.Error() != expected.Error() { + t.Errorf("want error %s, got %s", err, expected) + } +} + +func TestGlockConfig_LoadEmptyLine(t *testing.T) { + pkg, err := parseGlockLine("") + if err != nil { + t.Fatalf("%#v", err) + } + if pkg != nil { + t.Errorf("want package nil, got %+v", pkg) + } +} + +func TestGlockConfig_Import(t *testing.T) { + h := test.NewHelper(t) + defer h.Cleanup() + + ctx := importertest.NewTestContext(h) + sm, err := ctx.SourceManager() + h.Must(err) + defer sm.Release() + + h.TempDir(filepath.Join("src", importertest.RootProject)) + h.TempCopy(filepath.Join(importertest.RootProject, glockfile), glockfile) + projectRoot := h.Path(importertest.RootProject) + + // Capture stderr so we can verify output + verboseOutput := &bytes.Buffer{} + ctx.Err = log.New(verboseOutput, "", 0) + + g := NewImporter(ctx.Err, false, sm) // Disable verbose so that we don't print values that change each test run + if !g.HasDepMetadata(projectRoot) { + t.Fatal("Expected the importer to detect the glock configuration files") + } + + m, l, err := g.Import(projectRoot, importertest.RootProject) + h.Must(err) + + if m == nil { + t.Fatal("Expected the manifest to be generated") + } + + if l == nil { + t.Fatal("Expected the lock to be generated") + } + + goldenFile := "golden.txt" + got := verboseOutput.String() + want := h.GetTestFileString(goldenFile) + if want != got { + if *test.UpdateGolden { + if err := h.WriteTestFile(goldenFile, got); err != nil { + t.Fatalf("%+v", errors.Wrapf(err, "Unable to write updated golden file %s", goldenFile)) + } + } else { + t.Fatalf("want %s, got %s", want, got) + } + } +} diff --git a/internal/importers/glock/testdata/GLOCKFILE b/internal/importers/glock/testdata/GLOCKFILE new file mode 100644 index 0000000000..27f499a427 --- /dev/null +++ b/internal/importers/glock/testdata/GLOCKFILE @@ -0,0 +1,3 @@ +cmd github.com/golang/lint +github.com/sdboyer/deptest 3f4c3bea144e112a69bbe5d8d01c1b09a544253f +github.com/sdboyer/deptestdos 5c607206be5decd28e6263ffffdcee067266015e diff --git a/internal/importers/glock/testdata/golden.txt b/internal/importers/glock/testdata/golden.txt new file mode 100644 index 0000000000..3d7781b4f3 --- /dev/null +++ b/internal/importers/glock/testdata/golden.txt @@ -0,0 +1,6 @@ +Detected glock configuration files... +Converting from GLOCKFILE ... + Using ^0.8.1 as initial constraint for imported dep github.com/sdboyer/deptest + Trying v0.8.1 (3f4c3be) as initial lock for imported dep github.com/sdboyer/deptest + Using ^2.0.0 as initial constraint for imported dep github.com/sdboyer/deptestdos + Trying v2.0.0 (5c60720) as initial lock for imported dep github.com/sdboyer/deptestdos diff --git a/internal/importers/importers.go b/internal/importers/importers.go index 3f535e2c5e..d54277c14b 100644 --- a/internal/importers/importers.go +++ b/internal/importers/importers.go @@ -10,6 +10,7 @@ import ( "github.com/golang/dep" "github.com/golang/dep/gps" "github.com/golang/dep/internal/importers/glide" + "github.com/golang/dep/internal/importers/glock" "github.com/golang/dep/internal/importers/godep" "github.com/golang/dep/internal/importers/govend" "github.com/golang/dep/internal/importers/govendor" @@ -39,5 +40,6 @@ func BuildAll(logger *log.Logger, verbose bool, sm gps.SourceManager) []Importer govend.NewImporter(logger, verbose, sm), gvt.NewImporter(logger, verbose, sm), govendor.NewImporter(logger, verbose, sm), + glock.NewImporter(logger, verbose, sm), } }