Merge branch 'master' into add-gps

This commit is contained in:
Kris Nova 2017-05-01 20:34:30 -06:00 коммит произвёл GitHub
Родитель 379102bd93 7948c8a848
Коммит 4d5c597e0f
48 изменённых файлов: 876 добавлений и 333 удалений

10
.codeclimate.yml Normal file
Просмотреть файл

@ -0,0 +1,10 @@
engines:
gofmt:
enabled: true
govet:
enabled: true
ratings:
paths:
- "**.go"
exclude_paths:
- vendor/

Просмотреть файл

@ -16,8 +16,13 @@ matrix:
install:
- echo "This is an override of the default install deps step in travis."
before_script:
# OSX as of El Capitan sets an exit trap that interacts poorly with our
# set -e below. So, unset the trap.
# Related: https://superuser.com/questions/1044130/why-am-i-having-how-can-i-fix-this-error-shell-session-update-command-not-f
- if [[ "$(go env GOHOSTOS)" == "darwin" ]]; then trap EXIT; fi
- PKGS=$(go list ./... | grep -v /vendor/)
- go get -v honnef.co/go/tools/cmd/{gosimple,staticcheck}
- npm install -g codeclimate-test-reporter
script:
- go build -v ./cmd/dep
- go vet $PKGS
@ -26,5 +31,10 @@ script:
- ./hack/validate-gofmt.bash
- ./hack/validate-vendor.bash
- gosimple $PKGS
- go test -race $PKGS
#- go test -race $PKGS
- go build ./hack/licenseok
- set -e; for pkg in $PKGS; do go test -race -coverprofile=profile.out -covermode=atomic $pkg; if [[ -f profile.out ]]; then cat profile.out >> coverage.txt; rm profile.out; fi; done
- find . -path ./vendor -prune -o -type f -name "*.go" -printf '%P\n' | xargs ./licenseok
- ./hack/validate-vendor.bash
after_success:
- codeclimate-test-reporter < coverage.txt

14
FAQ.md
Просмотреть файл

