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 metadata into oci manifest when 'kpm push' #205

Merged
merged 3 commits into from
Oct 23, 2023
Merged
Show file tree
Hide file tree
Changes from 2 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: 3 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ go 1.19
require (
github.com/BurntSushi/toml v1.2.1
github.com/docker/distribution v2.8.1+incompatible
github.com/opencontainers/image-spec v1.1.0-rc2.0.20221005185240-3a7f492d3f1b
github.com/opencontainers/image-spec v1.1.0-rc4
github.com/otiai10/copy v1.9.0
github.com/sirupsen/logrus v1.9.0
github.com/urfave/cli/v2 v2.25.0
Expand Down Expand Up @@ -81,7 +81,7 @@ require (
golang.org/x/crypto v0.7.0 // indirect
golang.org/x/mod v0.9.0 // indirect
golang.org/x/net v0.9.0 // indirect
golang.org/x/sync v0.1.0 // indirect
golang.org/x/sync v0.3.0 // indirect
golang.org/x/sys v0.7.0 // indirect
golang.org/x/text v0.9.0 // indirect
golang.org/x/tools v0.7.0 // indirect
Expand All @@ -108,5 +108,5 @@ require (
github.com/thoas/go-funk v0.9.3
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
oras.land/oras-go v1.2.3
oras.land/oras-go/v2 v2.0.2
oras.land/oras-go/v2 v2.3.0
)
12 changes: 6 additions & 6 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -311,8 +311,8 @@ github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE=
github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.1.0-rc2.0.20221005185240-3a7f492d3f1b h1:YWuSjZCQAPM8UUBLkYUk1e+rZcvWHJmFb6i6rM44Xs8=
github.com/opencontainers/image-spec v1.1.0-rc2.0.20221005185240-3a7f492d3f1b/go.mod h1:3OVijpioIKYWTqjiG0zfF6wvoJ4fAXGbjdZuI2NgsRQ=
github.com/opencontainers/image-spec v1.1.0-rc4 h1:oOxKUJWnFC4YGHCCMNql1x4YaDfYBTS5Y4x/Cgeo1E0=
github.com/opencontainers/image-spec v1.1.0-rc4/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8=
github.com/otiai10/copy v1.9.0 h1:7KFNiCgZ91Ru4qW4CWPf/7jqtxLagGRmIxWldPP9VY4=
github.com/otiai10/copy v1.9.0/go.mod h1:hsfX19wcn0UWIHUQ3/4fHuehhk2UyArQ9dVFAn3FczI=
github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE=
Expand Down Expand Up @@ -522,8 +522,8 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
Expand Down Expand Up @@ -769,8 +769,8 @@ kcl-lang.io/kcl-go v0.6.0-alpha.1 h1:BhMQ2GNRp9AccWxB2QydvZF6g6iVlPkkSSPa+RAdYx0
kcl-lang.io/kcl-go v0.6.0-alpha.1/go.mod h1:tHMTzp0dtah8Hn6h6UfnwbrEv737+TKf0xjIJkUkuaU=
oras.land/oras-go v1.2.3 h1:v8PJl+gEAntI1pJ/LCrDgsuk+1PKVavVEPsYIHFE5uY=
oras.land/oras-go v1.2.3/go.mod h1:M/uaPdYklze0Vf3AakfarnpoEckvw0ESbRdN8Z1vdJg=
oras.land/oras-go/v2 v2.0.2 h1:3aSQdJ7EUC0ft2e9PjJB9Jzastz5ojPA4LzZ3Q4YbUc=
oras.land/oras-go/v2 v2.0.2/go.mod h1:PWnWc/Kyyg7wUTUsDHshrsJkzuxXzreeMd6NrfdnFSo=
oras.land/oras-go/v2 v2.3.0 h1:lqX1aXdN+DAmDTKjiDyvq85cIaI4RkIKp/PghWlAGIU=
oras.land/oras-go/v2 v2.3.0/go.mod h1:GeAwLuC4G/JpNwkd+bSZ6SkDMGaaYglt6YK2WvZP7uQ=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
Expand Down
4 changes: 3 additions & 1 deletion pkg/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -832,7 +832,9 @@ func (c *KpmClient) PushToOci(localPath string, ociOpts *opt.OciOptions) error {
)
}

return ociCli.Push(localPath, ociOpts.Tag)
return ociCli.PushWithOciManifest(localPath, ociOpts.Tag, &opt.OciManifestOptions{
Annotations: ociOpts.Annotations,
})
}

