Skip to content

feat: unified dependency system #402

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion pkg/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import (
"kcl-lang.io/kpm/pkg/runner"
"kcl-lang.io/kpm/pkg/settings"
"kcl-lang.io/kpm/pkg/utils"
"kcl-lang.io/kpm/pkg/unified"
)

// KpmClient is the client of kpm.
Expand Down Expand Up @@ -67,11 +68,14 @@ func NewKpmClient() (*KpmClient, error) {
return nil, err
}

ds := unified.NewDependencySystem()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Format the code.

depDownloader := downloader.NewDepDownloader("", ds)

return &KpmClient{
logWriter: os.Stdout,
settings: *settings,
homePath: homePath,
DepDownloader: &downloader.DepDownloader{},
DepDownloader: depDownloader,
}, nil
}

Expand Down
206 changes: 32 additions & 174 deletions pkg/downloader/downloader.go
Original file line number Diff line number Diff line change
@@ -1,18 +1,10 @@
package downloader

import (
"errors"
"fmt"
"io"
"os"
"path/filepath"

v1 "github.com/opencontainers/image-spec/specs-go/v1"
"kcl-lang.io/kpm/pkg/git"
"kcl-lang.io/kpm/pkg/oci"
"kcl-lang.io/kpm/pkg/reporter"
"kcl-lang.io/kpm/pkg/settings"
"kcl-lang.io/kpm/pkg/utils"
remoteauth "oras.land/oras-go/v2/registry/remote/auth"
)

Expand All @@ -27,14 +19,16 @@ type DownloadOptions struct {
// LogWriter is the writer to write the log.
LogWriter io.Writer
// credsClient is the client to get the credentials.
credsClient *CredClient
CredsClient CredentialClient
// Platform is the platform for OCI downloads
Platform string
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cloud you please use a Platform type?

}

type Option func(*DownloadOptions)

func WithCredsClient(credsClient *CredClient) Option {
return func(do *DownloadOptions) {
do.credsClient = credsClient
do.CredsClient = credsClient
}
}

Expand Down Expand Up @@ -70,187 +64,51 @@ func NewDownloadOptions(opts ...Option) *DownloadOptions {
return do
}

func WithPlatform(platform string) Option {
return func(do *DownloadOptions) {
do.Platform = platform
}
}

// Downloader is the interface for downloading a package.
type Downloader interface {
Download(opts DownloadOptions) error
}

