Respect dry-run flag during dep ensure

This commit is contained in:
Carolyn Van Slyck 2017-03-09 13:28:42 -06:00
Родитель 1c50c3ad88
Коммит e43a3b839e
13 изменённых файлов: 583 добавлений и 287 удалений

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

@ -147,25 +147,26 @@ func (cmd *ensureCommand) Run(ctx *dep.Ctx, args []string) error {
return errors.Wrap(err, "ensure Solve()")
}
sw := dep.SafeWriter{
Root: p.AbsRoot,
Lock: p.Lock,
NewLock: solution,
SourceManager: sm,
}
if !cmd.update {
sw.Manifest = p.Manifest
}
// check if vendor exists, because if the locks are the same but
// vendor does not exist we should write vendor
vendorExists, err := dep.IsNonEmptyDir(filepath.Join(sw.Root, "vendor"))
vendorExists, err := dep.IsNonEmptyDir(filepath.Join(p.AbsRoot, "vendor"))
if err != nil {
return errors.Wrap(err, "ensure vendor is a directory")
}
writeV := !vendorExists && solution != nil
return errors.Wrap(sw.WriteAllSafe(writeV), "grouped write of manifest, lock and vendor")
var sw dep.SafeWriter
var manifest *dep.Manifest
if !cmd.update {
manifest = p.Manifest
}
sw.Prepare(manifest, p.Lock, solution, writeV)
if cmd.dryRun {
return sw.PrintPreparedActions()
}
return errors.Wrap(sw.Write(p.AbsRoot, sm), "grouped write of manifest, lock and vendor")
}
func applyUpdateArgs(args []string, params *gps.SolveParameters) {

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

@ -106,13 +106,13 @@ func (cmd *initCommand) Run(ctx *dep.Ctx, args []string) error {
if err != nil {
return err
}
m := dep.Manifest{
m := &dep.Manifest{
Dependencies: pd.constraints,
}
// Make an initial lock from what knowledge we've collected about the
// versions on disk
l := dep.Lock{
l := &dep.Lock{
P: make([]gps.LockedProject, 0, len(pd.ondisk)),
}
@ -134,19 +134,13 @@ func (cmd *initCommand) Run(ctx *dep.Ctx, args []string) error {
)
}
sw := dep.SafeWriter{
Root: root,
SourceManager: sm,
Manifest: &m,
}
if len(pd.notondisk) > 0 {
vlogf("Solving...")
params := gps.SolveParameters{
RootDir: root,
RootPackageTree: pkgT,
Manifest: &m,
Lock: &l,
Manifest: m,
Lock: l,
}
if *verbose {
@ -163,14 +157,14 @@ func (cmd *initCommand) Run(ctx *dep.Ctx, args []string) error {
handleAllTheFailuresOfTheWorld(err)
return err
}
sw.Lock = dep.LockFromInterface(soln)
} else {
sw.Lock = &l
l = dep.LockFromInterface(soln)
}
vlogf("Writing manifest and lock files.")
if err := sw.WriteAllSafe(true); err != nil {
var sw dep.SafeWriter
sw.Prepare(m, l, nil, true)
if err := sw.Write(root, sm); err != nil {
return errors.Wrap(err, "safe write of manifest and lock")
}

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

@ -179,15 +179,9 @@ func (cmd *removeCommand) Run(ctx *dep.Ctx, args []string) error {
return err
}
sw := dep.SafeWriter{
Root: p.AbsRoot,
Manifest: p.Manifest,
Lock: p.Lock,
NewLock: soln,
SourceManager: sm,
}
if err := sw.WriteAllSafe(false); err != nil {
var sw dep.SafeWriter
sw.Prepare(p.Manifest, p.Lock, soln, false)
if err := sw.Write(p.AbsRoot, sm); err != nil {
return errors.Wrap(err, "grouped write of manifest, lock and vendor")
}
return nil

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

@ -0,0 +1,21 @@
{
"memo": "9a5243dd3fa20feeaa20398e7283d6c566532e2af1aae279a010df34793761c5",
"projects": [
{
"name": "github.com/sdboyer/deptest",
"version": "v0.8.0",
"revision": "ff2948a2ac8f538c4ecd55962e919d1e13e74baf",
"packages": [
"."
]
},
{
"name": "github.com/sdboyer/deptestdos",
"version": "v2.0.0",
"revision": "5c607206be5decd28e6263ffffdcee067266015e",
"packages": [
"."
]
}
]
}

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

@ -0,0 +1,7 @@
{
"dependencies": {
"github.com/sdboyer/deptest": {
"version": "~0.8.0"
}
}
}

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

@ -0,0 +1,21 @@
{
"memo": "9a5243dd3fa20feeaa20398e7283d6c566532e2af1aae279a010df34793761c5",
"projects": [
{
"name": "github.com/sdboyer/deptest",
"version": "v0.8.0",
"revision": "ff2948a2ac8f538c4ecd55962e919d1e13e74baf",
"packages": [
"."
]
},
{
"name": "github.com/sdboyer/deptestdos",
"version": "v2.0.0",
"revision": "5c607206be5decd28e6263ffffdcee067266015e",
"packages": [
"."
]
}
]
}

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

@ -0,0 +1,18 @@
// 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 main
import (
"github.com/sdboyer/deptest"
"github.com/sdboyer/deptestdos"
)
func main() {
err := nil
if err != nil {
deptest.Map["yo yo!"]
}
deptestdos.diMeLo("whatev")
}

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

@ -0,0 +1,7 @@
{
"dependencies": {
"github.com/sdboyer/deptest": {
"version": "~0.8.0"
}
}
}

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

@ -0,0 +1,6 @@
{
"commands": [
["init"],
["ensure", "-n", "-update", "github.com/sdboyer/deptest"]
]
}

25
lock.go
Просмотреть файл

@ -102,16 +102,7 @@ func (l *Lock) MarshalJSON() ([]byte, error) {
}
v := lp.Version()
// Figure out how to get the underlying revision
switch tv := v.(type) {
case gps.UnpairedVersion:
// TODO we could error here, if we want to be very defensive about not
// allowing a lock to be written if without an immmutable revision
case gps.Revision:
ld.Revision = tv.String()
case gps.PairedVersion:
ld.Revision = tv.Underlying().String()
}
ld.Revision = GetRevisionFromVersion(v)
switch v.Type() {
case gps.IsBranch:
@ -134,6 +125,20 @@ func (l *Lock) MarshalJSON() ([]byte, error) {
return buf.Bytes(), err
}
func GetRevisionFromVersion(v gps.Version) string {
// Figure out how to get the underlying revision
switch tv := v.(type) {
case gps.UnpairedVersion:
// TODO we could error here, if we want to be very defensive about not
// allowing a lock to be written if without an immmutable revision
case gps.Revision:
return tv.String()
case gps.PairedVersion:
return tv.Underlying().String()
}
return ""
}
// LockFromInterface converts an arbitrary gps.Lock to dep's representation of a
// lock. If the input is already dep's *lock, the input is returned directly.
//

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

@ -513,11 +513,20 @@ func (h *Helper) Path(name string) string {
// MustExist fails if path does not exist.
func (h *Helper) MustExist(path string) {
if !h.Exist(path) {
h.t.Fatalf("%+v", errors.Errorf("%s does not exist but should", path))
if err := h.ShouldExist(path); err != nil {
h.t.Fatalf("%+v", err)
}
}
// ShouldExist returns an error if path does not exist.
func (h *Helper) ShouldExist(path string) error {
if !h.Exist(path) {
return errors.Errorf("%s does not exist but should", path)
}
return nil
}
// Exist returns whether or not a path exists
func (h *Helper) Exist(path string) bool {
if _, err := os.Stat(path); err != nil {
@ -532,11 +541,20 @@ func (h *Helper) Exist(path string) bool {
// MustNotExist fails if path exists.
func (h *Helper) MustNotExist(path string) {
if h.Exist(path) {
h.t.Fatalf("%+v", errors.Errorf("%s exists but should not", path))
if err := h.ShouldNotExist(path); err != nil {
h.t.Fatalf("%+v", err)
}
}
// ShouldNotExist returns an error if path exists.
func (h *Helper) ShouldNotExist(path string) error {
if h.Exist(path) {
return errors.Errorf("%s exists but should not", path)
}
return nil
}
// Cleanup cleans up a test that runs testgo.
func (h *Helper) Cleanup() {
if h.wd != "" {

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

@ -21,85 +21,132 @@ import (
// It is not impervious to errors (writing to disk is hard), but it should
// guard against non-arcane failure conditions.
type SafeWriter struct {
Root string // absolute path of root dir in which to write
Manifest *Manifest // the manifest to write, if any
Lock *Lock // the old lock, if any
NewLock gps.Lock // the new lock, if any
SourceManager gps.SourceManager
Payload *SafeWriterPayload
}
// WriteAllSafe writes out some combination of config yaml, lock, and a vendor
// tree, to a temp dir, then moves them into place if and only if all the write
// 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.
//
// - If a sw.Manifest is provided, it will be written to the standard manifest file
// name beneath sw.Root
// - If sw.Lock is provided without an sw.NewLock, it will be written to the standard
// lock file name in the root dir, but vendor will NOT be written
// - If sw.Lock and sw.NewLock are both provided and are equivalent, then neither lock
// nor vendor will be written
// - If sw.Lock and sw.NewLock are both provided and are not equivalent,
// the nl will be written to the same location as above, and a vendor
// tree will be written to sw.Root/vendor
// - If sw.NewLock is provided and sw.Lockock is not, it will write both a lock
// and vendor dir in the same way
// - If the forceVendor param is true, then vendor will be unconditionally
// written out based on sw.NewLock if present, else sw.Lock, else error.
//
// Any of m, l, or nl can be omitted; the grouped write operation will continue
// for whichever inputs are present. A SourceManager is only required if vendor
// is being written.
func (sw SafeWriter) WriteAllSafe(forceVendor bool) error {
// Decide which writes we need to do
var writeM, writeL, writeV bool
writeV = forceVendor
// SafeWriterPayload represents the actions SafeWriter will execute when SafeWriter.Write is called.
type SafeWriterPayload struct {
Manifest *Manifest
Lock *Lock
LockDiff *LockDiff
ForceWriteVendor bool
}
if sw.Manifest != nil {
writeM = true
func (payload *SafeWriterPayload) HasLock() bool {
return payload.Lock != nil
}
func (payload *SafeWriterPayload) HasManifest() bool {
return payload.Manifest != nil
}
func (payload *SafeWriterPayload) HasVendor() bool {
// TODO(carolynvs) this can be calculated based on if we are writing the lock
// init -> switch to newlock
// ensure checks existence, why not move that into the prep?
return payload.ForceWriteVendor
}
// LockDiff is the set of differences between an existing lock file and an updated lock file.
// TODO(carolynvs) this should be moved to gps
type LockDiff struct {
Add []gps.LockedProject
Remove []gps.LockedProject
Modify []LockedProjectDiff
}
// LockedProjectDiff contains the before and after snapshot of a project reference.
// TODO(carolynvs) this should be moved to gps
type LockedProjectDiff struct {
Current gps.LockedProject // Current represents the project reference as defined in the existing lock file.
Updated gps.LockedProject // Updated represents the desired project reference.
}
// 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 lock is provided it will be written to the standard
// lock file name in the root dir, but vendor will NOT be written
// - If lock and newLock are both provided and are equivalent, then neither lock
// nor vendor will be written
// - If lock and newLock are both provided and are not equivalent,
// the newLock will be written to the same location as above, and a vendor
// tree will be written to the vendor directory
// - If newLock is provided and lock is not, it will write both a lock
// and the vendor directory in the same way
// - If the forceVendor param is true, then vendor/ will be unconditionally
// written out based on newLock if present, else lock, else error.
func (sw *SafeWriter) Prepare(manifest *Manifest, lock *Lock, newLock gps.Lock, forceVendor bool) {
sw.Payload = &SafeWriterPayload{
Manifest: manifest,
ForceWriteVendor: forceVendor,
}
if sw.NewLock != nil {
if sw.Lock == nil {
writeL, writeV = true, true
if newLock != nil {
rlf := LockFromInterface(newLock)
if lock == nil {
sw.Payload.Lock = rlf
sw.Payload.ForceWriteVendor = true
} else {
rlf := LockFromInterface(sw.NewLock)
if !locksAreEquivalent(rlf, sw.Lock) {
writeL, writeV = true, true
if !locksAreEquivalent(rlf, lock) {
sw.Payload.Lock = rlf
sw.Payload.ForceWriteVendor = true
}
}
} else if sw.Lock != nil {
writeL = true
} else if lock != nil {
sw.Payload.Lock = lock
}
}
if sw.Root == "" {
func (payload SafeWriterPayload) validate(root string, sm gps.SourceManager) error {
if root == "" {
return errors.New("root path must be non-empty")
}
if is, err := IsDir(sw.Root); !is {
if is, err := IsDir(root); !is {
if err != nil {
return err
}
return fmt.Errorf("root path %q does not exist", sw.Root)
return fmt.Errorf("root path %q does not exist", root)
}
if !writeM && !writeL && !writeV {
if payload.HasVendor() && sm == nil {
return errors.New("must provide a SourceManager if writing out a vendor dir")
}
if payload.HasVendor() && payload.Lock == nil {
return errors.New("must provide a lock in order to write out vendor")
}
return nil
}
// Write saves some combination of config yaml, lock, and a vendor tree.
// root is the absolute path of root dir in which to write.
// sm is only required if vendor is being written.
//
// It first writes to a temp dir, then moves them in place if and only if all the write
// 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)
if err != nil {
return err
}
if !sw.Payload.HasManifest() && !sw.Payload.HasLock() && !sw.Payload.HasVendor() {
// nothing to do
return nil
}
if writeV && sw.SourceManager == nil {
return errors.New("must provide a SourceManager if writing out a vendor dir")
}
if writeV && sw.Lock == nil && sw.NewLock == nil {
return errors.New("must provide a lock in order to write out vendor")
}
mpath := filepath.Join(sw.Root, ManifestName)
lpath := filepath.Join(sw.Root, LockName)
vpath := filepath.Join(sw.Root, "vendor")
mpath := filepath.Join(root, ManifestName)
lpath := filepath.Join(root, LockName)
vpath := filepath.Join(root, "vendor")
td, err := ioutil.TempDir(os.TempDir(), "dep")
if err != nil {
@ -107,35 +154,20 @@ func (sw SafeWriter) WriteAllSafe(forceVendor bool) error {
}
defer os.RemoveAll(td)
if writeM {
if err := writeFile(filepath.Join(td, ManifestName), sw.Manifest); err != nil {
if sw.Payload.HasManifest() {
if err := writeFile(filepath.Join(td, ManifestName), sw.Payload.Manifest); err != nil {
return errors.Wrap(err, "failed to write manifest file to temp dir")
}
}
if writeL {
if sw.NewLock == nil {
// the new lock is nil but the flag is on, so we must be writing
// the other one
if err := writeFile(filepath.Join(td, LockName), sw.Lock); err != nil {
return errors.Wrap(err, "failed to write lock file to temp dir")
}
} else {
rlf := LockFromInterface(sw.NewLock)
if err := writeFile(filepath.Join(td, LockName), rlf); err != nil {
return errors.Wrap(err, "failed to write lock file to temp dir")
}
if sw.Payload.HasLock() {
if err := writeFile(filepath.Join(td, LockName), sw.Payload.Lock); err != nil {
return errors.Wrap(err, "failed to write lock file to temp dir")
}
}
if writeV {
// Prefer the nl, but take the l if only that's available, as could be the
// case if true was passed for forceVendor.
l := sw.NewLock
if l == nil {
l = sw.Lock
}
err = gps.WriteDepTree(filepath.Join(td, "vendor"), l, sw.SourceManager, true)
if sw.Payload.HasVendor() {
err = gps.WriteDepTree(filepath.Join(td, "vendor"), sw.Payload.Lock, sm, true)
if err != nil {
return errors.Wrap(err, "error while writing out vendor tree")
}
@ -150,7 +182,7 @@ func (sw SafeWriter) WriteAllSafe(forceVendor bool) error {
var failerr error
var vendorbak string
if writeM {
if sw.Payload.HasManifest() {
if _, err := os.Stat(mpath); err == nil {
// Move out the old one.
tmploc := filepath.Join(td, ManifestName+".orig")
@ -168,7 +200,7 @@ func (sw SafeWriter) WriteAllSafe(forceVendor bool) error {
}
}
if writeL {
if sw.Payload.HasLock() {
if _, err := os.Stat(lpath); err == nil {
// Move out the old one.
tmploc := filepath.Join(td, LockName+".orig")
@ -187,7 +219,7 @@ func (sw SafeWriter) WriteAllSafe(forceVendor bool) error {
}
}
if writeV {
if sw.Payload.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
@ -215,7 +247,7 @@ func (sw SafeWriter) WriteAllSafe(forceVendor bool) 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 writeV {
if sw.Payload.HasVendor() {
// Nothing we can really do about an error at this point, so ignore it
os.RemoveAll(vendorbak)
}
@ -230,3 +262,38 @@ fail:
}
return failerr
}
func (sw *SafeWriter) PrintPreparedActions() error {
if sw.Payload.HasManifest() {
fmt.Println("Would have written the following manifest.json:")
m, err := sw.Payload.Manifest.MarshalJSON()
if err != nil {
return errors.Wrap(err, "ensure DryRun cannot read manifest")
}
fmt.Println(string(m))
}
if sw.Payload.HasLock() {
fmt.Println("Would have written the following lock.json:")
m, err := sw.Payload.Lock.MarshalJSON()
if err != nil {
return errors.Wrap(err, "ensure DryRun cannot read lock")
}
fmt.Println(string(m))
}
if sw.Payload.HasVendor() {
fmt.Println("Would have written the following projects to the vendor directory:")
for _, project := range sw.Payload.Lock.Projects() {
prj := project.Ident()
rev := GetRevisionFromVersion(project.Version())
if prj.Source == "" {
fmt.Printf("%s@%s\n", prj.ProjectRoot, rev)
} else {
fmt.Printf("%s -> %s@%s\n", prj.ProjectRoot, prj.Source, rev)
}
}
}
return nil
}

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

@ -7,10 +7,10 @@ package dep
import (
"os"
"path/filepath"
"strconv"
"testing"
"github.com/golang/dep/test"
"github.com/pkg/errors"
)
func TestTxnWriterBadInputs(t *testing.T) {
@ -18,190 +18,327 @@ func TestTxnWriterBadInputs(t *testing.T) {
defer h.Cleanup()
h.TempDir("txnwriter")
td := h.Path(".")
root := h.Path(".")
var sw SafeWriter
pc, err := NewContext()
if err != nil {
t.Fatal(err)
}
sm, err := pc.SourceManager()
defer sm.Release()
if err != nil {
t.Fatal(err)
}
sw.Prepare(nil, nil, nil, false)
// no root
if err := sw.WriteAllSafe(false); err == nil {
t.Errorf("should have errored without a root path, but did not")
}
sw.Root = td
if err := sw.WriteAllSafe(false); err != nil {
t.Errorf("write with only root should be fine, just a no-op, but got err %q", err)
}
if err := sw.WriteAllSafe(true); err == nil {
t.Errorf("should fail because no source manager was provided for writing vendor")
if err := sw.Write("", nil); err == nil {
t.Fatalf("should have errored without a root path, but did not")
}
if err := sw.WriteAllSafe(true); err == nil {
t.Errorf("should fail because no lock was provided from which to write vendor")
// noop
if err := sw.Write(root, nil); err != nil {
t.Fatalf("write with only root should be fine, just a no-op, but got err %q", err)
}
// force vendor with missing source manager
sw.Prepare(nil, nil, nil, true)
if !sw.Payload.HasVendor() {
t.Fatalf("writeV not propogated")
}
if err := sw.Write(root, nil); err == nil {
t.Fatalf("should fail because no source manager was provided for writing vendor")
}
// force vendor with missing lock
if err := sw.Write(root, sm); err == nil {
t.Fatalf("should fail because no lock was provided from which to write vendor")
}
// now check dir validation
sw.Root = filepath.Join(td, "nonexistent")
if err := sw.WriteAllSafe(false); err == nil {
t.Errorf("should have errored with nonexistent dir for root path, but did not")
sw.Prepare(nil, nil, nil, false)
missingroot := filepath.Join(root, "nonexistent")
if err := sw.Write(missingroot, sm); err == nil {
t.Fatalf("should have errored with nonexistent dir for root path, but did not")
}
sw.Root = filepath.Join(td, "myfile")
srcf, err := os.Create(sw.Root)
fileroot := filepath.Join(root, "myfile")
srcf, err := os.Create(fileroot)
if err != nil {
t.Fatal(err)
}
srcf.Close()
if err := sw.WriteAllSafe(false); err == nil {
t.Errorf("should have errored when root path is a file, but did not")
if err := sw.Write(fileroot, sm); err == nil {
t.Fatalf("should have errored when root path is a file, but did not")
}
}
func TestTxnWriter(t *testing.T) {
const safeWriterProject = "safewritertest"
const safeWriterGoldenManifest = "txn_writer/expected_manifest.json"
const safeWriterGoldenLock = "txn_writer/expected_lock.json"
func TestSafeWriter_ManifestOnly(t *testing.T) {
test.NeedsExternalNetwork(t)
test.NeedsGit(t)
h := test.NewHelper(t)
defer h.Cleanup()
h.TempDir("")
defer h.Cleanup()
c := &Ctx{
GOPATH: h.Path("."),
}
sm, err := c.SourceManager()
defer sm.Release()
h.Must(err)
pc := NewTestProjectContext(h, safeWriterProject)
defer pc.Release()
pc.CopyFile(ManifestName, safeWriterGoldenManifest)
pc.Load()
var sw SafeWriter
var mpath, lpath, vpath string
var count int
reset := func() {
pr := filepath.Join("src", "txnwriter"+strconv.Itoa(count))
h.TempDir(pr)
sw.Prepare(pc.Project.Manifest, nil, nil, false)
sw = SafeWriter{
Root: h.Path(pr),
SourceManager: sm,
}
h.Cd(sw.Root)
mpath = filepath.Join(sw.Root, ManifestName)
lpath = filepath.Join(sw.Root, LockName)
vpath = filepath.Join(sw.Root, "vendor")
count++
// Verify prepared actions
if !sw.Payload.HasManifest() {
t.Fatal("Expected the payload to contain the manifest")
}
reset()
// super basic manifest and lock
goldenm := "txn_writer/expected_manifest.json"
goldenl := "txn_writer/expected_lock.json"
wantm := h.GetTestFileString(goldenm)
wantl := h.GetTestFileString(goldenl)
m, err := readManifest(h.GetTestFile(goldenm))
h.Must(err)
l, err := readLock(h.GetTestFile(goldenl))
h.Must(err)
// Just write manifest
sw.Manifest = m
h.Must(sw.WriteAllSafe(false))
h.MustExist(mpath)
h.MustNotExist(lpath)
h.MustNotExist(vpath)
gotm := h.ReadManifest()
if wantm != gotm {
if *test.UpdateGolden {
wantm = gotm
if err = h.WriteTestFile(goldenm, gotm); err != nil {
t.Fatal(err)
}
} else {
t.Fatalf("expected %s, got %s", wantm, gotm)
}
if sw.Payload.HasLock() {
t.Fatal("Did not expect the payload to contain the lock")
}
if sw.Payload.HasVendor() {
t.Fatal("Did not expect the payload to contain the vendor directory")
}
// Manifest and lock, but no vendor
sw.Lock = l
h.Must(sw.WriteAllSafe(false))
h.MustExist(mpath)
h.MustExist(lpath)
h.MustNotExist(vpath)
// Write changes
err := sw.Write(pc.Project.AbsRoot, pc.SourceManager)
h.Must(errors.Wrap(err, "SafeWriter.Write failed"))
gotm = h.ReadManifest()
if wantm != gotm {
t.Fatalf("expected %s, got %s", wantm, gotm)
// Verify file system changes
if err := pc.ManifestShouldMatchGolden(safeWriterGoldenManifest); err != nil {
t.Fatal(err)
}
gotl := h.ReadLock()
if wantl != gotl {
if *test.UpdateGolden {
wantl = gotl
if err = h.WriteTestFile(goldenl, gotl); err != nil {
t.Fatal(err)
}
} else {
t.Fatalf("expected %s, got %s", wantl, gotl)
}
if err := pc.LockShouldNotExist(); err != nil {
t.Fatal(err)
}
h.Must(sw.WriteAllSafe(true))
h.MustExist(mpath)
h.MustExist(lpath)
h.MustExist(vpath)
h.MustExist(filepath.Join(vpath, "github.com", "sdboyer", "dep-test"))
gotm = h.ReadManifest()
if wantm != gotm {
t.Fatalf("expected %s, got %s", wantm, gotm)
if err := pc.VendorShouldNotExist(); err != nil {
t.Fatal(err)
}
}
func TestSafeWriter_ManifestAndUnmodifiedLock(t *testing.T) {
test.NeedsExternalNetwork(t)
test.NeedsGit(t)
h := test.NewHelper(t)
defer h.Cleanup()
pc := NewTestProjectContext(h, safeWriterProject)
defer pc.Release()
pc.CopyFile(ManifestName, safeWriterGoldenManifest)
pc.CopyFile(LockName, safeWriterGoldenLock)
pc.Load()
var sw SafeWriter
sw.Prepare(pc.Project.Manifest, pc.Project.Lock, nil, false)
// Verify prepared actions
if !sw.Payload.HasManifest() {
t.Fatal("Expected the payload to contain the manifest")
}
if !sw.Payload.HasLock() {
t.Fatal("Expected the payload to contain the lock")
}
if sw.Payload.HasVendor() {
t.Fatal("Did not expect the payload to contain the vendor directory")
}
// Write changes
err := sw.Write(pc.Project.AbsRoot, pc.SourceManager)
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.VendorShouldNotExist(); err != nil {
t.Fatal(err)
}
}
func TestSafeWriter_ManifestAndUnmodifiedLockWithForceVendor(t *testing.T) {
test.NeedsExternalNetwork(t)
test.NeedsGit(t)
h := test.NewHelper(t)
defer h.Cleanup()
pc := NewTestProjectContext(h, safeWriterProject)
defer pc.Release()
pc.CopyFile(ManifestName, safeWriterGoldenManifest)
pc.CopyFile(LockName, safeWriterGoldenLock)
pc.Load()
var sw SafeWriter
sw.Prepare(pc.Project.Manifest, pc.Project.Lock, nil, true)
// Verify prepared actions
if !sw.Payload.HasManifest() {
t.Fatal("Expected the payload to contain the manifest")
}
if !sw.Payload.HasLock() {
t.Fatal("Expected the payload to contain the lock")
}
if !sw.Payload.HasVendor() {
t.Fatal("Expected the payload to the vendor directory")
}
// Write changes
err := sw.Write(pc.Project.AbsRoot, pc.SourceManager)
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)
}
}
func TestSafeWriter_UnmodifiedLock(t *testing.T) {
test.NeedsExternalNetwork(t)
test.NeedsGit(t)
h := test.NewHelper(t)
defer h.Cleanup()
pc := NewTestProjectContext(h, safeWriterProject)
defer pc.Release()
pc.CopyFile(LockName, safeWriterGoldenLock)
pc.Load()
var sw SafeWriter
sw.Prepare(nil, pc.Project.Lock, pc.Project.Lock, false)
// Verify prepared actions
if sw.Payload.HasManifest() {
t.Fatal("Did not expect the payload to contain the manifest")
}
if sw.Payload.HasLock() {
t.Fatal("Did not expect the payload to contain the lock")
}
if sw.Payload.HasVendor() {
t.Fatal("Did not expect the payload to contain the vendor directory")
}
// Write changes
err := sw.Write(pc.Project.AbsRoot, pc.SourceManager)
h.Must(errors.Wrap(err, "SafeWriter.Write failed"))
// Verify file system changes
// locks are equivalent, so nothing gets written
if err := pc.ManifestShouldNotExist(); err != nil {
t.Fatal(err)
}
if err := pc.VendorShouldNotExist(); err != nil {
t.Fatal(err)
}
}
func TestSafeWriter_ModifiedLockForceVendor(t *testing.T) {
test.NeedsExternalNetwork(t)
test.NeedsGit(t)
h := test.NewHelper(t)
defer h.Cleanup()
pc := NewTestProjectContext(h, safeWriterProject)
defer pc.Release()
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, true)
// Verify prepared actions
if sw.Payload.HasManifest() {
t.Fatal("Did not expect the payload to contain the manifest")
}
if !sw.Payload.HasLock() {
t.Fatal("Expected the payload to contain the lock")
}
if !sw.Payload.HasVendor() {
t.Fatal("Expected the payload to the vendor directory")
}
// Write changes
err := sw.Write(pc.Project.AbsRoot, pc.SourceManager)
h.Must(errors.Wrap(err, "SafeWriter.Write failed"))
// Verify file system changes
if err := pc.ManifestShouldNotExist(); 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)
}
}
func TestSafeWriter_ForceVendorWhenVendorAlreadyExists(t *testing.T) {
test.NeedsExternalNetwork(t)
test.NeedsGit(t)
h := test.NewHelper(t)
defer h.Cleanup()
pc := NewTestProjectContext(h, safeWriterProject)
defer pc.Release()
pc.CopyFile(LockName, safeWriterGoldenLock)
pc.Load()
var sw SafeWriter
// Populate vendor
sw.Prepare(nil, pc.Project.Lock, nil, true)
err := sw.Write(pc.Project.AbsRoot, pc.SourceManager)
h.Must(errors.Wrap(err, "SafeWriter.Write failed"))
// Verify prepared actions
sw.Prepare(nil, nil, pc.Project.Lock, true)
if sw.Payload.HasManifest() {
t.Fatal("Did not expect the payload to contain the manifest")
}
if !sw.Payload.HasLock() {
t.Fatal("Expected the payload to contain the lock")
}
if !sw.Payload.HasVendor() {
t.Fatal("Expected the payload to the vendor directory")
}
err = sw.Write(pc.Project.AbsRoot, pc.SourceManager)
h.Must(errors.Wrap(err, "SafeWriter.Write failed"))
// Verify file system changes
if err := pc.ManifestShouldNotExist(); 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)
}
gotl = h.ReadLock()
if wantl != gotl {
t.Fatalf("expected %s, got %s", wantl, gotl)
}
// start fresh, ignoring the manifest now
reset()
sw.Lock = l
sw.NewLock = l
h.Must(sw.WriteAllSafe(false))
// locks are equivalent, so nothing gets written
h.MustNotExist(mpath)
h.MustNotExist(lpath)
h.MustNotExist(vpath)
l2 := new(Lock)
*l2 = *l
// zero out the input hash to ensure non-equivalency
l2.Memo = []byte{}
sw.Lock = l2
h.Must(sw.WriteAllSafe(true))
h.MustNotExist(mpath)
h.MustExist(lpath)
h.MustExist(vpath)
h.MustExist(filepath.Join(vpath, "github.com", "sdboyer", "dep-test"))
gotl = h.ReadLock()
if wantl != gotl {
t.Fatalf("expected %s, got %s", wantl, gotl)
}
// repeat op to ensure good behavior when vendor dir already exists
sw.Lock = nil
h.Must(sw.WriteAllSafe(true))
h.MustNotExist(mpath)
h.MustExist(lpath)
h.MustExist(vpath)
h.MustExist(filepath.Join(vpath, "github.com", "sdboyer", "dep-test"))
gotl = h.ReadLock()
if wantl != gotl {
t.Fatalf("expected %s, got %s", wantl, gotl)
}
// TODO test txn rollback cases. maybe we can force errors with chmodding?
}