diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 19f4ed328b..019f5fdfbe 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -63,6 +63,11 @@ jobs: make release-init-package ARCH=amd64 AGENT_IMAGE_TAG=$GITHUB_REF_NAME make release-init-package ARCH=arm64 AGENT_IMAGE_TAG=$GITHUB_REF_NAME + - name: Publish Init Package as OCI and Skeleton + run: | + make publish-init-package ARCH=amd64 REPOSITORY_URL=ghcr.io/defenseunicorns/packages + make publish-init-package ARCH=arm64 REPOSITORY_URL=ghcr.io/defenseunicorns/packages + # Create a CVE report based on this build - name: Create release time CVE report run: "make cve-report" diff --git a/Makefile b/Makefile index d82bf23151..cd2e099bec 100644 --- a/Makefile +++ b/Makefile @@ -130,6 +130,11 @@ ib-init-package: --set REGISTRY_IMAGE="ironbank/opensource/docker/registry-v2" \ --set REGISTRY_IMAGE_TAG="2.8.2" +# INTERNAL: used to publish the init package +publish-init-package: + $(ZARF_BIN) package publish build/zarf-init-$(ARCH)-$(CLI_VERSION).tar.zst oci://$(REPOSITORY_URL) + $(ZARF_BIN) package publish . oci://$(REPOSITORY_URL) + build-examples: ## Build all of the example packages @test -s $(ZARF_BIN) || $(MAKE) build-cli diff --git a/src/cmd/initialize.go b/src/cmd/initialize.go index 27b0e202ec..dceff5b3b0 100644 --- a/src/cmd/initialize.go +++ b/src/cmd/initialize.go @@ -17,6 +17,7 @@ import ( "github.com/defenseunicorns/zarf/src/config" "github.com/defenseunicorns/zarf/src/config/lang" "github.com/defenseunicorns/zarf/src/pkg/message" + "github.com/defenseunicorns/zarf/src/pkg/oci" "github.com/defenseunicorns/zarf/src/pkg/packager" "github.com/defenseunicorns/zarf/src/pkg/utils" "github.com/defenseunicorns/zarf/src/pkg/utils/helpers" @@ -114,17 +115,17 @@ func downloadInitPackage(downloadCacheTarget string) error { } var confirmDownload bool - url := packager.GetInitPackageRemote("") + url := oci.GetInitPackageURL(config.GetArch(), config.CLIVersion) // Give the user the choice to download the init-package and note that this does require an internet connection - message.Question(fmt.Sprintf(lang.CmdInitDownloadAsk, url)) + message.Question(fmt.Sprintf(lang.CmdInitPullAsk, url)) - message.Note(lang.CmdInitDownloadNote) + message.Note(lang.CmdInitPullNote) // Prompt the user if --confirm not specified if !confirmDownload { prompt := &survey.Confirm{ - Message: lang.CmdInitDownloadConfirm, + Message: lang.CmdInitPullConfirm, } if err := survey.AskOne(prompt, &confirmDownload); err != nil { return fmt.Errorf(lang.ErrConfirmCancel, err.Error()) @@ -133,10 +134,10 @@ func downloadInitPackage(downloadCacheTarget string) error { // If the user wants to download the init-package, download it if confirmDownload { - return utils.DownloadToFile(url, downloadCacheTarget, "") + return oci.DownloadPackageTarball(url, downloadCacheTarget, config.CommonOptions.OCIConcurrency) } // Otherwise, exit and tell the user to manually download the init-package - return errors.New(lang.CmdInitDownloadErrManual) + return errors.New(lang.CmdInitPullErrManual) } func validateInitFlags() error { diff --git a/src/cmd/tools/zarf.go b/src/cmd/tools/zarf.go index b09922da98..3fd96c4654 100644 --- a/src/cmd/tools/zarf.go +++ b/src/cmd/tools/zarf.go @@ -17,9 +17,9 @@ import ( "github.com/defenseunicorns/zarf/src/internal/packager/git" "github.com/defenseunicorns/zarf/src/internal/packager/helm" "github.com/defenseunicorns/zarf/src/pkg/message" + "github.com/defenseunicorns/zarf/src/pkg/oci" "github.com/defenseunicorns/zarf/src/pkg/packager" "github.com/defenseunicorns/zarf/src/pkg/pki" - "github.com/defenseunicorns/zarf/src/pkg/utils" "github.com/defenseunicorns/zarf/src/pkg/utils/helpers" "github.com/defenseunicorns/zarf/src/types" "github.com/sigstore/cosign/pkg/cosign" @@ -174,9 +174,9 @@ var downloadInitCmd = &cobra.Command{ Run: func(cmd *cobra.Command, args []string) { initPackageName := packager.GetInitPackageName("") target := filepath.Join(outputDirectory, initPackageName) - url := packager.GetInitPackageRemote("") - err := utils.DownloadToFile(url, target, "") - if err != nil { + url := oci.GetInitPackageURL(config.GetArch(), config.CLIVersion) + + if err := oci.DownloadPackageTarball(url, target, config.CommonOptions.OCIConcurrency); err != nil { message.Fatalf(err, lang.CmdToolsDownloadInitErr, err.Error()) } }, diff --git a/src/config/lang/english.go b/src/config/lang/english.go index 10a65bb925..fed1a43312 100644 --- a/src/config/lang/english.go +++ b/src/config/lang/english.go @@ -144,10 +144,10 @@ const ( CmdInitErrValidateArtifact = "the 'artifact-push-username' and 'artifact-push-token' flags must be provided if the 'artifact-url' flag is provided" CmdInitErrUnableCreateCache = "Unable to create the cache directory: %s" - CmdInitDownloadAsk = "It seems the init package could not be found locally, but can be downloaded from %s" - CmdInitDownloadNote = "Note: This will require an internet connection." - CmdInitDownloadConfirm = "Do you want to download this init package?" - CmdInitDownloadErrManual = "download the init package manually and place it in the current working directory" + CmdInitPullAsk = "It seems the init package could not be found locally, but can be pulled from oci://%s" + CmdInitPullNote = "Note: This will require an internet connection." + CmdInitPullConfirm = "Do you want to pull this init package?" + CmdInitPullErrManual = "pull the init package manually and place it in the current working directory" CmdInitFlagSet = "Specify deployment variables to set on the command line (KEY=value)" diff --git a/src/pkg/oci/utils.go b/src/pkg/oci/utils.go index a74880f922..876725b15b 100644 --- a/src/pkg/oci/utils.go +++ b/src/pkg/oci/utils.go @@ -8,12 +8,15 @@ import ( "context" "errors" "fmt" + "os" + "path/filepath" "strings" "github.com/defenseunicorns/zarf/src/pkg/message" "github.com/defenseunicorns/zarf/src/pkg/utils" "github.com/defenseunicorns/zarf/src/pkg/utils/helpers" "github.com/defenseunicorns/zarf/src/types" + "github.com/mholt/archiver/v3" ocispec "github.com/opencontainers/image-spec/specs-go/v1" "oras.land/oras-go/v2/registry" ) @@ -91,3 +94,36 @@ func RemoveDuplicateDescriptors(descriptors []ocispec.Descriptor) []ocispec.Desc } return list } + +// GetInitPackageURL returns the URL for the init package for the given architecture and version. +func GetInitPackageURL(arch, version string) string { + return fmt.Sprintf("ghcr.io/defenseunicorns/packages/init:%s-%s", version, arch) +} + +// DownloadPackageTarball downloads the given OCI package and saves as a tarball. +func DownloadPackageTarball(url, destinationTarball string, concurrency int) error { + remote, err := NewOrasRemote(url) + if err != nil { + return err + } + + tmp, err := utils.MakeTempDir() + if err != nil { + return err + } + defer os.RemoveAll(tmp) + + _, err = remote.PullPackage(tmp, concurrency) + if err != nil { + return err + } + + allTheLayers, err := filepath.Glob(filepath.Join(tmp, "*")) + if err != nil { + return err + } + + _ = os.Remove(destinationTarball) + + return archiver.Archive(allTheLayers, destinationTarball) +} diff --git a/src/pkg/packager/common.go b/src/pkg/packager/common.go index 1a986ffc58..4c31f34757 100644 --- a/src/pkg/packager/common.go +++ b/src/pkg/packager/common.go @@ -157,11 +157,6 @@ func (p *Packager) GetPackageName() string { return fmt.Sprintf("%s.%s", packageFileName, suffix) } -// GetInitPackageRemote returns the URL for a remote init package for the given architecture -func GetInitPackageRemote(arch string) string { - return fmt.Sprintf("https://github.com/%s/releases/download/%s/%s", config.GithubProject, config.CLIVersion, GetInitPackageName(arch)) -} - // ClearTempPaths removes the temp directory and any files within it. func (p *Packager) ClearTempPaths() { // Remove the temp directory, but don't throw an error if it fails diff --git a/src/pkg/packager/publish.go b/src/pkg/packager/publish.go index 1bc07147d0..09d7d763b5 100644 --- a/src/pkg/packager/publish.go +++ b/src/pkg/packager/publish.go @@ -103,6 +103,10 @@ func (p *Packager) loadSkeleton() error { return fmt.Errorf("unable to read the zarf.yaml in %s: %s", base, err.Error()) } + if p.cfg.Pkg.Kind == types.ZarfInitConfig { + p.cfg.Pkg.Metadata.Version = config.CLIVersion + } + err = p.composeComponents() if err != nil { return err