// DepDownloader is the downloader for the package.
// Only support the OCI and git source.
type DepDownloader struct {
*OciDownloader
*GitDownloader
type CredentialClient interface {
Credential(registry string) (*remoteauth.Credential, error)
}

// GitDownloader is the downloader for the git source.
type GitDownloader struct{}
// New DependencySystem interface
type DependencySystem interface {
Get(opts DownloadOptions) error
}

// OciDownloader is the downloader for the OCI source.
type OciDownloader struct {
Platform string
// DepDownloader is the downloader for the package.
// Only support the OCI and git source.
type DepDownloader struct {
dependencySystem DependencySystem
Platform string
}

func NewOciDownloader(platform string) *DepDownloader {
return &DepDownloader{
OciDownloader: &OciDownloader{
Platform: platform,
},
}
// New constructor for DepDownloader
func NewDepDownloader(platform string, ds DependencySystem) *DepDownloader {
return &DepDownloader{
dependencySystem: ds,
Platform: platform,
}
}

func (d *DepDownloader) Download(opts DownloadOptions) error {
// Dispatch the download to the specific downloader by package source.
if opts.Source.Oci != nil || opts.Source.Registry != nil {
if opts.Source.Registry != nil {
opts.Source.Oci = opts.Source.Registry.Oci
}
if d.OciDownloader == nil {
d.OciDownloader = &OciDownloader{}
}
return d.OciDownloader.Download(opts)
}

if opts.Source.Git != nil {
if d.GitDownloader == nil {
d.GitDownloader = &GitDownloader{}
}
return d.GitDownloader.Download(opts)
}
return nil
// If platform is not set in options, use the one from DepDownloader
if opts.Platform == "" {
opts.Platform = d.Platform
}
return d.dependencySystem.Get(opts)
}

// Platform option struct.
type Platform struct {
PlatformSpec string
Platform *v1.Platform
}

func (d *OciDownloader) Download(opts DownloadOptions) error {
// download the package from the OCI registry
ociSource := opts.Source.Oci
if ociSource == nil {
return errors.New("oci source is nil")
}

localPath := opts.LocalPath

repoPath := utils.JoinPath(ociSource.Reg, ociSource.Repo)

var cred *remoteauth.Credential
var err error
if opts.credsClient != nil {
cred, err = opts.credsClient.Credential(ociSource.Reg)
if err != nil {
return err
}
} else {
cred = &remoteauth.Credential{}
}

ociCli, err := oci.NewOciClientWithOpts(
oci.WithCredential(cred),
oci.WithRepoPath(repoPath),
oci.WithSettings(&opts.Settings),
)

if err != nil {
return err
}

ociCli.PullOciOptions.Platform = d.Platform

if len(ociSource.Tag) == 0 {
tagSelected, err := ociCli.TheLatestTag()
if err != nil {
return err
}

reporter.ReportMsgTo(
fmt.Sprintf("the lastest version '%s' will be downloaded", tagSelected),
opts.LogWriter,
)

ociSource.Tag = tagSelected
}

reporter.ReportMsgTo(
fmt.Sprintf(
"downloading '%s:%s' from '%s/%s:%s'",
ociSource.Repo, ociSource.Tag, ociSource.Reg, ociSource.Repo, ociSource.Tag,
),
opts.LogWriter,
)

err = ociCli.Pull(localPath, ociSource.Tag)
if err != nil {
return err
}

matches, _ := filepath.Glob(filepath.Join(localPath, "*.tar"))
if matches == nil || len(matches) != 1 {
// then try to glob tgz file
matches, _ = filepath.Glob(filepath.Join(localPath, "*.tgz"))
if matches == nil || len(matches) != 1 {
return fmt.Errorf("failed to find the downloaded kcl package tar file in '%s'", localPath)
}
}

tarPath := matches[0]
if utils.IsTar(tarPath) {
err = utils.UnTarDir(tarPath, localPath)
} else {
err = utils.ExtractTarball(tarPath, localPath)
}
if err != nil {
return fmt.Errorf("failed to untar the kcl package tar from '%s' into '%s'", tarPath, localPath)
}

// After untar the downloaded kcl package tar file, remove the tar file.
if utils.DirExists(tarPath) {
rmErr := os.Remove(tarPath)
if rmErr != nil {
return fmt.Errorf("failed to remove the downloaded kcl package tar file '%s'", tarPath)
}
}

return err
}

func (d *GitDownloader) Download(opts DownloadOptions) error {
var msg string
if len(opts.Source.Git.Tag) != 0 {
msg = fmt.Sprintf("with tag '%s'", opts.Source.Git.Tag)
}

if len(opts.Source.Git.Commit) != 0 {
msg = fmt.Sprintf("with commit '%s'", opts.Source.Git.Commit)
}

if len(opts.Source.Git.Branch) != 0 {
msg = fmt.Sprintf("with branch '%s'", opts.Source.Git.Branch)
}

reporter.ReportMsgTo(
fmt.Sprintf("cloning '%s' %s", opts.Source.Git.Url, msg),
opts.LogWriter,
)
// download the package from the git repo
gitSource := opts.Source.Git
if gitSource == nil {
return errors.New("git source is nil")
}

_, err := git.CloneWithOpts(
git.WithCommit(gitSource.Commit),
git.WithBranch(gitSource.Branch),
git.WithTag(gitSource.Tag),
git.WithRepoURL(gitSource.Url),
git.WithLocalPath(opts.LocalPath),
)

if err != nil {
return err
}

return nil
}
Loading
Loading