Skip to content
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

feat: add checking for the exists tag when 'kpm push'. #161

Merged
merged 1 commit into from
Aug 21, 2023
Merged
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
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
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'
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>'
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
Loading