@ -17,16 +17,22 @@ Summarize the question and quote the reply, linking back to the original comment
* [Why did dep use a different revision for package X instead of the revision in the lock file?](#why-did-dep-use-a-different-revision-for-package-x-instead-of-the-revision-in-the-lock-file)
## What is a direct or transitive dependency?
* Direct dependencies appear in at least one import statement from your project - are dependencies that are imported by your project.
* Direct dependencies are dependencies that are imported directly by your project: they appear in at least one import statement from your project.
* Transitive dependencies are the dependencies of your dependencies. Necessary to compile but are not directly used by your code.
## Should I commit my vendor directory?
Committing the vendor directory is totally up to you. There is no general advice that applies in all cases.
It's up to you:
**Pros**: it's the only way to get truly reproducible builds, as it guards against upstream renames and deletes; and you don't need an extra `dep ensure` step on fresh clones to build your repo.
**Pros**
**Cons**: your repo will be bigger, potentially a lot bigger; and PR diffs are more annoying.
- it's the only way to get truly reproducible builds, as it guards against upstream renames and deletes
- you don't need an extra `dep ensure` step (to fetch dependencies) on fresh clones to build your repo
**Cons**
- your repo will be bigger, potentially a lot bigger
- PR diffs are more annoying
## Why is it `dep ensure` instead of `dep install`?

2
Gopkg.lock сгенерированный
Просмотреть файл

@ -1,4 +1,4 @@
memo = "099c73630ad2c4f0894ed8646e2e4b5a9f635c85661a77fbf3b9f9dd78c77e87"
memo = "71329a18f735441776be73d92d064f26a67fa30e616bbdfbb47e8dc68bda8c5c"
[[projects]]
branch = "2.x"

Просмотреть файл

@ -1,3 +1,5 @@
# Temporarily, until gps moves in and this becomes a direct dep again
required = ["github.com/Masterminds/semver"]
[[dependencies]]
branch = "2.x"

Просмотреть файл

@ -1,6 +1,6 @@
# Dep
Linux: [![Build Status](https://travis-ci.org/golang/dep.svg?branch=master)](https://travis-ci.org/golang/dep) | Windows: [![Build status](https://ci.appveyor.com/api/projects/status/4pu2xnnrikol2gsf/branch/master?svg=true)](https://ci.appveyor.com/project/golang/dep/branch/master)
Linux: [![Build Status](https://travis-ci.org/golang/dep.svg?branch=master)](https://travis-ci.org/golang/dep) | Windows: [![Build status](https://ci.appveyor.com/api/projects/status/4pu2xnnrikol2gsf/branch/master?svg=true)](https://ci.appveyor.com/project/golang/dep/branch/master) | [![Code Climate](https://codeclimate.com/github/golang/dep/badges/gpa.svg)](https://codeclimate.com/github/golang/dep)
Dep is a prototype dependency management tool. It requires Go 1.7 or newer to compile.

Просмотреть файл

@ -23,7 +23,7 @@ func (a Analyzer) DeriveManifestAndLock(path string, n gps.ProjectRoot) (gps.Man
}
f, err := os.Open(mf)
if err != nil {
return nil, nil, nil
return nil, nil, err
}
defer f.Close()

Просмотреть файл

@ -5,15 +5,15 @@
package dep
import (
"io/ioutil"
"os"
"path/filepath"
"runtime"
"testing"
"github.com/golang/dep/test"
)
func TestDeriveManifestAndLock(t *testing.T) {
func TestAnalyzerDeriveManifestAndLock(t *testing.T) {
h := test.NewHelper(t)
defer h.Cleanup()
@ -49,17 +49,75 @@ func TestDeriveManifestAndLock(t *testing.T) {
}
}
func TestDeriveManifestAndLockDoesNotExist(t *testing.T) {
dir, err := ioutil.TempDir("", "dep")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(dir)
func TestAnalyzerDeriveManifestAndLockDoesNotExist(t *testing.T) {
h := test.NewHelper(t)
defer h.Cleanup()
h.TempDir("dep")
a := Analyzer{}
m, l, err := a.DeriveManifestAndLock(dir, "my/fake/project")
m, l, err := a.DeriveManifestAndLock(h.Path("dep"), "my/fake/project")
if m != nil || l != nil || err != nil {
t.Fatalf("expected manifest & lock & err to be nil: m -> %#v l -> %#v err-> %#v", m, l, err)
}
}
func TestAnalyzerDeriveManifestAndLockCannotOpen(t *testing.T) {
if runtime.GOOS == "windows" {
// TODO: find an implementation that works on Microsoft
// Windows. Setting permissions works differently there.
// os.Chmod(..., 0222) below is not enough for the file
// to be write-only (unreadable), and os.Chmod(...,
// 0000) returns an invalid argument error.
t.Skip("skipping on windows")
}
h := test.NewHelper(t)
defer h.Cleanup()
h.TempDir("dep")
// Create an empty manifest file
h.TempFile(filepath.Join("dep", ManifestName), "")
// Change its mode so that it cannot be read
err := os.Chmod(filepath.Join(h.Path("dep"), ManifestName), 0222)
if err != nil {
t.Fatal(err)
}
a := Analyzer{}
m, l, err := a.DeriveManifestAndLock(h.Path("dep"), "my/fake/project")
if m != nil || l != nil || err == nil {
t.Fatalf("expected manifest & lock to be nil, err to be not nil: m -> %#v l -> %#v err -> %#v", m, l, err)
}
}
func TestAnalyzerDeriveManifestAndLockInvalidManifest(t *testing.T) {
h := test.NewHelper(t)
defer h.Cleanup()
h.TempDir("dep")
// Create a manifest with invalid contents
h.TempFile(filepath.Join("dep", ManifestName), "invalid manifest")
a := Analyzer{}
m, l, err := a.DeriveManifestAndLock(h.Path("dep"), "my/fake/project")
if m != nil || l != nil || err == nil {
t.Fatalf("expected manifest & lock & err to be nil: m -> %#v l -> %#v err-> %#v", m, l, err)
}
}
func TestAnalyzerInfo(t *testing.T) {
a := Analyzer{}
name, vers := a.Info()
if name != "dep" || vers != 1 {
t.Fatalf("expected name to be 'dep' and version to be 1: name -> %q vers -> %d", name, vers)
}
}

Просмотреть файл

@ -9,6 +9,7 @@ import (
"encoding/hex"
"flag"
"fmt"
"go/build"
"log"
"os"
"path/filepath"
@ -27,7 +28,7 @@ const ensureLongHelp = `
Ensure is used to fetch project dependencies into the vendor folder, as well as
to set version constraints for specific dependencies. It takes user input,
solves the updated dependency graph of the project, writes any changes to the
manifest and lock file, and places dependencies in the vendor folder.
lock file, and places dependencies in the vendor folder.
Package spec:
@ -130,6 +131,10 @@ func (cmd *ensureCommand) Run(ctx *dep.Ctx, args []string) error {
return errors.Wrap(err, "ensure ListPackage for project")
}
if err := checkErrors(params.RootPackageTree.Packages); err != nil {
return err
}
if cmd.update {
applyUpdateArgs(args, &params)
} else {
@ -160,19 +165,16 @@ func (cmd *ensureCommand) Run(ctx *dep.Ctx, args []string) error {
writeV = dep.VendorAlways
}
var sw dep.SafeWriter
var manifest *dep.Manifest
if !cmd.update {
manifest = p.Manifest
}
newLock := dep.LockFromInterface(solution)
sw.Prepare(manifest, p.Lock, newLock, writeV)
sw, err := dep.NewSafeWriter(nil, p.Lock, newLock, writeV)
if err != nil {
return err
}
if cmd.dryRun {
return sw.PrintPreparedActions()
}
return errors.Wrap(sw.Write(p.AbsRoot, sm), "grouped write of manifest, lock and vendor")
return errors.Wrap(sw.Write(p.AbsRoot, sm, true), "grouped write of manifest, lock and vendor")
}
func applyUpdateArgs(args []string, params *gps.SolveParameters) {
@ -366,3 +368,30 @@ func deduceConstraint(s string) gps.Constraint {
// TODO: if there is amgibuity here, then prompt the user?
return gps.NewVersion(s)
}
func checkErrors(m map[string]pkgtree.PackageOrErr) error {
noGoErrors, pkgErrors := 0, 0
for _, poe := range m {
if poe.Err != nil {
switch poe.Err.(type) {
case *build.NoGoError:
noGoErrors++
default:
pkgErrors++
}
}
}
if len(m) == 0 || len(m) == noGoErrors {
return errors.New("all dirs lacked any go code")
}
if len(m) == pkgErrors {
return errors.New("all dirs had go code with errors")
}
if len(m) == pkgErrors+noGoErrors {
return errors.Errorf("%d dirs had errors and %d had no go code", pkgErrors, noGoErrors)
}
return nil
}

Просмотреть файл

@ -21,16 +21,21 @@ import (
const initShortHelp = `Initialize a new project with manifest and lock files`
const initLongHelp = `
Initialize the project at filepath root by parsing its dependencies and writing
manifest and lock files. If root isn't specified, use the current directory.
Initialize the project at filepath root by parsing its dependencies, writing
manifest and lock files, and vendoring the dependencies. If root isn't
specified, use the current directory.
The version of each dependency will reflect the current state of the GOPATH. If
a dependency doesn't exist in the GOPATH, it won't be written to the manifest,
but it will be solved-for, and will appear in the lock.
a dependency doesn't exist in the GOPATH, a version will be selected from the
versions available from the upstream source per the following algorithm:
Note: init may use the network to solve the dependency graph.
- Tags conforming to semver (sorted by semver rules)
- Default branch(es) (sorted lexicographically)
- Non-semver tags (sorted lexicographically)
Note: init does NOT vendor dependencies at the moment. See dep ensure.
A Gopkg.toml file will be written with inferred version constraints for all
direct dependencies. Gopkg.lock will be written with precise versions, and
vendor/ will be populated with the precise versions written to Gopkg.lock.
`
func (cmd *initCommand) Name() string { return "init" }
@ -39,9 +44,13 @@ func (cmd *initCommand) ShortHelp() string { return initShortHelp }
func (cmd *initCommand) LongHelp() string { return initLongHelp }
func (cmd *initCommand) Hidden() bool { return false }
func (cmd *initCommand) Register(fs *flag.FlagSet) {}
func (cmd *initCommand) Register(fs *flag.FlagSet) {
fs.BoolVar(&cmd.noExamples, "no-examples", false, "don't include example in Gopkg.toml")
}
type initCommand struct{}
type initCommand struct {
noExamples bool
}
func trimPathPrefix(p1, p2 string) string {
if internal.HasFilepathPrefix(p1, p2) {
@ -74,7 +83,7 @@ func (cmd *initCommand) Run(ctx *dep.Ctx, args []string) error {
return err
}
if mok {
return errors.Errorf("manifest file %q already exists", mf)
return errors.Errorf("manifest already exists: %s", mf)
}
// Manifest file does not exist.
@ -135,38 +144,59 @@ func (cmd *initCommand) Run(ctx *dep.Ctx, args []string) error {
)
}
if len(pd.notondisk) > 0 {
internal.Vlogf("Solving...")
params := gps.SolveParameters{
RootDir: root,
RootPackageTree: pkgT,
Manifest: m,
Lock: l,
ProjectAnalyzer: dep.Analyzer{},
}
if *verbose {
params.Trace = true
params.TraceLogger = log.New(os.Stderr, "", 0)
}
s, err := gps.Prepare(params, sm)
if err != nil {
return errors.Wrap(err, "prepare solver")
}
soln, err := s.Solve()
if err != nil {
handleAllTheFailuresOfTheWorld(err)
return err
}
l = dep.LockFromInterface(soln)
// Run solver with project versions found on disk
internal.Vlogf("Solving...")
params := gps.SolveParameters{
RootDir: root,
RootPackageTree: pkgT,
Manifest: m,
Lock: l,
ProjectAnalyzer: dep.Analyzer{},
}
if *verbose {
params.Trace = true
params.TraceLogger = log.New(os.Stderr, "", 0)
}
s, err := gps.Prepare(params, sm)
if err != nil {
return errors.Wrap(err, "prepare solver")
}
soln, err := s.Solve()
if err != nil {
handleAllTheFailuresOfTheWorld(err)
return err
}
l = dep.LockFromInterface(soln)
// Pick notondisk project constraints from solution and add to manifest
for k, _ := range pd.notondisk {
for _, x := range l.Projects() {
if k == x.Ident().ProjectRoot {
m.Dependencies[k] = getProjectPropertiesFromVersion(x.Version())
break
}
}
}
// Run gps.Prepare with appropriate constraint solutions from solve run
// to generate the final lock memo.
s, err = gps.Prepare(params, sm)
if err != nil {
return errors.Wrap(err, "prepare solver")
}
l.Memo = s.HashInputs()
internal.Vlogf("Writing manifest and lock files.")
var sw dep.SafeWriter
sw.Prepare(m, nil, l, dep.VendorAlways)
if err := sw.Write(root, sm); err != nil {
sw, err := dep.NewSafeWriter(m, nil, l, dep.VendorAlways)
if err != nil {
return err
}
if err := sw.Write(root, sm, cmd.noExamples); err != nil {
return errors.Wrap(err, "safe write of manifest and lock")
}
@ -200,7 +230,6 @@ func isStdLib(path string) bool {
// TODO solve failures can be really creative - we need to be similarly creative
// in handling them and informing the user appropriately
func handleAllTheFailuresOfTheWorld(err error) {
fmt.Printf("solve error: %s\n", err)
}
func hasImportPathPrefix(s, prefix string) bool {
@ -210,6 +239,34 @@ func hasImportPathPrefix(s, prefix string) bool {
return strings.HasPrefix(s, prefix+"/")
}
// getProjectPropertiesFromVersion takes a gps.Version and returns a proper
// gps.ProjectProperties with Constraint value based on the provided version.
func getProjectPropertiesFromVersion(v gps.Version) gps.ProjectProperties {
pp := gps.ProjectProperties{}
// extract version and ignore if it's revision only
switch tv := v.(type) {
case gps.PairedVersion:
v = tv.Unpair()
case gps.Revision:
return pp
}
switch v.Type() {
case gps.IsBranch, gps.IsVersion:
pp.Constraint = v
case gps.IsSemver:
// TODO: remove "^" when https://github.com/golang/dep/issues/225 is ready.
c, err := gps.NewSemverConstraint("^" + v.String())
if err != nil {
panic(err)
}
pp.Constraint = c
}
return pp
}
type projectData struct {
constraints gps.ProjectConstraints // constraints that could be found
dependencies map[gps.ProjectRoot][]string // all dependencies (imports) found by project root
@ -264,16 +321,7 @@ func getProjectData(ctx *dep.Ctx, pkgT pkgtree.PackageTree, cpr string, sm *gps.
}
ondisk[pr] = v
pp := gps.ProjectProperties{}
switch v.Type() {
case gps.IsBranch, gps.IsVersion, gps.IsRevision:
pp.Constraint = v
case gps.IsSemver:
c, _ := gps.NewSemverConstraint("^" + v.String())
pp.Constraint = c
}
constraints[pr] = pp
constraints[pr] = getProjectPropertiesFromVersion(v)
}
internal.Vlogf("Analyzing transitive imports...")

Просмотреть файл

@ -4,7 +4,12 @@
package main
import "testing"
import (
"reflect"
"testing"
"github.com/sdboyer/gps"
)
func TestContains(t *testing.T) {
a := []string{"a", "b", "abcd"}
@ -33,3 +38,46 @@ func TestIsStdLib(t *testing.T) {
}
}
}
func TestGetProjectPropertiesFromVersion(t *testing.T) {
wantSemver, _ := gps.NewSemverConstraint("^v1.0.0")
cases := []struct {
version, want gps.Constraint
}{
{
version: gps.NewBranch("foo-branch"),
want: gps.NewBranch("foo-branch"),
},
{
version: gps.NewVersion("foo-version"),
want: gps.NewVersion("foo-version"),
},
{
version: gps.NewVersion("v1.0.0"),
want: wantSemver,
},
{
version: gps.NewBranch("foo-branch").Is("some-revision"),
want: gps.NewBranch("foo-branch"),
},
{
version: gps.NewVersion("foo-version").Is("some-revision"),
want: gps.NewVersion("foo-version"),
},
{
version: gps.Revision("some-revision"),
want: nil,
},
{
version: gps.NewVersion("v1.0.0").Is("some-revision"),
want: wantSemver,
},
}
for _, c := range cases {
actualProp := getProjectPropertiesFromVersion(c.version.(gps.Version))
if !reflect.DeepEqual(c.want, actualProp.Constraint) {
t.Fatalf("Constraints are not as expected: \n\t(GOT) %v\n\t(WNT) %v", actualProp.Constraint, c.want)
}
}
}

Просмотреть файл

@ -19,6 +19,10 @@ func TestIntegration(t *testing.T) {
test.NeedsGit(t)
filepath.Walk(filepath.Join("testdata", "harness_tests"), func(path string, info os.FileInfo, err error) error {
if err != nil {
t.Fatal("error walking filepath")
}
wd, err := os.Getwd()
if err != nil {
panic(err)
@ -51,13 +55,18 @@ func TestIntegration(t *testing.T) {
// Run commands
testProj.RecordImportPaths()
for _, args := range testCase.Commands {
var err error
for i, args := range testCase.Commands {
err = testProj.DoRun(args)
if err != nil {
t.Fatalf("%v", err)
if err != nil && i < len(testCase.Commands)-1 {
t.Fatalf("cmd %s raised an unexpected error: %s", args[0], err.Error())
}
}
// Check error raised in final command
testCase.CompareError(err, testProj.GetStderr())
// Check final manifest and lock
testCase.CompareFile(dep.ManifestName, testProj.ProjPath(dep.ManifestName))
testCase.CompareFile(dep.LockName, testProj.ProjPath(dep.LockName))

Просмотреть файл

@ -19,7 +19,7 @@ import (
const removeShortHelp = `Remove a dependency from the project`
const removeLongHelp = `
Remove a dependency from the project's manifest file, lock file, and vendor
Remove a dependency from the project's lock file, and vendor
folder. If the project includes that dependency in its import graph, remove will
fail unless -force is specified.
`
@ -180,10 +180,13 @@ func (cmd *removeCommand) Run(ctx *dep.Ctx, args []string) error {
return err
}
var sw dep.SafeWriter
newLock := dep.LockFromInterface(soln)
sw.Prepare(p.Manifest, p.Lock, newLock, dep.VendorOnChanged)
if err := sw.Write(p.AbsRoot, sm); err != nil {
sw, err := dep.NewSafeWriter(nil, p.Lock, newLock, dep.VendorOnChanged)
if err != nil {
return err
}
if err := sw.Write(p.AbsRoot, sm, true); err != nil {
return errors.Wrap(err, "grouped write of manifest, lock and vendor")
}
return nil

14
cmd/dep/testdata/harness_tests/README.md поставляемый
Просмотреть файл

@ -28,7 +28,7 @@ desired. The test name will consist of the directory path from `testdata` to
the test case directory itself. In the above example, the test name would be
`category1/subcategory1/case1`, and could be singled out with the `-run` option
of `go test` (i.e.
`go test github.com/golang/dep/cmp/dep -run Integration/category1/subcategory1/case1`).
`go test github.com/golang/dep/cmd/dep -run Integration/category1/subcategory1/case1`).
New tests can be added simply by adding a new directory with the json file to
the `testdata` tree. There is no need for code modification - the new test
will be included automatically.
@ -58,7 +58,8 @@ The `testcase.json` file has the following format:
"github.com/sdboyer/deptestdos",
"github.com/sdboyer/deptesttres",
"github.com/sdboyer/deptestquatro"
]
],
"error-expected": "something went wrong"
}
All of the categories are optional - if the `imports` list for a test is empty,
@ -72,9 +73,10 @@ The test procedure is as follows:
4. Fetch the repos and versions in `gopath-initial` into `$TMPDIR/src` directory
5. Fetch the repos and versions in `vendor-initial` to the project's `vendor` directory
6. Run `commands` on the project, in declaration order
7. Check the resulting files against those in the `final` input directory
8. Check the `vendor` directory for the projects listed under `vendor-final`
9. Check that there were no changes to `src` listings
10. Clean up
7. Ensure that, if any errors are raised, it is only by the final command and their string output matches `error-expected`
8. Check the resulting files against those in the `final` input directory
9. Check the `vendor` directory for the projects listed under `vendor-final`
10. Check that there were no changes to `src` listings
11. Clean up
Note that for the remote fetches, only git repos are currently supported.

2
cmd/dep/testdata/harness_tests/ensure/empty/case1/final/Gopkg.lock сгенерированный поставляемый
Просмотреть файл

@ -1,4 +1,4 @@
memo = "4b36ae008ef4be09dee7e2ae00606d44fd75f4310fd0d0ef6e744690290569de"
memo = "14b07b05e0f01051b03887ab2bf80b516bc5510ea92f75f76c894b1745d8850c"
[[projects]]
name = "github.com/sdboyer/deptest"

Просмотреть файл

@ -1,9 +1,56 @@
# Example:
## Gopkg.toml example (these lines may be deleted)
## "required" lists a set of packages (not projects) that must be included in
## Gopkg.lock. This list is merged with the set of packages imported by the current
## project. Use it when your project needs a package it doesn't explicitly import -
## including "main" packages.
# required = ["github.com/user/thing/cmd/thing"]
## "ignored" lists a set of packages (not projects) that are ignored when
## dep statically analyzes source code. Ignored packages can be in this project,
## or in a dependency.
# ignored = ["github.com/user/project/badpkg"]
## Dependencies define constraints on dependent projects. They are respected by
## dep whether coming from the Gopkg.toml of the current project or a dependency.
# [[dependencies]]
# source = "https://github.com/myfork/package.git"
# branch = "master"
# name = "github.com/vendor/package"
# Note: revision will depend on your repository type, i.e git, svc, bzr etc...
# revision = "abc123"
## Required: the root import path of the project being constrained.
# name = "github.com/user/project"
#
## Recommended: the version constraint to enforce for the project.
## Only one of "branch", "version" or "revision" can be specified.
# version = "1.0.0"
# branch = "master"
# revision = "abc123"
#
## Optional: an alternate location (URL or import path) for the project's source.
# source = "https://github.com/myfork/package.git"
## Overrides have the same structure as [[dependencies]], but supercede all
## [[dependencies]] declarations from all projects. Only the current project's
## [[overrides]] are applied.
##
## Overrides are a sledgehammer. Use them only as a last resort.
# [[overrides]]
## Required: the root import path of the project being constrained.
# name = "github.com/user/project"
#
## Optional: specifying a version constraint override will cause all other
## constraints on this project to be ignored; only the overriden constraint
## need be satisfied.
## Again, only one of "branch", "version" or "revision" can be specified.
# version = "1.0.0"
# branch = "master"
# revision = "abc123"
#
## Optional: specifying an alternate source location as an override will
## enforce that the alternate location is used for that project, regardless of
## what source location any dependent projects specify.
# source = "https://github.com/myfork/package.git"
[[dependencies]]
name = "github.com/sdboyer/deptest"
version = "^1.0.0"

Просмотреть файл

@ -1,4 +1,3 @@
[[dependencies]]
name = "github.com/sdboyer/deptest"
version = "^0.8.0"
version = "~0.8.0"

Просмотреть файл

@ -1,4 +1,56 @@
[[overrides]]
## Gopkg.toml example (these lines may be deleted)
## "required" lists a set of packages (not projects) that must be included in
## Gopkg.lock. This list is merged with the set of packages imported by the current
## project. Use it when your project needs a package it doesn't explicitly import -
## including "main" packages.
# required = ["github.com/user/thing/cmd/thing"]
## "ignored" lists a set of packages (not projects) that are ignored when
## dep statically analyzes source code. Ignored packages can be in this project,
## or in a dependency.
# ignored = ["github.com/user/project/badpkg"]
## Dependencies define constraints on dependent projects. They are respected by
## dep whether coming from the Gopkg.toml of the current project or a dependency.
# [[dependencies]]
## Required: the root import path of the project being constrained.
# name = "github.com/user/project"
#
## Recommended: the version constraint to enforce for the project.
## Only one of "branch", "version" or "revision" can be specified.
# version = "1.0.0"
# branch = "master"
# revision = "abc123"
#
## Optional: an alternate location (URL or import path) for the project's source.
# source = "https://github.com/myfork/package.git"
## Overrides have the same structure as [[dependencies]], but supercede all
## [[dependencies]] declarations from all projects. Only the current project's
## [[overrides]] are applied.
##
## Overrides are a sledgehammer. Use them only as a last resort.
# [[overrides]]
## Required: the root import path of the project being constrained.
# name = "github.com/user/project"
#
## Optional: specifying a version constraint override will cause all other
## constraints on this project to be ignored; only the overriden constraint
## need be satisfied.
## Again, only one of "branch", "version" or "revision" can be specified.
# version = "1.0.0"
# branch = "master"
# revision = "abc123"
#
## Optional: specifying an alternate source location as an override will
## enforce that the alternate location is used for that project, regardless of
## what source location any dependent projects specify.
# source = "https://github.com/myfork/package.git"
[[dependencies]]
name = "github.com/sdboyer/deptest"
version = "1.0.0"
version = "^1.0.0"

1
cmd/dep/testdata/harness_tests/ensure/pkg-errors/case1/final/Gopkg.lock сгенерированный поставляемый Normal file
Просмотреть файл

@ -0,0 +1 @@
memo = "ab4fef131ee828e96ba67d31a7d690bd5f2f42040c6766b1b12fe856f87e0ff7"

Просмотреть файл

Просмотреть файл

@ -0,0 +1,7 @@
{
"commands": [
["init", "-no-examples"],
["ensure", "-update"]
],
"error-expected" : "all dirs lacked any go code"
}

2
cmd/dep/testdata/harness_tests/init/case1/final/Gopkg.lock сгенерированный поставляемый
Просмотреть файл

@ -1,4 +1,4 @@
memo = ""
memo = "88d2718cda70cce45158f953d2c6ead79c1db38e67e9704aff72be8fddb096e7"
[[projects]]
name = "github.com/sdboyer/deptest"

Просмотреть файл

@ -5,4 +5,3 @@
[[dependencies]]
name = "github.com/sdboyer/deptestdos"
revision = "a0196baa11ea047dd65037287451d36b861b00ea"

Просмотреть файл

@ -1,6 +1,6 @@
{
"commands": [
["init"]
["init", "-no-examples"]
],
"gopath-initial": {
"github.com/sdboyer/deptest": "v0.8.0",

2
cmd/dep/testdata/harness_tests/init/case2/final/Gopkg.lock сгенерированный поставляемый
Просмотреть файл

@ -1,4 +1,4 @@
memo = "88d2718cda70cce45158f953d2c6ead79c1db38e67e9704aff72be8fddb096e7"
memo = "b4fe6e8bceac924197838b6ea47989abbdd3a8d31035d20ee0a1dabc0994c368"
[[projects]]
name = "github.com/sdboyer/deptest"

Просмотреть файл

@ -2,3 +2,7 @@
[[dependencies]]
name = "github.com/sdboyer/deptest"
version = ">=0.8.0, <1.0.0"
[[dependencies]]
name = "github.com/sdboyer/deptestdos"
version = "^2.0.0"

Просмотреть файл

@ -1,6 +1,6 @@
{
"commands": [
["init"]
["init", "-no-examples"]
],
"gopath-initial": {
"github.com/sdboyer/deptest": "v0.8.0"

2
cmd/dep/testdata/harness_tests/init/case3/final/Gopkg.lock сгенерированный поставляемый
Просмотреть файл

@ -1,4 +1,4 @@
memo = ""
memo = "af9a783a5430dabcaaf44683c09e2b729e1c0d61f13bfdf6677c4fd0b41387ca"
[[projects]]
branch = "master"

Просмотреть файл

@ -5,4 +5,3 @@
[[dependencies]]
name = "github.com/sdboyer/deptestdos"
revision = "a0196baa11ea047dd65037287451d36b861b00ea"

Просмотреть файл

@ -1,6 +1,6 @@
{
"commands": [
["init"]
["init", "-no-examples"]
],
"gopath-initial": {
"github.com/sdboyer/deptestdos": "a0196baa11ea047dd65037287451d36b861b00ea"

Просмотреть файл

Просмотреть файл

Просмотреть файл

@ -0,0 +1,6 @@
{
"commands": [
["init"]
],
"error-expected" : "manifest already exists:"
}

2
cmd/dep/testdata/harness_tests/init/skip-hidden/final/Gopkg.lock сгенерированный поставляемый
Просмотреть файл

@ -1,4 +1,4 @@
memo = "4b36ae008ef4be09dee7e2ae00606d44fd75f4310fd0d0ef6e744690290569de"
memo = "14b07b05e0f01051b03887ab2bf80b516bc5510ea92f75f76c894b1745d8850c"
[[projects]]
name = "github.com/sdboyer/deptest"

Просмотреть файл

@ -1,9 +1,4 @@
# Example:
# [[dependencies]]
# source = "https://github.com/myfork/package.git"
# branch = "master"
# name = "github.com/vendor/package"
# Note: revision will depend on your repository type, i.e git, svc, bzr etc...
# revision = "abc123"
# version = "1.0.0"
[[dependencies]]
name = "github.com/sdboyer/deptest"
version = "^1.0.0"

Просмотреть файл

@ -1,6 +1,6 @@
{
"commands": [
["init"]
["init", "-no-examples"]
],
"vendor-final": [
"github.com/sdboyer/deptest"

Просмотреть файл

@ -1,4 +1,11 @@
[[dependencies]]
name = "github.com/not/used"
version = "2.0.0"
[[dependencies]]
name = "github.com/sdboyer/deptest"
version = ">=0.8.0, <1.0.0"
[[dependencies]]
name = "github.com/sdboyer/deptestdos"
revision = "a0196baa11ea047dd65037287451d36b861b00ea"

Просмотреть файл

@ -1,8 +1,11 @@
[[dependencies]]
name = "github.com/not/used"
version = "2.0.0"
[[dependencies]]
name = "github.com/sdboyer/deptest"
name = "github.com/sdboyer/deptest"
version = ">=0.8.0, <1.0.0"
[[dependencies]]
name = "github.com/sdboyer/deptestdos"
revision = "a0196baa11ea047dd65037287451d36b861b00ea"
name = "github.com/sdboyer/deptestdos"
revision = "a0196baa11ea047dd65037287451d36b861b00ea"

Просмотреть файл

@ -1,3 +1,6 @@
[[dependencies]]
name = "github.com/not/used"
version = "2.0.0"
[[dependencies]]
name = "github.com/sdboyer/deptest"
@ -5,4 +8,4 @@
[[dependencies]]
name = "github.com/sdboyer/deptestdos"
revision = "a0196baa11ea047dd65037287451d36b861b00ea"
revision = "a0196baa11ea047dd65037287451d36b861b00ea"

Просмотреть файл

@ -1,3 +1,6 @@
[[dependencies]]
name = "github.com/not/used"
version = "2.0.0"
[[dependencies]]
name = "github.com/sdboyer/deptest"
@ -5,4 +8,4 @@
[[dependencies]]
name = "github.com/sdboyer/deptestdos"
revision = "a0196baa11ea047dd65037287451d36b861b00ea"
revision = "a0196baa11ea047dd65037287451d36b861b00ea"

Просмотреть файл

@ -1,4 +1,3 @@
[[dependencies]]
name = "github.com/sdboyer/deptest"
version = ">=0.8.0, <1.0.0"
version = "^0.8.0"

Просмотреть файл

@ -352,22 +352,15 @@ func TestResolveProjectRoot(t *testing.T) {
tg := test.NewHelper(t)
defer tg.Cleanup()
tg.TempDir("go")
tg.TempDir("go/src")
tg.TempDir("go/src/real")
tg.TempDir("go/src/real/path")
tg.TempDir("go/src/sym")
tg.TempDir("gotwo") // Another directory used as a GOPATH
tg.TempDir("gotwo/src")
tg.TempDir("gotwo/src/real")
// Another directory used as a GOPATH
tg.TempDir("gotwo/src/real/path")
tg.TempDir("gotwo/src/sym")
tg.TempDir("sym") // Directory for symlinks
tg.Setenv("GOPATH", tg.Path(filepath.Join(".", "go")))
ctx := &Ctx{
GOPATH: tg.Path(filepath.Join(".", "go")),
GOPATHS: []string{
@ -376,45 +369,65 @@ func TestResolveProjectRoot(t *testing.T) {
},
}
realPath := filepath.Join(ctx.GOPATH, "src", "real", "path")
realPathTwo := filepath.Join(ctx.GOPATHS[1], "src", "real", "path")
symlinkedPath := filepath.Join(tg.Path("."), "sym", "symlink")
symlinkedInGoPath := filepath.Join(ctx.GOPATH, "src/sym/path")
symlinkedInOtherGoPath := filepath.Join(tg.Path("."), "sym", "symtwo")
os.Symlink(realPath, symlinkedPath)
os.Symlink(realPath, symlinkedInGoPath)
os.Symlink(realPathTwo, symlinkedInOtherGoPath)
// Real path should be returned, no symlinks to deal with
p, err := ctx.resolveProjectRoot(realPath)
if err != nil {
t.Fatalf("Error resolving project root: %s", err)
}
if p != realPath {
t.Fatalf("Want path to be %s, got %s", realPath, p)
testcases := []struct {
name string
path string
resolvedPath string
symlink bool
expectErr bool
}{
{
name: "no-symlinks",
path: filepath.Join(ctx.GOPATH, "src/real/path"),
resolvedPath: filepath.Join(ctx.GOPATH, "src/real/path"),
},
{
name: "symlink-outside-gopath",
path: filepath.Join(tg.Path("."), "sym/symlink"),
resolvedPath: filepath.Join(ctx.GOPATH, "src/real/path"),
symlink: true,
},
{
name: "symlink-in-another-gopath",
path: filepath.Join(tg.Path("."), "sym/symtwo"),
resolvedPath: filepath.Join(ctx.GOPATHS[1], "src/real/path"),
symlink: true,
},
{
name: "symlink-in-gopath",
path: filepath.Join(ctx.GOPATH, "src/sym/path"),
resolvedPath: filepath.Join(ctx.GOPATH, "src/real/path"),
symlink: true,
expectErr: true,
},
}
// Real path should be returned, symlink is outside GOPATH
p, err = ctx.resolveProjectRoot(symlinkedPath)
if err != nil {
t.Fatalf("Error resolving project root: %s", err)
}
if p != realPath {
t.Fatalf("Want path to be %s, got %s", realPath, p)
}
for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) {
if tc.symlink {
if err := os.Symlink(tc.resolvedPath, tc.path); err != nil {
if runtime.GOOS == "windows" {
t.Skipf("Not testing Windows symlinks because: %s", err)
} else {
t.Fatal(err)
}
}
}
// Real path should be returned, symlink is in another GOPATH
p, err = ctx.resolveProjectRoot(symlinkedInOtherGoPath)
if err != nil {
t.Fatalf("Error resolving project root: %s", err)
}
if p != realPathTwo {
t.Fatalf("Want path to be %s, got %s", realPathTwo, p)
}
p, err := ctx.resolveProjectRoot(tc.path)
if err != nil {
if !tc.expectErr {
t.Fatalf("Error resolving project root: %s", err)
}
return
}
if err == nil && tc.expectErr {
t.Fatal("Wanted an error")
}
// Symlinked path is inside GOPATH, should return error
_, err = ctx.resolveProjectRoot(symlinkedInGoPath)
if err == nil {
t.Fatalf("Wanted an error")
if p != tc.resolvedPath {
t.Errorf("Want path to be %s, got %s", tc.resolvedPath, p)
}
})
}
}

Просмотреть файл

@ -190,6 +190,8 @@ func TestIsEmpty(t *testing.T) {
}
h := test.NewHelper(t)
defer h.Cleanup()
h.TempDir("empty")
tests := map[string]string{
wd: "true",

Просмотреть файл

@ -12,6 +12,7 @@ import (
"os"
"path/filepath"
"regexp"
"strings"
"testing"
)
@ -27,6 +28,7 @@ type IntegrationTestCase struct {
initialPath string
finalPath string
Commands [][]string `json:"commands"`
ErrorExpected string `json:"error-expected"`
GopathInitial map[string]string `json:"gopath-initial"`
VendorInitial map[string]string `json:"vendor-initial"`
VendorFinal []string `json:"vendor-final"`
@ -126,6 +128,22 @@ func (tc *IntegrationTestCase) CompareFile(goldenPath, working string) {
}
}
// CompareError compares exected and actual error
func (tc *IntegrationTestCase) CompareError(err error, stderr string) {
wantExists, want := tc.ErrorExpected != "", tc.ErrorExpected
gotExists, got := stderr != "" && err != nil, stderr
if wantExists && gotExists {
if !strings.Contains(got, want) {
tc.t.Errorf("expected error containing %s, got error %s", want, got)
}
} else if !wantExists && gotExists {
tc.t.Fatal("error raised where none was expected")
} else if wantExists && !gotExists {
tc.t.Error("error not raised where one was expected")
}
}
func (tc *IntegrationTestCase) CompareVendorPaths(gotVendorPaths []string) {
if *UpdateGolden {
tc.VendorFinal = gotVendorPaths

Просмотреть файл

@ -139,6 +139,11 @@ func (p *IntegrationTestProject) RunGit(dir string, args ...string) {
}
}
// GetStderr gets the Stderr output from test run
func (p *IntegrationTestProject) GetStderr() string {
return p.stderr.String()
}
func (p *IntegrationTestProject) GetVendorGit(ip string) {
parse := strings.Split(ip, "/")
gitDir := strings.Join(parse[:len(parse)-1], string(filepath.Separator))

52
testdata/txn_writer/expected_manifest.toml поставляемый
Просмотреть файл

@ -1,4 +1,56 @@
## Gopkg.toml example (these lines may be deleted)
## "required" lists a set of packages (not projects) that must be included in
## Gopkg.lock. This list is merged with the set of packages imported by the current
## project. Use it when your project needs a package it doesn't explicitly import -
## including "main" packages.
# required = ["github.com/user/thing/cmd/thing"]
## "ignored" lists a set of packages (not projects) that are ignored when
## dep statically analyzes source code. Ignored packages can be in this project,
## or in a dependency.
# ignored = ["github.com/user/project/badpkg"]
## Dependencies define constraints on dependent projects. They are respected by
## dep whether coming from the Gopkg.toml of the current project or a dependency.
# [[dependencies]]
## Required: the root import path of the project being constrained.
# name = "github.com/user/project"
#
## Recommended: the version constraint to enforce for the project.
## Only one of "branch", "version" or "revision" can be specified.
# version = "1.0.0"
# branch = "master"
# revision = "abc123"
#
## Optional: an alternate location (URL or import path) for the project's source.
# source = "https://github.com/myfork/package.git"
## Overrides have the same structure as [[dependencies]], but supercede all
## [[dependencies]] declarations from all projects. Only the current project's
## [[overrides]] are applied.
##
## Overrides are a sledgehammer. Use them only as a last resort.
# [[overrides]]
## Required: the root import path of the project being constrained.
# name = "github.com/user/project"
#
## Optional: specifying a version constraint override will cause all other
## constraints on this project to be ignored; only the overriden constraint
## need be satisfied.
## Again, only one of "branch", "version" or "revision" can be specified.
# version = "1.0.0"
# branch = "master"
# revision = "abc123"
#
## Optional: specifying an alternate source location as an override will
## enforce that the alternate location is used for that project, regardless of
## what source location any dependent projects specify.
# source = "https://github.com/myfork/package.git"
[[dependencies]]
name = "github.com/sdboyer/dep-test"
version = "1.0.0"

Просмотреть файл

@ -21,15 +21,58 @@ import (
// Example string to be written to the manifest file
// if no dependencies are found in the project
// during `dep init`
const exampleToml = `
# Example:
const exampleTOML = `
## Gopkg.toml example (these lines may be deleted)
## "required" lists a set of packages (not projects) that must be included in
## Gopkg.lock. This list is merged with the set of packages imported by the current
## project. Use it when your project needs a package it doesn't explicitly import -
## including "main" packages.
# required = ["github.com/user/thing/cmd/thing"]
## "ignored" lists a set of packages (not projects) that are ignored when
## dep statically analyzes source code. Ignored packages can be in this project,
## or in a dependency.
# ignored = ["github.com/user/project/badpkg"]
## Dependencies define constraints on dependent projects. They are respected by
## dep whether coming from the Gopkg.toml of the current project or a dependency.
# [[dependencies]]
# source = "https://github.com/myfork/package.git"
# branch = "master"
# name = "github.com/vendor/package"
# Note: revision will depend on your repository type, i.e git, svc, bzr etc...
# revision = "abc123"
## Required: the root import path of the project being constrained.
# name = "github.com/user/project"
#
## Recommended: the version constraint to enforce for the project.
## Only one of "branch", "version" or "revision" can be specified.
# version = "1.0.0"
# branch = "master"
# revision = "abc123"
#
## Optional: an alternate location (URL or import path) for the project's source.
# source = "https://github.com/myfork/package.git"
## Overrides have the same structure as [[dependencies]], but supercede all
## [[dependencies]] declarations from all projects. Only the current project's
## [[overrides]] are applied.
##
## Overrides are a sledgehammer. Use them only as a last resort.
# [[overrides]]
## Required: the root import path of the project being constrained.
# name = "github.com/user/project"
#
## Optional: specifying a version constraint override will cause all other
## constraints on this project to be ignored; only the overriden constraint
## need be satisfied.
## Again, only one of "branch", "version" or "revision" can be specified.
# version = "1.0.0"
# branch = "master"
# revision = "abc123"
#
## Optional: specifying an alternate source location as an override will
## enforce that the alternate location is used for that project, regardless of
## what source location any dependent projects specify.
# source = "https://github.com/myfork/package.git"
`
// SafeWriter transactionalizes writes of manifest, lock, and vendor dir, both
@ -39,27 +82,63 @@ const exampleToml = `
// It is not impervious to errors (writing to disk is hard), but it should
// guard against non-arcane failure conditions.
type SafeWriter struct {
Payload *SafeWriterPayload
}
// SafeWriterPayload represents the actions SafeWriter will execute when SafeWriter.Write is called.
type SafeWriterPayload struct {
Manifest *Manifest
Lock *Lock
LockDiff *gps.LockDiff
WriteVendor bool
}
func (payload *SafeWriterPayload) HasLock() bool {
return payload.Lock != nil
// NewSafeWriter sets up a SafeWriter to write a set of config yaml, lock and vendor tree.
//
// - If manifest is provided, it will be written to the standard manifest file
// name beneath root.
// - If newLock is provided, it will be written to the standard lock file
// name beneath root.
// - If vendor is VendorAlways, or is VendorOnChanged and the locks are different,
// the vendor directory will be written beneath root based on newLock.
// - If oldLock is provided without newLock, error.
// - If vendor is VendorAlways without a newLock, error.
func NewSafeWriter(manifest *Manifest, oldLock, newLock *Lock, vendor VendorBehavior) (*SafeWriter, error) {
sw := &SafeWriter{
Manifest: manifest,
Lock: newLock,
}
if oldLock != nil {
if newLock == nil {
return nil, errors.New("must provide newLock when oldLock is specified")
}
sw.LockDiff = gps.DiffLocks(oldLock, newLock)
}
switch vendor {
case VendorAlways:
sw.WriteVendor = true
case VendorOnChanged:
if sw.LockDiff != nil || (newLock != nil && oldLock == nil) {
sw.WriteVendor = true
}
}
if sw.WriteVendor && newLock == nil {
return nil, errors.New("must provide newLock in order to write out vendor")
}
return sw, nil
}
func (payload *SafeWriterPayload) HasManifest() bool {
return payload.Manifest != nil
// HasLock checks if a Lock is present in the SafeWriter
func (sw *SafeWriter) HasLock() bool {
return sw.Lock != nil
}
func (payload *SafeWriterPayload) HasVendor() bool {
return payload.WriteVendor
// HasManifest checks if a Manifest is present in the SafeWriter
func (sw *SafeWriter) HasManifest() bool {
return sw.Manifest != nil
}
// HasVendor returns the if SafeWriter should write to vendor
func (sw *SafeWriter) HasVendor() bool {
return sw.WriteVendor
}
type rawStringDiff struct {
@ -178,47 +257,7 @@ const (
VendorNever
)
// Prepare to write a set of config yaml, lock and vendor tree.
//
// - If manifest is provided, it will be written to the standard manifest file
// name beneath root.
// - If newLock is provided, it will be written to the standard lock file
// name beneath root.
// - If vendor is VendorAlways, or is VendorOnChanged and the locks are different,
// the vendor directory will be written beneath root based on newLock.
// - If oldLock is provided without newLock, error.
// - If vendor is VendorAlways without a newLock, error.
func (sw *SafeWriter) Prepare(manifest *Manifest, oldLock, newLock *Lock, vendor VendorBehavior) error {
sw.Payload = &SafeWriterPayload{
Manifest: manifest,
Lock: newLock,
}
if oldLock != nil {
if newLock == nil {
return errors.New("must provide newLock when oldLock is specified")
}
sw.Payload.LockDiff = gps.DiffLocks(oldLock, newLock)
}
switch vendor {
case VendorAlways:
sw.Payload.WriteVendor = true
case VendorOnChanged:
if sw.Payload.LockDiff != nil || (newLock != nil && oldLock == nil) {
sw.Payload.WriteVendor = true
}
}
if sw.Payload.WriteVendor && newLock == nil {
return errors.New("must provide newLock in order to write out vendor")
}
return nil
}
func (payload SafeWriterPayload) validate(root string, sm gps.SourceManager) error {
func (sw SafeWriter) validate(root string, sm gps.SourceManager) error {
if root == "" {
return errors.New("root path must be non-empty")
}
@ -229,7 +268,7 @@ func (payload SafeWriterPayload) validate(root string, sm gps.SourceManager) err
return errors.Errorf("root path %q does not exist", root)
}
if payload.HasVendor() && sm == nil {
if sw.HasVendor() && sm == nil {
return errors.New("must provide a SourceManager if writing out a vendor dir")
}
@ -244,18 +283,13 @@ func (payload SafeWriterPayload) validate(root string, sm gps.SourceManager) err
// operations succeeded. It also does its best to roll back if any moves fail.
// This mostly guarantees that dep cannot exit with a partial write that would
// leave an undefined state on disk.
func (sw *SafeWriter) Write(root string, sm gps.SourceManager) error {
if sw.Payload == nil {
return errors.New("Cannot call SafeWriter.Write before SafeWriter.Prepare")
}
err := sw.Payload.validate(root, sm)
func (sw *SafeWriter) Write(root string, sm gps.SourceManager, noExamples bool) error {
err := sw.validate(root, sm)
if err != nil {
return err
}
if !sw.Payload.HasManifest() && !sw.Payload.HasLock() && !sw.Payload.HasVendor() {
if !sw.HasManifest() && !sw.HasLock() && !sw.HasVendor() {
// nothing to do
return nil
}
@ -270,30 +304,48 @@ func (sw *SafeWriter) Write(root string, sm gps.SourceManager) error {
}
defer os.RemoveAll(td)
if sw.Payload.HasManifest() {
if sw.Payload.Manifest.IsEmpty() {
err := modifyWithString(filepath.Join(td, ManifestName), exampleToml)
if err != nil {
return errors.Wrap(err, "failed to generate example text")
}
} else if err := writeFile(filepath.Join(td, ManifestName), sw.Payload.Manifest); err != nil {
if sw.HasManifest() {
// Always write the example text to the bottom of the TOML file.
tb, err := sw.Manifest.MarshalTOML()
if err != nil {
return errors.Wrap(err, "failed to marshal manifest to TOML")
}
var initOutput string
// If examples are NOT disabled, use the example text
if !noExamples {
initOutput = exampleTOML
}
// 0666 is before umask; mirrors behavior of os.Create (used by
// writeFile())
if err = ioutil.WriteFile(filepath.Join(td, ManifestName), append([]byte(initOutput), tb...), 0666); err != nil {
return errors.Wrap(err, "failed to write manifest file to temp dir")
}
}
if sw.Payload.HasLock() {
if err := writeFile(filepath.Join(td, LockName), sw.Payload.Lock); err != nil {
if sw.HasLock() {
if err := writeFile(filepath.Join(td, LockName), sw.Lock); err != nil {
return errors.Wrap(err, "failed to write lock file to temp dir")
}
}
if sw.Payload.HasVendor() {
err = gps.WriteDepTree(filepath.Join(td, "vendor"), sw.Payload.Lock, sm, true)
if sw.HasVendor() {
err = gps.WriteDepTree(filepath.Join(td, "vendor"), sw.Lock, sm, true)
if err != nil {
return errors.Wrap(err, "error while writing out vendor tree")
}
}
// Ensure vendor/.git is preserved if present
if hasDotGit(vpath) {
err = renameWithFallback(filepath.Join(vpath, ".git"), filepath.Join(td, "vendor/.git"))
if _, ok := err.(*os.LinkError); ok {
return errors.Wrap(err, "failed to preserve vendor/.git")
}
}
// Move the existing files and dirs to the temp dir while we put the new
// ones in, to provide insurance against errors for as long as possible.
type pathpair struct {
@ -303,7 +355,7 @@ func (sw *SafeWriter) Write(root string, sm gps.SourceManager) error {
var failerr error
var vendorbak string
if sw.Payload.HasManifest() {
if sw.HasManifest() {
if _, err := os.Stat(mpath); err == nil {
// Move out the old one.
tmploc := filepath.Join(td, ManifestName+".orig")
@ -321,7 +373,7 @@ func (sw *SafeWriter) Write(root string, sm gps.SourceManager) error {
}
}
if sw.Payload.HasLock() {
if sw.HasLock() {
if _, err := os.Stat(lpath); err == nil {
// Move out the old one.
tmploc := filepath.Join(td, LockName+".orig")
@ -340,7 +392,7 @@ func (sw *SafeWriter) Write(root string, sm gps.SourceManager) error {
}
}
if sw.Payload.HasVendor() {
if sw.HasVendor() {
if _, err := os.Stat(vpath); err == nil {
// Move out the old vendor dir. just do it into an adjacent dir, to
// try to mitigate the possibility of a pointless cross-filesystem
@ -368,7 +420,7 @@ func (sw *SafeWriter) Write(root string, sm gps.SourceManager) error {
// Renames all went smoothly. The deferred os.RemoveAll will get the temp
// dir, but if we wrote vendor, we have to clean that up directly
if sw.Payload.HasVendor() {
if sw.HasVendor() {
// Nothing we can really do about an error at this point, so ignore it
os.RemoveAll(vendorbak)
}
@ -385,26 +437,26 @@ fail:
}
func (sw *SafeWriter) PrintPreparedActions() error {
if sw.Payload.HasManifest() {
if sw.HasManifest() {
fmt.Printf("Would have written the following %s:\n", ManifestName)
m, err := sw.Payload.Manifest.MarshalTOML()
m, err := sw.Manifest.MarshalTOML()
if err != nil {
return errors.Wrap(err, "ensure DryRun cannot serialize manifest")
}
fmt.Println(string(m))
}
if sw.Payload.HasLock() {
if sw.Payload.LockDiff == nil {
if sw.HasLock() {
if sw.LockDiff == nil {
fmt.Printf("Would have written the following %s:\n", LockName)
l, err := sw.Payload.Lock.MarshalTOML()
l, err := sw.Lock.MarshalTOML()
if err != nil {
return errors.Wrap(err, "ensure DryRun cannot serialize lock")
}
fmt.Println(string(l))
} else {
fmt.Printf("Would have written the following changes to %s:\n", LockName)
diff, err := formatLockDiff(*sw.Payload.LockDiff)
diff, err := formatLockDiff(*sw.LockDiff)
if err != nil {
return errors.Wrap(err, "ensure DryRun cannot serialize the lock diff")
}
@ -412,9 +464,9 @@ func (sw *SafeWriter) PrintPreparedActions() error {
}
}
if sw.Payload.HasVendor() {
if sw.HasVendor() {
fmt.Println("Would have written the following projects to the vendor directory:")
for _, project := range sw.Payload.Lock.Projects() {
for _, project := range sw.Lock.Projects() {
prj := project.Ident()
rev, _, _ := gps.VersionComponentStrings(project.Version())
if prj.Source == "" {
@ -526,6 +578,13 @@ func deleteDirs(toDelete []string) error {
return nil
}
// hasDotGit checks if a given path has .git file or directory in it.
func hasDotGit(path string) bool {
gitfilepath := filepath.Join(path, ".git")
_, err := os.Stat(gitfilepath)
return err == nil
}
type byLen []string
func (a byLen) Len() int { return len(a) }

Просмотреть файл

@ -5,6 +5,8 @@
package dep
import (
"io/ioutil"
"os"
"path/filepath"
"strings"
"testing"
@ -23,10 +25,8 @@ func TestSafeWriter_BadInput_MissingRoot(t *testing.T) {
pc := NewTestProjectContext(h, safeWriterProject)
defer pc.Release()
var sw SafeWriter
sw.Prepare(nil, nil, nil, VendorOnChanged)
err := sw.Write("", pc.SourceManager)
sw, _ := NewSafeWriter(nil, nil, nil, VendorOnChanged)
err := sw.Write("", pc.SourceManager, false)
if err == nil {
t.Fatal("should have errored without a root path, but did not")
@ -43,10 +43,8 @@ func TestSafeWriter_BadInput_MissingSourceManager(t *testing.T) {
pc.CopyFile(LockName, safeWriterGoldenLock)
pc.Load()
var sw SafeWriter
sw.Prepare(nil, nil, pc.Project.Lock, VendorAlways)
err := sw.Write(pc.Project.AbsRoot, nil)
sw, _ := NewSafeWriter(nil, nil, pc.Project.Lock, VendorAlways)
err := sw.Write(pc.Project.AbsRoot, nil, false)
if err == nil {
t.Fatal("should have errored without a source manager when forceVendor is true, but did not")
@ -61,9 +59,7 @@ func TestSafeWriter_BadInput_ForceVendorMissingLock(t *testing.T) {
pc := NewTestProjectContext(h, safeWriterProject)
defer pc.Release()
var sw SafeWriter
err := sw.Prepare(nil, nil, nil, VendorAlways)
_, err := NewSafeWriter(nil, nil, nil, VendorAlways)
if err == nil {
t.Fatal("should have errored without a lock when forceVendor is true, but did not")
} else if !strings.Contains(err.Error(), "newLock") {
@ -79,9 +75,7 @@ func TestSafeWriter_BadInput_OldLockOnly(t *testing.T) {
pc.CopyFile(LockName, safeWriterGoldenLock)
pc.Load()
var sw SafeWriter
err := sw.Prepare(nil, pc.Project.Lock, nil, VendorAlways)
_, err := NewSafeWriter(nil, pc.Project.Lock, nil, VendorAlways)
if err == nil {
t.Fatal("should have errored with only an old lock, but did not")
} else if !strings.Contains(err.Error(), "oldLock") {
@ -95,11 +89,10 @@ func TestSafeWriter_BadInput_NonexistentRoot(t *testing.T) {
pc := NewTestProjectContext(h, safeWriterProject)
defer pc.Release()
var sw SafeWriter
sw.Prepare(nil, nil, nil, VendorOnChanged)
sw, _ := NewSafeWriter(nil, nil, nil, VendorOnChanged)
missingroot := filepath.Join(pc.Project.AbsRoot, "nonexistent")
err := sw.Write(missingroot, pc.SourceManager)
err := sw.Write(missingroot, pc.SourceManager, false)
if err == nil {
t.Fatal("should have errored with nonexistent dir for root path, but did not")
@ -114,11 +107,10 @@ func TestSafeWriter_BadInput_RootIsFile(t *testing.T) {
pc := NewTestProjectContext(h, safeWriterProject)
defer pc.Release()
var sw SafeWriter
sw.Prepare(nil, nil, nil, VendorOnChanged)
sw, _ := NewSafeWriter(nil, nil, nil, VendorOnChanged)
fileroot := pc.CopyFile("fileroot", "txn_writer/badinput_fileroot")
err := sw.Write(fileroot, pc.SourceManager)
err := sw.Write(fileroot, pc.SourceManager, false)
if err == nil {
t.Fatal("should have errored when root path is a file, but did not")
@ -139,22 +131,21 @@ func TestSafeWriter_Manifest(t *testing.T) {
pc.CopyFile(ManifestName, safeWriterGoldenManifest)
pc.Load()
var sw SafeWriter
sw.Prepare(pc.Project.Manifest, nil, nil, VendorOnChanged)
sw, _ := NewSafeWriter(pc.Project.Manifest, nil, nil, VendorOnChanged)
// Verify prepared actions
if !sw.Payload.HasManifest() {
if !sw.HasManifest() {
t.Fatal("Expected the payload to contain the manifest")
}
if sw.Payload.HasLock() {
if sw.HasLock() {
t.Fatal("Did not expect the payload to contain the lock")
}
if sw.Payload.HasVendor() {
if sw.HasVendor() {
t.Fatal("Did not expect the payload to contain the vendor directory")
}
// Write changes
err := sw.Write(pc.Project.AbsRoot, pc.SourceManager)
err := sw.Write(pc.Project.AbsRoot, pc.SourceManager, false)
h.Must(errors.Wrap(err, "SafeWriter.Write failed"))
// Verify file system changes
@ -182,22 +173,21 @@ func TestSafeWriter_ManifestAndUnmodifiedLock(t *testing.T) {
pc.CopyFile(LockName, safeWriterGoldenLock)
pc.Load()
var sw SafeWriter
sw.Prepare(pc.Project.Manifest, pc.Project.Lock, pc.Project.Lock, VendorOnChanged)
sw, _ := NewSafeWriter(pc.Project.Manifest, pc.Project.Lock, pc.Project.Lock, VendorOnChanged)
// Verify prepared actions
if !sw.Payload.HasManifest() {
if !sw.HasManifest() {
t.Fatal("Expected the payload to contain the manifest")
}
if !sw.Payload.HasLock() {
if !sw.HasLock() {
t.Fatal("Expected the payload to contain the lock.")
}
if sw.Payload.HasVendor() {
if sw.HasVendor() {
t.Fatal("Did not expect the payload to contain the vendor directory")
}
// Write changes
err := sw.Write(pc.Project.AbsRoot, pc.SourceManager)
err := sw.Write(pc.Project.AbsRoot, pc.SourceManager, false)
h.Must(errors.Wrap(err, "SafeWriter.Write failed"))
// Verify file system changes
@ -225,22 +215,21 @@ func TestSafeWriter_ManifestAndUnmodifiedLockWithForceVendor(t *testing.T) {
pc.CopyFile(LockName, safeWriterGoldenLock)
pc.Load()
var sw SafeWriter
sw.Prepare(pc.Project.Manifest, pc.Project.Lock, pc.Project.Lock, VendorAlways)
sw, _ := NewSafeWriter(pc.Project.Manifest, pc.Project.Lock, pc.Project.Lock, VendorAlways)
// Verify prepared actions
if !sw.Payload.HasManifest() {
if !sw.HasManifest() {
t.Fatal("Expected the payload to contain the manifest")
}
if !sw.Payload.HasLock() {
if !sw.HasLock() {
t.Fatal("Expected the payload to contain the lock")
}
if !sw.Payload.HasVendor() {
if !sw.HasVendor() {
t.Fatal("Expected the payload to contain the vendor directory")
}
// Write changes
err := sw.Write(pc.Project.AbsRoot, pc.SourceManager)
err := sw.Write(pc.Project.AbsRoot, pc.SourceManager, false)
h.Must(errors.Wrap(err, "SafeWriter.Write failed"))
// Verify file system changes
@ -270,25 +259,24 @@ func TestSafeWriter_ModifiedLock(t *testing.T) {
pc.CopyFile(LockName, safeWriterGoldenLock)
pc.Load()
var sw SafeWriter
originalLock := new(Lock)
*originalLock = *pc.Project.Lock
originalLock.Memo = []byte{} // zero out the input hash to ensure non-equivalency
sw.Prepare(nil, originalLock, pc.Project.Lock, VendorOnChanged)
sw, _ := NewSafeWriter(nil, originalLock, pc.Project.Lock, VendorOnChanged)
// Verify prepared actions
if sw.Payload.HasManifest() {
if sw.HasManifest() {
t.Fatal("Did not expect the payload to contain the manifest")
}
if !sw.Payload.HasLock() {
if !sw.HasLock() {
t.Fatal("Expected the payload to contain the lock")
}
if !sw.Payload.HasVendor() {
if !sw.HasVendor() {
t.Fatal("Expected the payload to contain the vendor directory")
}
// Write changes
err := sw.Write(pc.Project.AbsRoot, pc.SourceManager)
err := sw.Write(pc.Project.AbsRoot, pc.SourceManager, false)
h.Must(errors.Wrap(err, "SafeWriter.Write failed"))
// Verify file system changes
@ -318,25 +306,24 @@ func TestSafeWriter_ModifiedLockSkipVendor(t *testing.T) {
pc.CopyFile(LockName, safeWriterGoldenLock)
pc.Load()
var sw SafeWriter
originalLock := new(Lock)
*originalLock = *pc.Project.Lock
originalLock.Memo = []byte{} // zero out the input hash to ensure non-equivalency
sw.Prepare(nil, originalLock, pc.Project.Lock, VendorNever)
sw, _ := NewSafeWriter(nil, originalLock, pc.Project.Lock, VendorNever)
// Verify prepared actions
if sw.Payload.HasManifest() {
if sw.HasManifest() {
t.Fatal("Did not expect the payload to contain the manifest")
}
if !sw.Payload.HasLock() {
if !sw.HasLock() {
t.Fatal("Expected the payload to contain the lock")
}
if sw.Payload.HasVendor() {
if sw.HasVendor() {
t.Fatal("Did not expect the payload to contain the vendor directory")
}
// Write changes
err := sw.Write(pc.Project.AbsRoot, pc.SourceManager)
err := sw.Write(pc.Project.AbsRoot, pc.SourceManager, false)
h.Must(errors.Wrap(err, "SafeWriter.Write failed"))
// Verify file system changes
@ -363,25 +350,23 @@ func TestSafeWriter_ForceVendorWhenVendorAlreadyExists(t *testing.T) {
pc.CopyFile(LockName, safeWriterGoldenLock)
pc.Load()
var sw SafeWriter
// Populate vendor
sw.Prepare(nil, pc.Project.Lock, pc.Project.Lock, VendorAlways)
err := sw.Write(pc.Project.AbsRoot, pc.SourceManager)
sw, _ := NewSafeWriter(nil, pc.Project.Lock, pc.Project.Lock, VendorAlways)
err := sw.Write(pc.Project.AbsRoot, pc.SourceManager, false)
h.Must(errors.Wrap(err, "SafeWriter.Write failed"))
// Verify prepared actions
sw.Prepare(nil, nil, pc.Project.Lock, VendorAlways)
if sw.Payload.HasManifest() {
sw, _ = NewSafeWriter(nil, nil, pc.Project.Lock, VendorAlways)
if sw.HasManifest() {
t.Fatal("Did not expect the payload to contain the manifest")
}
if !sw.Payload.HasLock() {
if !sw.HasLock() {
t.Fatal("Expected the payload to contain the lock")
}
if !sw.Payload.HasVendor() {
if !sw.HasVendor() {
t.Fatal("Expected the payload to contain the vendor directory ")
}
err = sw.Write(pc.Project.AbsRoot, pc.SourceManager)
err = sw.Write(pc.Project.AbsRoot, pc.SourceManager, false)
h.Must(errors.Wrap(err, "SafeWriter.Write failed"))
// Verify file system changes
@ -410,26 +395,25 @@ func TestSafeWriter_NewLock(t *testing.T) {
defer pc.Release()
pc.Load()
var sw SafeWriter
lf := h.GetTestFile(safeWriterGoldenLock)
defer lf.Close()
newLock, err := readLock(lf)
h.Must(err)
sw.Prepare(nil, nil, newLock, VendorOnChanged)
sw, _ := NewSafeWriter(nil, nil, newLock, VendorOnChanged)
// Verify prepared actions
if sw.Payload.HasManifest() {
if sw.HasManifest() {
t.Fatal("Did not expect the payload to contain the manifest")
}
if !sw.Payload.HasLock() {
if !sw.HasLock() {
t.Fatal("Expected the payload to contain the lock")
}
if !sw.Payload.HasVendor() {
if !sw.HasVendor() {
t.Fatal("Expected the payload to contain the vendor directory")
}
// Write changes
err = sw.Write(pc.Project.AbsRoot, pc.SourceManager)
err = sw.Write(pc.Project.AbsRoot, pc.SourceManager, false)
h.Must(errors.Wrap(err, "SafeWriter.Write failed"))
// Verify file system changes
@ -455,26 +439,25 @@ func TestSafeWriter_NewLockSkipVendor(t *testing.T) {
defer pc.Release()
pc.Load()
var sw SafeWriter
lf := h.GetTestFile(safeWriterGoldenLock)
defer lf.Close()
newLock, err := readLock(lf)
h.Must(err)
sw.Prepare(nil, nil, newLock, VendorNever)
sw, _ := NewSafeWriter(nil, nil, newLock, VendorNever)
// Verify prepared actions
if sw.Payload.HasManifest() {
if sw.HasManifest() {
t.Fatal("Did not expect the payload to contain the manifest")
}
if !sw.Payload.HasLock() {
if !sw.HasLock() {
t.Fatal("Expected the payload to contain the lock")
}
if sw.Payload.HasVendor() {
if sw.HasVendor() {
t.Fatal("Did not expect the payload to contain the vendor directory")
}
// Write changes
err = sw.Write(pc.Project.AbsRoot, pc.SourceManager)
err = sw.Write(pc.Project.AbsRoot, pc.SourceManager, false)
h.Must(errors.Wrap(err, "SafeWriter.Write failed"))
// Verify file system changes
@ -506,11 +489,10 @@ func TestSafeWriter_DiffLocks(t *testing.T) {
updatedLock, err := readLock(ulf)
h.Must(err)
var sw SafeWriter
sw.Prepare(nil, pc.Project.Lock, updatedLock, VendorOnChanged)
sw, _ := NewSafeWriter(nil, pc.Project.Lock, updatedLock, VendorOnChanged)
// Verify lock diff
diff := sw.Payload.LockDiff
diff := sw.LockDiff
if diff == nil {
t.Fatal("Expected the payload to contain a diff of the lock files")
}
@ -522,3 +504,66 @@ func TestSafeWriter_DiffLocks(t *testing.T) {
t.Fatal(err)
}
}
func TestHasDotGit(t *testing.T) {
// Create a tempdir with .git file
td, err := ioutil.TempDir(os.TempDir(), "dotGitFile")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(td)
os.OpenFile(td+string(filepath.Separator)+".git", os.O_CREATE, 0777)
if !hasDotGit(td) {
t.Fatal("Expected hasDotGit to find .git")
}
}
func TestSafeWriter_VendorDotGitPreservedWithForceVendor(t *testing.T) {
h := test.NewHelper(t)
defer h.Cleanup()
pc := NewTestProjectContext(h, safeWriterProject)
defer pc.Release()
gitDirPath := filepath.Join(pc.Project.AbsRoot, "vendor", ".git")
os.MkdirAll(gitDirPath, 0777)
dummyFile := filepath.Join("vendor", ".git", "badinput_fileroot")
pc.CopyFile(dummyFile, "txn_writer/badinput_fileroot")
pc.CopyFile(ManifestName, safeWriterGoldenManifest)
pc.CopyFile(LockName, safeWriterGoldenLock)
pc.Load()
sw, _ := NewSafeWriter(pc.Project.Manifest, pc.Project.Lock, pc.Project.Lock, VendorAlways)
// Verify prepared actions
if !sw.HasManifest() {
t.Fatal("Expected the payload to contain the manifest")
}
if !sw.HasLock() {
t.Fatal("Expected the payload to contain the lock")
}
if !sw.HasVendor() {
t.Fatal("Expected the payload to contain the vendor directory")
}
err := sw.Write(pc.Project.AbsRoot, pc.SourceManager, false)
h.Must(errors.Wrap(err, "SafeWriter.Write failed"))
// Verify file system changes
if err := pc.ManifestShouldMatchGolden(safeWriterGoldenManifest); err != nil {
t.Fatal(err)
}
if err := pc.LockShouldMatchGolden(safeWriterGoldenLock); err != nil {
t.Fatal(err)
}
if err := pc.VendorShouldExist(); err != nil {
t.Fatal(err)
}
if err := pc.VendorFileShouldExist("github.com/sdboyer/dep-test"); err != nil {
t.Fatal(err)
}
if err := pc.VendorFileShouldExist(".git/badinput_fileroot"); err != nil {
t.Fatal(err)
}
}