Vendoring improved
* update dagger init with package manager downloading stdlib Signed-off-by: Tihomir Jovicic <tihomir.jovicic.develop@gmail.com> * split mod get and update functions Signed-off-by: Tihomir Jovicic <tihomir.jovicic.develop@gmail.com> * write to package checksum to dagger.sum when installing/updating Signed-off-by: Tihomir Jovicic <tihomir.jovicic.develop@gmail.com> * encure checksums are valid when compiling input Signed-off-by: Tihomir Jovicic <tihomir.jovicic.develop@gmail.com> * remove references to github.com/tjovicic in docs 1010 and 1011 Signed-off-by: Tihomir Jovicic <tihomir.jovicic.develop@gmail.com> * refactor mod get command Signed-off-by: Tihomir Jovicic <tihomir.jovicic.develop@gmail.com> * update logic of moving dir when installing packages Signed-off-by: Tihomir Jovicic <tihomir.jovicic.develop@gmail.com> * fix linting errors Signed-off-by: Tihomir Jovicic <tihomir.jovicic.develop@gmail.com> * revert changes to 1010 docs Signed-off-by: Tihomir Jovicic <tihomir.jovicic.develop@gmail.com> * updating error log line in mod/get Signed-off-by: Tihomir Jovicic <tihomir.jovicic.develop@gmail.com> * fix ci tests when using vendoring Signed-off-by: Tihomir Jovicic <tihomir.jovicic.develop@gmail.com> * update alpha.dagger.io version to v0.1 Signed-off-by: Tihomir Jovicic <tihomir.jovicic.develop@gmail.com> * fix mod repo test Signed-off-by: Tihomir Jovicic <tihomir.jovicic.develop@gmail.com> * return error if package already installed Signed-off-by: Tihomir Jovicic <tihomir.jovicic.develop@gmail.com> * remove already installed packages when installing Signed-off-by: Tihomir Jovicic <tihomir.jovicic.develop@gmail.com> * fix issue when vendoring stdlib Signed-off-by: Tihomir Jovicic <tihomir.jovicic.develop@gmail.com> * update mod command with filelock while installing Signed-off-by: Tihomir Jovicic <tihomir.jovicic.develop@gmail.com> * fix linting errors Signed-off-by: Tihomir Jovicic <tihomir.jovicic.develop@gmail.com> * fix path of mod lock file Signed-off-by: Tihomir Jovicic <tihomir.jovicic.develop@gmail.com> Co-authored-by: Tihomir Jovicic <tihomir.jovicic.develop@gmail.com> Signed-off-by: Sam Alba <sam.alba@gmail.com>
This commit is contained in:
31
mod/checksum.go
Normal file
31
mod/checksum.go
Normal file
@@ -0,0 +1,31 @@
|
||||
package mod
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
|
||||
"golang.org/x/mod/sumdb/dirhash"
|
||||
)
|
||||
|
||||
func dirChecksum(dirPath string) (string, error) {
|
||||
err := cleanDirForChecksum(dirPath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
checksum, err := dirhash.HashDir(dirPath, "", dirhash.DefaultHash)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return checksum, nil
|
||||
}
|
||||
|
||||
func cleanDirForChecksum(dirPath string) error {
|
||||
if err := os.RemoveAll(path.Join(dirPath, ".git")); err != nil {
|
||||
return fmt.Errorf("error cleaning up .git directory")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
293
mod/file.go
Normal file
293
mod/file.go
Normal file
@@ -0,0 +1,293 @@
|
||||
package mod
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
const modFilePath = "./cue.mod/dagger.mod"
|
||||
const sumFilePath = "./cue.mod/dagger.sum"
|
||||
const lockFilePath = "./cue.mod/dagger.lock"
|
||||
const destBasePath = "./cue.mod/pkg"
|
||||
const tmpBasePath = "./cue.mod/tmp"
|
||||
|
||||
// file is the parsed, interpreted form of dagger.mod file.
|
||||
type file struct {
|
||||
requires []*Require
|
||||
workspacePath string
|
||||
}
|
||||
|
||||
func readPath(workspacePath string) (*file, error) {
|
||||
pMod := path.Join(workspacePath, modFilePath)
|
||||
fMod, err := os.Open(pMod)
|
||||
if err != nil {
|
||||
if !errors.Is(err, fs.ErrNotExist) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// dagger.mod doesn't exist, let's create an empty file
|
||||
if fMod, err = os.Create(pMod); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
pSum := path.Join(workspacePath, sumFilePath)
|
||||
fSum, err := os.Open(pSum)
|
||||
if err != nil {
|
||||
if !errors.Is(err, fs.ErrNotExist) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// dagger.sum doesn't exist, let's create an empty file
|
||||
if fSum, err = os.Create(pSum); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
modFile, err := read(fMod, fSum)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
modFile.workspacePath = workspacePath
|
||||
|
||||
return modFile, nil
|
||||
}
|
||||
|
||||
func read(fMod, fSum io.Reader) (*file, error) {
|
||||
bMod, err := ioutil.ReadAll(fMod)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
bSum, err := ioutil.ReadAll(fSum)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
modLines := nonEmptyLines(bMod)
|
||||
sumLines := nonEmptyLines(bSum)
|
||||
|
||||
if len(modLines) != len(sumLines) {
|
||||
return nil, fmt.Errorf("length of dagger.mod and dagger.sum files differ")
|
||||
}
|
||||
|
||||
requires := make([]*Require, 0, len(modLines))
|
||||
for i := 0; i < len(modLines); i++ {
|
||||
modSplit := strings.Split(modLines[i], " ")
|
||||
if len(modSplit) != 2 {
|
||||
return nil, fmt.Errorf("line in the mod file doesn't contain 2 elements")
|
||||
}
|
||||
|
||||
sumSplit := strings.Split(sumLines[i], " ")
|
||||
if len(sumSplit) != 2 {
|
||||
return nil, fmt.Errorf("line in the sum file doesn't contain 2 elements")
|
||||
}
|
||||
|
||||
if modSplit[0] != sumSplit[0] {
|
||||
return nil, fmt.Errorf("repos in mod and sum line don't match: %s and %s", modSplit[0], sumSplit[0])
|
||||
}
|
||||
|
||||
require, err := newRequire(modSplit[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
require.version = modSplit[1]
|
||||
require.checksum = sumSplit[1]
|
||||
|
||||
requires = append(requires, require)
|
||||
}
|
||||
|
||||
return &file{requires: requires}, nil
|
||||
}
|
||||
|
||||
var spaceRgx = regexp.MustCompile(`\s+`)
|
||||
|
||||
func nonEmptyLines(b []byte) []string {
|
||||
s := strings.ReplaceAll(string(b), "\r\n", "\n")
|
||||
split := strings.Split(s, "\n")
|
||||
|
||||
lines := make([]string, 0, len(split))
|
||||
for _, l := range split {
|
||||
trimmed := strings.TrimSpace(l)
|
||||
if trimmed == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
trimmed = spaceRgx.ReplaceAllString(trimmed, " ")
|
||||
|
||||
lines = append(lines, trimmed)
|
||||
}
|
||||
|
||||
return lines
|
||||
}
|
||||
|
||||
func (f *file) install(req *Require) error {
|
||||
// cleaning up possible leftovers
|
||||
tmpPath := path.Join(f.workspacePath, tmpBasePath, req.fullPath())
|
||||
defer os.RemoveAll(tmpPath)
|
||||
|
||||
// clone to a tmp directory
|
||||
r, err := clone(req, tmpPath, viper.GetString("private-key-file"), viper.GetString("private-key-password"))
|
||||
if err != nil {
|
||||
return fmt.Errorf("error downloading package %s: %w", req, err)
|
||||
}
|
||||
|
||||
destPath := path.Join(f.workspacePath, destBasePath, req.fullPath())
|
||||
|
||||
// requirement is new, so we should move the cloned files from tmp to pkg and add it to the mod file
|
||||
existing := f.searchInstalledRequire(req)
|
||||
if existing == nil {
|
||||
if err = replace(req, tmpPath, destPath); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
checksum, err := dirChecksum(destPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
req.checksum = checksum
|
||||
|
||||
f.requires = append(f.requires, req)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// checkout the cloned repo to that tag, change the version in the existing requirement and
|
||||
// replace the code in the /pkg folder
|
||||
existing.version = req.version
|
||||
if err = r.checkout(req.version); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = replace(req, tmpPath, destPath); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
checksum, err := dirChecksum(destPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
existing.checksum = checksum
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *file) updateToLatest(req *Require) (*Require, error) {
|
||||
// check if it doesn't exist
|
||||
existing := f.searchInstalledRequire(req)
|
||||
if existing == nil {
|
||||
return nil, fmt.Errorf("package %s isn't already installed", req.fullPath())
|
||||
}
|
||||
|
||||
// cleaning up possible leftovers
|
||||
tmpPath := path.Join(f.workspacePath, tmpBasePath, existing.fullPath())
|
||||
defer os.RemoveAll(tmpPath)
|
||||
|
||||
// clone to a tmp directory
|
||||
gitRepo, err := clone(existing, tmpPath, viper.GetString("private-key-file"), viper.GetString("private-key-password"))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error downloading package %s: %w", existing, err)
|
||||
}
|
||||
|
||||
// checkout the latest tag
|
||||
latestTag, err := gitRepo.latestTag()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c, err := compareVersions(latestTag, existing.version)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if c < 0 {
|
||||
return nil, fmt.Errorf("latest git tag is less than the current version")
|
||||
}
|
||||
|
||||
existing.version = latestTag
|
||||
if err = gitRepo.checkout(existing.version); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// move the package from tmp to pkg directory
|
||||
destPath := path.Join(f.workspacePath, destBasePath, existing.fullPath())
|
||||
if err = replace(existing, tmpPath, destPath); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
checksum, err := dirChecksum(destPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req.checksum = checksum
|
||||
|
||||
return existing, nil
|
||||
}
|
||||
|
||||
func (f *file) searchInstalledRequire(r *Require) *Require {
|
||||
for _, existing := range f.requires {
|
||||
if existing.fullPath() == r.fullPath() {
|
||||
return existing
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *file) ensure() error {
|
||||
for _, require := range f.requires {
|
||||
requirePath := path.Join(f.workspacePath, destBasePath, require.fullPath())
|
||||
|
||||
checksum, err := dirChecksum(requirePath)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if require.checksum != checksum {
|
||||
return fmt.Errorf("wrong checksum for %s", require.fullPath())
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *file) write() error {
|
||||
// write dagger.mod file
|
||||
var bMod bytes.Buffer
|
||||
for _, r := range f.requires {
|
||||
bMod.WriteString(fmt.Sprintf("%s %s\n", r.fullPath(), r.version))
|
||||
}
|
||||
|
||||
err := ioutil.WriteFile(path.Join(f.workspacePath, modFilePath), bMod.Bytes(), 0600)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// write dagger.sum file
|
||||
var bSum bytes.Buffer
|
||||
for _, r := range f.requires {
|
||||
bSum.WriteString(fmt.Sprintf("%s %s\n", r.fullPath(), r.checksum))
|
||||
}
|
||||
|
||||
err = ioutil.WriteFile(path.Join(f.workspacePath, sumFilePath), bSum.Bytes(), 0600)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
54
mod/file_test.go
Normal file
54
mod/file_test.go
Normal file
@@ -0,0 +1,54 @@
|
||||
package mod
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestReadFile(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
modFile string
|
||||
sumFile string
|
||||
want *file
|
||||
}{
|
||||
{
|
||||
name: "module file with valid dependencies",
|
||||
modFile: `
|
||||
github.com/tjovicic/test xyz
|
||||
github.com/bla/bla abc
|
||||
`,
|
||||
sumFile: `
|
||||
github.com/tjovicic/test h1:hash
|
||||
github.com/bla/bla h1:hash
|
||||
`,
|
||||
want: &file{
|
||||
requires: []*Require{
|
||||
{
|
||||
repo: "github.com/tjovicic/test",
|
||||
path: "",
|
||||
version: "xyz",
|
||||
},
|
||||
{
|
||||
repo: "github.com/bla/bla",
|
||||
path: "",
|
||||
version: "abc",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
got, err := read(strings.NewReader(c.modFile), strings.NewReader(c.sumFile))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if len(got.requires) != len(c.want.requires) {
|
||||
t.Errorf("requires length differs: want %d, got %d", len(c.want.requires), len(got.requires))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
135
mod/mod.go
Normal file
135
mod/mod.go
Normal file
@@ -0,0 +1,135 @@
|
||||
package mod
|
||||
|
||||
import (
|
||||
"path"
|
||||
|
||||
"github.com/gofrs/flock"
|
||||
)
|
||||
|
||||
func InstallStdlib(workspace string) error {
|
||||
_, err := Install(workspace, "alpha.dagger.io@v0.1")
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func Install(workspace, repoName string) (*Require, error) {
|
||||
require, err := newRequire(repoName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
modfile, err := readPath(workspace)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fileLock := flock.New(path.Join(workspace, lockFilePath))
|
||||
if err := fileLock.Lock(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = modfile.install(require)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = modfile.write(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := fileLock.Unlock(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return require, nil
|
||||
}
|
||||
|
||||
func InstallAll(workspace string, repoNames []string) ([]*Require, error) {
|
||||
installedRequires := make([]*Require, 0, len(repoNames))
|
||||
var err error
|
||||
|
||||
for _, repoName := range repoNames {
|
||||
var require *Require
|
||||
|
||||
if require, err = Install(workspace, repoName); err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
installedRequires = append(installedRequires, require)
|
||||
}
|
||||
|
||||
return installedRequires, err
|
||||
}
|
||||
|
||||
func Update(workspace, repoName string) (*Require, error) {
|
||||
require, err := newRequire(repoName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
modfile, err := readPath(workspace)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fileLock := flock.New(path.Join(workspace, lockFilePath))
|
||||
if err := fileLock.Lock(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
updatedRequire, err := modfile.updateToLatest(require)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = modfile.write(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := fileLock.Unlock(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return updatedRequire, nil
|
||||
}
|
||||
|
||||
func UpdateAll(workspace string, repoNames []string) ([]*Require, error) {
|
||||
updatedRequires := make([]*Require, 0, len(repoNames))
|
||||
var err error
|
||||
|
||||
for _, repoName := range repoNames {
|
||||
var require *Require
|
||||
|
||||
if require, err = Update(workspace, repoName); err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
updatedRequires = append(updatedRequires, require)
|
||||
}
|
||||
|
||||
return updatedRequires, err
|
||||
}
|
||||
|
||||
func UpdateInstalled(workspace string) ([]*Require, error) {
|
||||
modfile, err := readPath(workspace)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
repoNames := make([]string, 0, len(modfile.requires))
|
||||
|
||||
for _, require := range modfile.requires {
|
||||
repoNames = append(repoNames, require.String())
|
||||
}
|
||||
|
||||
return UpdateAll(workspace, repoNames)
|
||||
}
|
||||
|
||||
func Ensure(workspace string) error {
|
||||
modfile, err := readPath(workspace)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return modfile.ensure()
|
||||
}
|
124
mod/repo.go
Normal file
124
mod/repo.go
Normal file
@@ -0,0 +1,124 @@
|
||||
package mod
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/go-git/go-git/v5/plumbing/transport/ssh"
|
||||
|
||||
"github.com/go-git/go-git/v5"
|
||||
"github.com/go-git/go-git/v5/plumbing"
|
||||
"github.com/hashicorp/go-version"
|
||||
)
|
||||
|
||||
type repo struct {
|
||||
contents *git.Repository
|
||||
}
|
||||
|
||||
func clone(require *Require, dir string, privateKeyFile, privateKeyPassword string) (*repo, error) {
|
||||
if err := os.RemoveAll(dir); err != nil {
|
||||
return nil, fmt.Errorf("error cleaning up tmp directory")
|
||||
}
|
||||
if err := os.MkdirAll(dir, 0755); err != nil {
|
||||
return nil, fmt.Errorf("error creating tmp dir for cloning package")
|
||||
}
|
||||
|
||||
o := git.CloneOptions{
|
||||
URL: fmt.Sprintf("https://%s", require.cloneRepo),
|
||||
}
|
||||
|
||||
if privateKeyFile != "" {
|
||||
publicKeys, err := ssh.NewPublicKeysFromFile("git", privateKeyFile, privateKeyPassword)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
o.Auth = publicKeys
|
||||
o.URL = fmt.Sprintf("git@%s", strings.Replace(require.cloneRepo, "/", ":", 1))
|
||||
}
|
||||
|
||||
r, err := git.PlainClone(dir, false, &o)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rr := &repo{
|
||||
contents: r,
|
||||
}
|
||||
|
||||
if require.version == "" {
|
||||
latestTag, err := rr.latestTag()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
require.version = latestTag
|
||||
}
|
||||
|
||||
if err := rr.checkout(require.version); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return rr, nil
|
||||
}
|
||||
|
||||
func (r *repo) checkout(version string) error {
|
||||
h, err := r.contents.ResolveRevision(plumbing.Revision(version))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w, err := r.contents.Worktree()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = w.Checkout(&git.CheckoutOptions{
|
||||
Hash: *h,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *repo) listTags() ([]string, error) {
|
||||
iter, err := r.contents.Tags()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var tags []string
|
||||
if err := iter.ForEach(func(ref *plumbing.Reference) error {
|
||||
tags = append(tags, ref.Name().Short())
|
||||
return nil
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return tags, nil
|
||||
}
|
||||
|
||||
func (r *repo) latestTag() (string, error) {
|
||||
versionsRaw, err := r.listTags()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
versions := make([]*version.Version, len(versionsRaw))
|
||||
for i, raw := range versionsRaw {
|
||||
v, _ := version.NewVersion(raw)
|
||||
versions[i] = v
|
||||
}
|
||||
|
||||
if len(versions) == 0 {
|
||||
return "", fmt.Errorf("repo doesn't have any tags")
|
||||
}
|
||||
|
||||
sort.Sort(version.Collection(versions))
|
||||
|
||||
return versions[len(versions)-1].Original(), nil
|
||||
}
|
93
mod/repo_test.go
Normal file
93
mod/repo_test.go
Normal file
@@ -0,0 +1,93 @@
|
||||
package mod
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestClone(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
require Require
|
||||
privateKeyFile string
|
||||
privateKeyPassword string
|
||||
}{
|
||||
{
|
||||
name: "resolving shorter hash version",
|
||||
require: Require{
|
||||
cloneRepo: "github.com/dagger/universe",
|
||||
clonePath: "stdlib",
|
||||
version: "24d7af3fc2a3e9c7cc2",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "resolving branch name",
|
||||
require: Require{
|
||||
cloneRepo: "github.com/dagger/universe",
|
||||
clonePath: "stdlib",
|
||||
version: "main",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "resolving tag",
|
||||
require: Require{
|
||||
cloneRepo: "github.com/dagger/universe",
|
||||
clonePath: "stdlib",
|
||||
version: "v0.1",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "dagger private repo",
|
||||
require: Require{
|
||||
cloneRepo: "github.com/dagger/test",
|
||||
clonePath: "",
|
||||
version: "main",
|
||||
},
|
||||
privateKeyFile: "./test-ssh-keys/id_ed25519_test",
|
||||
privateKeyPassword: "",
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
tmpDir, err := ioutil.TempDir("", "clone")
|
||||
if err != nil {
|
||||
t.Fatal("error creating tmp dir")
|
||||
}
|
||||
|
||||
defer os.Remove(tmpDir)
|
||||
|
||||
_, err = clone(&c.require, tmpDir, c.privateKeyFile, c.privateKeyPassword)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestListTags(t *testing.T) {
|
||||
tmpDir, err := ioutil.TempDir("", "clone")
|
||||
if err != nil {
|
||||
t.Fatal("error creating tmp dir")
|
||||
}
|
||||
defer os.Remove(tmpDir)
|
||||
|
||||
r, err := clone(&Require{
|
||||
cloneRepo: "github.com/dagger/universe",
|
||||
clonePath: "stdlib",
|
||||
version: "",
|
||||
}, tmpDir, "", "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
tags, err := r.listTags()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if len(tags) == 0 {
|
||||
t.Errorf("could not list repo tags")
|
||||
}
|
||||
}
|
90
mod/require.go
Normal file
90
mod/require.go
Normal file
@@ -0,0 +1,90 @@
|
||||
package mod
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Require struct {
|
||||
repo string
|
||||
path string
|
||||
|
||||
cloneRepo string
|
||||
clonePath string
|
||||
|
||||
version string
|
||||
checksum string
|
||||
}
|
||||
|
||||
func newRequire(repoName string) (*Require, error) {
|
||||
switch {
|
||||
case strings.HasPrefix(repoName, "github.com"):
|
||||
return parseGithubRepoName(repoName)
|
||||
case strings.HasPrefix(repoName, "alpha.dagger.io"):
|
||||
return parseDaggerRepoName(repoName)
|
||||
default:
|
||||
return nil, fmt.Errorf("repo name does not match suported providers")
|
||||
}
|
||||
}
|
||||
|
||||
var githubRepoNameRegex = regexp.MustCompile(`(github.com/[a-zA-Z0-9_.-]+/[a-zA-Z0-9_.-]+)([a-zA-Z0-9/_.-]*)@?([0-9a-zA-Z.-]*)`)
|
||||
|
||||
func parseGithubRepoName(repoName string) (*Require, error) {
|
||||
repoMatches := githubRepoNameRegex.FindStringSubmatch(repoName)
|
||||
|
||||
if len(repoMatches) < 4 {
|
||||
return nil, fmt.Errorf("issue when parsing github repo")
|
||||
}
|
||||
|
||||
return &Require{
|
||||
repo: repoMatches[1],
|
||||
path: repoMatches[2],
|
||||
version: repoMatches[3],
|
||||
|
||||
cloneRepo: repoMatches[1],
|
||||
clonePath: repoMatches[2],
|
||||
}, nil
|
||||
}
|
||||
|
||||
var daggerRepoNameRegex = regexp.MustCompile(`alpha.dagger.io([a-zA-Z0-9/_.-]*)@?([0-9a-zA-Z.-]*)`)
|
||||
|
||||
func parseDaggerRepoName(repoName string) (*Require, error) {
|
||||
repoMatches := daggerRepoNameRegex.FindStringSubmatch(repoName)
|
||||
|
||||
if len(repoMatches) < 3 {
|
||||
return nil, fmt.Errorf("issue when parsing dagger repo")
|
||||
}
|
||||
|
||||
return &Require{
|
||||
repo: "alpha.dagger.io",
|
||||
path: repoMatches[1],
|
||||
version: repoMatches[2],
|
||||
|
||||
cloneRepo: "github.com/dagger/universe",
|
||||
clonePath: path.Join("/stdlib", repoMatches[1]),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (r *Require) String() string {
|
||||
return fmt.Sprintf("%s@%s", r.fullPath(), r.version)
|
||||
}
|
||||
|
||||
func (r *Require) fullPath() string {
|
||||
return path.Join(r.repo, r.path)
|
||||
}
|
||||
|
||||
func replace(r *Require, sourceRepoPath, destPath string) error {
|
||||
// remove previous package directory
|
||||
if err := os.RemoveAll(destPath); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := os.Rename(path.Join(sourceRepoPath, r.clonePath), destPath); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
123
mod/require_test.go
Normal file
123
mod/require_test.go
Normal file
@@ -0,0 +1,123 @@
|
||||
package mod
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestParseArgument(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
in string
|
||||
want *Require
|
||||
hasError bool
|
||||
}{
|
||||
{
|
||||
name: "Random",
|
||||
in: "abcd/bla@:/xyz",
|
||||
hasError: true,
|
||||
},
|
||||
{
|
||||
name: "Dagger repo",
|
||||
in: "github.com/dagger/dagger",
|
||||
want: &Require{
|
||||
repo: "github.com/dagger/dagger",
|
||||
path: "",
|
||||
version: "",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Dagger repo with path",
|
||||
in: "github.com/dagger/dagger/stdlib",
|
||||
want: &Require{
|
||||
repo: "github.com/dagger/dagger",
|
||||
path: "/stdlib",
|
||||
version: "",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Dagger repo with longer path",
|
||||
in: "github.com/dagger/dagger/stdlib/test/test",
|
||||
want: &Require{
|
||||
repo: "github.com/dagger/dagger",
|
||||
path: "/stdlib/test/test",
|
||||
version: "",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Dagger repo with path and version",
|
||||
in: "github.com/dagger/dagger/stdlib@v0.1",
|
||||
want: &Require{
|
||||
repo: "github.com/dagger/dagger",
|
||||
path: "/stdlib",
|
||||
version: "v0.1",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Dagger repo with longer path and version tag",
|
||||
in: "github.com/dagger/dagger/stdlib/test/test@v0.0.1",
|
||||
want: &Require{
|
||||
repo: "github.com/dagger/dagger",
|
||||
path: "/stdlib/test/test",
|
||||
version: "v0.0.1",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Alpha Dagger repo with path",
|
||||
in: "alpha.dagger.io/gcp/gke@v0.1.0-alpha.20",
|
||||
want: &Require{
|
||||
repo: "alpha.dagger.io",
|
||||
path: "/gcp/gke",
|
||||
version: "v0.1.0-alpha.20",
|
||||
|
||||
cloneRepo: "github.com/dagger/dagger",
|
||||
clonePath: "/stdlib/gcp/gke",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Alpha Dagger repo",
|
||||
in: "alpha.dagger.io@v0.1.0-alpha.23",
|
||||
want: &Require{
|
||||
repo: "alpha.dagger.io",
|
||||
path: "",
|
||||
version: "v0.1.0-alpha.23",
|
||||
|
||||
cloneRepo: "github.com/dagger/dagger",
|
||||
clonePath: "/stdlib",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Dagger repo with longer path and commit version",
|
||||
in: "github.com/dagger/dagger/stdlib/test/test@26a1d46d1b3c",
|
||||
want: &Require{
|
||||
repo: "github.com/dagger/dagger",
|
||||
path: "/stdlib/test/test",
|
||||
version: "26a1d46d1b3c",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
got, err := newRequire(c.in)
|
||||
if err != nil && c.hasError {
|
||||
return
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if got.repo != c.want.repo {
|
||||
t.Errorf("repos differ: want %s, got %s", c.want.repo, got.repo)
|
||||
}
|
||||
|
||||
if got.path != c.want.path {
|
||||
t.Errorf("paths differ: want %s, got %s", c.want.path, got.path)
|
||||
}
|
||||
|
||||
if got.version != c.want.version {
|
||||
t.Errorf("versions differ: want %s, got %s", c.want.version, got.version)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
7
mod/test-ssh-keys/id_ed25519_test
Normal file
7
mod/test-ssh-keys/id_ed25519_test
Normal file
@@ -0,0 +1,7 @@
|
||||
-----BEGIN OPENSSH PRIVATE KEY-----
|
||||
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
|
||||
QyNTUxOQAAACCpGsk8WLx7gXCXX1muGhKjlkqaqykF1X198WQMkBO2pwAAAKC5Ec8WuRHP
|
||||
FgAAAAtzc2gtZWQyNTUxOQAAACCpGsk8WLx7gXCXX1muGhKjlkqaqykF1X198WQMkBO2pw
|
||||
AAAEBXE9Uht+QHuyK7+yYcZFVWOJ3qkhUh/wn289nDKDPHKakayTxYvHuBcJdfWa4aEqOW
|
||||
SpqrKQXVfX3xZAyQE7anAAAAGnRpaG9taXIuam92aWNpY0B0b3B0YWwuY29tAQID
|
||||
-----END OPENSSH PRIVATE KEY-----
|
1
mod/test-ssh-keys/id_ed25519_test.pub
Normal file
1
mod/test-ssh-keys/id_ed25519_test.pub
Normal file
@@ -0,0 +1 @@
|
||||
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKkayTxYvHuBcJdfWa4aEqOWSpqrKQXVfX3xZAyQE7an tihomir.jovicic@toptal.com
|
27
mod/version.go
Normal file
27
mod/version.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package mod
|
||||
|
||||
import "github.com/hashicorp/go-version"
|
||||
|
||||
// compareVersions returns -1 if the first argument is less or 1 if it's greater than the second argument.
|
||||
// It returns 0 if they are equal.
|
||||
func compareVersions(reqV1, reqV2 string) (int, error) {
|
||||
v1, err := version.NewVersion(reqV1)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
v2, err := version.NewVersion(reqV2)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if v1.LessThan(v2) {
|
||||
return -1, nil
|
||||
}
|
||||
|
||||
if v1.Equal(v2) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
return 1, nil
|
||||
}
|
Reference in New Issue
Block a user