diff --git a/glide.lock b/glide.lock index 3d3222867ddd50d4f9b9d5432a7c265af17d35a7..62620c5ded26d6461e28801b4c5ce280fdfcb974 100644 --- a/glide.lock +++ b/glide.lock @@ -228,4 +228,6 @@ imports: - term - name: github.com/streadway/simpleuuid version: 6617b501e485b77e61b98cd533aefff9e258b5a7 +- name: github.com/Masterminds/glide + version: 84607742b10f492430762d038e954236bbaf23f7 devImports: [] diff --git a/glide.yaml b/glide.yaml index 6b1ab4221fd9d4dfccf5d67eaea6e9ed43438e48..d832bc2213309b201c8c52bda69fbb9e87ef52f5 100644 --- a/glide.yaml +++ b/glide.yaml @@ -29,3 +29,5 @@ import: - package: github.com/Sirupsen/logrus version: ^0.11.0 - package: github.com/streadway/simpleuuid +- package: github.com/Masterminds/glide + version: ~0.12.3 \ No newline at end of file diff --git a/util/hell/README.md b/util/hell/README.md new file mode 100644 index 0000000000000000000000000000000000000000..8fe7fdb9ee299488204c8a9dda55a9b7542ce2dc --- /dev/null +++ b/util/hell/README.md @@ -0,0 +1,4 @@ +> Hell is other people's packages + +While we wait for working package management in go we need a way to make +maintaining the glide.lock by hand less painful. \ No newline at end of file diff --git a/util/hell/cmd/hell/main.go b/util/hell/cmd/hell/main.go new file mode 100644 index 0000000000000000000000000000000000000000..879bfdef528efb893e6fbedbbaff8eed603c92c4 --- /dev/null +++ b/util/hell/cmd/hell/main.go @@ -0,0 +1,117 @@ +package main + +import ( + "fmt" + "os" + + "path/filepath" + + "github.com/Masterminds/glide/cache" + "github.com/Masterminds/glide/cfg" + "github.com/Masterminds/glide/msg" + "github.com/Masterminds/glide/path" + "github.com/Masterminds/glide/util" + "github.com/eris-ltd/eris-db/util/hell" + "github.com/spf13/cobra" + "github.com/Masterminds/glide/action" + "github.com/Masterminds/glide/repo" +) + +func main() { + hellCmd := &cobra.Command{ + Use: "hell", + Short: "Hell makes the most of it being warm", + Long: "", + Run: func(cmd *cobra.Command, args []string) { cmd.Help() }, + } + + // Lock merge command + var baseGlideLockFile, depGlideLockFile string + lockMergeCmd := &cobra.Command{ + Use: "lock-merge", + Short: "Merge glide.lock files together", + Long: "This command merges two glide.lock files into a single one by copying all dependencies " + + "from a base glide.lock and an override glide.lock to an output glide.lock with dependencies " + + "from override taking precedence over those from base.", + Run: func(cmd *cobra.Command, args []string) { + baseLockFile, err := cfg.ReadLockFile(baseGlideLockFile) + if err != nil { + fmt.Printf("Could not read file: %s\n", err) + os.Exit(1) + } + overrideLockFile, err := cfg.ReadLockFile(depGlideLockFile) + if err != nil { + fmt.Printf("Could not read file: %s\n", err) + os.Exit(1) + } + mergedLockFile, err := hell.MergeGlideLockFiles(baseLockFile, overrideLockFile) + if err != nil { + fmt.Printf("Could not merge lock files: %s\n", err) + os.Exit(1) + } + mergedBytes, err := mergedLockFile.Marshal() + if err != nil { + fmt.Printf("Could not marshal lock file: %s\n", err) + os.Exit(1) + } + os.Stdout.Write(mergedBytes) + }, + } + lockMergeCmd.PersistentFlags().StringVarP(&baseGlideLockFile, "base", "b", "", "base lock file") + lockMergeCmd.PersistentFlags().StringVarP(&depGlideLockFile, "override", "o", "", "override lock file") + + // Lock update + interactive := false + getTransitiveCmd := &cobra.Command{ + Use: "get", + Short: "gets a remote dependency to this project along with its transtive dependencies.", + Long: "Gets a remote dependency and its transitive dependencies by adding the remote " + + "depednency to this project's glide.yaml and merging the remote dependency's " + + "glide.lock into this project's glide.lock", + Run: func(cmd *cobra.Command, args []string) { + if len(args) != 1 { + msg.Die("%s requires a single argument of the remote dependency\n", cmd.Name()) + } + rootPackage, _ := util.NormalizeName(args[0]) + // Add dependency to glide + action.Get(args, repo.NewInstaller() , false, true, false, !interactive, false) + // Now hunt down the repo cache + dep := action.EnsureConfig().Imports.Get(rootPackage) + key, err := cache.Key(dep.Remote()) + if err != nil { + msg.Die("%s requires a single argument of the remote dependency\n", cmd.Name()) + } + cacheDir := filepath.Join(cache.Location(), "src", key) + + if path.HasLock(cacheDir) { + msg.Info("Found dependency lock file, merging into project lock file") + lockPath := filepath.Join(".", path.LockFile) + baseLockFile, err := cfg.ReadLockFile(lockPath) + if err != nil { + msg.Die("Could not read base lock file: %s", err) + } + overrideLockFile, err := cfg.ReadLockFile(filepath.Join(cacheDir, path.LockFile)) + if err != nil { + msg.Die("Could not read dependency lock file: %s", err) + } + mergedLockFile, err := hell.MergeGlideLockFiles(baseLockFile, overrideLockFile) + if err != nil { + msg.Die("Could not merge lock files: %s\n", err) + } + err = mergedLockFile.WriteFile(lockPath) + if err != nil { + msg.Die("Could not write merged lock file: %s", err) + } + } else { + msg.Info("Did not find dependency lock file, so nothing merged intoo project lock file") + } + }, + } + + getTransitiveCmd.PersistentFlags().BoolVarP(&interactive, "interactive", "i", false, + "set dependency verion interactively") + + hellCmd.AddCommand(lockMergeCmd) + hellCmd.AddCommand(getTransitiveCmd) + lockMergeCmd.Execute() +} diff --git a/util/hell/merge.go b/util/hell/merge.go new file mode 100644 index 0000000000000000000000000000000000000000..242d22af176eb084b0b41ae061e5b9b397883c47 --- /dev/null +++ b/util/hell/merge.go @@ -0,0 +1,88 @@ +package hell + +import ( + "crypto/sha256" + "fmt" + + "sort" + + "github.com/Masterminds/glide/cfg" +) + +// Merges two glide lock files together, letting dependencies from 'base' be overwritten +// by those from 'override'. Returns the resultant glide lock file bytes +func MergeGlideLockFiles(baseLockFile, overrideLockFile *cfg.Lockfile) (*cfg.Lockfile, error) { + imports := make(map[string]*cfg.Lock, len(baseLockFile.Imports)) + devImports := make(map[string]*cfg.Lock, len(baseLockFile.DevImports)) + // Copy the base dependencies into a map + for _, lock := range baseLockFile.Imports { + imports[lock.Name] = lock + } + for _, lock := range baseLockFile.DevImports { + devImports[lock.Name] = lock + } + // Override base dependencies and add any extra ones + for _, lock := range overrideLockFile.Imports { + imports[lock.Name] = mergeLocks(imports[lock.Name], lock) + } + for _, lock := range overrideLockFile.DevImports { + devImports[lock.Name] = mergeLocks(imports[lock.Name], lock) + } + + deps := make([]*cfg.Dependency, 0, len(imports)) + devDeps := make([]*cfg.Dependency, 0, len(devImports)) + + // Flatten to Dependencies + for _, lock := range imports { + deps = append(deps, pinnedDependencyFromLock(lock)) + } + + for _, lock := range devImports { + devDeps = append(devDeps, pinnedDependencyFromLock(lock)) + } + + hasher := sha256.New() + hasher.Write(([]byte)(baseLockFile.Hash)) + hasher.Write(([]byte)(overrideLockFile.Hash)) + + return cfg.NewLockfile(deps, devDeps, fmt.Sprintf("%x", hasher.Sum(nil))) +} + +func mergeLocks(baseLock, overrideLock *cfg.Lock) *cfg.Lock { + lock := overrideLock.Clone() + if baseLock == nil { + return lock + } + + // Merge and dedupe subpackages + subpackages := make([]string, 0, len(lock.Subpackages)+len(baseLock.Subpackages)) + for _, sp := range lock.Subpackages { + subpackages = append(subpackages, sp) + } + for _, sp := range baseLock.Subpackages { + subpackages = append(subpackages, sp) + } + + sort.Stable(sort.StringSlice(subpackages)) + + dedupeSubpackages := make([]string, 0, len(subpackages)) + + lastSp := "" + elided := 0 + for _, sp := range subpackages { + if lastSp == sp { + elided++ + } else { + dedupeSubpackages = append(dedupeSubpackages, sp) + lastSp = sp + } + } + lock.Subpackages = dedupeSubpackages[:len(dedupeSubpackages)-elided] + return lock +} + +func pinnedDependencyFromLock(lock *cfg.Lock) *cfg.Dependency { + dep := cfg.DependencyFromLock(lock) + dep.Pin = lock.Version + return dep +} diff --git a/util/hell/merge_test.go b/util/hell/merge_test.go new file mode 100644 index 0000000000000000000000000000000000000000..cd59f4d07e309e87bdf2be42ec64528ca73d4a09 --- /dev/null +++ b/util/hell/merge_test.go @@ -0,0 +1,72 @@ +package hell + +import ( + "testing" + + "strings" + + "github.com/Masterminds/glide/cfg" + "github.com/stretchr/testify/assert" +) + +const baseLockYml = `imports: +- name: github.com/gogo/protobuf + version: 82d16f734d6d871204a3feb1a73cb220cc92574c +- name: github.com/tendermint/tendermint + version: aaea0c5d2e3ecfbf29f2608f9d43649ec7f07f50 + subpackages: + - node + - proxy + - types + - version + - consensus + - rpc/core/types + - blockchain + - mempool + - rpc/core + - state +` +const overrideLockYml = `imports: +- name: github.com/tendermint/tendermint + version: 764091dfbb035f1b28da4b067526e04c6a849966 + subpackages: + - benchmarks + - proxy + - types + - version +` +const expectedLockYml = `imports: +- name: github.com/gogo/protobuf + version: 82d16f734d6d871204a3feb1a73cb220cc92574c +- name: github.com/tendermint/tendermint + version: 764091dfbb035f1b28da4b067526e04c6a849966 + subpackages: + - benchmarks + - blockchain + - consensus + - mempool + - node + - proxy + - rpc/core + - rpc/core/types +testImports: [] +` + +func TestMergeGlideLockFiles(t *testing.T) { + baseLockFile, err := cfg.LockfileFromYaml(([]byte)(baseLockYml)) + assert.NoError(t, err, "Lockfile should parse") + + overrideLockFile, err := cfg.LockfileFromYaml(([]byte)(overrideLockYml)) + assert.NoError(t, err, "Lockfile should parse") + + mergedLockFile, err := MergeGlideLockFiles(baseLockFile, overrideLockFile) + assert.NoError(t, err, "Lockfiles should merge") + + mergedYmlBytes, err := mergedLockFile.Marshal() + assert.NoError(t, err, "Lockfile should marshal") + + ymlLines := strings.Split(string(mergedYmlBytes), "\n") + // Drop the updated and hash lines + actualYml := strings.Join(ymlLines[2:], "\n") + assert.Equal(t, expectedLockYml, actualYml) +}