diff --git a/cmd/jb/main.go b/cmd/jb/main.go index 39d5504..3e3daf5 100644 --- a/cmd/jb/main.go +++ b/cmd/jb/main.go @@ -38,8 +38,6 @@ const ( initSubcommand = "init" basePath = ".jsonnetpkg" srcDirName = "src" - jsonnetFile = "jsonnetfile.json" - jsonnetLockFile = "jsonnetfile.lock.json" ) var ( @@ -82,9 +80,9 @@ func Main() int { func RunSubcommand(ctx context.Context, cfg config, subcommand string, args []string) error { switch subcommand { case initSubcommand: - return ioutil.WriteFile(jsonnetFile, []byte("{}"), 0644) + return ioutil.WriteFile(pkg.JsonnetFile, []byte("{}"), 0644) case installSubcommand: - m, err := loadJsonnetfile(jsonnetFile) + m, err := pkg.LoadJsonnetfile(pkg.JsonnetFile) if err != nil { return errors.Wrap(err, "failed to load jsonnetfile") } @@ -169,56 +167,9 @@ func RunSubcommand(ctx context.Context, cfg config, subcommand string, args []st return errors.Wrap(err, "failed to create jsonnet home path") } - lock := &spec.JsonnetFile{} - for _, dep := range m.Dependencies { - tmp := filepath.Join(cfg.JsonnetHome, ".tmp") - err = os.MkdirAll(tmp, os.ModePerm) - if err != nil { - return errors.Wrap(err, "failed to create general tmp dir") - } - tmpDir, err := ioutil.TempDir(tmp, fmt.Sprintf("jsonnetpkg-%s-%s", dep.Name, dep.Version)) - if err != nil { - return errors.Wrap(err, "failed to create tmp dir") - } - defer os.RemoveAll(tmpDir) - - subdir := "" - var p pkg.Interface - if dep.Source.GitSource != nil { - p = pkg.NewGitPackage(dep.Source.GitSource) - subdir = dep.Source.GitSource.Subdir - } - lockVersion, err := p.Install(ctx, tmpDir, dep.Version) - if err != nil { - return errors.Wrap(err, "failed to install package") - } - - lockPackage := spec.Dependency{ - Name: dep.Name, - Source: dep.Source, - Version: lockVersion, - } - lock.Dependencies = append(lock.Dependencies, lockPackage) - - destPath := path.Join(cfg.JsonnetHome, dep.Name) - if err != nil { - return errors.Wrap(err, "failed to find destination path for package") - } - - fmt.Println("Moving", tmpDir, "to", destPath) - err = os.MkdirAll(path.Dir(destPath), os.ModePerm) - if err != nil { - return errors.Wrap(err, "failed to create parent path") - } - - err = os.RemoveAll(destPath) - if err != nil { - return errors.Wrap(err, "failed to clean previous destination path") - } - err = os.Rename(path.Join(tmpDir, subdir), destPath) - if err != nil { - return errors.Wrap(err, "failed to move package") - } + lock, err := pkg.Install(ctx, m, cfg.JsonnetHome) + if err != nil { + return errors.Wrap(err, "failed to install") } b, err := json.MarshalIndent(m, "", " ") @@ -226,7 +177,7 @@ func RunSubcommand(ctx context.Context, cfg config, subcommand string, args []st return errors.Wrap(err, "failed to encode jsonnet file") } - err = ioutil.WriteFile(jsonnetFile, b, 0644) + err = ioutil.WriteFile(pkg.JsonnetFile, b, 0644) if err != nil { return errors.Wrap(err, "failed to write jsonnet file") } @@ -236,7 +187,7 @@ func RunSubcommand(ctx context.Context, cfg config, subcommand string, args []st return errors.Wrap(err, "failed to encode jsonnet file") } - err = ioutil.WriteFile(jsonnetLockFile, b, 0644) + err = ioutil.WriteFile(pkg.JsonnetLockFile, b, 0644) if err != nil { return errors.Wrap(err, "failed to write lock file") } @@ -247,34 +198,6 @@ func RunSubcommand(ctx context.Context, cfg config, subcommand string, args []st return nil } -func libPathFromPackage(homeDir string, p spec.Dependency) string { - return path.Join(homeDir, p.Name) -} - -func flattenGitRemote(gitRemote string) string { - // In order for a git remote to be represented in a directory name, "/" - // must be replaced. Replacing ":" may be problematic when using git server - // with port. - return strings.Replace(strings.Replace(gitRemote, "/", "-", -1), ":", "-", -1) -} - -func loadJsonnetfile(filename string) (spec.JsonnetFile, error) { - m := spec.JsonnetFile{} - - f, err := os.Open(filename) - if err != nil { - return m, err - } - defer f.Close() - - err = json.NewDecoder(f).Decode(&m) - if err != nil { - return m, err - } - - return m, nil -} - func main() { os.Exit(Main()) } diff --git a/pkg/git.go b/pkg/git.go index 3ea5bc6..2607871 100644 --- a/pkg/git.go +++ b/pkg/git.go @@ -19,6 +19,7 @@ import ( "context" "os" "os/exec" + "path" "strings" "github.com/jsonnet-bundler/jsonnet-bundler/spec" @@ -34,12 +35,12 @@ func NewGitPackage(source *spec.GitSource) Interface { } } -func (p *GitPackage) Install(ctx context.Context, dir, version string) (string, error) { +func (p *GitPackage) Install(ctx context.Context, dir, version string) (lockVersion string, err error) { cmd := exec.CommandContext(ctx, "git", "clone", p.Source.Remote, dir) cmd.Stdin = os.Stdin cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr - err := cmd.Run() + err = cmd.Run() if err != nil { return "", err } @@ -64,5 +65,11 @@ func (p *GitPackage) Install(ctx context.Context, dir, version string) (string, } commitHash := strings.TrimSpace(b.String()) + + err = os.RemoveAll(path.Join(dir, ".git")) + if err != nil { + return "", err + } + return commitHash, nil } diff --git a/pkg/interface.go b/pkg/interface.go index 3f6a27f..79384af 100644 --- a/pkg/interface.go +++ b/pkg/interface.go @@ -14,7 +14,9 @@ package pkg -import "context" +import ( + "context" +) type Interface interface { Install(ctx context.Context, dir, version string) (lockVersion string, err error) diff --git a/pkg/packages.go b/pkg/packages.go new file mode 100644 index 0000000..2448b32 --- /dev/null +++ b/pkg/packages.go @@ -0,0 +1,144 @@ +// Copyright 2018 jsonnet-bundler authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package pkg + +import ( + "context" + "encoding/json" + "fmt" + "io/ioutil" + "os" + "path" + "path/filepath" + + "github.com/jsonnet-bundler/jsonnet-bundler/spec" + "github.com/pkg/errors" +) + +var ( + JsonnetFile = "jsonnetfile.json" + JsonnetLockFile = "jsonnetfile.lock.json" + VersionMismatch = errors.New("multiple colliding versions specified") +) + +func Install(ctx context.Context, m spec.JsonnetFile, dir string) (lock *spec.JsonnetFile, err error) { + lock = &spec.JsonnetFile{} + for _, dep := range m.Dependencies { + tmp := filepath.Join(dir, ".tmp") + err = os.MkdirAll(tmp, os.ModePerm) + if err != nil { + return nil, errors.Wrap(err, "failed to create general tmp dir") + } + tmpDir, err := ioutil.TempDir(tmp, fmt.Sprintf("jsonnetpkg-%s-%s", dep.Name, dep.Version)) + if err != nil { + return nil, errors.Wrap(err, "failed to create tmp dir") + } + defer os.RemoveAll(tmpDir) + + subdir := "" + var p Interface + if dep.Source.GitSource != nil { + p = NewGitPackage(dep.Source.GitSource) + subdir = dep.Source.GitSource.Subdir + } + + lockVersion, err := p.Install(ctx, tmpDir, dep.Version) + if err != nil { + return nil, errors.Wrap(err, "failed to install package") + } + // need to deduplicate/error when multiple entries + lock.Dependencies, err = insertDependency(lock.Dependencies, spec.Dependency{ + Name: dep.Name, + Source: dep.Source, + Version: lockVersion, + }) + if err != nil { + return nil, errors.Wrap(err, "failed to insert dependency to lock dependencies") + } + + destPath := path.Join(dir, dep.Name) + if err != nil { + return nil, errors.Wrap(err, "failed to find destination path for package") + } + + err = os.MkdirAll(path.Dir(destPath), os.ModePerm) + if err != nil { + return nil, errors.Wrap(err, "failed to create parent path") + } + + err = os.RemoveAll(destPath) + if err != nil { + return nil, errors.Wrap(err, "failed to clean previous destination path") + } + err = os.Rename(path.Join(tmpDir, subdir), destPath) + if err != nil { + return nil, errors.Wrap(err, "failed to move package") + } + + if _, err := os.Stat(path.Join(destPath, JsonnetFile)); !os.IsNotExist(err) { + depsDeps, err := LoadJsonnetfile(path.Join(destPath, JsonnetFile)) + if err != nil { + return nil, err + } + + depsInstalledByDependency, err := Install(ctx, depsDeps, dir) + if err != nil { + return nil, err + } + + for _, d := range depsInstalledByDependency.Dependencies { + lock.Dependencies, err = insertDependency(lock.Dependencies, d) + if err != nil { + return nil, errors.Wrap(err, "failed to insert dependency to lock dependencies") + } + } + } + } + + return lock, nil +} + +func insertDependency(deps []spec.Dependency, newDep spec.Dependency) ([]spec.Dependency, error) { + res := []spec.Dependency{} + for _, d := range deps { + if d.Name == newDep.Name { + if d.Version != newDep.Version { + return nil, VersionMismatch + } + res = append(res, d) + } else { + res = append(res, d) + } + } + + return res, nil +} + +func LoadJsonnetfile(filename string) (spec.JsonnetFile, error) { + m := spec.JsonnetFile{} + + f, err := os.Open(filename) + if err != nil { + return m, err + } + defer f.Close() + + err = json.NewDecoder(f).Decode(&m) + if err != nil { + return m, err + } + + return m, nil +}