diff --git a/Gopkg.lock b/Gopkg.lock
index d06653d..2b101e4 100644
--- a/Gopkg.lock
+++ b/Gopkg.lock
@@ -27,16 +27,18 @@
version = "v1.3.1"
[[projects]]
+ branch = "master"
name = "github.com/golang/dep"
packages = [
+ ".",
"gps",
"gps/internal/pb",
"gps/paths",
"gps/pkgtree",
"internal/fs"
]
- revision = "37d9ea0ac16f0e0a05afc3b60e1ac8c364b6c329"
- version = "v0.4.1"
+ revision = "fa9f32339c8855ebe7e7bc66e549036a7e06d37a"
+ source = "https://github.com/CrushedPixel/dep"
[[projects]]
branch = "master"
@@ -77,11 +79,7 @@
[[projects]]
branch = "master"
name = "golang.org/x/net"
- packages = [
- "context",
- "html",
- "html/atom"
- ]
+ packages = ["context"]
revision = "dc948dff8834a7fe1ca525f8d04e261c2b56e70d"
[[projects]]
@@ -99,6 +97,6 @@
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
- inputs-digest = "a317572c6678a0a7322ac0c531cf3f8bc12c055c48d24411227b930158a2f8bc"
+ inputs-digest = "5ba2894346289bbb0f893f7893e38248bd7621f1a558d4927a46063b1ad3a9d2"
solver-name = "gps-cdcl"
solver-version = 1
diff --git a/Gopkg.toml b/Gopkg.toml
index a829c0f..e8a96ac 100644
--- a/Gopkg.toml
+++ b/Gopkg.toml
@@ -26,16 +26,9 @@
[[constraint]]
+ branch = "master"
name = "github.com/golang/dep"
- version = "0.4.1"
-
-[[constraint]]
- name = "github.com/pelletier/go-toml"
- version = "1.1.0"
-
-[[constraint]]
- name = "github.com/pkg/errors"
- version = "0.8.0"
+ source = "https://github.com/CrushedPixel/dep"
[prune]
go-tests = true
diff --git a/deps.go b/deps.go
new file mode 100644
index 0000000..0cee618
--- /dev/null
+++ b/deps.go
@@ -0,0 +1,52 @@
+package main
+
+import (
+ "fmt"
+)
+
+const depNixFormat = `
+ {
+ goPackagePath = "%s";
+ fetch = {
+ type = "%s";
+ url = "%s";
+ rev = "%s";
+ sha256 = "%s";
+ };
+ }`
+
+// Dep represents a project dependency
+// to write to deps.nix.
+type Dep struct {
+ PackagePath string
+ VCS string
+ URL string
+ Revision string
+ SHA256 string
+}
+
+// toNix converts d into a nix set
+// for use in the generated deps.nix.
+func (d *Dep) toNix() string {
+ return fmt.Sprintf(depNixFormat,
+ d.PackagePath, d.VCS, d.URL,
+ d.Revision, d.SHA256)
+}
+
+const depsFileHeader = `# file generated from Gopkg.lock using dep2nix (https://github.com/nixcloud/dep2nix)
+[`
+const depsFileFooter = `
+]`
+
+type Deps []*Dep
+
+// toNix converts d into a deps.nix file
+// for use with pkgs.buildGoPackage.
+func (d Deps) toNix() string {
+ nix := depsFileHeader
+ for _, dep := range d {
+ nix += dep.toNix()
+ }
+ nix += depsFileFooter
+ return nix
+}
diff --git a/deps.nix b/deps.nix
index 326afae..fc9280e 100644
--- a/deps.nix
+++ b/deps.nix
@@ -46,9 +46,9 @@
goPackagePath = "github.com/golang/dep";
fetch = {
type = "git";
- url = "https://github.com/golang/dep";
- rev = "37d9ea0ac16f0e0a05afc3b60e1ac8c364b6c329";
- sha256 = "0183xq5l4sinnclynv6xi85vmk69mqpy5wjfsgh8bxwziq3vkd7y";
+ url = "https://github.com/CrushedPixel/dep";
+ rev = "fa9f32339c8855ebe7e7bc66e549036a7e06d37a";
+ sha256 = "1knaxs1ji1b0b68393f24r8qzvahxz9x7rqwc8jsjlshvpz0hlm6";
};
}
diff --git a/lock.go b/lock.go
deleted file mode 100644
index 1997a20..0000000
--- a/lock.go
+++ /dev/null
@@ -1,203 +0,0 @@
-// 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 (
- "bytes"
- "encoding/hex"
- "io"
- "sort"
-
- "github.com/golang/dep/gps"
- "github.com/pelletier/go-toml"
- "github.com/pkg/errors"
-)
-
-// LockName is the lock file name used by dep.
-const LockName = "Gopkg.lock"
-
-// Lock holds lock file data and implements gps.Lock.
-type Lock struct {
- SolveMeta SolveMeta
- P []gps.LockedProject
-}
-
-// SolveMeta holds solver meta data.
-type SolveMeta struct {
- InputsDigest []byte
- AnalyzerName string
- AnalyzerVersion int
- SolverName string
- SolverVersion int
-}
-
-type rawLock struct {
- SolveMeta solveMeta `toml:"solve-meta"`
- Projects []rawLockedProject `toml:"projects"`
-}
-
-type solveMeta struct {
- InputsDigest string `toml:"inputs-digest"`
- AnalyzerName string `toml:"analyzer-name"`
- AnalyzerVersion int `toml:"analyzer-version"`
- SolverName string `toml:"solver-name"`
- SolverVersion int `toml:"solver-version"`
-}
-
-type rawLockedProject struct {
- Name string `toml:"name"`
- Branch string `toml:"branch,omitempty"`
- Revision string `toml:"revision"`
- Version string `toml:"version,omitempty"`
- Source string `toml:"source,omitempty"`
- Packages []string `toml:"packages"`
-}
-
-func readLock(r io.Reader) (*Lock, error) {
- buf := &bytes.Buffer{}
- _, err := buf.ReadFrom(r)
- if err != nil {
- return nil, errors.Wrap(err, "Unable to read byte stream")
- }
-
- raw := rawLock{}
- err = toml.Unmarshal(buf.Bytes(), &raw)
- if err != nil {
- return nil, errors.Wrap(err, "Unable to parse the lock as TOML")
- }
-
- return fromRawLock(raw)
-}
-
-func fromRawLock(raw rawLock) (*Lock, error) {
- var err error
- l := &Lock{
- P: make([]gps.LockedProject, len(raw.Projects)),
- }
-
- l.SolveMeta.InputsDigest, err = hex.DecodeString(raw.SolveMeta.InputsDigest)
- if err != nil {
- return nil, errors.Errorf("invalid hash digest in lock's memo field")
- }
-
- l.SolveMeta.AnalyzerName = raw.SolveMeta.AnalyzerName
- l.SolveMeta.AnalyzerVersion = raw.SolveMeta.AnalyzerVersion
- l.SolveMeta.SolverName = raw.SolveMeta.SolverName
- l.SolveMeta.SolverVersion = raw.SolveMeta.SolverVersion
-
- for i, ld := range raw.Projects {
- r := gps.Revision(ld.Revision)
-
- var v gps.Version = r
- if ld.Version != "" {
- if ld.Branch != "" {
- return nil, errors.Errorf("lock file specified both a branch (%s) and version (%s) for %s", ld.Branch, ld.Version, ld.Name)
- }
- v = gps.NewVersion(ld.Version).Pair(r)
- } else if ld.Branch != "" {
- v = gps.NewBranch(ld.Branch).Pair(r)
- } else if r == "" {
- return nil, errors.Errorf("lock file has entry for %s, but specifies no branch or version", ld.Name)
- }
-
- id := gps.ProjectIdentifier{
- ProjectRoot: gps.ProjectRoot(ld.Name),
- Source: ld.Source,
- }
- l.P[i] = gps.NewLockedProject(id, v, ld.Packages)
- }
-
- return l, nil
-}
-
-// InputsDigest returns the hash of inputs which produced this lock data.
-func (l *Lock) InputsDigest() []byte {
- return l.SolveMeta.InputsDigest
-}
-
-// Projects returns the list of LockedProjects contained in the lock data.
-func (l *Lock) Projects() []gps.LockedProject {
- return l.P
-}
-
-// HasProjectWithRoot checks if the lock contains a project with the provided
-// ProjectRoot.
-//
-// This check is O(n) in the number of projects.
-func (l *Lock) HasProjectWithRoot(root gps.ProjectRoot) bool {
- for _, p := range l.P {
- if p.Ident().ProjectRoot == root {
- return true
- }
- }
-
- return false
-}
-
-// toRaw converts the manifest into a representation suitable to write to the lock file
-func (l *Lock) toRaw() rawLock {
- raw := rawLock{
- SolveMeta: solveMeta{
- InputsDigest: hex.EncodeToString(l.SolveMeta.InputsDigest),
- AnalyzerName: l.SolveMeta.AnalyzerName,
- AnalyzerVersion: l.SolveMeta.AnalyzerVersion,
- SolverName: l.SolveMeta.SolverName,
- SolverVersion: l.SolveMeta.SolverVersion,
- },
- Projects: make([]rawLockedProject, len(l.P)),
- }
-
- sort.Slice(l.P, func(i, j int) bool {
- return l.P[i].Ident().Less(l.P[j].Ident())
- })
-
- for k, lp := range l.P {
- id := lp.Ident()
- ld := rawLockedProject{
- Name: string(id.ProjectRoot),
- Source: id.Source,
- Packages: lp.Packages(),
- }
-
- v := lp.Version()
- ld.Revision, ld.Branch, ld.Version = gps.VersionComponentStrings(v)
-
- raw.Projects[k] = ld
- }
-
- return raw
-}
-
-// MarshalTOML serializes this lock into TOML via an intermediate raw form.
-func (l *Lock) MarshalTOML() ([]byte, error) {
- raw := l.toRaw()
- var buf bytes.Buffer
- enc := toml.NewEncoder(&buf).ArraysWithOneElementPerLine(true)
- err := enc.Encode(raw)
- return buf.Bytes(), errors.Wrap(err, "Unable to marshal lock to TOML string")
-}
-
-// LockFromSolution converts a gps.Solution to dep's representation of a lock.
-//
-// Data is defensively copied wherever necessary to ensure the resulting *lock
-// shares no memory with the original lock.
-func LockFromSolution(in gps.Solution) *Lock {
- h, p := in.InputsDigest(), in.Projects()
-
- l := &Lock{
- SolveMeta: SolveMeta{
- InputsDigest: make([]byte, len(h)),
- AnalyzerName: in.AnalyzerName(),
- AnalyzerVersion: in.AnalyzerVersion(),
- SolverName: in.SolverName(),
- SolverVersion: in.SolverVersion(),
- },
- P: make([]gps.LockedProject, len(p)),
- }
-
- copy(l.SolveMeta.InputsDigest, h)
- copy(l.P, p)
- return l
-}
diff --git a/main.go b/main.go
index 0a2b809..782b9b8 100644
--- a/main.go
+++ b/main.go
@@ -5,28 +5,18 @@
package main
import (
- "bufio"
"bytes"
"encoding/json"
"flag"
"fmt"
- "github.com/pelletier/go-toml"
- "io"
+ "github.com/golang/dep"
+ "github.com/golang/dep/gps"
+ "io/ioutil"
"log"
- "net/http"
"os"
"os/exec"
"path/filepath"
- "strings"
-
- "golang.org/x/net/html"
-)
-
-var (
- data *os.File
- part []byte
- count int
- buffer *bytes.Buffer
+ "time"
)
var (
@@ -34,215 +24,121 @@ var (
outputFileFlag = flag.String("o", "deps.nix", "output nix file")
)
-// FindRealPath queries url to try to locate real vcs path
-// The meta tag has the form:
-//
-//
-// For example,
-// import "example.org/pkg/foo"
-//
-// will result in the following requests:
-// https://example.org/pkg/foo?go-get=1 (preferred)
-// http://example.org/pkg/foo?go-get=1 (fallback, only with -insecure)
-func FindRealPath(url string) (string, error) {
- // golang http client will follow redirects, so if http don't work should query https if 301 redirect
- resp, err := http.Get("http://" + url + "?go-get=1")
- if err != nil {
- return "", fmt.Errorf("Failed to query %v", url)
- }
- defer resp.Body.Close()
-
- z := html.NewTokenizer(resp.Body)
- for {
- tt := z.Next()
-
- switch {
- case tt == html.ErrorToken:
- // End of the document, we're done
- return "", fmt.Errorf("end of body")
- case tt == html.StartTagToken:
- t := z.Token()
-
- // Check if the token is an tag
- isMeta := t.Data == "meta"
- if !isMeta {
- continue
- }
-
- // Extract vcs url
- for _, a := range t.Attr {
- if a.Key == "name" && a.Val == "go-import" {
- var content []string
- for _, b := range t.Attr {
- if b.Key == "content" {
- content = strings.Fields(b.Val)
- }
- }
-
- if len(content) < 3 {
- return "", fmt.Errorf("could not find content attribute for meta tag")
- }
-
- // go help importpath
- // content[0] : original import path
- // content[1] : vcs type
- // content[2] : vcs url
-
- // expand for non git vcs
- if content[1] == "git" {
- return content[2], nil
- }
- return "", fmt.Errorf("could not find git url")
- }
- }
- }
- }
-
-}
-
-// IsCommonPath checks to see if it's one of the common vcs locations go get supports
-// see `go help importpath`
-func IsCommonPath(url string) bool {
- // from `go help importpath`
- commonPaths := [...]string{
- "bitbucket.org",
- "github.com",
- "launchpad.net",
- "hub.jazz.net",
- }
- for _, path := range commonPaths {
- if strings.Split(url, "/")[0] == path {
- return true
- }
- }
- return false
-}
-
func main() {
flag.Parse()
+ logger := log.New(os.Stdout, "", 0)
+
+ defer func(start time.Time) {
+ logger.Printf("Finished execution in %s.\n", time.Since(start).Round(time.Second).String())
+ }(time.Now())
+ // parse input file path
inFile, err := filepath.Abs(*inputFileFlag)
if err != nil {
- log.Fatalln("Invalid input file path:", err.Error())
+ logger.Fatalln("Invalid input file path:", err.Error())
}
+ // parse output file path
outFile, err := filepath.Abs(*outputFileFlag)
if err != nil {
- log.Fatalln("Invalid output file path:", err.Error())
+ logger.Fatalln("Invalid output file path:", err.Error())
}
- data, err = os.Open(inFile)
+ // parse lock file
+ f, err := os.Open(inFile)
if err != nil {
- log.Fatalln("Error opening input file:", err.Error())
- }
- defer data.Close()
-
- reader := bufio.NewReader(data)
- buffer = bytes.NewBuffer(make([]byte, 0))
- part = make([]byte, 1024)
-
- for {
- if count, err = reader.Read(part); err != nil {
- break
- }
- buffer.Write(part[:count])
- }
- if err != io.EOF {
- log.Fatalln("Error reading input file:", err.Error())
+ logger.Fatalln("Error opening input file:", err.Error())
}
+ defer f.Close()
- raw := rawLock{}
- err = toml.Unmarshal(buffer.Bytes(), &raw)
+ lock, err := dep.ReadLock(f)
if err != nil {
- log.Fatalln("Error parsing lock file:", err.Error())
+ logger.Fatalln("Error parsing lock file:", err.Error())
}
- //fmt.Println(raw.Projects)
- fmt.Printf("Found %d libraries to process: \n", len(raw.Projects))
+ logger.Printf("Found %d projects to process.\n", len(lock.Projects()))
- for i := 0; i < len(raw.Projects); i++ {
- t := raw.Projects[i]
- fmt.Println(t.Name)
+ // create temporary directory for source manager cache
+ cachedir, err := ioutil.TempDir(os.TempDir(), "")
+ if err != nil {
+ logger.Fatalln(err)
}
- fmt.Print("\n\n")
-
- var godepnix string
-
- godepnix += `
- # file automatically generated from Gopkg.lock with https://github.com/nixcloud/dep2nix (golang dep)
- [
- `
-
- for i := 0; i < len(raw.Projects); i++ {
-
- t := raw.Projects[i]
+ defer os.RemoveAll(cachedir)
- var url string
- // check if it's a common git path `go get` supports and if not find real path
- if !IsCommonPath(t.Name) {
- realURL, err := FindRealPath(t.Name)
-
- if err != nil {
- //fmt.Printf("could not find real git url for import path %v: %+v\n", t.Name, err)
- log.Fatal(err)
- }
- url = realURL
- } else {
- url = "https://" + t.Name
- }
+ // create source manager
+ sm, err := gps.NewSourceManager(gps.SourceManagerConfig{
+ Cachedir: cachedir,
+ Logger: logger,
+ })
+ if err != nil {
+ logger.Fatalln(err)
+ }
- fmt.Println(" * Processing: \"" + t.Name + "\"")
+ // Process all projects, converting them into deps
+ var deps Deps
+ for _, project := range lock.Projects() {
+ fmt.Printf("* Processing: \"%s\"\n", project.Ident().ProjectRoot)
- cmd := exec.Command("nix-prefetch-git", url, "--rev", t.Revision, "--quiet")
- var out bytes.Buffer
- cmd.Stdout = &out
- err := cmd.Run()
+ // get repository for project
+ src, err := sm.SourceFor(project.Ident())
if err != nil {
- log.Fatal(err)
+ logger.Fatalln(err)
}
+ repo := src.Repo()
- type response struct {
- Url string `json:"url"`
- Rev string `json:"rev"`
- Date string `json:"date"`
- SHA256 string `json:"sha256"`
- FetchSubmodules bool `json:"fetchSubmodules"`
+ // get vcs type
+ typ := string(repo.Vcs())
+ if typ != "git" {
+ logger.Fatalln("non-git repositories are not supported yet")
}
- var jsonStr = out.String()
- var res response
- err1 := json.Unmarshal([]byte(jsonStr), &res)
+ // check out repository
+ if err := repo.Get(); err != nil {
+ logger.Fatalln("error fetching project:", err.Error())
+ }
+ // get resolved revision
+ rev, err := src.Repo().Version()
if err != nil {
- fmt.Println("There was a problem in decoding the result from nix-prefetch-git returned JSON:")
- fmt.Println(jsonStr)
- fmt.Println(err1)
- os.Exit(1)
+ logger.Fatal(err)
}
- //fmt.Println(res)
-
- godepnix += `
- {
- goPackagePath = "` + t.Name + `";
- fetch = {
- type = "git";
- url = "` + url + `";
- rev = "` + res.Rev + `";
- sha256 = "` + res.SHA256 + `";
- };
- }
- `
+ // use locally fetched repository as remote for nix-prefetch-git
+ // to it being downloaded from the remote again
+ localUrl := fmt.Sprintf("file://%s", repo.LocalPath())
+ // use nix-prefetch-git to get the hash of the checkout
+ cmd := exec.Command("nix-prefetch-git", "--url", localUrl, "--rev", rev, "--quiet")
+ var out bytes.Buffer
+ cmd.Stdout = &out
+ if err := cmd.Run(); err != nil {
+ logger.Fatal(err)
+ }
+ // extract hash from response
+ res := &struct {
+ SHA256 string `json:"sha256"`
+ }{}
+ json.Unmarshal(out.Bytes(), res)
+
+ // create dep instance
+ deps = append(deps, &Dep{
+ PackagePath: string(project.Ident().ProjectRoot),
+ VCS: string(typ),
+ URL: src.Repo().Remote(),
+ Revision: rev,
+ SHA256: res.SHA256,
+ })
}
- godepnix += "\n]"
- //fmt.Println(godepnix)
-
- f, _ := os.Create(outFile)
- defer f.Close()
+ // write deps to output file
+ out, err := os.Create(outFile)
+ if err != nil {
+ logger.Fatalln("Error creating output file:", err.Error())
+ }
+ defer out.Close()
- _, _ = f.WriteString(godepnix)
- fmt.Printf("\n -> Wrote %s, everything fine!\n", outFile)
+ if _, err := out.WriteString(deps.toNix()); err != nil {
+ logger.Fatalln("Error writing output file:", err.Error())
+ }
- os.Exit(0)
+ fmt.Printf("\n -> Wrote to %s.\n", outFile)
}