From 8614b09cfb3f9f0b8f3026067f50a2980efa84a8 Mon Sep 17 00:00:00 2001 From: Alex Date: Tue, 3 Feb 2026 23:00:44 -0500 Subject: [PATCH 1/2] feat: barge sync feat: barge sync chore: sync docs --- .dagger/checks.go | 14 +- .dagger/main.go | 52 +----- .env | 2 +- README.md | 18 +++ cmd/barge/main.go | 26 ++- copy_test.go | 187 +++++++++++++--------- destination.go | 4 + go.mod | 26 ++- go.sum | 64 +++++--- internal/artifactory/destination.go | 4 + internal/chartmuseum/destination.go | 4 + internal/directory/destination.go | 5 + internal/file/destination.go | 13 ++ internal/oci/destination.go | 10 ++ internal/oci/source.go | 12 ++ internal/repo/source.go | 30 ++++ internal/util/{user.go => auth.go} | 0 internal/util/registry.go | 61 ++++--- source.go | 4 + sync.go | 239 ++++++++++++++++++++++++++++ sync_test.go | 125 +++++++++++++++ testdata/README.md | 12 -- testdata/test-0.2.0.tgz | Bin 4920 -> 0 bytes 23 files changed, 712 insertions(+), 200 deletions(-) rename internal/util/{user.go => auth.go} (100%) create mode 100644 sync.go create mode 100644 sync_test.go delete mode 100644 testdata/README.md delete mode 100644 testdata/test-0.2.0.tgz diff --git a/.dagger/checks.go b/.dagger/checks.go index 185730f..145c5f6 100644 --- a/.dagger/checks.go +++ b/.dagger/checks.go @@ -22,21 +22,9 @@ func (m *BargeDev) IsFmted(ctx context.Context) error { func (m *BargeDev) TestsPass( ctx context.Context, // +optional - githubRepo string, - // +optional githubToken *dagger.Secret, ) error { - oci := []string{} - if githubToken != nil && githubRepo != "" { - oci = append(oci, fmt.Sprintf("ghcr.io/%s/charts/test", githubRepo)) - } - - test, err := m.Test(ctx, oci, githubToken) - if err != nil { - return err - } - - if _, err = test.CombinedOutput(ctx); err != nil { + if _, err := m.Test(ctx, githubToken); err != nil { return err } diff --git a/.dagger/main.go b/.dagger/main.go index ec4bc54..2c738b9 100644 --- a/.dagger/main.go +++ b/.dagger/main.go @@ -4,7 +4,6 @@ package main import ( "context" - "fmt" "strings" "github.com/frantjc/barge/.dagger/internal/dagger" @@ -62,51 +61,11 @@ func (m *BargeDev) Fmt(ctx context.Context) *dagger.Changeset { func (m *BargeDev) Test( ctx context.Context, // +optional - oci []string, - // +optional githubToken *dagger.Secret, -) (*dagger.Container, error) { - chartmuseum := dag.Container(). - From("ghcr.io/helm/chartmuseum:v0.16.3"). - WithExposedPort(8080). - WithEnvVariable("DEBUG", "1"). - WithEnvVariable("STORAGE", "local"). - WithEnvVariable("STORAGE_LOCAL_ROOTDIR", "/tmp"). - AsService() - chartmuseumAlias := "chartmuseum" - chartmuseumURL := fmt.Sprintf("http://%s:8080", chartmuseumAlias) - chartmuseumRepo := chartmuseumAlias - - registry := dag.Container(). - From("docker.io/distribution/distribution:3"). - WithExposedPort(5000). - AsService() - registryAlias := "registry" - - oci = append(oci, - fmt.Sprintf("%s:5000/test", registryAlias), - fmt.Sprintf("%s:5000/test:tag", registryAlias), - ) - - test := []string{ - "go", "test", "-race", "-cover", "-test.v", - "-cm", - chartmuseumURL, - "-oci", - strings.Join(oci, ","), - "-repo", - strings.Join([]string{ - fmt.Sprintf("%s/test", chartmuseumRepo), - fmt.Sprintf("%s/test?version=0.2.0", chartmuseumRepo), - }, ","), - "-http", - fmt.Sprintf("%s/charts/test-0.2.0.tgz", chartmuseumURL), - } - test = append(test, "./...") - +) (string, error) { return dag.Go(dagger.GoOpts{ Module: m.Source, - AdditionalWolfiPackages: []string{"helm-4", "curl"}, + AdditionalWolfiPackages: []string{"helm-4"}, }). Container(). With(func(r *dagger.Container) *dagger.Container { @@ -116,11 +75,8 @@ func (m *BargeDev) Test( } return r }). - WithServiceBinding(chartmuseumAlias, chartmuseum). - WithExec([]string{"curl", "-X", "POST", "-F", "chart=@testdata/test-0.2.0.tgz", fmt.Sprintf("%s/api/charts", chartmuseumURL)}). - WithExec([]string{"helm", "repo", "add", chartmuseumRepo, chartmuseumURL}). - WithServiceBinding(registryAlias, registry). - WithExec(test), nil + WithExec([]string{"go", "test", "-race", "-cover", "-test.v", "./..."}, dagger.ContainerWithExecOpts{ExperimentalPrivilegedNesting: true}). + CombinedOutput(ctx) } func (m *BargeDev) Release( diff --git a/.env b/.env index 5a4bdb0..94406a3 100644 --- a/.env +++ b/.env @@ -1,4 +1,4 @@ +bargeDev_test_githubToken=env://GITHUB_TOKEN bargeDev_testsPass_githubToken=env://GITHUB_TOKEN -bargeDev_testsPass_githubRepo=frantjc/barge bargeDev_release_githubToken=env://GITHUB_TOKEN bargeDev_release_githubRepo=frantjc/barge diff --git a/README.md b/README.md index b625350..bb2c678 100644 --- a/README.md +++ b/README.md @@ -35,3 +35,21 @@ barge cp https://github.com/frantjc/barge/raw/refs/heads/main/testdata/test-0.1. ```sh barge cp oci://ghcr.io/frantjc/barge/charts/test ./test.tgz ``` + +- You want to do all of the above at once where `barge-sync.yml` looks like: + +```yml +--- +sources: + - url: repo://chartmuseum + charts: + chartmuseum: + - 3.9.0 + chartsBySemver: + chartmuseum: ">= 3.10" +``` + +```sh +helm repo add chartmuseum https://chartmuseum.github.io/charts +barge sync barge-sync.yml oci://example.io +``` diff --git a/cmd/barge/main.go b/cmd/barge/main.go index fcb3514..d7eb2bd 100644 --- a/cmd/barge/main.go +++ b/cmd/barge/main.go @@ -44,7 +44,7 @@ func newBarge() *cobra.Command { Version: SemVer(), SilenceErrors: true, SilenceUsage: true, - PersistentPreRun: func(cmd *cobra.Command, args []string) { + PersistentPreRun: func(cmd *cobra.Command, _ []string) { cmd.SetContext( util.SloggerInto( util.StdoutInto( @@ -62,17 +62,17 @@ func newBarge() *cobra.Command { }, } ) - cmd.Flags().BoolP("help", "h", false, "Help for "+cmd.Name()) + cmd.PersistentFlags().BoolP("help", "h", false, "Help for "+cmd.Name()) cmd.Flags().Bool("version", false, "Version for "+cmd.Name()) cmd.SetVersionTemplate("{{ .Name }}{{ .Version }}") slogConfig.AddFlags(cmd.PersistentFlags()) - cmd.AddCommand(newCopy()) + cmd.AddCommand(newCopy(), newSync()) return cmd } func newCopy() *cobra.Command { cmd := &cobra.Command{ - Use: "copy", + Use: "copy src dest", Aliases: []string{"cp"}, SilenceErrors: true, SilenceUsage: true, @@ -84,3 +84,21 @@ func newCopy() *cobra.Command { barge.AddFlags(cmd.Flags()) return cmd } + +func newSync() *cobra.Command { + var ( + syncOpts = new(barge.SyncOpts) + cmd = &cobra.Command{ + Use: "sync config dest", + SilenceErrors: true, + SilenceUsage: true, + Args: cobra.ExactArgs(2), + RunE: func(cmd *cobra.Command, args []string) error { + return barge.Sync(cmd.Context(), args[0], args[1]) + }, + } + ) + cmd.Flags().BoolVar(&syncOpts.FailFast, "fail-fast", false, "Exit when the first source fails to sync") + barge.AddFlags(cmd.Flags()) + return cmd +} diff --git a/copy_test.go b/copy_test.go index bf970f7..8409df5 100644 --- a/copy_test.go +++ b/copy_test.go @@ -1,116 +1,155 @@ package barge_test import ( - "flag" + "context" "fmt" "net/url" "os" - "path" - "strings" + "os/exec" "testing" + "dagger.io/dagger" "github.com/frantjc/barge" _ "github.com/frantjc/barge/internal/archive" - _ "github.com/frantjc/barge/internal/artifactory" _ "github.com/frantjc/barge/internal/chartmuseum" _ "github.com/frantjc/barge/internal/directory" _ "github.com/frantjc/barge/internal/file" _ "github.com/frantjc/barge/internal/http" _ "github.com/frantjc/barge/internal/oci" - _ "github.com/frantjc/barge/internal/release" _ "github.com/frantjc/barge/internal/repo" + "github.com/frantjc/barge/internal/util" "github.com/frantjc/barge/testdata" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "helm.sh/helm/v3/pkg/chart/loader" ) -var ( - oci string - chartmuseum string - repo string - http string -) +func Command(t testing.TB, name string, arg ...string) *exec.Cmd { + t.Helper() + cmd := exec.CommandContext(t.Context(), name, arg...) + cmd.Stdout = t.Output() + cmd.Stderr = t.Output() + return cmd +} + +func Context(t testing.TB) context.Context { + t.Helper() + return util.StdoutInto(util.StderrInto(t.Context(), t.Output()), t.Output()) +} + +func Chartmuseum(t testing.TB, dag *dagger.Client) *url.URL { + t.Helper() + ctx := t.Context() + chartmuseum, err := dag.Container(). + From("ghcr.io/helm/chartmuseum:v0.16.3"). + WithExposedPort(8080). + WithEnvVariable("DEBUG", "1"). + WithEnvVariable("STORAGE", "local"). + WithEnvVariable("STORAGE_LOCAL_ROOTDIR", "/tmp"). + AsService(). + Start(ctx) + require.NoError(t, err) + t.Cleanup(func() { + _, err = chartmuseum.Stop(context.WithoutCancel(ctx)) + assert.NoError(t, err) + }) + rawChartmuseumURL, err := chartmuseum.Endpoint(ctx, dagger.ServiceEndpointOpts{Scheme: "chartmuseum"}) + require.NoError(t, err) + chartmuseumURL, err := url.Parse(rawChartmuseumURL) + chartmuseumURL.RawQuery = "insecure=1" + require.NoError(t, err) -func init() { - flag.StringVar(&oci, "oci", "", "run copy tests against these comma-delimited registries") - flag.StringVar(&chartmuseum, "cm", "", "run copy tests against these comma-delimited urls") - flag.StringVar(&repo, "repo", "", "run copy tests against these comma-delimited repos") - flag.StringVar(&http, "http", "", "run copy tests against these comma-delimited http(s)") + return chartmuseumURL +} + +func Registry(t testing.TB, dag *dagger.Client) *url.URL { + t.Helper() + ctx := t.Context() + registry, err := dag.Container(). + From("docker.io/distribution/distribution:3"). + WithExposedPort(5000). + AsService(). + Start(ctx) + require.NoError(t, err) + t.Cleanup(func() { + _, err = registry.Stop(context.WithoutCancel(ctx)) + assert.NoError(t, err) + }) + rawRegistryURL, err := registry.Endpoint(ctx, dagger.ServiceEndpointOpts{Scheme: "oci"}) + require.NoError(t, err) + registryURL, err := url.Parse(rawRegistryURL) + require.NoError(t, err) + + return registryURL } func FuzzCopy(f *testing.F) { + ctx := Context(f) + tmp, err := os.CreateTemp(f.TempDir(), "test-0.1.0.tgz") - assert.NoError(f, err) + require.NoError(f, err) _, err = tmp.Write(testdata.ChartArchive) - assert.NoError(f, err) - assert.NoError(f, tmp.Close()) + require.NoError(f, err) + require.NoError(f, tmp.Close()) + chart, err := loader.LoadFile(tmp.Name()) + require.NoError(f, err) archive := fmt.Sprintf("archive://%s", tmp.Name()) directory := fmt.Sprintf("directory://%s", f.TempDir()) - file := f.TempDir() - f.Add(archive, directory) - f.Add(directory, archive) + f.Add(directory, f.TempDir()) + + file := f.TempDir() f.Add(archive, file) - f.Add(file, archive) + f.Add(file, f.TempDir()) - for _, o := range strings.Split(oci, ",") { - if o == "" { - continue - } else if !strings.Contains(o, "://") { - o = fmt.Sprintf("oci://%s", o) - } - u, err := url.Parse(o) - assert.NoError(f, err) - oci := fmt.Sprintf("oci://%s", path.Join(u.Host, u.Path)) - f.Add(archive, oci) - f.Add(oci, archive) - } + if dag, err := dagger.Connect(ctx); assert.NoError(f, err) { + f.Cleanup(func() { + assert.NoError(f, dag.Close()) + }) - for _, c := range strings.Split(chartmuseum, ",") { - if c == "" { - continue - } - u, err := url.Parse(c) - assert.NoError(f, err) - switch u.Scheme { - case "cm", "chartmuseum": - case "http": - u.Scheme = "chartmuseum" - q := u.Query() - q.Set("insecure", "1") - u.RawQuery = q.Encode() - case "https": - u.Scheme = "chartmuseum" - default: - f.Fatalf("unknown scheme in -cm=%s", c) + chartmuseumURL := Chartmuseum(f, dag) + require.NoError(f, barge.Copy(ctx, archive, chartmuseumURL.String())) + + if helm, err := exec.LookPath("helm"); assert.NoError(f, err) { + repo := "chartmuseum" + repoURL := url.URL{ + Scheme: "http", + Host: chartmuseumURL.Host, + } + add := Command(f, helm, "repo", "add", repo, repoURL.String()) + require.NoError(f, add.Run()) + + f.Add(fmt.Sprintf("repo://%s/%s", repo, chart.Name()), f.TempDir()) + f.Add(fmt.Sprintf("repo://%s/%s?version=%s", repo, chart.Name(), chart.Metadata.Version), f.TempDir()) } - chartmuseum := u.String() - f.Add(archive, chartmuseum) - } - for _, r := range strings.Split(repo, ",") { - if r == "" { - continue - } else if !strings.Contains(r, "://") { - r = fmt.Sprintf("repo://%s", r) + httpURL := &url.URL{ + Scheme: "http", + Host: chartmuseumURL.Host, + Path: chartmuseumURL.JoinPath("charts/test-0.1.0.tgz").Path, } - u, err := url.Parse(r) - assert.NoError(f, err) - repo := u.String() - f.Add(repo, archive) + f.Add(httpURL.String(), f.TempDir()) + + registryURL := Registry(f, dag) + oci := registryURL.JoinPath("test") + f.Add(archive, oci.String()) + f.Add(oci.String(), f.TempDir()) + ociWithTag := registryURL.JoinPath("test:tag") + f.Add(archive, ociWithTag.String()) + f.Add(ociWithTag.String(), f.TempDir()) } - for _, h := range strings.Split(http, ",") { - if h == "" { - continue - } - u, err := url.Parse(h) - assert.NoError(f, err) - http := u.String() - f.Add(http, archive) + if username, _, err := util.GetGitHubAuth(ctx); assert.NoError(f, err) { + ghcr := fmt.Sprintf("oci://ghcr.io/%s/barge/charts/%s", username, chart.Name()) + ghcrWithTag := fmt.Sprintf("%s:%s", ghcr, chart.Metadata.Version) + f.Add(archive, ghcr) + f.Add(ghcr, f.TempDir()) + f.Add(archive, ghcrWithTag) + f.Add(ghcrWithTag, f.TempDir()) } f.Fuzz(func(t *testing.T, src, dest string) { - assert.NoError(t, barge.Copy(t.Context(), src, dest)) + require.NoError(t, barge.Copy(t.Context(), src, dest)) }) } @@ -119,6 +158,6 @@ func FuzzCopyError(f *testing.F) { f.Add(f.TempDir(), "bar://") f.Fuzz(func(t *testing.T, src, dest string) { - assert.Error(t, barge.Copy(t.Context(), src, dest)) + require.Error(t, barge.Copy(t.Context(), src, dest)) }) } diff --git a/destination.go b/destination.go index f7c652b..58724e7 100644 --- a/destination.go +++ b/destination.go @@ -29,3 +29,7 @@ func RegisterDestination(o Destination, scheme string, schemes ...string) { destMux[s] = o } } + +type SyncableDestination interface { + Sync(context.Context, *url.URL, *chart.Chart) error +} diff --git a/go.mod b/go.mod index f4eec2e..6b6f8df 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,8 @@ module github.com/frantjc/barge go 1.25.4 require ( + dagger.io/dagger v0.19.10 + github.com/Masterminds/semver/v3 v3.4.0 github.com/cli/cli/v2 v2.83.2 github.com/fluxcd/pkg/auth v0.33.0 github.com/frantjc/x v0.0.0-20251203020658-a4e29ee5477f @@ -15,6 +17,29 @@ require ( sigs.k8s.io/yaml v1.6.0 ) +require ( + github.com/99designs/gqlgen v0.17.81 // indirect + github.com/Khan/genqlient v0.8.1 // indirect + github.com/adrg/xdg v0.5.3 // indirect + github.com/cenkalti/backoff/v5 v5.0.3 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 // indirect + github.com/sosodev/duration v1.3.1 // indirect + github.com/vektah/gqlparser/v2 v2.5.30 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.14.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.14.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.38.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.38.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0 // indirect + go.opentelemetry.io/otel/log v0.14.0 // indirect + go.opentelemetry.io/otel/sdk v1.38.0 // indirect + go.opentelemetry.io/otel/sdk/log v0.14.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.38.0 // indirect + go.opentelemetry.io/proto/otlp v1.8.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20251022142026-3a174f9686a8 // indirect +) + require ( al.essio.dev/pkg/shellescape v1.6.0 // indirect cloud.google.com/go/auth v0.17.0 // indirect @@ -32,7 +57,6 @@ require ( github.com/BurntSushi/toml v1.5.0 // indirect github.com/MakeNowJust/heredoc v1.0.0 // indirect github.com/Masterminds/goutils v1.1.1 // indirect - github.com/Masterminds/semver/v3 v3.4.0 // indirect github.com/Masterminds/sprig/v3 v3.3.0 // indirect github.com/Masterminds/squirrel v1.5.4 // indirect github.com/alecthomas/chroma/v2 v2.19.0 // indirect diff --git a/go.sum b/go.sum index 48e4947..890625f 100644 --- a/go.sum +++ b/go.sum @@ -6,10 +6,14 @@ cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIi cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c= cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs= cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10= +dagger.io/dagger v0.19.10 h1:farO4CggSoWCId92QeZWKpP5mY9YwsVhNYuUzpqsKqw= +dagger.io/dagger v0.19.10/go.mod h1:BjAJWl4Lx7XRW7nooNjBi0ZAC5Ici2pkthkdBIZdbTI= dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8= dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA= filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= +github.com/99designs/gqlgen v0.17.81 h1:kCkN/xVyRb5rEQpuwOHRTYq83i0IuTQg9vdIiwEerTs= +github.com/99designs/gqlgen v0.17.81/go.mod h1:vgNcZlLwemsUhYim4dC1pvFP5FX0pr2Y+uYUoHFb1ig= github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU= github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= github.com/AlecAivazis/survey/v2 v2.3.7 h1:6I/u8FvytdGsgonrYsVn2t8t4QiRnh6QSTqkkhIiSjQ= @@ -36,6 +40,8 @@ github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU= github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU= +github.com/Khan/genqlient v0.8.1 h1:wtOCc8N9rNynRLXN3k3CnfzheCUNKBcvXmVv5zt6WCs= +github.com/Khan/genqlient v0.8.1/go.mod h1:R2G6DzjBvCbhjsEajfRjbWdVglSH/73kSivC9TLWVjU= github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE= github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= @@ -48,12 +54,16 @@ github.com/Masterminds/squirrel v1.5.4 h1:uUcX/aBc8O7Fg9kaISIUsHXdKuqehiXAMQTYX8 github.com/Masterminds/squirrel v1.5.4/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10= github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s= github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w= +github.com/adrg/xdg v0.5.3 h1:xRnxJXne7+oWDatRhR1JLnvuccuIeCoBu2rtuLqQB78= +github.com/adrg/xdg v0.5.3/go.mod h1:nlTsY+NNiCBGCK2tpm09vRqfVzrc2fLmXGpBLF0zlTQ= github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0= github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k= github.com/alecthomas/chroma/v2 v2.19.0 h1:Im+SLRgT8maArxv81mULDWN8oKxkzboH07CHesxElq4= github.com/alecthomas/chroma/v2 v2.19.0/go.mod h1:RVX6AvYm4VfYe/zsk7mjHueLDZor3aWCNE14TFlepBk= github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc= github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= +github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ= +github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= @@ -108,8 +118,8 @@ github.com/bshuster-repo/logrus-logstash-hook v1.0.0 h1:e+C0SB5R1pu//O4MQ3f9cFuP github.com/bshuster-repo/logrus-logstash-hook v1.0.0/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk= github.com/catppuccin/go v0.3.0 h1:d+0/YicIq+hSTo5oPuRi5kOpqkVA5tAsU6dNhvRu+aY= github.com/catppuccin/go v0.3.0/go.mod h1:8IHJuMGaUUjQM82qBrGNBv7LFq6JI3NnQCF6MOlZjpc= -github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= -github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM= +github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chai2010/gettext-go v1.0.2 h1:1Lwwip6Q2QGsAdl/ZKPCwTe9fe0CjlUbqj5bFNSjIRk= @@ -485,8 +495,8 @@ github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 h1:KRzFb2m7YtdldCEkzs6KqmJw4nqEVZGK7IN2kJkjTuQ= github.com/santhosh-tekuri/jsonschema/v6 v6.0.2/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU= -github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= -github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= +github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I= github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= github.com/shurcooL/githubv4 v0.0.0-20240727222349-48295856cce7 h1:cYCy18SHPKRkvclm+pWm1Lk4YrREb4IOIb/YdFO0p2M= @@ -495,6 +505,8 @@ github.com/shurcooL/graphql v0.0.0-20230722043721-ed46e5a46466 h1:17JxqqJY66GmZV github.com/shurcooL/graphql v0.0.0-20230722043721-ed46e5a46466/go.mod h1:9dIRpgIY7hVhoqfe0/FcYp0bpInZaT7dc3BYOprrIUE= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/sosodev/duration v1.3.1 h1:qtHBDMQ6lvMQsL15g4aopM4HEfOaYuhWBw3NPTtlqq4= +github.com/sosodev/duration v1.3.1/go.mod h1:RQIBBX0+fMLc/D9+Jb/fwvVmo0eZvDDEERAikUR6SDg= github.com/spf13/cast v1.9.2 h1:SsGfm7M8QOFtEzumm7UZrZdLLquNdzFYfIbEXntcFbE= github.com/spf13/cast v1.9.2/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s= @@ -513,6 +525,8 @@ github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/thlib/go-timezone-local v0.0.6 h1:Ii3QJ4FhosL/+eCZl6Hsdr4DDU4tfevNoV83yAEo2tU= github.com/thlib/go-timezone-local v0.0.6/go.mod h1:/Tnicc6m/lsJE0irFMA0LfIwTBo4QP7A8IfyIv4zZKI= +github.com/vektah/gqlparser/v2 v2.5.30 h1:EqLwGAFLIzt1wpx1IPpY67DwUujF1OfzgEyDsLrN6kE= +github.com/vektah/gqlparser/v2 v2.5.30/go.mod h1:D1/VCZtV3LPnQrcPBeR/q5jkSQIPti0uYCP/RI0gIeo= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/xlab/treeprint v1.2.0 h1:HzHnuAF1plUN2zGlAFHbSQP2qJ0ZAD3XF5XD7OesXRQ= @@ -538,20 +552,20 @@ go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 h1:RbKq8BG go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0/go.mod h1:h06DGIukJOevXaj/xrNjhi/2098RZzcLTbc0jDAUbsg= go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= -go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.8.0 h1:WzNab7hOOLzdDF/EoWCt4glhrbMPVMOO5JYTmpz36Ls= -go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.8.0/go.mod h1:hKvJwTzJdp90Vh7p6q/9PAOd55dI6WA6sWj62a/JvSs= -go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.8.0 h1:S+LdBGiQXtJdowoJoQPEtI52syEP/JYBUpjO49EQhV8= -go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.8.0/go.mod h1:5KXybFvPGds3QinJWQT7pmXf+TN5YIa7CNYObWRkj50= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.32.0 h1:j7ZSD+5yn+lo3sGV69nW04rRR0jhYnBwjuX3r0HvnK0= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.32.0/go.mod h1:WXbYJTUaZXAbYd8lbgGuvih0yuCfOFC5RJoYnoLcGz8= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.32.0 h1:t/Qur3vKSkUCcDVaSumWF2PKHt85pc7fRvFuoVT8qFU= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.32.0/go.mod h1:Rl61tySSdcOJWoEgYZVtmnKdA0GeKrSqkHC1t+91CH8= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 h1:OeNbIYk/2C15ckl7glBlOBp5+WlYsOElzTNmiPW/x60= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0/go.mod h1:7Bept48yIeqxP2OZ9/AqIpYS94h2or0aB4FypJTc8ZM= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0 h1:tgJ0uaNS4c98WRNUEx5U3aDlrDOI5Rs+1Vifcw4DJ8U= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0/go.mod h1:U7HYyW0zt/a9x5J1Kjs+r1f/d4ZHnYFclhYY2+YbeoE= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.33.0 h1:wpMfgF8E1rkrT1Z6meFh1NDtownE9Ii3n3X2GJYjsaU= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.33.0/go.mod h1:wAy0T/dUbs468uOlkT31xjvqQgEVXv58BRFWEgn5v/0= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.14.0 h1:OMqPldHt79PqWKOMYIAQs3CxAi7RLgPxwfFSwr4ZxtM= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.14.0/go.mod h1:1biG4qiqTxKiUCtoWDPpL3fB3KxVwCiGw81j3nKMuHE= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.14.0 h1:QQqYw3lkrzwVsoEX0w//EhH/TCnpRdEenKBOOEIMjWc= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.14.0/go.mod h1:gSVQcr17jk2ig4jqJ2DX30IdWH251JcNAecvrqTxH1s= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.38.0 h1:vl9obrcoWVKp/lwl8tRE33853I8Xru9HFbw/skNeLs8= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.38.0/go.mod h1:GAXRxmLJcVM3u22IjTg74zWBrRCKq8BnOqUVLodpcpw= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.38.0 h1:Oe2z/BCg5q7k4iXC3cqJxKYg0ieRiOqF0cecFYdPTwk= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.38.0/go.mod h1:ZQM5lAJpOsKnYagGg/zV2krVqTtaVdYdDkhMoX6Oalg= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 h1:GqRJVj7UmLjCVyVJ3ZFLdPRmhDUp2zFmQe3RHIOsw24= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0/go.mod h1:ri3aaHSmCTVYu2AWv44YMauwAQc0aqI9gHKIcSbI1pU= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0 h1:lwI4Dc5leUqENgGuQImwLo4WnuXFPetmPpkLi2IrX54= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0/go.mod h1:Kz/oCE7z5wuyhPxsXDuaPteSWqjSBD5YaSdbxZYGbGk= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0 h1:aTL7F04bJHUlztTsNGJ2l+6he8c+y/b//eR0jjjemT4= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0/go.mod h1:kldtb7jDTeol0l3ewcmd8SDvx3EmIE7lyvqbasU3QC4= go.opentelemetry.io/otel/exporters/prometheus v0.54.0 h1:rFwzp68QMgtzu9PgP3jm9XaMICI6TsofWWPcBDKwlsU= go.opentelemetry.io/otel/exporters/prometheus v0.54.0/go.mod h1:QyjcV9qDP6VeK5qPyKETvNjmaaEc7+gqjh4SS0ZYzDU= go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.8.0 h1:CHXNXwfKWfzS65yrlB2PVds1IBZcdsX8Vepy9of0iRU= @@ -560,20 +574,22 @@ go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.32.0 h1:SZmDnHcgp3zwlP go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.32.0/go.mod h1:fdWW0HtZJ7+jNpTKUR0GpMEDP69nR8YBJQxNiVCE3jk= go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.32.0 h1:cC2yDI3IQd0Udsux7Qmq8ToKAx1XCilTQECZ0KDZyTw= go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.32.0/go.mod h1:2PD5Ex6z8CFzDbTdOlwyNIUywRr1DN0ospafJM1wJ+s= -go.opentelemetry.io/otel/log v0.8.0 h1:egZ8vV5atrUWUbnSsHn6vB8R21G2wrKqNiDt3iWertk= -go.opentelemetry.io/otel/log v0.8.0/go.mod h1:M9qvDdUTRCopJcGRKg57+JSQ9LgLBrwwfC32epk5NX8= +go.opentelemetry.io/otel/log v0.14.0 h1:2rzJ+pOAZ8qmZ3DDHg73NEKzSZkhkGIua9gXtxNGgrM= +go.opentelemetry.io/otel/log v0.14.0/go.mod h1:5jRG92fEAgx0SU/vFPxmJvhIuDU9E1SUnEQrMlJpOno= go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA= go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI= go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E= go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg= -go.opentelemetry.io/otel/sdk/log v0.8.0 h1:zg7GUYXqxk1jnGF/dTdLPrK06xJdrXgqgFLnI4Crxvs= -go.opentelemetry.io/otel/sdk/log v0.8.0/go.mod h1:50iXr0UVwQrYS45KbruFrEt4LvAdCaWWgIrsN3ZQggo= +go.opentelemetry.io/otel/sdk/log v0.14.0 h1:JU/U3O7N6fsAXj0+CXz21Czg532dW2V4gG1HE/e8Zrg= +go.opentelemetry.io/otel/sdk/log v0.14.0/go.mod h1:imQvII+0ZylXfKU7/wtOND8Hn4OpT3YUoIgqJVksUkM= +go.opentelemetry.io/otel/sdk/log/logtest v0.14.0 h1:Ijbtz+JKXl8T2MngiwqBlPaHqc4YCaP/i13Qrow6gAM= +go.opentelemetry.io/otel/sdk/log/logtest v0.14.0/go.mod h1:dCU8aEL6q+L9cYTqcVOk8rM9Tp8WdnHOPLiBgp0SGOA= go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM= go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA= go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE= go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs= -go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4= -go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4= +go.opentelemetry.io/proto/otlp v1.8.0 h1:fRAZQDcAFHySxpJ1TwlA1cJ4tvcrw7nXl9xWWC8N5CE= +go.opentelemetry.io/proto/otlp v1.8.0/go.mod h1:tIeYOeNBU4cvmPqpaji1P+KbB4Oloai8wN4rWzRrFF0= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= diff --git a/internal/artifactory/destination.go b/internal/artifactory/destination.go index 3c1a03d..0f0572b 100644 --- a/internal/artifactory/destination.go +++ b/internal/artifactory/destination.go @@ -72,3 +72,7 @@ func (d *destination) Write(ctx context.Context, u *url.URL, c *chart.Chart) err return nil } + +func (d *destination) Sync(ctx context.Context, u *url.URL, c *chart.Chart) error { + return d.Write(ctx, u, c) +} diff --git a/internal/chartmuseum/destination.go b/internal/chartmuseum/destination.go index c45f229..787632b 100644 --- a/internal/chartmuseum/destination.go +++ b/internal/chartmuseum/destination.go @@ -88,3 +88,7 @@ func (d *destination) Write(ctx context.Context, u *url.URL, c *chart.Chart) err return nil } + +func (d *destination) Sync(ctx context.Context, u *url.URL, c *chart.Chart) error { + return d.Write(ctx, u, c) +} diff --git a/internal/directory/destination.go b/internal/directory/destination.go index 727806d..e0406e8 100644 --- a/internal/directory/destination.go +++ b/internal/directory/destination.go @@ -2,6 +2,7 @@ package directory import ( "context" + "fmt" "net/url" "path/filepath" @@ -23,3 +24,7 @@ type destination struct{} func (d *destination) Write(ctx context.Context, u *url.URL, c *chart.Chart) error { return util.WriteChartToDirectory(ctx, c, filepath.Join(u.Host, u.Path)) } + +func (d *destination) Sync(ctx context.Context, u *url.URL, c *chart.Chart) error { + return util.WriteChartToFile(c, filepath.Join(u.Host, u.Path, fmt.Sprintf("%s-%s.tgz", c.Name(), c.Metadata.Version))) +} diff --git a/internal/file/destination.go b/internal/file/destination.go index 7c4eca4..88d19cb 100644 --- a/internal/file/destination.go +++ b/internal/file/destination.go @@ -2,6 +2,7 @@ package file import ( "context" + "fmt" "net/url" "os" "path/filepath" @@ -33,3 +34,15 @@ func (d *destination) Write(ctx context.Context, u *url.URL, c *chart.Chart) err return util.WriteChartToFile(c, name) } + +func (d *destination) Sync(ctx context.Context, u *url.URL, c *chart.Chart) error { + name := filepath.Join(u.Host, u.Path) + + if fi, err := os.Stat(name); err != nil { + return err + } else if fi.IsDir() { + return d.Write(ctx, u.JoinPath(fmt.Sprintf("%s-%s.tgz", c.Name(), c.Metadata.Version)), c) + } + + return fmt.Errorf("cannot sync to a file; try a directory") +} diff --git a/internal/oci/destination.go b/internal/oci/destination.go index ab91b19..a0f9d19 100644 --- a/internal/oci/destination.go +++ b/internal/oci/destination.go @@ -4,6 +4,7 @@ import ( "context" "io" "net/url" + "strings" "github.com/frantjc/barge" "github.com/frantjc/barge/internal/util" @@ -45,3 +46,12 @@ func (d *destination) Write(ctx context.Context, u *url.URL, c *chart.Chart) err return nil } + +func (d *destination) Sync(ctx context.Context, u *url.URL, c *chart.Chart) error { + v := u.JoinPath() + v.Path, _, _ = strings.Cut(v.Path, ":") + q := v.Query() + q.Set("version", c.Metadata.Version) + v.RawQuery = q.Encode() + return d.Write(ctx, v.JoinPath(c.Name()), c) +} diff --git a/internal/oci/source.go b/internal/oci/source.go index 4e1a03b..2c3b6d9 100644 --- a/internal/oci/source.go +++ b/internal/oci/source.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "net/url" + "strings" "github.com/frantjc/barge" "github.com/frantjc/barge/internal/util" @@ -35,3 +36,14 @@ func (s *source) Open(ctx context.Context, u *url.URL) (*chart.Chart, error) { return loader.LoadArchive(bytes.NewReader(res.Chart.Data)) } + +func (s *source) Versions(ctx context.Context, u *url.URL, name string) ([]string, error) { + r, err := util.NewRegistryClientFromURL(ctx, u) + if err != nil { + return nil, err + } + + ref, _, _ := strings.Cut(util.RefFromURL(u.JoinPath(name)), ":") + + return r.Tags(ref) +} diff --git a/internal/repo/source.go b/internal/repo/source.go index b40fd50..b7b514d 100644 --- a/internal/repo/source.go +++ b/internal/repo/source.go @@ -106,3 +106,33 @@ func (s *source) Open(ctx context.Context, u *url.URL) (*chart.Chart, error) { return nil, fmt.Errorf("could not get chart from urls: %w", errs) } + +func (s *source) Versions(ctx context.Context, u *url.URL, name string) ([]string, error) { + settings := barge.HelmSettings() + + repos, err := repo.LoadFile(settings.RepositoryConfig) + if err != nil { + return nil, err + } + + entry := repos.Get(u.Host) + if entry == nil { + return nil, fmt.Errorf("unknown repo %s", u.Host) + } + + index, err := repo.LoadIndexFile( + filepath.Join(settings.RepositoryCache, helmpath.CacheIndexFile(u.Host)), + ) + if err != nil { + return nil, err + } + + versions, ok := index.Entries[name] + if !ok { + return nil, fmt.Errorf("chart %s not found in repo %s", name, u.Host) + } + + return xslices.Map(versions, func(v *repo.ChartVersion, _ int) string { + return v.Version + }), nil +} diff --git a/internal/util/user.go b/internal/util/auth.go similarity index 100% rename from internal/util/user.go rename to internal/util/auth.go diff --git a/internal/util/registry.go b/internal/util/registry.go index 358900b..9104a40 100644 --- a/internal/util/registry.go +++ b/internal/util/registry.go @@ -19,6 +19,38 @@ import ( orasauth "oras.land/oras-go/v2/registry/remote/auth" ) +func GetGitHubAuth(ctx context.Context) (string, string, error) { + stdout := StdoutFrom(ctx) + + cfg, err := factory.New("v0.0.0-unknown").Config() + if err != nil { + return "", "", err + } + + authCfg := cfg.Authentication() + + httpClient, err := api.NewHTTPClient(api.HTTPClientOptions{ + Config: authCfg, + Log: stdout, + }) + if err != nil { + return "", "", err + } + + username, err := authCfg.ActiveUser("github.com") + if err != nil { + var nerr error + username, nerr = api.CurrentLoginName(api.NewClientFromHTTP(httpClient), "github.com") + if nerr != nil { + return "", "", fmt.Errorf("%v: %v", err, nerr) + } + } + + password, _ := authCfg.ActiveToken("github.com") + + return username, password, nil +} + func NewRegistryClientFromURL(ctx context.Context, u *url.URL) (*registry.Client, error) { stdout := StdoutFrom(ctx) opts := []registry.ClientOption{registry.ClientOptWriter(stdout)} @@ -31,32 +63,11 @@ func NewRegistryClientFromURL(ctx context.Context, u *url.URL) (*registry.Client } else if provider := u.Query().Get("provider"); provider != "" { opts = append(opts, cliOptForURLAndProvider(u, provider)) } else if hostname := u.Hostname(); hostname == "ghcr.io" { - cfg, err := factory.New("v0.0.0-unknown").Config() - if err != nil { - return nil, err - } - - authCfg := cfg.Authentication() - - httpClient, err := api.NewHTTPClient(api.HTTPClientOptions{ - Config: authCfg, - Log: stdout, - }) + username, password, err := GetGitHubAuth(ctx) if err != nil { return nil, err } - username, err := authCfg.ActiveUser("github.com") - if err != nil { - var nerr error - username, nerr = api.CurrentLoginName(api.NewClientFromHTTP(httpClient), "github.com") - if nerr != nil { - return nil, fmt.Errorf("%v: %v", err, nerr) - } - } - - password, _ := authCfg.ActiveToken("github.com") - opts = append(opts, registry.ClientOptBasicAuth(username, password)) } else if xslices.Some([]string{".azurecr.io", ".azurecr.us", ".azurecr.cn"}, func(suffix string, _ int) bool { return strings.HasSuffix(hostname, suffix) @@ -78,7 +89,11 @@ func RefFromURL(u *url.URL) string { if strings.Contains(u.Path, ":") { return ref } - return fmt.Sprintf("%s:latest", ref) + tag := u.Query().Get("version") + if tag == "" { + tag = "latest" + } + return fmt.Sprintf("%s:%s", ref, tag) } func cliOptForURLAndProvider(u *url.URL, provider string) registry.ClientOption { diff --git a/source.go b/source.go index a8b7244..e4bb04f 100644 --- a/source.go +++ b/source.go @@ -29,3 +29,7 @@ func RegisterSource(o Source, scheme string, schemes ...string) { srcMux[s] = o } } + +type QueryableSource interface { + Versions(context.Context, *url.URL, string) ([]string, error) +} diff --git a/sync.go b/sync.go new file mode 100644 index 0000000..f88b381 --- /dev/null +++ b/sync.go @@ -0,0 +1,239 @@ +package barge + +import ( + "context" + "encoding/json" + "fmt" + "net/url" + "os" + + "github.com/Masterminds/semver/v3" + "github.com/frantjc/barge/internal/util" + "golang.org/x/sync/errgroup" + "sigs.k8s.io/yaml" +) + +type URL url.URL + +func (j *URL) UnmarshalJSON(data []byte) error { + var raw string + if err := json.Unmarshal(data, &raw); err != nil { + return err + } + + u, err := url.Parse(raw) + if err != nil { + return err + } + + *j = URL(*u) + return nil +} + +func (j URL) MarshalJSON() ([]byte, error) { + return json.Marshal(j.String()) +} + +func (j URL) String() string { + u := url.URL(j) + return u.String() +} + +func (j URL) JoinPath(elem ...string) URL { + u := url.URL(j) + return URL(*u.JoinPath(elem...)) +} + +func (j URL) Query() url.Values { + u := url.URL(j) + return u.Query() +} + +type Constraints semver.Constraints + +func (j *Constraints) UnmarshalJSON(data []byte) error { + var raw string + if err := json.Unmarshal(data, &raw); err != nil { + return err + } + + c, err := semver.NewConstraint(raw) + if err != nil { + return err + } + + *j = Constraints(*c) + return nil +} + +func (j Constraints) MarshalJSON() ([]byte, error) { + return json.Marshal(j.String()) +} + +func (j Constraints) String() string { + return semver.Constraints(j).String() +} + +type SourceConfig struct { + URL URL `json:"url"` + Charts map[string][]string `json:"charts,omitempty"` + ChartsBySemver map[string]Constraints `json:"chartsBySemver,omitempty"` +} + +type SyncConfig struct { + Sources []SourceConfig `json:"sources"` +} + +type SyncOpts struct { + FailFast bool +} + +type SyncOpt interface { + Apply(*SyncOpts) +} + +func (s *SyncOpts) Apply(opts *SyncOpts) { + if s != nil { + if opts != nil { + opts.FailFast = s.FailFast + } else { + opts = s + } + } +} + +func (s *SyncConfig) Sync(ctx context.Context, dest string, opts ...SyncOpts) error { + o := &SyncOpts{} + + for _, opt := range opts { + opt.Apply(o) + } + + log := util.SloggerFrom(ctx) + + d, err := url.Parse(os.ExpandEnv(dest)) + if err != nil { + return err + } + + if d.Scheme == "" { + d.Scheme = "file" + } + + destination, ok := destMux[d.Scheme] + if !ok { + return fmt.Errorf("no destination registered for scheme: %s", d.Scheme) + } + + syncableDestination, ok := destination.(SyncableDestination) + if !ok { + return fmt.Errorf("not a syncable destination scheme: %s", d.Scheme) + } + + eg := new(errgroup.Group) + if o.FailFast { + eg, ctx = errgroup.WithContext(ctx) + } + + for _, src := range s.Sources { + eg.Go(func() error { + s, err := url.Parse(os.ExpandEnv(src.URL.String())) + if err != nil { + return err + } + + if s.Scheme == "" { + s.Scheme = "file" + } + + source, ok := srcMux[s.Scheme] + if !ok { + return fmt.Errorf("no source registered for scheme: %s", s.Scheme) + } + + if len(src.Charts) > 0 || len(src.ChartsBySemver) > 0 { + for name, versions := range src.Charts { + for _, version := range versions { + t := s.JoinPath(name) + q := t.Query() + q.Set("version", version) + t.RawQuery = q.Encode() + + chart, err := source.Open(ctx, t) + if err != nil { + return err + } + + eg.Go(func() error { + return syncableDestination.Sync(ctx, d, chart) + }) + } + } + + if len(src.ChartsBySemver) > 0 { + queryableSource, ok := source.(QueryableSource) + if !ok { + return fmt.Errorf("not a queryable source scheme: %s", s.Scheme) + } + + for name, constraints := range src.ChartsBySemver { + versions, err := queryableSource.Versions(ctx, s, name) + if err != nil { + return err + } + + for _, version := range versions { + v, err := semver.NewVersion(version) + if err != nil { + log.Debug("skipping invalid semver") + continue + } + + t := s.JoinPath(name) + q := t.Query() + q.Set("version", version) + t.RawQuery = q.Encode() + + chart, err := source.Open(ctx, t) + if err != nil { + return err + } + + if semver.Constraints(constraints).Check(v) { + eg.Go(func() error { + log.Info("syncing", "chart", chart.Name(), "version", chart.Metadata.Version, "destination", d.String()) + return syncableDestination.Sync(ctx, d, chart) + }) + } + } + } + } + + return nil + } + + chart, err := source.Open(ctx, s) + if err != nil { + return err + } + + return syncableDestination.Sync(ctx, d, chart) + }) + } + + return eg.Wait() +} + +func Sync(ctx context.Context, cfg, dest string) error { + b, err := os.ReadFile(cfg) + if err != nil { + return err + } + + s := &SyncConfig{} + if err := yaml.Unmarshal(b, s); err != nil { + return err + } + + return s.Sync(ctx, dest) +} diff --git a/sync_test.go b/sync_test.go new file mode 100644 index 0000000..f917560 --- /dev/null +++ b/sync_test.go @@ -0,0 +1,125 @@ +package barge_test + +import ( + "fmt" + "net/url" + "os" + "os/exec" + "path/filepath" + "testing" + + "dagger.io/dagger" + "github.com/frantjc/barge" + _ "github.com/frantjc/barge/internal/archive" + _ "github.com/frantjc/barge/internal/chartmuseum" + _ "github.com/frantjc/barge/internal/directory" + _ "github.com/frantjc/barge/internal/file" + _ "github.com/frantjc/barge/internal/http" + _ "github.com/frantjc/barge/internal/oci" + _ "github.com/frantjc/barge/internal/repo" + "github.com/frantjc/barge/testdata" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "sigs.k8s.io/yaml" +) + +func FuzzSync(f *testing.F) { + ctx := Context(f) + + tmp, err := os.CreateTemp(f.TempDir(), "test-0.1.0.tgz") + require.NoError(f, err) + _, err = tmp.Write(testdata.ChartArchive) + require.NoError(f, err) + require.NoError(f, tmp.Close()) + archiveURL, err := url.Parse(fmt.Sprintf("archive://%s", tmp.Name())) + require.NoError(f, err) + + tmp, err = os.CreateTemp(f.TempDir(), "archive-sync-config.yml") + require.NoError(f, err) + + b, err := yaml.Marshal(&barge.SyncConfig{ + Sources: []barge.SourceConfig{ + { + URL: barge.URL(*archiveURL), + }, + }, + }) + require.NoError(f, err) + _, err = tmp.Write(b) + require.NoError(f, err) + require.NoError(f, tmp.Close()) + archiveSyncCfg := tmp.Name() + + f.Add(archiveSyncCfg, os.TempDir()) + + if dag, err := dagger.Connect(ctx); assert.NoError(f, err) { + f.Cleanup(func() { + assert.NoError(f, dag.Close()) + }) + + chartmuseumURL := Chartmuseum(f, dag) + + if helm, err := exec.LookPath("helm"); assert.NoError(f, err) { + repo := "chartmuseum" + repoURL := url.URL{ + Scheme: "http", + Host: chartmuseumURL.Host, + } + add := Command(f, helm, "repo", "add", repo, repoURL.String()) + require.NoError(f, add.Run()) + } + + f.Add(archiveSyncCfg, chartmuseumURL.String()) + + registryURL := Registry(f, dag) + f.Add(archiveSyncCfg, registryURL.String()) + } + + f.Fuzz(func(t *testing.T, cfg, dest string) { + require.NoError(t, barge.Sync(ctx, cfg, dest)) + }) +} + +func FuzzSyncError(f *testing.F) { + ctx := Context(f) + + tmp, err := os.CreateTemp(f.TempDir(), "test-0.1.0.tgz") + require.NoError(f, err) + _, err = tmp.Write(testdata.ChartArchive) + require.NoError(f, err) + require.NoError(f, tmp.Close()) + archiveURL, err := url.Parse(fmt.Sprintf("archive://%s", tmp.Name())) + require.NoError(f, err) + + tmp, err = os.CreateTemp(f.TempDir(), "archive-sync-config.yml") + require.NoError(f, err) + + b, err := yaml.Marshal(&barge.SyncConfig{ + Sources: []barge.SourceConfig{ + { + URL: barge.URL(*archiveURL), + }, + }, + }) + require.NoError(f, err) + _, err = tmp.Write(b) + require.NoError(f, err) + require.NoError(f, tmp.Close()) + cfg := tmp.Name() + + f.Add(cfg, "invalid://") + f.Add(cfg, "oci://does-not-exist") + + if dag, err := dagger.Connect(ctx); assert.NoError(f, err) { + f.Cleanup(func() { + assert.NoError(f, dag.Close()) + }) + + registryURL := Registry(f, dag) + f.Add(filepath.Join(f.TempDir(), "does-not-exist.yaml"), registryURL.String()) + } + + f.Fuzz(func(t *testing.T, cfg, dest string) { + assert.Error(t, barge.Sync(ctx, cfg, dest)) + }) +} diff --git a/testdata/README.md b/testdata/README.md deleted file mode 100644 index 79beb99..0000000 --- a/testdata/README.md +++ /dev/null @@ -1,12 +0,0 @@ -# testdata - -`test-0.1.0.tgz` is used by Go tests directly, while `test-0.2.0.tgz` is used by Dagger to prepare a repository to pass to the Go tests as an argument. - -Both were generated via the following sequence of commands: - -```sh -helm create test -cd test/ -# sed -i 's/0.1.0/0.2.0/g' -helm package . -``` diff --git a/testdata/test-0.2.0.tgz b/testdata/test-0.2.0.tgz deleted file mode 100644 index 7f84194bac46a346a448f69a6293e09ee6e57c30..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4920 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 From 39efb63d35f29f58c3cbd9327a64e471ad30952a Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 4 Feb 2026 18:32:12 -0500 Subject: [PATCH 2/2] fix: ci tests --- .dagger/checks.go | 4 +++- .dagger/main.go | 8 +++++++- .env | 2 ++ copy_test.go | 4 ++-- internal/util/registry.go | 20 ++++++++++---------- 5 files changed, 24 insertions(+), 14 deletions(-) diff --git a/.dagger/checks.go b/.dagger/checks.go index 145c5f6..23ed884 100644 --- a/.dagger/checks.go +++ b/.dagger/checks.go @@ -23,8 +23,10 @@ func (m *BargeDev) TestsPass( ctx context.Context, // +optional githubToken *dagger.Secret, + // +optional + githubRepo string, ) error { - if _, err := m.Test(ctx, githubToken); err != nil { + if _, err := m.Test(ctx, githubToken, githubRepo); err != nil { return err } diff --git a/.dagger/main.go b/.dagger/main.go index 2c738b9..10754e5 100644 --- a/.dagger/main.go +++ b/.dagger/main.go @@ -62,6 +62,8 @@ func (m *BargeDev) Test( ctx context.Context, // +optional githubToken *dagger.Secret, + // +optional + githubRepo string, ) (string, error) { return dag.Go(dagger.GoOpts{ Module: m.Source, @@ -70,9 +72,13 @@ func (m *BargeDev) Test( Container(). With(func(r *dagger.Container) *dagger.Container { if githubToken != nil { - return r. + r = r. WithSecretVariable("GITHUB_TOKEN", githubToken) } + if githubRepo != "" { + r = r. + WithEnvVariable("GITHUB_REPOSITORY", githubRepo) + } return r }). WithExec([]string{"go", "test", "-race", "-cover", "-test.v", "./..."}, dagger.ContainerWithExecOpts{ExperimentalPrivilegedNesting: true}). diff --git a/.env b/.env index 94406a3..da8d53e 100644 --- a/.env +++ b/.env @@ -1,4 +1,6 @@ bargeDev_test_githubToken=env://GITHUB_TOKEN +bargeDev_test_githubRepo=frantjc/barge bargeDev_testsPass_githubToken=env://GITHUB_TOKEN +bargeDev_testsPass_githubRepo=frantjc/barge bargeDev_release_githubToken=env://GITHUB_TOKEN bargeDev_release_githubRepo=frantjc/barge diff --git a/copy_test.go b/copy_test.go index 8409df5..9211d8f 100644 --- a/copy_test.go +++ b/copy_test.go @@ -139,8 +139,8 @@ func FuzzCopy(f *testing.F) { f.Add(ociWithTag.String(), f.TempDir()) } - if username, _, err := util.GetGitHubAuth(ctx); assert.NoError(f, err) { - ghcr := fmt.Sprintf("oci://ghcr.io/%s/barge/charts/%s", username, chart.Name()) + if githubRepository := os.Getenv("GITHUB_REPOSITORY"); githubRepository != "" { + ghcr := fmt.Sprintf("oci://ghcr.io/%s/charts/%s", githubRepository, chart.Name()) ghcrWithTag := fmt.Sprintf("%s:%s", ghcr, chart.Metadata.Version) f.Add(archive, ghcr) f.Add(ghcr, f.TempDir()) diff --git a/internal/util/registry.go b/internal/util/registry.go index 9104a40..e59c942 100644 --- a/internal/util/registry.go +++ b/internal/util/registry.go @@ -20,8 +20,6 @@ import ( ) func GetGitHubAuth(ctx context.Context) (string, string, error) { - stdout := StdoutFrom(ctx) - cfg, err := factory.New("v0.0.0-unknown").Config() if err != nil { return "", "", err @@ -29,16 +27,18 @@ func GetGitHubAuth(ctx context.Context) (string, string, error) { authCfg := cfg.Authentication() - httpClient, err := api.NewHTTPClient(api.HTTPClientOptions{ - Config: authCfg, - Log: stdout, - }) - if err != nil { - return "", "", err - } - username, err := authCfg.ActiveUser("github.com") if err != nil { + stdout := StdoutFrom(ctx) + + httpClient, err := api.NewHTTPClient(api.HTTPClientOptions{ + Config: authCfg, + Log: stdout, + }) + if err != nil { + return "", "", err + } + var nerr error username, nerr = api.CurrentLoginName(api.NewClientFromHTTP(httpClient), "github.com") if nerr != nil {