// LoginOci will login to the oci registry.
Expand Down
2 changes: 1 addition & 1 deletion pkg/cmd/cmd_push.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,13 +167,13 @@ func pushPackage(ociUrl string, kclPkg *pkg.KclPkg, vendorMode bool, kpmcli *cli
"only support url scheme 'oci://'.",
)
}
ociOpts.Annotations = oci.GenOciManifestFromPkg(kclPkg)

reporter.ReportMsgTo(fmt.Sprintf("kpm: package '%s' will be pushed", kclPkg.GetPkgName()), kpmcli.GetLogWriter())
// 4. Push it.
err = kpmcli.PushToOci(tarPath, ociOpts)
if err != (*reporter.KpmEvent)(nil) {
return err
}

return nil
}
30 changes: 17 additions & 13 deletions pkg/constants/constants.go
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
package constants

const (
KFilePathSuffix = ".k"
TarPathSuffix = ".tar"
OciScheme = "oci"
FileEntry = "file"
FileWithKclModEntry = "file_with_kcl_mod"
UrlEntry = "url"
RefEntry = "ref"
TarEntry = "tar"
KCL_MOD = "kcl.mod"
OCI_SEPARATOR = ":"
KCL_PKG_TAR = "*.tar"
DEFAULT_KCL_FILE_NAME = "main.k"
DEFAULT_KCL_FILE_CONTENT = "The_first_kcl_program = 'Hello World!'"
KFilePathSuffix = ".k"
TarPathSuffix = ".tar"
OciScheme = "oci"
FileEntry = "file"
FileWithKclModEntry = "file_with_kcl_mod"
UrlEntry = "url"
RefEntry = "ref"
TarEntry = "tar"
KCL_MOD = "kcl.mod"
OCI_SEPARATOR = ":"
KCL_PKG_TAR = "*.tar"
DEFAULT_KCL_FILE_NAME = "main.k"
DEFAULT_KCL_FILE_CONTENT = "The_first_kcl_program = 'Hello World!'"
DEFAULT_KCL_OCI_MANIFEST_NAME = "org.kcllang.package.name"
DEFAULT_KCL_OCI_MANIFEST_VERSION = "org.kcllang.package.version"
DEFAULT_KCL_OCI_MANIFEST_DESCRIPTION = "org.kcllang.package.description"
DEFAULT_CREATE_OCI_MANIFEST_TIME = "org.opencontainers.image.created"
)
27 changes: 23 additions & 4 deletions pkg/oci/oci.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ import (

v1 "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/thoas/go-funk"
"kcl-lang.io/kpm/pkg/constants"
"kcl-lang.io/kpm/pkg/errors"
"kcl-lang.io/kpm/pkg/opt"
pkg "kcl-lang.io/kpm/pkg/package"
"kcl-lang.io/kpm/pkg/reporter"
"kcl-lang.io/kpm/pkg/semver"
"kcl-lang.io/kpm/pkg/settings"
Expand Down Expand Up @@ -199,6 +202,11 @@ func (ociClient *OciClient) ContainsTag(tag string) (bool, *reporter.KpmEvent) {

// Push will push the oci artifacts to oci registry from local path
func (ociClient *OciClient) Push(localPath, tag string) *reporter.KpmEvent {
return ociClient.PushWithOciManifest(localPath, tag, &opt.OciManifestOptions{})
}

// PushWithManifest will push the oci artifacts to oci registry from local path
func (ociClient *OciClient) PushWithOciManifest(localPath, tag string, manifest_opts *opt.OciManifestOptions) *reporter.KpmEvent {
Peefy marked this conversation as resolved.
Show resolved Hide resolved
// 0. Create a file store
fs, err := file.New(filepath.Dir(localPath))
if err != nil {
Expand All @@ -222,10 +230,13 @@ func (ociClient *OciClient) Push(localPath, tag string) *reporter.KpmEvent {
fileDescriptors = append(fileDescriptors, fileDescriptor)
}

// 2. Pack the files and tag the packed manifest
manifestDescriptor, err := oras.Pack(*ociClient.ctx, fs, DEFAULT_OCI_ARTIFACT_TYPE, fileDescriptors, oras.PackOptions{
PackImageManifest: true,
})
// 2. Pack the files, tag the packed manifest and add metadata as annotations
packOpts := oras.PackManifestOptions{
ManifestAnnotations: manifest_opts.Annotations,
Layers: fileDescriptors,
}
manifestDescriptor, err := oras.PackManifest(*ociClient.ctx, fs, oras.PackManifestVersion1_1_RC4, DEFAULT_OCI_ARTIFACT_TYPE, packOpts)

if err != nil {
return reporter.NewErrorEvent(reporter.FailedPush, err, fmt.Sprintf("failed to pack package in '%s'", localPath))
}
Expand Down Expand Up @@ -315,3 +326,11 @@ func Push(localPath, hostName, repoName, tag string, settings *settings.Settings
// Push the oci package by the oci client.
return ociClient.Push(localPath, tag)
}

func GenOciManifestFromPkg(kclPkg *pkg.KclPkg) map[string]string {
res := make(map[string]string)
res[constants.DEFAULT_KCL_OCI_MANIFEST_NAME] = kclPkg.GetPkgName()
res[constants.DEFAULT_KCL_OCI_MANIFEST_VERSION] = kclPkg.GetPkgVersion()
res[constants.DEFAULT_KCL_OCI_MANIFEST_DESCRIPTION] = kclPkg.GetPkgDescription()
return res
}
13 changes: 9 additions & 4 deletions pkg/opt/opt.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,10 +142,11 @@ func (opts *GitOptions) Validate() error {
// OciOptions for download oci packages.
// kpm will download packages from oci registry by '{Reg}/{Repo}/{PkgName}:{Tag}'.
type OciOptions struct {
Reg string
Repo string
Tag string
PkgName string
Reg string
Repo string
Tag string
PkgName string
Annotations map[string]string
}

func (opts *OciOptions) Validate() error {
Expand Down Expand Up @@ -213,3 +214,7 @@ func ParseOciUrl(ociUrl string) (*OciOptions, *reporter.KpmEvent) {
func (oci *OciOptions) AddStoragePathSuffix(pathPrefix string) string {
return filepath.Join(filepath.Join(filepath.Join(pathPrefix, oci.Reg), oci.Repo), oci.Tag)
}

type OciManifestOptions struct {
Annotations map[string]string
}
7 changes: 4 additions & 3 deletions pkg/package/modfile.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,10 @@ const (

// 'Package' is the kcl package section of 'kcl.mod'.
type Package struct {
Name string `toml:"name,omitempty"` // kcl package name
Edition string `toml:"edition,omitempty"` // kcl compiler version
Version string `toml:"version,omitempty"` // kcl package version
Name string `toml:"name,omitempty"` // kcl package name
Edition string `toml:"edition,omitempty"` // kcl compiler version
Version string `toml:"version,omitempty"` // kcl package version
Description string `toml:"description,omitempty"` // kcl package description
}

// 'ModFile' is kcl package file 'kcl.mod'.
Expand Down
14 changes: 14 additions & 0 deletions pkg/package/modfile_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,20 @@ import (
"kcl-lang.io/kpm/pkg/utils"
)

func TestModFileWithDesc(t *testing.T) {
testPath := getTestDir("test_mod_with_desc")
isExist, err := ModFileExists(testPath)
assert.Equal(t, isExist, true)
assert.Equal(t, err, nil)
modFile, err := LoadModFile(testPath)
assert.Equal(t, modFile.Pkg.Name, "test_mod_with_desc")
assert.Equal(t, modFile.Pkg.Version, "0.0.1")
assert.Equal(t, modFile.Pkg.Edition, "0.0.1")
assert.Equal(t, modFile.Pkg.Description, "This is a test module with a description")
assert.Equal(t, len(modFile.Dependencies.Deps), 0)
assert.Equal(t, err, nil)
}

func TestModFileExists(t *testing.T) {
testDir := initTestDir("test_data_modfile")
// there is no 'kcl.mod' and 'kcl.mod.lock'.
Expand Down
10 changes: 10 additions & 0 deletions pkg/package/package.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,3 +184,13 @@ const LOCK_FILE_NAME = "kcl.mod.lock"
func (kclPkg *KclPkg) GetLockFilePath() string {
return filepath.Join(kclPkg.HomePath, LOCK_FILE_NAME)
}

// GetPkgVersion returns the version of package.
func (KclPkg *KclPkg) GetPkgVersion() string {
return KclPkg.ModFile.Pkg.Version
}

// GetPkgDescription returns the description of package.
func (KclPkg *KclPkg) GetPkgDescription() string {
return KclPkg.ModFile.Pkg.Description
}
5 changes: 5 additions & 0 deletions pkg/package/test_data/test_mod_with_desc/kcl.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[package]
name = "test_mod_with_desc"
edition = "0.0.1"
version = "0.0.1"
description = "This is a test module with a description"
Empty file.
1 change: 1 addition & 0 deletions pkg/package/test_data/test_mod_with_desc/main.k
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
The_first_kcl_program = 'Hello World!'
5 changes: 5 additions & 0 deletions pkg/package/toml.go
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,7 @@ func (mod *ModFile) UnmarshalTOML(data interface{}) error {
const NAME_FLAG = "name"
const EDITION_FLAG = "edition"
const VERSION_FLAG = "version"
const DESCRIPTION_FLAG = "description"

func (pkg *Package) UnmarshalTOML(data interface{}) error {
meta, ok := data.(map[string]interface{})
Expand All @@ -228,6 +229,10 @@ func (pkg *Package) UnmarshalTOML(data interface{}) error {
if v, ok := meta[VERSION_FLAG].(string); ok {
pkg.Version = v
}

if v, ok := meta[DESCRIPTION_FLAG].(string); ok {
pkg.Description = v
}
return nil
}

Expand Down
72 changes: 72 additions & 0 deletions test/e2e/kpm_test.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
package e2e

import (
"context"
"encoding/json"
"os"
"path/filepath"
"strings"

"github.com/onsi/ginkgo/v2"
"github.com/onsi/gomega"
v1 "github.com/opencontainers/image-spec/specs-go/v1"
"kcl-lang.io/kpm/pkg/constants"
"oras.land/oras-go/v2"
"oras.land/oras-go/v2/registry/remote"
"oras.land/oras-go/v2/registry/remote/auth"
"oras.land/oras-go/v2/registry/remote/retry"
)

var _ = ginkgo.Describe("Kpm CLI Testing", func() {
Expand Down Expand Up @@ -138,4 +146,68 @@ var _ = ginkgo.Describe("Kpm CLI Testing", func() {
})
}
})

ginkgo.Context("testing 'test oci '", func() {
testSuitesRoot := filepath.Join(filepath.Join(filepath.Join(GetWorkDir(), TEST_SUITES_DIR), "kpm"), "test_oci")
testSuites := LoadAllTestSuites(testSuitesRoot)
testDataRoot := filepath.Join(filepath.Join(GetWorkDir(), TEST_SUITES_DIR), "test_data")
for _, ts := range testSuites {
ts := ts
ginkgo.It(ts.GetTestSuiteInfo(), func() {
// 1. Push a package with metadata in OCI manifest
workspace := GetWorkspace()

CopyDir(filepath.Join(testDataRoot, ts.Name), filepath.Join(workspace, ts.Name))

input := ReplaceAllKeyByValue(ts.Input, "<workspace>", filepath.Join(workspace, ts.Name))

stdout, stderr, err := ExecKpmWithWorkDir(input, filepath.Join(workspace, ts.Name))

expectedStdout := ReplaceAllKeyByValue(ts.ExpectStdout, "<workspace>", workspace)
expectedStderr := ReplaceAllKeyByValue(ts.ExpectStderr, "<workspace>", workspace)

gomega.Expect(err).ShouldNot(gomega.HaveOccurred())
if !IsIgnore(expectedStdout) {
gomega.Expect(stdout).To(gomega.ContainSubstring(expectedStdout))
}
if !IsIgnore(expectedStderr) {
gomega.Expect(stderr).To(gomega.ContainSubstring(expectedStderr))
}

bytes, err := os.ReadFile(filepath.Join(testDataRoot, ts.Name, "expected_oci_manifest.json"))
gomega.Expect(err).ShouldNot(gomega.HaveOccurred())

// 2. fetch the metadata in OCI manifest to check if the metadata is correct
repo, err := remote.NewRepository("localhost:5001/test/test_push_with_oci_manifest")
gomega.Expect(err).ShouldNot(gomega.HaveOccurred())
repo.PlainHTTP = true
repo.Client = &auth.Client{
Client: retry.DefaultClient,
Cache: auth.DefaultCache,
Credential: auth.StaticCredential("localhost:5001", auth.Credential{
Username: "test",
Password: "1234",
}),
}

gomega.Expect(err).ShouldNot(gomega.HaveOccurred())
_, manifestContent, err := oras.FetchBytes(context.Background(), repo, "0.0.1", oras.DefaultFetchBytesOptions)
gomega.Expect(err).ShouldNot(gomega.HaveOccurred())
var manifest_got v1.Manifest
err = json.Unmarshal([]byte(manifestContent), &manifest_got)
gomega.Expect(err).ShouldNot(gomega.HaveOccurred())

var manifest_expect v1.Manifest
err = json.Unmarshal(bytes, &manifest_expect)
gomega.Expect(err).ShouldNot(gomega.HaveOccurred())
gomega.Expect(len(manifest_expect.Annotations)).To(gomega.Equal(len(manifest_got.Annotations)))
gomega.Expect(manifest_expect.Annotations[constants.DEFAULT_KCL_OCI_MANIFEST_NAME]).
To(gomega.Equal(manifest_got.Annotations[constants.DEFAULT_KCL_OCI_MANIFEST_NAME]))
gomega.Expect(manifest_expect.Annotations[constants.DEFAULT_KCL_OCI_MANIFEST_VERSION]).
To(gomega.Equal(manifest_got.Annotations[constants.DEFAULT_KCL_OCI_MANIFEST_VERSION]))
gomega.Expect(manifest_expect.Annotations[constants.DEFAULT_KCL_OCI_MANIFEST_DESCRIPTION]).
To(gomega.Equal(manifest_got.Annotations[constants.DEFAULT_KCL_OCI_MANIFEST_DESCRIPTION]))
})
}
})
})
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,3 @@
kpm: package 'test_push_with_oci_manifest' will be pushed
kpm: pushed [registry] localhost:5001/test/test_push_with_oci_manifest
kpm: digest: sha256:
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"schemaVersion":2,"mediaType":"application/vnd.oci.image.manifest.v1+json","artifactType":"application/vnd.oci.image.layer.v1.tar","config":{"mediaType":"application/vnd.oci.empty.v1+json","digest":"sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a","size":2,"data":"e30="},"layers":[{"mediaType":"application/vnd.oci.image.layer.v1.tar","digest":"sha256:1d0b4bf7380ee67c23d48a16ebfbcc0720e9d0c59a1fe0f603e452ee8e99068f","size":4608,"annotations":{"org.opencontainers.image.title":"test_push_with_oci_manifest-0.0.1.tar"}}],"annotations":{"org.kcllang.package.description":"This is the description of the package","org.kcllang.package.name":"test_push_with_oci_manifest","org.kcllang.package.version":"0.0.1","org.opencontainers.image.created":"2023-10-23T06:24:49Z"}}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[package]
name = "test_push_with_oci_manifest"
edition = "0.0.1"
version = "0.0.1"
description = "This is the description of the package"

Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
The_first_kcl_program = 'Hello World!'
Loading