Skip to content
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
1 change: 0 additions & 1 deletion cmd/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@ downloads; use --no-cache to force re-download. Any flags provided via

# Execute target from remote Makefile artifact, bypassing cache
remake run -f ghcr.io/myorg/myrepo:latest --no-cache deploy`,
Args: cobra.MinimumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
app.Cfg.NoCache = noCache
return app.Run(context.Background(), file, makeFlags, args)
Expand Down
4 changes: 2 additions & 2 deletions internal/cache/oci_cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ func (c *OCIRepository) Push(ctx context.Context, reference string, data []byte)
if strings.Contains(reference, "://") && !strings.HasPrefix(reference, "oci://") {
return fmt.Errorf("invalid OCI reference: %s", reference)
}
raw := strings.TrimPrefix(reference, "oci://")
raw := strings.ToLower(strings.TrimPrefix(reference, "oci://"))
ref, err := parseRef(raw, name.WithDefaultRegistry(c.cfg.DefaultRegistry))
if err != nil {
return err
Expand Down Expand Up @@ -115,7 +115,7 @@ func (c *OCIRepository) Pull(ctx context.Context, reference string) (string, err
if strings.Contains(reference, "://") && !strings.HasPrefix(reference, "oci://") {
return "", fmt.Errorf("invalid OCI reference: %s", reference)
}
raw := strings.TrimPrefix(reference, "oci://")
raw := strings.ToLower(strings.TrimPrefix(reference, "oci://"))

ref, err := name.ParseReference(raw, name.WithDefaultRegistry(c.cfg.DefaultRegistry))
if err != nil {
Expand Down
18 changes: 18 additions & 0 deletions internal/client/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -537,6 +537,24 @@ func TestOCIClientPushFileStoreCloseError(t *testing.T) {
}
}

func TestOCIClientPushAbsPathError(t *testing.T) {
// Stub absPathFunc to simulate failure
origAbs := absPathFunc
defer func() { absPathFunc = origAbs }()
absPathFunc = func(path string) (string, error) {
return "", fmt.Errorf("abs error")
}

cfg := &config.Config{DefaultRegistry: "example.com"}
client := NewOCIClient(cfg)

// Call Push: path value doesn't matter, stub will error first
err := client.Push(context.Background(), "oci://example.com/repo:tag", "somepath")
if err == nil || !strings.Contains(err.Error(), "failed to resolve absolute path somepath: abs error") {
t.Errorf("expected abs path error, got %v", err)
}
}

func TestOCIClientPushPackManifestError(t *testing.T) {
tmpFile, err := os.CreateTemp("", "test*.txt")
if err != nil {
Expand Down
25 changes: 20 additions & 5 deletions internal/client/oci_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ var (
packManifest = oras.PackManifest
copyFunc = oras.Copy
contentFetcher = content.FetchAll
absPathFunc = filepath.Abs
)

// OCIClient provides an implementation of Client for OCI registries.
Expand Down Expand Up @@ -87,19 +88,23 @@ func (c *OCIClient) Login(ctx context.Context, registry, user, pass string) erro
// Push uploads the local file at path as an OCI artifact to the given reference.
// It tags the artifact with the reference identifier and pushes it to the remote repository.
func (c *OCIClient) Push(ctx context.Context, reference, path string) error {
// Validate and parse reference
if strings.Contains(reference, "://") && !strings.HasPrefix(reference, "oci://") {
return fmt.Errorf("invalid OCI reference: %s", reference)
}
raw := strings.TrimPrefix(reference, "oci://")
raw := strings.ToLower(strings.TrimPrefix(reference, "oci://"))
ref, err := name.ParseReference(raw, name.WithDefaultRegistry(c.cfg.DefaultRegistry))
if err != nil {
return err
}
repoRef := ref.Context()

repo, err := newRepository(repoRef.RegistryStr() + "/" + repoRef.RepositoryStr())
if err != nil {
return err
}

// Authenticate if credentials present
key := config.NormalizeKey(repoRef.RegistryStr())
user := viper.GetString("registries." + key + ".username")
pass := viper.GetString("registries." + key + ".password")
Expand All @@ -111,19 +116,28 @@ func (c *OCIClient) Push(ctx context.Context, reference, path string) error {
}
}

dir := filepath.Dir(path)
// Resolve absolute path and split directory
absPath, err := absPathFunc(path)
if err != nil {
return fmt.Errorf("failed to resolve absolute path %s: %w", path, err)
}
dir := filepath.Dir(absPath)

// Prepare a file store rooted at the file's directory
fs, err := newFileStore(dir)
if err != nil {
return err
return fmt.Errorf("creating file store: %w", err)
}
defer func() { _ = fs.Close() }()

// Add the file using its absolute path to ensure tests find it
mediaType := "application/vnd.remake.file"
fileDesc, err := fs.Add(ctx, path, mediaType, "")
fileDesc, err := fs.Add(ctx, absPath, mediaType, "")
if err != nil {
return fmt.Errorf("adding file to store: %w", err)
}

// Pack manifest using injected function
artifactType := "application/vnd.remake.artifact"
opts := oras.PackManifestOptions{Layers: []v1.Descriptor{fileDesc}}
manifestDesc, err := packManifest(ctx, fs, oras.PackManifestVersion1_1, artifactType, opts)
Expand All @@ -137,6 +151,7 @@ func (c *OCIClient) Push(ctx context.Context, reference, path string) error {
tag := ref.Identifier()
_ = fs.Tag(ctx, manifestDesc, tag)

// Push to remote using injected function
if _, err := copyFunc(ctx, fs, tag, repo, tag, oras.DefaultCopyOptions); err != nil {
return fmt.Errorf("pushing to remote: %w", err)
}
Expand All @@ -149,7 +164,7 @@ func (c *OCIClient) Pull(ctx context.Context, reference string) ([]byte, error)
if strings.Contains(reference, "://") && !strings.HasPrefix(reference, "oci://") {
return nil, fmt.Errorf("invalid OCI reference: %s", reference)
}
raw := strings.TrimPrefix(reference, "oci://")
raw := strings.ToLower(strings.TrimPrefix(reference, "oci://"))
ref, err := name.ParseReference(raw, name.WithDefaultRegistry(c.cfg.DefaultRegistry))
if err != nil {
return nil, err
Expand Down