github.com/Racer159/jackal@v0.32.7-0.20240401174413-0bd2339e4f2e/src/pkg/zoci/push.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // SPDX-FileCopyrightText: 2021-Present The Jackal Authors
     3  
     4  // Package zoci contains functions for interacting with Jackal packages stored in OCI registries.
     5  package zoci
     6  
     7  import (
     8  	"context"
     9  	"fmt"
    10  
    11  	"github.com/Racer159/jackal/src/pkg/layout"
    12  	"github.com/Racer159/jackal/src/pkg/message"
    13  	"github.com/Racer159/jackal/src/types"
    14  	"github.com/defenseunicorns/pkg/helpers"
    15  	"github.com/defenseunicorns/pkg/oci"
    16  	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
    17  	"oras.land/oras-go/v2"
    18  	"oras.land/oras-go/v2/content/file"
    19  )
    20  
    21  // PublishPackage publishes the jackal package to the remote repository.
    22  func (r *Remote) PublishPackage(ctx context.Context, pkg *types.JackalPackage, paths *layout.PackagePaths, concurrency int) error {
    23  	src, err := file.New(paths.Base)
    24  	if err != nil {
    25  		return err
    26  	}
    27  	defer src.Close()
    28  
    29  	r.Log().Info(fmt.Sprintf("Publishing package to %s", r.Repo().Reference))
    30  	spinner := message.NewProgressSpinner("")
    31  	defer spinner.Stop()
    32  
    33  	// Get all of the layers in the package
    34  	var descs []ocispec.Descriptor
    35  	for name, path := range paths.Files() {
    36  		spinner.Updatef("Preparing layer %s", helpers.First30last30(name))
    37  
    38  		mediaType := JackalLayerMediaTypeBlob
    39  
    40  		desc, err := src.Add(ctx, name, mediaType, path)
    41  		if err != nil {
    42  			return err
    43  		}
    44  		descs = append(descs, desc)
    45  	}
    46  	spinner.Successf("Prepared all layers")
    47  
    48  	copyOpts := r.GetDefaultCopyOpts()
    49  	copyOpts.Concurrency = concurrency
    50  	total := oci.SumDescsSize(descs)
    51  
    52  	annotations := annotationsFromMetadata(&pkg.Metadata)
    53  
    54  	// assumes referrers API is not supported since OCI artifact
    55  	// media type is not supported
    56  	r.Repo().SetReferrersCapability(false)
    57  
    58  	// push the manifest config
    59  	manifestConfigDesc, err := r.CreateAndPushManifestConfig(ctx, annotations, JackalConfigMediaType)
    60  	if err != nil {
    61  		return err
    62  	}
    63  	root, err := r.PackAndTagManifest(ctx, src, descs, manifestConfigDesc, annotations)
    64  	if err != nil {
    65  		return err
    66  	}
    67  
    68  	total += manifestConfigDesc.Size
    69  
    70  	progressBar := message.NewProgressBar(total, fmt.Sprintf("Publishing %s:%s", r.Repo().Reference.Repository, r.Repo().Reference.Reference))
    71  	defer progressBar.Stop()
    72  	r.SetProgressWriter(progressBar)
    73  	defer r.ClearProgressWriter()
    74  
    75  	publishedDesc, err := oras.Copy(ctx, src, root.Digest.String(), r.Repo(), "", copyOpts)
    76  	if err != nil {
    77  		return err
    78  	}
    79  
    80  	if err := r.UpdateIndex(ctx, r.Repo().Reference.Reference, publishedDesc); err != nil {
    81  		return err
    82  	}
    83  
    84  	progressBar.Successf("Published %s [%s]", r.Repo().Reference, JackalLayerMediaTypeBlob)
    85  	return nil
    86  }
    87  
    88  func annotationsFromMetadata(metadata *types.JackalMetadata) map[string]string {
    89  	annotations := map[string]string{
    90  		ocispec.AnnotationTitle:       metadata.Name,
    91  		ocispec.AnnotationDescription: metadata.Description,
    92  	}
    93  
    94  	if url := metadata.URL; url != "" {
    95  		annotations[ocispec.AnnotationURL] = url
    96  	}
    97  	if authors := metadata.Authors; authors != "" {
    98  		annotations[ocispec.AnnotationAuthors] = authors
    99  	}
   100  	if documentation := metadata.Documentation; documentation != "" {
   101  		annotations[ocispec.AnnotationDocumentation] = documentation
   102  	}
   103  	if source := metadata.Source; source != "" {
   104  		annotations[ocispec.AnnotationSource] = source
   105  	}
   106  	if vendor := metadata.Vendor; vendor != "" {
   107  		annotations[ocispec.AnnotationVendor] = vendor
   108  	}
   109  
   110  	return annotations
   111  }