2016-12-23 17:49:24 +03:00
|
|
|
// 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.
|
|
|
|
|
2017-01-27 23:39:07 +03:00
|
|
|
package dep
|
2016-12-23 17:49:24 +03:00
|
|
|
|
|
|
|
import (
|
2017-03-01 23:52:09 +03:00
|
|
|
"bytes"
|
2016-12-23 17:49:24 +03:00
|
|
|
"fmt"
|
|
|
|
"io/ioutil"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
2017-03-01 23:52:09 +03:00
|
|
|
"sort"
|
|
|
|
"strings"
|
2016-12-23 17:49:24 +03:00
|
|
|
|
2017-03-23 01:11:43 +03:00
|
|
|
"github.com/pelletier/go-toml"
|
2016-12-23 18:34:20 +03:00
|
|
|
"github.com/pkg/errors"
|
2016-12-23 17:49:24 +03:00
|
|
|
"github.com/sdboyer/gps"
|
|
|
|
)
|
|
|
|
|
2017-04-13 15:30:07 +03:00
|
|
|
// Example string to be written to the manifest file
|
|
|
|
// if no dependencies are found in the project
|
|
|
|
// during `dep init`
|
|
|
|
const exampleToml = `
|
|
|
|
# Example:
|
|
|
|
# [[dependencies]]
|
2017-04-18 16:25:21 +03:00
|
|
|
# source = "https://github.com/myfork/package.git"
|
2017-04-13 15:30:07 +03:00
|
|
|
# branch = "master"
|
|
|
|
# name = "github.com/vendor/package"
|
2017-04-18 16:25:21 +03:00
|
|
|
# Note: revision will depend on your repository type, i.e git, svc, bzr etc...
|
2017-04-14 00:45:32 +03:00
|
|
|
# revision = "abc123"
|
|
|
|
# version = "1.0.0"
|
2017-04-13 15:30:07 +03:00
|
|
|
`
|
|
|
|
|
2017-01-27 23:39:07 +03:00
|
|
|
// SafeWriter transactionalizes writes of manifest, lock, and vendor dir, both
|
2016-12-23 17:49:24 +03:00
|
|
|
// individually and in any combination, into a pseudo-atomic action with
|
|
|
|
// transactional rollback.
|
|
|
|
//
|
|
|
|
// It is not impervious to errors (writing to disk is hard), but it should
|
|
|
|
// guard against non-arcane failure conditions.
|
2017-01-27 23:39:07 +03:00
|
|
|
type SafeWriter struct {
|
2017-03-09 22:28:42 +03:00
|
|
|
Payload *SafeWriterPayload
|
2016-12-23 17:49:24 +03:00
|
|
|
}
|
|
|
|
|
2017-03-09 22:28:42 +03:00
|
|
|
// SafeWriterPayload represents the actions SafeWriter will execute when SafeWriter.Write is called.
|
|
|
|
type SafeWriterPayload struct {
|
2017-03-13 17:34:32 +03:00
|
|
|
Manifest *Manifest
|
|
|
|
Lock *Lock
|
2017-04-11 18:38:50 +03:00
|
|
|
LockDiff *gps.LockDiff
|
2017-03-13 17:34:32 +03:00
|
|
|
WriteVendor bool
|
2017-03-09 22:28:42 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
func (payload *SafeWriterPayload) HasLock() bool {
|
|
|
|
return payload.Lock != nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (payload *SafeWriterPayload) HasManifest() bool {
|
|
|
|
return payload.Manifest != nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (payload *SafeWriterPayload) HasVendor() bool {
|
2017-03-13 17:34:32 +03:00
|
|
|
return payload.WriteVendor
|
2017-03-09 22:28:42 +03:00
|
|
|
}
|
|
|
|
|
2017-04-11 18:38:50 +03:00
|
|
|
type rawStringDiff struct {
|
|
|
|
*gps.StringDiff
|
|
|
|
}
|
|
|
|
|
|
|
|
func (diff rawStringDiff) MarshalTOML() ([]byte, error) {
|
|
|
|
return []byte(diff.String()), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
type rawLockDiff struct {
|
|
|
|
*gps.LockDiff
|
|
|
|
}
|
|
|
|
|
|
|
|
type rawLockedProjectDiff struct {
|
|
|
|
Name gps.ProjectRoot `toml:"name"`
|
|
|
|
Source *rawStringDiff `toml:"source,omitempty"`
|
|
|
|
Version *rawStringDiff `toml:"version,omitempty"`
|
|
|
|
Branch *rawStringDiff `toml:"branch,omitempty"`
|
|
|
|
Revision *rawStringDiff `toml:"revision,omitempty"`
|
|
|
|
Packages []rawStringDiff `toml:"packages,omitempty"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func toRawLockedProjectDiff(diff gps.LockedProjectDiff) rawLockedProjectDiff {
|
|
|
|
// this is a shallow copy since we aren't modifying the raw diff
|
|
|
|
raw := rawLockedProjectDiff{Name: diff.Name}
|
|
|
|
if diff.Source != nil {
|
|
|
|
raw.Source = &rawStringDiff{diff.Source}
|
|
|
|
}
|
|
|
|
if diff.Version != nil {
|
|
|
|
raw.Version = &rawStringDiff{diff.Version}
|
|
|
|
}
|
|
|
|
if diff.Branch != nil {
|
|
|
|
raw.Branch = &rawStringDiff{diff.Branch}
|
|
|
|
}
|
|
|
|
if diff.Revision != nil {
|
|
|
|
raw.Revision = &rawStringDiff{diff.Revision}
|
|
|
|
}
|
|
|
|
raw.Packages = make([]rawStringDiff, len(diff.Packages))
|
|
|
|
for i := 0; i < len(diff.Packages); i++ {
|
|
|
|
raw.Packages[i] = rawStringDiff{&diff.Packages[i]}
|
|
|
|
}
|
|
|
|
return raw
|
2017-03-01 23:52:09 +03:00
|
|
|
}
|
|
|
|
|
2017-04-05 04:47:09 +03:00
|
|
|
type rawLockedProjectDiffs struct {
|
2017-04-11 18:38:50 +03:00
|
|
|
Projects []rawLockedProjectDiff `toml:"projects"`
|
2017-04-05 04:47:09 +03:00
|
|
|
}
|
|
|
|
|
2017-04-11 18:38:50 +03:00
|
|
|
func toRawLockedProjectDiffs(diffs []gps.LockedProjectDiff) rawLockedProjectDiffs {
|
|
|
|
raw := rawLockedProjectDiffs{
|
|
|
|
Projects: make([]rawLockedProjectDiff, len(diffs)),
|
|
|
|
}
|
|
|
|
|
|
|
|
for i := 0; i < len(diffs); i++ {
|
|
|
|
raw.Projects[i] = toRawLockedProjectDiff(diffs[i])
|
2017-03-01 23:52:09 +03:00
|
|
|
}
|
|
|
|
|
2017-04-11 18:38:50 +03:00
|
|
|
return raw
|
|
|
|
}
|
|
|
|
|
|
|
|
func formatLockDiff(diff gps.LockDiff) (string, error) {
|
2017-03-01 23:52:09 +03:00
|
|
|
var buf bytes.Buffer
|
|
|
|
|
2017-03-18 00:48:57 +03:00
|
|
|
if diff.HashDiff != nil {
|
2017-03-23 01:11:43 +03:00
|
|
|
buf.WriteString(fmt.Sprintf("Memo: %s\n\n", diff.HashDiff))
|
2017-03-18 00:48:57 +03:00
|
|
|
}
|
|
|
|
|
2017-04-11 18:38:50 +03:00
|
|
|
writeDiffs := func(diffs []gps.LockedProjectDiff) error {
|
|
|
|
raw := toRawLockedProjectDiffs(diffs)
|
2017-04-05 04:47:09 +03:00
|
|
|
chunk, err := toml.Marshal(raw)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
2017-03-23 01:11:43 +03:00
|
|
|
}
|
2017-04-05 04:47:09 +03:00
|
|
|
buf.Write(chunk)
|
2017-03-23 01:11:43 +03:00
|
|
|
buf.WriteString("\n")
|
|
|
|
return nil
|
|
|
|
}
|
2017-03-01 23:52:09 +03:00
|
|
|
|
2017-03-23 01:11:43 +03:00
|
|
|
if len(diff.Add) > 0 {
|
|
|
|
buf.WriteString("Add:")
|
|
|
|
err := writeDiffs(diff.Add)
|
2017-03-01 23:52:09 +03:00
|
|
|
if err != nil {
|
|
|
|
return "", errors.Wrap(err, "Unable to format LockDiff.Add")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(diff.Remove) > 0 {
|
2017-03-23 01:11:43 +03:00
|
|
|
buf.WriteString("Remove:")
|
|
|
|
err := writeDiffs(diff.Remove)
|
2017-03-01 23:52:09 +03:00
|
|
|
if err != nil {
|
|
|
|
return "", errors.Wrap(err, "Unable to format LockDiff.Remove")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(diff.Modify) > 0 {
|
2017-03-23 01:11:43 +03:00
|
|
|
buf.WriteString("Modify:")
|
|
|
|
err := writeDiffs(diff.Modify)
|
2017-03-01 23:52:09 +03:00
|
|
|
if err != nil {
|
|
|
|
return "", errors.Wrap(err, "Unable to format LockDiff.Modify")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return buf.String(), nil
|
2017-03-09 22:28:42 +03:00
|
|
|
}
|
|
|
|
|
2017-03-18 04:53:00 +03:00
|
|
|
// VendorBehavior defines when the vendor directory should be written.
|
|
|
|
type VendorBehavior int
|
|
|
|
|
|
|
|
const (
|
|
|
|
// VendorOnChanged indicates that the vendor directory should be written when the lock is new or changed.
|
|
|
|
VendorOnChanged VendorBehavior = iota
|
|
|
|
// VendorAlways forces the vendor directory to always be written.
|
|
|
|
VendorAlways
|
|
|
|
// VendorNever indicates the vendor directory should never be written.
|
|
|
|
VendorNever
|
|
|
|
)
|
|
|
|
|
2017-03-09 22:28:42 +03:00
|
|
|
// Prepare to write a set of config yaml, lock and vendor tree.
|
2016-12-23 17:49:24 +03:00
|
|
|
//
|
2017-03-09 22:28:42 +03:00
|
|
|
// - If manifest is provided, it will be written to the standard manifest file
|
|
|
|
// name beneath root.
|
2017-03-18 04:53:00 +03:00
|
|
|
// - 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 {
|
2017-04-13 15:30:07 +03:00
|
|
|
|
2017-03-09 22:28:42 +03:00
|
|
|
sw.Payload = &SafeWriterPayload{
|
2017-03-15 19:00:05 +03:00
|
|
|
Manifest: manifest,
|
2017-03-18 04:53:00 +03:00
|
|
|
Lock: newLock,
|
2016-12-23 17:49:24 +03:00
|
|
|
}
|
|
|
|
|
2017-03-18 04:53:00 +03:00
|
|
|
if oldLock != nil {
|
|
|
|
if newLock == nil {
|
|
|
|
return errors.New("must provide newLock when oldLock is specified")
|
|
|
|
}
|
2017-04-11 18:38:50 +03:00
|
|
|
sw.Payload.LockDiff = gps.DiffLocks(oldLock, newLock)
|
2017-03-15 19:00:05 +03:00
|
|
|
}
|
|
|
|
|
2017-03-18 04:53:00 +03:00
|
|
|
switch vendor {
|
|
|
|
case VendorAlways:
|
2017-03-15 19:00:05 +03:00
|
|
|
sw.Payload.WriteVendor = true
|
2017-03-18 04:53:00 +03:00
|
|
|
case VendorOnChanged:
|
|
|
|
if sw.Payload.LockDiff != nil || (newLock != nil && oldLock == nil) {
|
|
|
|
sw.Payload.WriteVendor = true
|
2016-12-23 17:49:24 +03:00
|
|
|
}
|
|
|
|
}
|
2017-03-15 19:00:05 +03:00
|
|
|
|
2017-03-18 04:53:00 +03:00
|
|
|
if sw.Payload.WriteVendor && newLock == nil {
|
|
|
|
return errors.New("must provide newLock in order to write out vendor")
|
2016-12-23 17:49:24 +03:00
|
|
|
}
|
2017-03-18 04:53:00 +03:00
|
|
|
|
|
|
|
return nil
|
2017-03-09 22:28:42 +03:00
|
|
|
}
|
2016-12-23 17:49:24 +03:00
|
|
|
|
2017-03-09 22:28:42 +03:00
|
|
|
func (payload SafeWriterPayload) validate(root string, sm gps.SourceManager) error {
|
|
|
|
if root == "" {
|
2016-12-30 06:15:23 +03:00
|
|
|
return errors.New("root path must be non-empty")
|
|
|
|
}
|
2017-03-09 22:28:42 +03:00
|
|
|
if is, err := IsDir(root); !is {
|
2016-12-30 06:15:23 +03:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2017-03-22 18:58:50 +03:00
|
|
|
return errors.Errorf("root path %q does not exist", root)
|
2016-12-23 17:49:24 +03:00
|
|
|
}
|
|
|
|
|
2017-03-09 22:28:42 +03:00
|
|
|
if payload.HasVendor() && sm == nil {
|
2017-01-25 00:48:14 +03:00
|
|
|
return errors.New("must provide a SourceManager if writing out a vendor dir")
|
2016-12-23 17:49:24 +03:00
|
|
|
}
|
|
|
|
|
2017-03-09 22:28:42 +03:00
|
|
|
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 {
|
2017-04-13 15:30:07 +03:00
|
|
|
|
2017-03-09 22:28:42 +03:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
|
|
|
mpath := filepath.Join(root, ManifestName)
|
|
|
|
lpath := filepath.Join(root, LockName)
|
|
|
|
vpath := filepath.Join(root, "vendor")
|
2016-12-30 06:56:11 +03:00
|
|
|
|
2017-01-19 03:11:38 +03:00
|
|
|
td, err := ioutil.TempDir(os.TempDir(), "dep")
|
2016-12-23 17:49:24 +03:00
|
|
|
if err != nil {
|
2016-12-23 18:34:20 +03:00
|
|
|
return errors.Wrap(err, "error while creating temp dir for writing manifest/lock/vendor")
|
2016-12-23 17:49:24 +03:00
|
|
|
}
|
|
|
|
defer os.RemoveAll(td)
|
|
|
|
|
2017-03-09 22:28:42 +03:00
|
|
|
if sw.Payload.HasManifest() {
|
2017-04-14 00:45:32 +03:00
|
|
|
if sw.Payload.Manifest.IsEmpty() {
|
2017-04-18 16:25:21 +03:00
|
|
|
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 {
|
2016-12-23 18:34:20 +03:00
|
|
|
return errors.Wrap(err, "failed to write manifest file to temp dir")
|
2016-12-23 17:49:24 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-09 22:28:42 +03:00
|
|
|
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")
|
2016-12-23 17:49:24 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-09 22:28:42 +03:00
|
|
|
if sw.Payload.HasVendor() {
|
|
|
|
err = gps.WriteDepTree(filepath.Join(td, "vendor"), sw.Payload.Lock, sm, true)
|
2016-12-23 17:49:24 +03:00
|
|
|
if err != nil {
|
2016-12-30 06:15:23 +03:00
|
|
|
return errors.Wrap(err, "error while writing out vendor tree")
|
2016-12-23 17:49:24 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-26 15:21:11 +03:00
|
|
|
// Ensure vendor/.git is preserved if present
|
|
|
|
if hasDotGit(vpath) {
|
|
|
|
err = os.Rename(filepath.Join(vpath, ".git"), filepath.Join(td, "vendor/.git"))
|
|
|
|
if _, ok := err.(*os.LinkError); ok {
|
|
|
|
return errors.Wrap(err, "failed to preserve vendor/.git")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-23 17:49:24 +03:00
|
|
|
// Move the existing files and dirs to the temp dir while we put the new
|
2017-01-25 10:29:04 +03:00
|
|
|
// ones in, to provide insurance against errors for as long as possible.
|
2016-12-23 17:49:24 +03:00
|
|
|
type pathpair struct {
|
|
|
|
from, to string
|
|
|
|
}
|
|
|
|
var restore []pathpair
|
2016-12-30 06:56:11 +03:00
|
|
|
var failerr error
|
|
|
|
var vendorbak string
|
2016-12-23 17:49:24 +03:00
|
|
|
|
2017-03-09 22:28:42 +03:00
|
|
|
if sw.Payload.HasManifest() {
|
2016-12-30 06:15:23 +03:00
|
|
|
if _, err := os.Stat(mpath); err == nil {
|
2017-01-25 10:29:04 +03:00
|
|
|
// Move out the old one.
|
2017-01-27 23:39:07 +03:00
|
|
|
tmploc := filepath.Join(td, ManifestName+".orig")
|
2017-01-01 04:11:05 +03:00
|
|
|
failerr = renameWithFallback(mpath, tmploc)
|
2016-12-23 17:49:24 +03:00
|
|
|
if failerr != nil {
|
2016-12-30 06:56:11 +03:00
|
|
|
goto fail
|
2016-12-23 17:49:24 +03:00
|
|
|
}
|
2016-12-30 06:56:11 +03:00
|
|
|
restore = append(restore, pathpair{from: tmploc, to: mpath})
|
2016-12-23 17:49:24 +03:00
|
|
|
}
|
|
|
|
|
2017-01-25 10:29:04 +03:00
|
|
|
// Move in the new one.
|
2017-01-27 23:39:07 +03:00
|
|
|
failerr = renameWithFallback(filepath.Join(td, ManifestName), mpath)
|
2016-12-23 17:49:24 +03:00
|
|
|
if failerr != nil {
|
2016-12-30 06:56:11 +03:00
|
|
|
goto fail
|
2016-12-23 17:49:24 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-09 22:28:42 +03:00
|
|
|
if sw.Payload.HasLock() {
|
2016-12-30 06:15:23 +03:00
|
|
|
if _, err := os.Stat(lpath); err == nil {
|
2017-01-25 10:29:04 +03:00
|
|
|
// Move out the old one.
|
2017-01-27 23:39:07 +03:00
|
|
|
tmploc := filepath.Join(td, LockName+".orig")
|
2016-12-23 17:49:24 +03:00
|
|
|
|
2017-01-01 04:11:05 +03:00
|
|
|
failerr = renameWithFallback(lpath, tmploc)
|
2016-12-23 17:49:24 +03:00
|
|
|
if failerr != nil {
|
2016-12-30 06:56:11 +03:00
|
|
|
goto fail
|
2016-12-23 17:49:24 +03:00
|
|
|
}
|
2016-12-30 06:56:11 +03:00
|
|
|
restore = append(restore, pathpair{from: tmploc, to: lpath})
|
2016-12-23 17:49:24 +03:00
|
|
|
}
|
|
|
|
|
2017-01-25 10:29:04 +03:00
|
|
|
// Move in the new one.
|
2017-01-27 23:39:07 +03:00
|
|
|
failerr = renameWithFallback(filepath.Join(td, LockName), lpath)
|
2016-12-23 17:49:24 +03:00
|
|
|
if failerr != nil {
|
2016-12-30 06:56:11 +03:00
|
|
|
goto fail
|
2016-12-23 17:49:24 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-09 22:28:42 +03:00
|
|
|
if sw.Payload.HasVendor() {
|
2016-12-30 06:15:23 +03:00
|
|
|
if _, err := os.Stat(vpath); err == nil {
|
2017-01-25 10:29:04 +03:00
|
|
|
// Move out the old vendor dir. just do it into an adjacent dir, to
|
2016-12-23 18:34:20 +03:00
|
|
|
// try to mitigate the possibility of a pointless cross-filesystem
|
2017-01-25 10:29:04 +03:00
|
|
|
// move with a temp directory.
|
2016-12-30 06:15:23 +03:00
|
|
|
vendorbak = vpath + ".orig"
|
2016-12-23 17:49:24 +03:00
|
|
|
if _, err := os.Stat(vendorbak); err == nil {
|
2016-12-30 06:56:11 +03:00
|
|
|
// If the adjacent dir already exists, bite the bullet and move
|
2017-01-25 10:29:04 +03:00
|
|
|
// to a proper tempdir.
|
2016-12-23 18:34:20 +03:00
|
|
|
vendorbak = filepath.Join(td, "vendor.orig")
|
2016-12-23 17:49:24 +03:00
|
|
|
}
|
2016-12-23 18:34:20 +03:00
|
|
|
|
2017-01-01 04:11:05 +03:00
|
|
|
failerr = renameWithFallback(vpath, vendorbak)
|
2016-12-23 17:49:24 +03:00
|
|
|
if failerr != nil {
|
2016-12-30 06:56:11 +03:00
|
|
|
goto fail
|
2016-12-23 17:49:24 +03:00
|
|
|
}
|
2016-12-30 06:56:11 +03:00
|
|
|
restore = append(restore, pathpair{from: vendorbak, to: vpath})
|
2016-12-23 17:49:24 +03:00
|
|
|
}
|
|
|
|
|
2017-01-25 10:29:04 +03:00
|
|
|
// Move in the new one.
|
2017-01-01 04:11:05 +03:00
|
|
|
failerr = renameWithFallback(filepath.Join(td, "vendor"), vpath)
|
2016-12-23 17:49:24 +03:00
|
|
|
if failerr != nil {
|
2016-12-30 06:56:11 +03:00
|
|
|
goto fail
|
2016-12-23 17:49:24 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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
|
2017-03-09 22:28:42 +03:00
|
|
|
if sw.Payload.HasVendor() {
|
2016-12-30 06:56:11 +03:00
|
|
|
// Nothing we can really do about an error at this point, so ignore it
|
2016-12-23 17:49:24 +03:00
|
|
|
os.RemoveAll(vendorbak)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
2016-12-30 06:56:11 +03:00
|
|
|
|
|
|
|
fail:
|
2017-01-25 10:29:04 +03:00
|
|
|
// If we failed at any point, move all the things back into place, then bail.
|
2016-12-30 06:56:11 +03:00
|
|
|
for _, pair := range restore {
|
2017-01-25 10:29:04 +03:00
|
|
|
// Nothing we can do on err here, as we're already in recovery mode.
|
2017-01-01 04:11:05 +03:00
|
|
|
renameWithFallback(pair.from, pair.to)
|
2016-12-30 06:56:11 +03:00
|
|
|
}
|
|
|
|
return failerr
|
2016-12-23 17:49:24 +03:00
|
|
|
}
|
2017-03-09 22:28:42 +03:00
|
|
|
|
|
|
|
func (sw *SafeWriter) PrintPreparedActions() error {
|
|
|
|
if sw.Payload.HasManifest() {
|
2017-04-02 22:36:22 +03:00
|
|
|
fmt.Printf("Would have written the following %s:\n", ManifestName)
|
2017-03-20 21:14:02 +03:00
|
|
|
m, err := sw.Payload.Manifest.MarshalTOML()
|
2017-03-09 22:28:42 +03:00
|
|
|
if err != nil {
|
2017-03-01 23:52:09 +03:00
|
|
|
return errors.Wrap(err, "ensure DryRun cannot serialize manifest")
|
2017-03-09 22:28:42 +03:00
|
|
|
}
|
|
|
|
fmt.Println(string(m))
|
|
|
|
}
|
|
|
|
|
|
|
|
if sw.Payload.HasLock() {
|
2017-03-15 18:06:07 +03:00
|
|
|
if sw.Payload.LockDiff == nil {
|
2017-04-02 22:36:22 +03:00
|
|
|
fmt.Printf("Would have written the following %s:\n", LockName)
|
2017-03-20 21:14:02 +03:00
|
|
|
l, err := sw.Payload.Lock.MarshalTOML()
|
2017-03-15 18:06:07 +03:00
|
|
|
if err != nil {
|
|
|
|
return errors.Wrap(err, "ensure DryRun cannot serialize lock")
|
|
|
|
}
|
|
|
|
fmt.Println(string(l))
|
|
|
|
} else {
|
2017-04-02 22:36:22 +03:00
|
|
|
fmt.Printf("Would have written the following changes to %s:\n", LockName)
|
2017-04-11 18:38:50 +03:00
|
|
|
diff, err := formatLockDiff(*sw.Payload.LockDiff)
|
2017-03-15 18:06:07 +03:00
|
|
|
if err != nil {
|
|
|
|
return errors.Wrap(err, "ensure DryRun cannot serialize the lock diff")
|
|
|
|
}
|
|
|
|
fmt.Println(diff)
|
2017-03-09 22:28:42 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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()
|
2017-04-11 18:38:50 +03:00
|
|
|
rev, _, _ := gps.VersionComponentStrings(project.Version())
|
2017-03-09 22:28:42 +03:00
|
|
|
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
|
|
|
|
}
|
2017-03-01 23:52:09 +03:00
|
|
|
|
2017-03-13 13:43:21 +03:00
|
|
|
func PruneProject(p *Project, sm gps.SourceManager) error {
|
|
|
|
td, err := ioutil.TempDir(os.TempDir(), "dep")
|
|
|
|
if err != nil {
|
|
|
|
return errors.Wrap(err, "error while creating temp dir for writing manifest/lock/vendor")
|
|
|
|
}
|
|
|
|
defer os.RemoveAll(td)
|
|
|
|
|
|
|
|
if err := gps.WriteDepTree(td, p.Lock, sm, true); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
var toKeep []string
|
|
|
|
for _, project := range p.Lock.Projects() {
|
|
|
|
projectRoot := string(project.Ident().ProjectRoot)
|
|
|
|
for _, pkg := range project.Packages() {
|
|
|
|
toKeep = append(toKeep, filepath.Join(projectRoot, pkg))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
toDelete, err := calculatePrune(td, toKeep)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := deleteDirs(toDelete); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
vpath := filepath.Join(p.AbsRoot, "vendor")
|
|
|
|
vendorbak := vpath + ".orig"
|
|
|
|
var failerr error
|
|
|
|
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
|
|
|
|
// move with a temp directory.
|
|
|
|
if _, err := os.Stat(vendorbak); err == nil {
|
|
|
|
// If the adjacent dir already exists, bite the bullet and move
|
|
|
|
// to a proper tempdir.
|
|
|
|
vendorbak = filepath.Join(td, "vendor.orig")
|
|
|
|
}
|
|
|
|
failerr = renameWithFallback(vpath, vendorbak)
|
|
|
|
if failerr != nil {
|
|
|
|
goto fail
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Move in the new one.
|
|
|
|
failerr = renameWithFallback(td, vpath)
|
|
|
|
if failerr != nil {
|
|
|
|
goto fail
|
|
|
|
}
|
|
|
|
|
|
|
|
os.RemoveAll(vendorbak)
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
|
|
|
fail:
|
|
|
|
renameWithFallback(vendorbak, vpath)
|
|
|
|
return failerr
|
|
|
|
}
|
|
|
|
|
|
|
|
func calculatePrune(vendorDir string, keep []string) ([]string, error) {
|
|
|
|
sort.Strings(keep)
|
|
|
|
toDelete := []string{}
|
|
|
|
err := filepath.Walk(vendorDir, func(path string, info os.FileInfo, err error) error {
|
|
|
|
if _, err := os.Lstat(path); err != nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
if !info.IsDir() {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
if path == vendorDir {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
name := strings.TrimPrefix(path, vendorDir+"/")
|
|
|
|
i := sort.Search(len(keep), func(i int) bool {
|
|
|
|
return name <= keep[i]
|
|
|
|
})
|
|
|
|
if i >= len(keep) || !strings.HasPrefix(keep[i], name) {
|
|
|
|
toDelete = append(toDelete, path)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
return toDelete, err
|
|
|
|
}
|
|
|
|
|
|
|
|
func deleteDirs(toDelete []string) error {
|
|
|
|
// sort by length so we delete sub dirs first
|
|
|
|
sort.Sort(byLen(toDelete))
|
|
|
|
for _, path := range toDelete {
|
|
|
|
if err := os.RemoveAll(path); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2017-04-26 15:21:11 +03:00
|
|
|
// 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)
|
|
|
|
if err != nil {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2017-03-13 13:43:21 +03:00
|
|
|
type byLen []string
|
|
|
|
|
|
|
|
func (a byLen) Len() int { return len(a) }
|
|
|
|
func (a byLen) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
|
|
|
func (a byLen) Less(i, j int) bool { return len(a[i]) > len(a[j]) }
|