Add ability to resolve and install transitive dependencies

This commit is contained in:
Frederic Branczyk 2018-04-25 09:01:00 +01:00
parent 5233278a99
commit f69df626e3
No known key found for this signature in database
GPG key ID: 7741A52782A90069
4 changed files with 163 additions and 87 deletions

View file

@ -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)
lock, err := pkg.Install(ctx, m, cfg.JsonnetHome)
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")
}
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())
}

View file

@ -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
}

View file

@ -14,7 +14,9 @@
package pkg
import "context"
import (
"context"
)
type Interface interface {
Install(ctx context.Context, dir, version string) (lockVersion string, err error)

144
pkg/packages.go Normal file
View file

@ -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
}