Merge pull request #522 from technosophos/feat/521-sort-subpackages

Sort subpackages for glide.yaml and glide.lock
diff --git a/cfg/config.go b/cfg/config.go
index 4b4e799..115c76d 100644
--- a/cfg/config.go
+++ b/cfg/config.go
@@ -5,6 +5,7 @@
 	"fmt"
 	"io/ioutil"
 	"reflect"
+	"sort"
 	"strings"
 
 	"github.com/Masterminds/glide/util"
@@ -567,6 +568,7 @@
 			s = append(s, item)
 		}
 	}
+	sort.Strings(s)
 	return s
 }
 
diff --git a/cfg/lock.go b/cfg/lock.go
index 215483c..8a26e24 100644
--- a/cfg/lock.go
+++ b/cfg/lock.go
@@ -34,6 +34,18 @@
 	return yml, nil
 }
 
+// MarshalYAML is a hook for gopkg.in/yaml.v2.
+// It sorts import subpackages lexicographically for reproducibility.
+func (lf *Lockfile) MarshalYAML() (interface{}, error) {
+	for _, imp := range lf.Imports {
+		sort.Strings(imp.Subpackages)
+	}
+	for _, imp := range lf.DevImports {
+		sort.Strings(imp.Subpackages)
+	}
+	return lf, nil
+}
+
 // WriteFile writes a Glide lock file.
 //
 // This is a convenience function that marshals the YAML and then writes it to
diff --git a/cfg/lock_test.go b/cfg/lock_test.go
index 78dcee3..81edcd0 100644
--- a/cfg/lock_test.go
+++ b/cfg/lock_test.go
@@ -2,6 +2,7 @@
 
 import (
 	"sort"
+	"strings"
 	"testing"
 )
 
@@ -32,3 +33,80 @@
 		t.Error("Sorting of dependencies failed")
 	}
 }
+
+const inputSubpkgYaml = `
+imports:
+- name: github.com/gogo/protobuf
+  version: 82d16f734d6d871204a3feb1a73cb220cc92574c
+  subpackages:
+  - plugin/equal
+  - sortkeys
+  - plugin/face
+  - plugin/gostring
+  - vanity
+  - plugin/grpc
+  - plugin/marshalto
+  - plugin/populate
+  - plugin/oneofcheck
+  - plugin/size
+  - plugin/stringer
+  - plugin/defaultcheck
+  - plugin/embedcheck
+  - plugin/description
+  - plugin/enumstringer
+  - gogoproto
+  - plugin/testgen
+  - plugin/union
+  - plugin/unmarshal
+  - protoc-gen-gogo/generator
+  - protoc-gen-gogo/plugin
+  - vanity/command
+  - protoc-gen-gogo/descriptor
+  - proto
+`
+const expectSubpkgYaml = `
+imports:
+- name: github.com/gogo/protobuf
+  version: 82d16f734d6d871204a3feb1a73cb220cc92574c
+  subpackages:
+  - gogoproto
+  - plugin/defaultcheck
+  - plugin/description
+  - plugin/embedcheck
+  - plugin/enumstringer
+  - plugin/equal
+  - plugin/face
+  - plugin/gostring
+  - plugin/grpc
+  - plugin/marshalto
+  - plugin/oneofcheck
+  - plugin/populate
+  - plugin/size
+  - plugin/stringer
+  - plugin/testgen
+  - plugin/union
+  - plugin/unmarshal
+  - proto
+  - protoc-gen-gogo/descriptor
+  - protoc-gen-gogo/generator
+  - protoc-gen-gogo/plugin
+  - sortkeys
+  - vanity
+  - vanity/command
+`
+
+func TestSortSubpackages(t *testing.T) {
+	lf, err := LockfileFromYaml([]byte(inputSubpkgYaml))
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	out, err := lf.Marshal()
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if !strings.Contains(string(out), expectSubpkgYaml) {
+		t.Errorf("Expected %q\nto contain\n%q")
+	}
+}