From 475eeea350217ca29eb9556d41a1e2082e8bc810 Mon Sep 17 00:00:00 2001 From: Mark Phelps <209477+markphelps@users.noreply.github.com> Date: Thu, 12 Dec 2024 15:01:10 -0500 Subject: [PATCH] chore: Prep 1.53.2 (#3720) * chore: prep 1.53 release (#3689) Signed-off-by: Mark Phelps <209477+markphelps@users.noreply.github.com> * chore: update README with new dashboard image (#3691) Signed-off-by: Mark Phelps <209477+markphelps@users.noreply.github.com> * chore: Update devenv (#3693) * chore: run devenv update Signed-off-by: Mark Phelps <209477+markphelps@users.noreply.github.com> * chore: update DEVELOPMENT.md Signed-off-by: Mark Phelps <209477+markphelps@users.noreply.github.com> --------- Signed-off-by: Mark Phelps <209477+markphelps@users.noreply.github.com> * fix(analytics): prevent panic with clickhouse setup (#3694) Signed-off-by: Roman Dmytrenko * chore(deps): bump alpine from 3.20.3 to 3.21.0 in /build (#3696) Bumps alpine from 3.20.3 to 3.21.0. --- updated-dependencies: - dependency-name: alpine dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump github.com/aws/aws-sdk-go-v2/service/s3 (#3703) Bumps [github.com/aws/aws-sdk-go-v2/service/s3](https://github.com/aws/aws-sdk-go-v2) from 1.69.0 to 1.71.0. - [Release notes](https://github.com/aws/aws-sdk-go-v2/releases) - [Commits](https://github.com/aws/aws-sdk-go-v2/compare/service/s3/v1.69.0...service/s3/v1.71.0) --- updated-dependencies: - dependency-name: github.com/aws/aws-sdk-go-v2/service/s3 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump github.com/Masterminds/semver/v3 from 3.3.0 to 3.3.1 (#3705) Bumps [github.com/Masterminds/semver/v3](https://github.com/Masterminds/semver) from 3.3.0 to 3.3.1. - [Release notes](https://github.com/Masterminds/semver/releases) - [Changelog](https://github.com/Masterminds/semver/blob/master/CHANGELOG.md) - [Commits](https://github.com/Masterminds/semver/compare/v3.3.0...v3.3.1) --- updated-dependencies: - dependency-name: github.com/Masterminds/semver/v3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump github.com/grpc-ecosystem/grpc-gateway/v2 (#3704) Bumps [github.com/grpc-ecosystem/grpc-gateway/v2](https://github.com/grpc-ecosystem/grpc-gateway) from 2.23.0 to 2.24.0. - [Release notes](https://github.com/grpc-ecosystem/grpc-gateway/releases) - [Changelog](https://github.com/grpc-ecosystem/grpc-gateway/blob/main/.goreleaser.yml) - [Commits](https://github.com/grpc-ecosystem/grpc-gateway/compare/v2.23.0...v2.24.0) --- updated-dependencies: - dependency-name: github.com/grpc-ecosystem/grpc-gateway/v2 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp (#3706) Bumps [go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp](https://github.com/open-telemetry/opentelemetry-go) from 1.31.0 to 1.32.0. - [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases) - [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/v1.31.0...v1.32.0) --- updated-dependencies: - dependency-name: go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump github.com/testcontainers/testcontainers-go (#3707) Bumps [github.com/testcontainers/testcontainers-go](https://github.com/testcontainers/testcontainers-go) from 0.33.0 to 0.34.0. - [Release notes](https://github.com/testcontainers/testcontainers-go/releases) - [Commits](https://github.com/testcontainers/testcontainers-go/compare/v0.33.0...v0.34.0) --- updated-dependencies: - dependency-name: github.com/testcontainers/testcontainers-go dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump golang.org/x/tools from 0.27.0 to 0.28.0 in /_tools (#3708) Bumps [golang.org/x/tools](https://github.com/golang/tools) from 0.27.0 to 0.28.0. - [Release notes](https://github.com/golang/tools/releases) - [Commits](https://github.com/golang/tools/compare/v0.27.0...v0.28.0) --- updated-dependencies: - dependency-name: golang.org/x/tools dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump yup from 1.4.0 to 1.5.0 in /ui (#3701) Bumps [yup](https://github.com/jquense/yup) from 1.4.0 to 1.5.0. - [Release notes](https://github.com/jquense/yup/releases) - [Changelog](https://github.com/jquense/yup/blob/master/CHANGELOG.md) - [Commits](https://github.com/jquense/yup/compare/v1.4.0...v1.5.0) --- updated-dependencies: - dependency-name: yup dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps-dev): bump dotenv from 16.4.5 to 16.4.7 in /ui (#3699) Bumps [dotenv](https://github.com/motdotla/dotenv) from 16.4.5 to 16.4.7. - [Changelog](https://github.com/motdotla/dotenv/blob/master/CHANGELOG.md) - [Commits](https://github.com/motdotla/dotenv/compare/v16.4.5...v16.4.7) --- updated-dependencies: - dependency-name: dotenv dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump @dnd-kit/core from 6.1.0 to 6.3.1 in /ui (#3702) Bumps [@dnd-kit/core](https://github.com/clauderic/dnd-kit/tree/HEAD/packages/core) from 6.1.0 to 6.3.1. - [Release notes](https://github.com/clauderic/dnd-kit/releases) - [Changelog](https://github.com/clauderic/dnd-kit/blob/master/packages/core/CHANGELOG.md) - [Commits](https://github.com/clauderic/dnd-kit/commits/@dnd-kit/core@6.3.1/packages/core) --- updated-dependencies: - dependency-name: "@dnd-kit/core" dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump lucide-react from 0.460.0 to 0.468.0 in /ui (#3700) Bumps [lucide-react](https://github.com/lucide-icons/lucide/tree/HEAD/packages/lucide-react) from 0.460.0 to 0.468.0. - [Release notes](https://github.com/lucide-icons/lucide/releases) - [Commits](https://github.com/lucide-icons/lucide/commits/0.468.0/packages/lucide-react) --- updated-dependencies: - dependency-name: lucide-react dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps-dev): bump prettier from 3.3.3 to 3.4.2 in /ui (#3698) Bumps [prettier](https://github.com/prettier/prettier) from 3.3.3 to 3.4.2. - [Release notes](https://github.com/prettier/prettier/releases) - [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md) - [Commits](https://github.com/prettier/prettier/compare/3.3.3...3.4.2) --- updated-dependencies: - dependency-name: prettier dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump codecov/codecov-action from 5.0.7 to 5.1.1 (#3697) Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 5.0.7 to 5.1.1. - [Release notes](https://github.com/codecov/codecov-action/releases) - [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/codecov/codecov-action/compare/v5.0.7...v5.1.1) --- updated-dependencies: - dependency-name: codecov/codecov-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(analytics)!: use rfc 3339 for dates (#3695) * fix: handle case where git repo already exists for local clone (#3712) * chore: try to reproduce issue with unit test Signed-off-by: Mark Phelps <209477+markphelps@users.noreply.github.com> * fix: handle case where git repo already exists in local clone Signed-off-by: Mark Phelps <209477+markphelps@users.noreply.github.com> --------- Signed-off-by: Mark Phelps <209477+markphelps@users.noreply.github.com> * chore: cherry pick git clone fix for 1.53.2 Signed-off-by: Mark Phelps <209477+markphelps@users.noreply.github.com> --------- Signed-off-by: Mark Phelps <209477+markphelps@users.noreply.github.com> Signed-off-by: Roman Dmytrenko Signed-off-by: dependabot[bot] Co-authored-by: Roman Dmytrenko Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- CHANGELOG.md | 6 ++ internal/storage/fs/git/store.go | 46 +++++++-- internal/storage/fs/git/store_test.go | 139 ++++++++++++++------------ 3 files changed, 119 insertions(+), 72 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6918d50254..b07aee2579 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,12 @@ This format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [v1.53.2](https://github.com/flipt-io/flipt/releases/tag/v1.53.2) - 2024-12-12 + +### Fixed + +- handle case where git repo already exists for local clone (#3712) + ## [v1.53.1](https://github.com/flipt-io/flipt/releases/tag/v1.53.1) - 2024-12-09 ### Fixed diff --git a/internal/storage/fs/git/store.go b/internal/storage/fs/git/store.go index 657257749a..efd6c2819d 100644 --- a/internal/storage/fs/git/store.go +++ b/internal/storage/fs/git/store.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "io/fs" + "os" "slices" "sync" @@ -45,6 +46,7 @@ type SnapshotStore struct { refTypeTag bool referenceResolver referenceResolver directory string + path string auth transport.AuthMethod insecureSkipTLS bool caBundle []byte @@ -120,6 +122,7 @@ func WithDirectory(directory string) containers.Option[SnapshotStore] { // The provided path is location for the dotgit folder. func WithFilesystemStorage(path string) containers.Option[SnapshotStore] { return func(ss *SnapshotStore) { + ss.path = path fs := osfs.New(path) ss.storage = filesystem.NewStorage(fs, cache.NewObjectLRUDefault()) } @@ -145,6 +148,19 @@ func NewSnapshotStore(ctx context.Context, logger *zap.Logger, url string, opts return nil, err } + empty := true + // if the path is set then we need to check if the directory is empty before + // attempting to clone the repository + if store.path != "" { + entries, err := os.ReadDir(store.path) + if empty = err != nil || len(entries) == 0; empty { + // either the directory is empty or we failed to read it + if err != nil && !os.IsNotExist(err) { + return nil, fmt.Errorf("failed to read directory: %w", err) + } + } + } + if !plumbing.IsHash(store.baseRef) { // if the base ref is not an explicit SHA then // attempt to clone either the explicit branch @@ -163,9 +179,16 @@ func NewSnapshotStore(ctx context.Context, logger *zap.Logger, url string, opts cloneOpts.SingleBranch = true } - store.repo, err = git.Clone(store.storage, nil, cloneOpts) - if err != nil { - return nil, fmt.Errorf("performing initial clone: %w", err) + if empty { + store.repo, err = git.Clone(store.storage, nil, cloneOpts) + if err != nil { + return nil, fmt.Errorf("performing initial clone: %w", err) + } + } else { + store.repo, err = git.Open(store.storage, nil) + if err != nil { + return nil, fmt.Errorf("opening existing repository: %w", err) + } } // do an initial fetch to setup remote tracking branches @@ -174,11 +197,18 @@ func NewSnapshotStore(ctx context.Context, logger *zap.Logger, url string, opts } } else { // fetch single reference - store.repo, err = git.InitWithOptions(store.storage, nil, git.InitOptions{ - DefaultBranch: plumbing.Main, - }) - if err != nil { - return nil, err + if empty { + store.repo, err = git.InitWithOptions(store.storage, nil, git.InitOptions{ + DefaultBranch: plumbing.Main, + }) + if err != nil { + return nil, err + } + } else { + store.repo, err = git.Open(store.storage, nil) + if err != nil { + return nil, fmt.Errorf("opening existing repository: %w", err) + } } if _, err = store.repo.CreateRemote(&config.RemoteConfig{ diff --git a/internal/storage/fs/git/store_test.go b/internal/storage/fs/git/store_test.go index 21c69ad069..187f097f93 100644 --- a/internal/storage/fs/git/store_test.go +++ b/internal/storage/fs/git/store_test.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "encoding/pem" + "fmt" "net/http/httptest" "os" "testing" @@ -122,83 +123,93 @@ flags: } func Test_Store_View_WithFilesystemStorage(t *testing.T) { - ch := make(chan struct{}) - store, skip := testStore(t, gitRepoURL, - WithFilesystemStorage(t.TempDir()), - WithPollOptions( - fs.WithInterval(time.Second), - fs.WithNotify(t, func(modified bool) { - if modified { - close(ch) - } - }), - )) - if skip { - return - } + dir := t.TempDir() + + // run 3 times to ensure we can handle case where directory is not empty + for i := range []int{1, 2, 3} { + i := i + t.Run(fmt.Sprintf("test-%d", i), func(t *testing.T) { + ch := make(chan struct{}) + store, skip := testStore(t, gitRepoURL, + WithFilesystemStorage(dir), + WithPollOptions( + fs.WithInterval(time.Second), + fs.WithNotify(t, func(modified bool) { + if modified { + close(ch) + } + }), + )) + if skip { + return + } - ctx, cancel := context.WithCancel(context.Background()) - t.Cleanup(cancel) + ctx, cancel := context.WithCancel(context.Background()) + t.Cleanup(cancel) - // pull repo - workdir := memfs.New() - repo, err := git.Clone(memory.NewStorage(), workdir, &git.CloneOptions{ - Auth: &http.BasicAuth{Username: "root", Password: "password"}, - URL: gitRepoURL, - RemoteName: "origin", - ReferenceName: plumbing.NewBranchReferenceName("main"), - }) - require.NoError(t, err) + // pull repo + workdir := memfs.New() + repo, err := git.Clone(memory.NewStorage(), workdir, &git.CloneOptions{ + Auth: &http.BasicAuth{Username: "root", Password: "password"}, + URL: gitRepoURL, + RemoteName: "origin", + ReferenceName: plumbing.NewBranchReferenceName("main"), + }) + require.NoError(t, err) - tree, err := repo.Worktree() - require.NoError(t, err) + tree, err := repo.Worktree() + require.NoError(t, err) - require.NoError(t, tree.Checkout(&git.CheckoutOptions{ - Branch: "refs/heads/main", - })) + require.NoError(t, tree.Checkout(&git.CheckoutOptions{ + Branch: "refs/heads/main", + })) - // update features.yml - fi, err := workdir.OpenFile("features.yml", os.O_TRUNC|os.O_RDWR, os.ModePerm) - require.NoError(t, err) + // update features.yml + fi, err := workdir.OpenFile("features.yml", os.O_TRUNC|os.O_RDWR, os.ModePerm) + require.NoError(t, err) - updated := []byte(`namespace: production + updated := []byte(`namespace: production flags: - key: foo - name: Foo`) + name: Foo + description: Foo description` + fmt.Sprintf(" %d", i)) + + _, err = fi.Write(updated) + require.NoError(t, err) + require.NoError(t, fi.Close()) + + // commit changes + _, err = tree.Commit("chore: update features.yml", &git.CommitOptions{ + All: true, + Author: &object.Signature{Email: "dev@flipt.io", Name: "dev"}, + }) + require.NoError(t, err) + + // push new commit + require.NoError(t, repo.Push(&git.PushOptions{ + Auth: &http.BasicAuth{Username: "root", Password: "password"}, + RemoteName: "origin", + })) + + // wait until the snapshot is updated or + // we timeout + select { + case <-ch: + case <-time.After(time.Minute): + t.Fatal("timed out waiting for snapshot") + } - _, err = fi.Write(updated) - require.NoError(t, err) - require.NoError(t, fi.Close()) + require.NoError(t, err) - // commit changes - _, err = tree.Commit("chore: update features.yml", &git.CommitOptions{ - All: true, - Author: &object.Signature{Email: "dev@flipt.io", Name: "dev"}, - }) - require.NoError(t, err) + t.Log("received new snapshot") - // push new commit - require.NoError(t, repo.Push(&git.PushOptions{ - Auth: &http.BasicAuth{Username: "root", Password: "password"}, - RemoteName: "origin", - })) + require.NoError(t, store.View(ctx, "", func(s storage.ReadOnlyStore) error { + _, err = s.GetFlag(ctx, storage.NewResource("production", "foo")) + return err + })) - // wait until the snapshot is updated or - // we timeout - select { - case <-ch: - case <-time.After(time.Minute): - t.Fatal("timed out waiting for snapshot") + }) } - - require.NoError(t, err) - - t.Log("received new snapshot") - - require.NoError(t, store.View(ctx, "", func(s storage.ReadOnlyStore) error { - _, err = s.GetFlag(ctx, storage.NewResource("production", "foo")) - return err - })) } func Test_Store_View_WithRevision(t *testing.T) {