Skip to content

Commit

Permalink
feat: add checking for the exists tag when 'kpm push'.
Browse files Browse the repository at this point in the history
  • Loading branch information
zong-zhe committed Aug 21, 2023
1 parent 9360824 commit 572a4eb
Show file tree
Hide file tree
Showing 23 changed files with 127 additions and 46 deletions.
2 changes: 1 addition & 1 deletion pkg/api/kpm_run.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ func RunOci(ociRef, version string, entryFiles []string, vendorMode bool, kclArg
// 2. Pull the tar.
err = oci.Pull(localPath, ociOpts.Reg, ociOpts.Repo, ociOpts.Tag)

if err != nil {
if err != (*reporter.KpmEvent)(nil) {
return "", err
}

Expand Down
2 changes: 1 addition & 1 deletion pkg/cmd/cmd_pull.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ func KpmPull(c *cli.Context) error {
// 2. Pull the tar.
err = oci.Pull(localPath, ociOpt.Reg, ociOpt.Repo, ociOpt.Tag)

if err != nil {
if err != (*reporter.KpmEvent)(nil) {
return err
}

Expand Down
45 changes: 25 additions & 20 deletions pkg/cmd/cmd_push.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
package cmd

import (
"fmt"
"net/url"
"os"

Expand Down Expand Up @@ -36,28 +37,31 @@ func NewPushCmd(settings *settings.Settings) *cli.Command {
},
},
Action: func(c *cli.Context) error {
return KpmPush(c, settings)
},
}
}

localTarPath := c.String(FLAG_TAR_PATH)
ociUrl := c.Args().First()

var err error
func KpmPush(c *cli.Context, settings *settings.Settings) error {
localTarPath := c.String(FLAG_TAR_PATH)
ociUrl := c.Args().First()

if len(localTarPath) == 0 {
// If the tar package to be pushed is not specified,
// the current kcl package is packaged into tar and pushed.
err = pushCurrentPackage(ociUrl, c.Bool(FLAG_VENDOR), settings)
} else {
// Else push the tar package specified.
err = pushTarPackage(ociUrl, localTarPath, c.Bool(FLAG_VENDOR), settings)
}
var err error

if err != nil {
return err
}
if len(localTarPath) == 0 {
// If the tar package to be pushed is not specified,
// the current kcl package is packaged into tar and pushed.
err = pushCurrentPackage(ociUrl, c.Bool(FLAG_VENDOR), settings)
} else {
// Else push the tar package specified.
err = pushTarPackage(ociUrl, localTarPath, c.Bool(FLAG_VENDOR), settings)
}

return nil
},
if err != nil {
return err
}

return nil
}

// genDefaultOciUrlForKclPkg will generate the default oci url from the current package.
Expand All @@ -83,13 +87,14 @@ func pushCurrentPackage(ociUrl string, vendorMode bool, settings *settings.Setti
pwd, err := os.Getwd()

if err != nil {
reporter.ExitWithReport("kpm: internal bug: failed to load working directory")
reporter.ReportEventToStderr(reporter.NewEvent(reporter.Bug, "internal bug: failed to load working directory"))
return err
}
// 1. Load the current kcl packege.
kclPkg, err := pkg.LoadKclPkg(pwd)

if err != nil {
reporter.ExitWithReport("kpm: failed to load package in " + pwd + ".")
reporter.ReportEventToStderr(reporter.NewEvent(reporter.FailedLoadKclMod, fmt.Sprintf("failed to load package in '%s'", pwd)))
return err
}

Expand Down Expand Up @@ -168,7 +173,7 @@ func pushPackage(ociUrl string, kclPkg *pkg.KclPkg, vendorMode bool, settings *s
reporter.Report("kpm: package '" + kclPkg.GetPkgName() + "' will be pushed.")
// 4. Push it.
err = oci.Push(tarPath, ociOpts.Reg, ociOpts.Repo, ociOpts.Tag, settings)
if err != nil {
if err != (*reporter.KpmEvent)(nil) {
return err
}

Expand Down
64 changes: 53 additions & 11 deletions pkg/oci/oci.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"path/filepath"

v1 "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/thoas/go-funk"
"kcl-lang.io/kpm/pkg/errors"
"kcl-lang.io/kpm/pkg/reporter"
"kcl-lang.io/kpm/pkg/semver"
Expand All @@ -18,6 +19,7 @@ import (
"oras.land/oras-go/v2"
"oras.land/oras-go/v2/content/file"
"oras.land/oras-go/v2/registry/remote"
"oras.land/oras-go/v2/registry/remote/errcode"
"oras.land/oras-go/v2/registry/remote/retry"
)

Expand Down Expand Up @@ -77,7 +79,7 @@ type OciClient struct {
// NewOciClient will new an OciClient.
// regName is the registry. e.g. ghcr.io or docker.io.
// repoName is the repo name on registry.
func NewOciClient(regName, repoName string) (*OciClient, error) {
func NewOciClient(regName, repoName string) (*OciClient, *reporter.KpmEvent) {
repoPath := utils.JoinPath(regName, repoName)
repo, err := remote.NewRepository(repoPath)

Expand Down Expand Up @@ -118,7 +120,7 @@ func NewOciClient(regName, repoName string) (*OciClient, error) {
}

// Pull will pull the oci artifacts from oci registry to local path.
func (ociClient *OciClient) Pull(localPath, tag string) error {
func (ociClient *OciClient) Pull(localPath, tag string) *reporter.KpmEvent {
// Create a file store
fs, err := file.New(localPath)
if err != nil {
Expand All @@ -140,7 +142,7 @@ func (ociClient *OciClient) Pull(localPath, tag string) error {
}

// TheLatestTag will return the latest tag of the kcl packages.
func (ociClient *OciClient) TheLatestTag() (string, error) {
func (ociClient *OciClient) TheLatestTag() (string, *reporter.KpmEvent) {
var tagSelected string

err := ociClient.repo.Tags(*ociClient.ctx, "", func(tags []string) error {
Expand All @@ -164,12 +166,40 @@ func (ociClient *OciClient) TheLatestTag() (string, error) {
return tagSelected, nil
}

// ContainsTag will check if the tag exists in the repo.
func (ociClient *OciClient) ContainsTag(tag string) (bool, *reporter.KpmEvent) {
var exists bool

err := ociClient.repo.Tags(*ociClient.ctx, "", func(tags []string) error {
exists = funk.ContainsString(tags, tag)
return nil
})

if err != nil {
// If the repo with tag is not found, return false.
errRes, ok := err.(*errcode.ErrorResponse)
if ok {
if len(errRes.Errors) == 1 && errRes.Errors[0].Code == errcode.ErrorCodeNameUnknown {
return false, nil
}
}
// If the user not login, return error.
return false, reporter.NewErrorEvent(
reporter.FailedGetPackageVersions,
err,
fmt.Sprintf("failed to access '%s'", ociClient.repo.Reference.String()),
)
}

return exists, nil
}

// Push will push the oci artifacts to oci registry from local path
func (ociClient *OciClient) Push(localPath, tag string) error {
func (ociClient *OciClient) Push(localPath, tag string) *reporter.KpmEvent {
// 0. Create a file store
fs, err := file.New(filepath.Dir(localPath))
if err != nil {
return err
return reporter.NewErrorEvent(reporter.FailedPush, err, "Failed to load store path ", localPath)
}
defer fs.Close()

Expand All @@ -184,7 +214,7 @@ func (ociClient *OciClient) Push(localPath, tag string) error {
// and a file path is created for each download, which is not good.
fileDescriptor, err := fs.Add(*ociClient.ctx, filepath.Base(name), DEFAULT_OCI_ARTIFACT_TYPE, "")
if err != nil {
return err
return reporter.NewErrorEvent(reporter.FailedPush, err, fmt.Sprintf("Failed to add file '%s'", name))
}
fileDescriptors = append(fileDescriptors, fileDescriptor)
}
Expand All @@ -194,18 +224,18 @@ func (ociClient *OciClient) Push(localPath, tag string) error {
PackImageManifest: true,
})
if err != nil {
return err
return reporter.NewErrorEvent(reporter.FailedPush, err, fmt.Sprintf("Failed to pack package in '%s'", localPath))
}

if err = fs.Tag(*ociClient.ctx, manifestDescriptor, tag); err != nil {
return err
return reporter.NewErrorEvent(reporter.FailedPush, err, fmt.Sprintf("Failed to tag package with tag '%s'", tag))
}

// 3. Copy from the file store to the remote repository
desc, err := oras.Copy(*ociClient.ctx, fs, tag, ociClient.repo, tag, oras.DefaultCopyOptions)

if err != nil {
return err
return reporter.NewErrorEvent(reporter.FailedPush, err, fmt.Sprintf("Failed to push '%s'", ociClient.repo.Reference))
}

reporter.Report("kpm: pushed [registry]", ociClient.repo.Reference)
Expand All @@ -231,7 +261,7 @@ func loadCredential(hostName string, settings *settings.Settings) (*remoteauth.C
}

// Pull will pull the oci artifacts from oci registry to local path.
func Pull(localPath, hostName, repoName, tag string) error {
func Pull(localPath, hostName, repoName, tag string) *reporter.KpmEvent {
ociClient, err := NewOciClient(hostName, repoName)
if err != nil {
return err
Expand Down Expand Up @@ -260,13 +290,25 @@ func Pull(localPath, hostName, repoName, tag string) error {
}

// Push will push the oci artifacts to oci registry from local path
func Push(localPath, hostName, repoName, tag string, settings *settings.Settings) error {
func Push(localPath, hostName, repoName, tag string, settings *settings.Settings) *reporter.KpmEvent {
// Create an oci client.
ociClient, err := NewOciClient(hostName, repoName)
if err != nil {
return err
}

exist, err := ociClient.ContainsTag(tag)
if err != (*reporter.KpmEvent)(nil) {
return err
}

if exist {
return reporter.NewErrorEvent(
reporter.PkgTagExists,
fmt.Errorf("package version '%s' already exists", tag),
)
}

// Push the oci package by the oci client.
return ociClient.Push(localPath, tag)
}
23 changes: 13 additions & 10 deletions pkg/package/modfile.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (

"github.com/BurntSushi/toml"
"kcl-lang.io/kcl-go/pkg/kcl"
"kcl-lang.io/kpm/pkg/errors"
"kcl-lang.io/kpm/pkg/git"
"kcl-lang.io/kpm/pkg/oci"
"kcl-lang.io/kpm/pkg/opt"
Expand Down Expand Up @@ -233,10 +232,14 @@ func (dep *Oci) Download(localPath string) (string, error) {
return "", err
}

matches, err := filepath.Glob(filepath.Join(localPath, "*.tar"))
if err != nil || len(matches) != 1 {
if err == nil {
err = errors.InvalidPkg
matches, finderr := filepath.Glob(filepath.Join(localPath, "*.tar"))
if finderr != nil || len(matches) != 1 {
if finderr == nil {
err = reporter.NewErrorEvent(
reporter.InvalidKclPkg,
err,
fmt.Sprintf("failed to find the kcl package tar from '%s'.", localPath),
)
}

return "", reporter.NewErrorEvent(
Expand All @@ -247,19 +250,19 @@ func (dep *Oci) Download(localPath string) (string, error) {
}

tarPath := matches[0]
err = utils.UnTarDir(tarPath, localPath)
if err != nil {
untarErr := utils.UnTarDir(tarPath, localPath)
if untarErr != nil {
return "", reporter.NewErrorEvent(
reporter.FailedUntarKclPkg,
err,
untarErr,
fmt.Sprintf("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) {
err = os.Remove(tarPath)
if err != nil {
rmErr := os.Remove(tarPath)
if rmErr != nil {
return "", reporter.NewErrorEvent(
reporter.FailedUntarKclPkg,
err,
Expand Down
10 changes: 9 additions & 1 deletion pkg/reporter/reporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,9 @@ const (
FailedLoadCredential
FailedCreateOciClient
FailedSelectLatestVersion
FailedGetPackageVersions
FailedCreateStorePath
FailedPush
FailedGetPkg
FailedAccessPkgPath
UnKnownPullWhat
Expand Down Expand Up @@ -88,6 +90,7 @@ const (
LocalPathNotExist
PathIsEmpty
AddItselfAsDep
PkgTagExists
)

// KpmEvent is the event used to show kpm logs to users.
Expand Down Expand Up @@ -147,11 +150,16 @@ func NewEvent(errType EventType, args ...string) *KpmEvent {
}
}

// ReportEvent reports the event to users to stdout.
// ReportEventToStdout reports the event to users to stdout.
func ReportEventToStdout(event *KpmEvent) {
fmt.Fprintf(os.Stdout, "%v", event.Event())
}

// ReportEventToStderr reports the event to users to stderr.
func ReportEventToStderr(event *KpmEvent) {
fmt.Fprintf(os.Stderr, "%v", event.Event())
}

// ReportEvent reports the event to users to stdout.
func ReportEventTo(event *KpmEvent, w io.Writer) {
fmt.Fprintf(w, "%v", event.Event())
Expand Down
3 changes: 3 additions & 0 deletions scripts/reg.sh
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,6 @@ docker run -p 5001:5000 \
-e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" \
-e "REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd" \
-d registry

# clean the registry
docker exec registry rm -rf /var/lib/registry/docker/registry/v2/repositories/
4 changes: 2 additions & 2 deletions test/e2e/kpm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,10 +103,10 @@ var _ = ginkgo.Describe("Kpm CLI Testing", func() {

gomega.Expect(err).ShouldNot(gomega.HaveOccurred())
if !IsIgnore(expectedStdout) {
gomega.Expect(stdout).To(gomega.Equal(expectedStdout))
gomega.Expect(stdout).To(gomega.ContainSubstring(expectedStdout))
}
if !IsIgnore(expectedStderr) {
gomega.Expect(stderr).To(gomega.Equal(expectedStderr))
gomega.Expect(stderr).To(gomega.ContainSubstring(expectedStderr))
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
KPM_HOME=""
KCLVM_VENDOR_HOME=""
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
kpm push
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
kpm: package 'kcl1' will be pushed.
kpm: package version '0.0.1' already exists
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
KPM_HOME=""
KCLVM_VENDOR_HOME=""
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
kpm push oci://not_exist_reg/not_exist_repo
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
kpm: package 'kcl1' will be pushed.
kpm: failed to access 'not_exist_reg/not_exist_repo'
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
KPM_HOME=""
KCLVM_VENDOR_HOME=""
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
kpm push
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
kpm: failed to load package in '<workspace>'
Empty file.
5 changes: 5 additions & 0 deletions test_push/kcl.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[package]
name = "test_push"
edition = "0.0.1"
version = "0.0.1"

Empty file added test_push/kcl.mod.lock
Empty file.
Loading

0 comments on commit 572a4eb

Please sign in to comment.