ソースを参照

implement publish

Signed-off-by: Nicolas De Loof <[email protected]>
Nicolas De Loof 2 年 前
コミット
5ca35c88be
2 ファイル変更121 行追加4 行削除
  1. 115 3
      pkg/compose/publish.go
  2. 6 1
      pkg/remote/oci.go

+ 115 - 3
pkg/compose/publish.go

@@ -18,24 +18,136 @@ package compose
 
 import (
 	"context"
+	"encoding/json"
+	"os"
 
 	"github.com/compose-spec/compose-go/types"
 	"github.com/distribution/reference"
+	"github.com/docker/buildx/util/imagetools"
 	"github.com/docker/compose/v2/pkg/api"
+	"github.com/docker/compose/v2/pkg/progress"
+	"github.com/opencontainers/go-digest"
+	"github.com/opencontainers/image-spec/specs-go"
+	v1 "github.com/opencontainers/image-spec/specs-go/v1"
 )
 
 func (s *composeService) Publish(ctx context.Context, project *types.Project, repository string, options api.PublishOptions) error {
+	return progress.RunWithTitle(ctx, func(ctx context.Context) error {
+		return s.publish(ctx, project, repository, options)
+	}, s.stdinfo(), "Publishing")
+}
+
+func (s *composeService) publish(ctx context.Context, project *types.Project, repository string, options api.PublishOptions) error {
 	err := s.Push(ctx, project, api.PushOptions{})
 	if err != nil {
 		return err
 	}
 
-	_, err = reference.ParseDockerRef(repository)
+	w := progress.ContextWriter(ctx)
+
+	named, err := reference.ParseDockerRef(repository)
+	if err != nil {
+		return err
+	}
+
+	resolver := imagetools.New(imagetools.Opt{
+		Auth: s.configFile(),
+	})
+
+	var layers []v1.Descriptor
+	for _, file := range project.ComposeFiles {
+		f, err := os.ReadFile(file)
+		if err != nil {
+			return err
+		}
+
+		w.Event(progress.Event{
+			ID:     file,
+			Text:   "publishing",
+			Status: progress.Working,
+		})
+		layer := v1.Descriptor{
+			MediaType: "application/vnd.docker.compose.file+yaml",
+			Digest:    digest.FromString(string(f)),
+			Size:      int64(len(f)),
+			Annotations: map[string]string{
+				"com.docker.compose": api.ComposeVersion,
+			},
+		}
+		layers = append(layers, layer)
+		err = resolver.Push(ctx, named, layer, f)
+		if err != nil {
+			w.Event(progress.Event{
+				ID:     file,
+				Text:   "publishing",
+				Status: progress.Error,
+			})
+
+			return err
+		}
+
+		w.Event(progress.Event{
+			ID:     file,
+			Text:   "published",
+			Status: progress.Done,
+		})
+	}
+
+	emptyConfig, err := json.Marshal(v1.ImageConfig{})
+	if err != nil {
+		return err
+	}
+	configDescriptor := v1.Descriptor{
+		MediaType: "application/vnd.docker.compose.project",
+		Digest:    digest.FromBytes(emptyConfig),
+		Size:      int64(len(emptyConfig)),
+		Annotations: map[string]string{
+			"com.docker.compose.version": api.ComposeVersion,
+		},
+	}
+	err = resolver.Push(ctx, named, configDescriptor, emptyConfig)
+	if err != nil {
+		return err
+	}
+
+	imageManifest, err := json.Marshal(v1.Manifest{
+		Versioned:    specs.Versioned{SchemaVersion: 2},
+		MediaType:    v1.MediaTypeImageManifest,
+		ArtifactType: "application/vnd.docker.compose.project",
+		Config:       configDescriptor,
+		Layers:       layers,
+	})
 	if err != nil {
 		return err
 	}
 
-	// TODO publish project.ComposeFiles
+	w.Event(progress.Event{
+		ID:     repository,
+		Text:   "publishing",
+		Status: progress.Working,
+	})
 
-	return api.ErrNotImplemented
+	err = resolver.Push(ctx, named, v1.Descriptor{
+		MediaType: v1.MediaTypeImageManifest,
+		Digest:    digest.FromString(string(imageManifest)),
+		Size:      int64(len(imageManifest)),
+		Annotations: map[string]string{
+			"com.docker.compose.version": api.ComposeVersion,
+		},
+		ArtifactType: "application/vnd.docker.compose.project",
+	}, imageManifest)
+	if err != nil {
+		w.Event(progress.Event{
+			ID:     repository,
+			Text:   "publishing",
+			Status: progress.Error,
+		})
+		return err
+	}
+	w.Event(progress.Event{
+		ID:     repository,
+		Text:   "published",
+		Status: progress.Done,
+	})
+	return nil
 }

+ 6 - 1
pkg/remote/oci.go

@@ -70,7 +70,7 @@ type ociRemoteLoader struct {
 	offline   bool
 }
 
-const prefix = "oci:"
+const prefix = "oci://"
 
 func (g ociRemoteLoader) Accept(path string) bool {
 	return strings.HasPrefix(path, prefix)
@@ -117,6 +117,11 @@ func (g ociRemoteLoader) Load(ctx context.Context, path string) (string, error)
 		if err != nil {
 			return "", err
 		}
+
+		if descriptor.Config.MediaType != "application/vnd.docker.compose.project" {
+			return "", fmt.Errorf("%s is not a compose project OCI artifact, but %s", ref.String(), descriptor.Config.MediaType)
+		}
+
 		for i, layer := range descriptor.Layers {
 			digested, err := reference.WithDigest(ref, layer.Digest)
 			if err != nil {