Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

If applied, this will add options including recursive functionality to File Client Get(Gitlab), and add Tree Client with create,get,and list functionalities #153

Merged
merged 13 commits into from
Aug 23, 2022

Conversation

ranatrk
Copy link
Contributor

@ranatrk ranatrk commented Jun 9, 2022

If applied, this commit will add options including recursive functionality to FileClient().Get(..) in Gitlab
If applied, this commit will add Tree Client with create,get,and list functionalities

Signed-off-by: Rana Tarek Hassan [email protected]

Description

Test results

Github test results:

go test -v ./github/                                           1 ↵  ✹ ✭main 
=== RUN   Test_DomainVariations
=== RUN   Test_DomainVariations/github.com_domain
=== RUN   Test_DomainVariations/custom_domain_without_protocol
=== RUN   Test_DomainVariations/custom_domain_with_https_protocol
=== RUN   Test_DomainVariations/custom_domain_with_http_protocol
--- PASS: Test_DomainVariations (0.00s)
    --- PASS: Test_DomainVariations/github.com_domain (0.00s)
    --- PASS: Test_DomainVariations/custom_domain_without_protocol (0.00s)
    --- PASS: Test_DomainVariations/custom_domain_with_https_protocol (0.00s)
    --- PASS: Test_DomainVariations/custom_domain_with_http_protocol (0.00s)
=== RUN   TestProvider
Running Suite: GitHub Provider Suite
====================================
Random Seed: 1658074057
Will run 10 of 10 specs

••
------------------------------
• [SLOW TEST:5.902 seconds]
GitHub Provider
/home/rana/Desktop/workspace/ranatrk/go-git-providers/github/integration_test.go:128
  should be possible to create an org repository
  /home/rana/Desktop/workspace/ranatrk/go-git-providers/github/integration_test.go:257
------------------------------
•
------------------------------
• [SLOW TEST:5.301 seconds]
GitHub Provider
/home/rana/Desktop/workspace/ranatrk/go-git-providers/github/integration_test.go:128
  should error at creation time if the repo already does exist
  /home/rana/Desktop/workspace/ranatrk/go-git-providers/github/integration_test.go:345
------------------------------
• [SLOW TEST:6.759 seconds]
GitHub Provider
/home/rana/Desktop/workspace/ranatrk/go-git-providers/github/integration_test.go:128
  should update if the repository already exists when reconciling
  /home/rana/Desktop/workspace/ranatrk/go-git-providers/github/integration_test.go:351
------------------------------
•
------------------------------
• [SLOW TEST:26.525 seconds]
GitHub Provider
/home/rana/Desktop/workspace/ranatrk/go-git-providers/github/integration_test.go:128
  should be possible to create a pr for a user repository
  /home/rana/Desktop/workspace/ranatrk/go-git-providers/github/integration_test.go:428
------------------------------
••Deleting repos starting with test-repo for user: ranatrk
repos, len: 16
.....
Deleting repos starting with test-repo in org: rana-test-org

Ran 10 of 10 Specs in 62.948 seconds
SUCCESS! -- 10 Passed | 0 Failed | 0 Pending | 0 Skipped
--- PASS: TestProvider (62.95s)
=== RUN   Test_getPermissionFromMap
=== RUN   Test_getPermissionFromMap/pull
=== RUN   Test_getPermissionFromMap/push
=== RUN   Test_getPermissionFromMap/admin
=== RUN   Test_getPermissionFromMap/none
=== RUN   Test_getPermissionFromMap/false_data
=== RUN   Test_getPermissionFromMap/not_all_specifed
--- PASS: Test_getPermissionFromMap (0.00s)
    --- PASS: Test_getPermissionFromMap/pull (0.00s)
    --- PASS: Test_getPermissionFromMap/push (0.00s)
    --- PASS: Test_getPermissionFromMap/admin (0.00s)
    --- PASS: Test_getPermissionFromMap/none (0.00s)
    --- PASS: Test_getPermissionFromMap/false_data (0.00s)
    --- PASS: Test_getPermissionFromMap/not_all_specifed (0.00s)
=== RUN   Test_validateAPIObject
=== RUN   Test_validateAPIObject/no_error_=>_nil
=== RUN   Test_validateAPIObject/one_error_=>_MultiError_&_InvalidServerData
--- PASS: Test_validateAPIObject (0.00s)
    --- PASS: Test_validateAPIObject/no_error_=>_nil (0.00s)
    --- PASS: Test_validateAPIObject/one_error_=>_MultiError_&_InvalidServerData (0.00s)
=== RUN   Test_allPages
=== RUN   Test_allPages/one_page_only,_no_error
=== RUN   Test_allPages/two_pages,_no_error
=== RUN   Test_allPages/four_pages,_error_at_second
--- PASS: Test_allPages (0.00s)
    --- PASS: Test_allPages/one_page_only,_no_error (0.00s)
    --- PASS: Test_allPages/two_pages,_no_error (0.00s)
    --- PASS: Test_allPages/four_pages,_error_at_second (0.00s)
=== RUN   ExampleOrganizationsClient_Get
--- PASS: ExampleOrganizationsClient_Get (0.30s)
=== RUN   ExampleOrgRepositoriesClient_Get
--- PASS: ExampleOrgRepositoriesClient_Get (2.43s)
PASS
ok      github.com/fluxcd/go-git-providers/github       65.677s

Gitlab test results:

go test -v ./gitlab                                                                                         ✹ ✭main 
=== RUN   TestSupportedDomain
=== RUN   TestSupportedDomain/gitlab.com_domain
=== RUN   TestSupportedDomain/custom_domain_without_protocol
=== RUN   TestSupportedDomain/custom_domain_with_https_protocol
=== RUN   TestSupportedDomain/custom_domain_with_http_protocol
--- PASS: TestSupportedDomain (0.00s)
    --- PASS: TestSupportedDomain/gitlab.com_domain (0.00s)
    --- PASS: TestSupportedDomain/custom_domain_without_protocol (0.00s)
    --- PASS: TestSupportedDomain/custom_domain_with_https_protocol (0.00s)
    --- PASS: TestSupportedDomain/custom_domain_with_http_protocol (0.00s)
=== RUN   TestProvider
Running Suite: GitLab Provider Suite
====================================
Random Seed: 1658074152
Will run 13 of 13 specs

••validating repo: test-org-repo-449

------------------------------
• [SLOW TEST:8.369 seconds]
GitLab Provider
/home/rana/Desktop/workspace/ranatrk/go-git-providers/gitlab/integration_test.go:171
  should be possible to create a group project
  /home/rana/Desktop/workspace/ranatrk/go-git-providers/gitlab/integration_test.go:348
------------------------------
•validating repo: test-org-repo-449

------------------------------
• [SLOW TEST:15.121 seconds]
GitLab Provider
/home/rana/Desktop/workspace/ranatrk/go-git-providers/gitlab/integration_test.go:171
  should update if the org repo already exists when reconciling
  /home/rana/Desktop/workspace/ranatrk/go-git-providers/gitlab/integration_test.go:394
------------------------------
validating repo: test-shared-org-repo-720
• [SLOW TEST:191.487 seconds]
GitLab Provider
/home/rana/Desktop/workspace/ranatrk/go-git-providers/gitlab/integration_test.go:171
  should update teams with access and permissions when reconciling
  /home/rana/Desktop/workspace/ranatrk/go-git-providers/gitlab/integration_test.go:442
------------------------------
• [SLOW TEST:21.386 seconds]
GitLab Provider
/home/rana/Desktop/workspace/ranatrk/go-git-providers/gitlab/integration_test.go:171
  should create, delete and reconcile deploy keys
  /home/rana/Desktop/workspace/ranatrk/go-git-providers/gitlab/integration_test.go:555
------------------------------
••
------------------------------
• [SLOW TEST:12.624 seconds]
GitLab Provider
/home/rana/Desktop/workspace/ranatrk/go-git-providers/gitlab/integration_test.go:171
  should update if the user repo already exists when reconciling
  /home/rana/Desktop/workspace/ranatrk/go-git-providers/gitlab/integration_test.go:683
------------------------------
• [SLOW TEST:58.571 seconds]
GitLab Provider
/home/rana/Desktop/workspace/ranatrk/go-git-providers/gitlab/integration_test.go:171
  should be possible to create a pr for a user repository
  /home/rana/Desktop/workspace/ranatrk/go-git-providers/gitlab/integration_test.go:732
------------------------------
•
------------------------------
• [SLOW TEST:7.669 seconds]
GitLab Provider
/home/rana/Desktop/workspace/ranatrk/go-git-providers/gitlab/integration_test.go:171
  should be possible to download files from path and branch specified with nested directory
  /home/rana/Desktop/workspace/ranatrk/go-git-providers/gitlab/integration_test.go:889
------------------------------
Deleting the user repo:  test-repo2-971
Deleting the org repo:  test-org-repo-449
Deleting the shared org repo:  test-shared-org-repo-720
Deleting repos starting with test-repo for user: ranatrk
Deleting the org repo: test-repo-720
Deleting the org repo: test-repo-679
Deleting repos starting with test-shared-org-repo in org: rana-test-org
Deleting repos starting with test-org-repo in org: rana-test-org

Ran 13 of 13 Specs in 337.350 seconds
SUCCESS! -- 13 Passed | 0 Failed | 0 Pending | 0 Skipped
--- PASS: TestProvider (337.35s)
=== RUN   Test_getGitProviderPermission
=== RUN   Test_getGitProviderPermission/pull
=== RUN   Test_getGitProviderPermission/push
=== RUN   Test_getGitProviderPermission/admin
=== RUN   Test_getGitProviderPermission/false_data
--- PASS: Test_getGitProviderPermission (0.00s)
    --- PASS: Test_getGitProviderPermission/pull (0.00s)
    --- PASS: Test_getGitProviderPermission/push (0.00s)
    --- PASS: Test_getGitProviderPermission/admin (0.00s)
    --- PASS: Test_getGitProviderPermission/false_data (0.00s)
=== RUN   Test_getGitlabPermission
=== RUN   Test_getGitlabPermission/pull
=== RUN   Test_getGitlabPermission/push
=== RUN   Test_getGitlabPermission/admin
--- PASS: Test_getGitlabPermission (0.00s)
    --- PASS: Test_getGitlabPermission/pull (0.00s)
    --- PASS: Test_getGitlabPermission/push (0.00s)
    --- PASS: Test_getGitlabPermission/admin (0.00s)
=== RUN   Test_validateAPIObject
=== RUN   Test_validateAPIObject/no_error_=>_nil
=== RUN   Test_validateAPIObject/one_error_=>_MultiError_&_InvalidServerData
--- PASS: Test_validateAPIObject (0.00s)
    --- PASS: Test_validateAPIObject/no_error_=>_nil (0.00s)
    --- PASS: Test_validateAPIObject/one_error_=>_MultiError_&_InvalidServerData (0.00s)
=== RUN   Test_allGroupPages
=== RUN   Test_allGroupPages/one_page_only,_no_error
=== RUN   Test_allGroupPages/two_pages,_no_error
=== RUN   Test_allGroupPages/four_pages,_error_at_second
--- PASS: Test_allGroupPages (0.00s)
    --- PASS: Test_allGroupPages/one_page_only,_no_error (0.00s)
    --- PASS: Test_allGroupPages/two_pages,_no_error (0.00s)
    --- PASS: Test_allGroupPages/four_pages,_error_at_second (0.00s)
=== RUN   TestExampleOrganizationsClient_Get
Name: fluxcd-testing-public. Location: fluxcd-testing-public.--- PASS: TestExampleOrganizationsClient_Get (0.60s)
=== RUN   ExampleOrgRepositoriesClient_Get
--- PASS: ExampleOrgRepositoriesClient_Get (0.57s)
PASS
ok      github.com/fluxcd/go-git-providers/gitlab       338.529s

@stefanprodan stefanprodan requested a review from souleb June 14, 2022 10:07
@stefanprodan stefanprodan added the enhancement New feature or request label Jun 14, 2022
}
subdirectoryPath := fmt.Sprintf("%v%v/", path, *file.Name)
// recursive call for child directories to get their content
childFiles, err := c.Get(ctx, subdirectoryPath, branch, optFns...)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a pretty expensive operation (each file requested is a separate API call).

You are not providing any way to limit it, and there's no way to know up front how many files will be fetched?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using the repository content get in github API there is no option to know sub-directory contents, but I believe a call can be added to fetch the tree to know how many files will be fetched.
But then what decides the limitations or the max number of files/sub-directories to be fetched? will an extra parameter of the max tree length be a good idea?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • You could provide a maxFiles and maxDepth options.
  • You could accept a func(files *gitprovider.CommitFile) that would let user count the number of files and exit early.

gitprovider/options.go Outdated Show resolved Hide resolved
github/client_repository_file.go Outdated Show resolved Hide resolved
github/client_repository_file.go Outdated Show resolved Hide resolved
github/client_repository_file.go Outdated Show resolved Hide resolved
}
subdirectoryPath := fmt.Sprintf("%v%v/", path, *file.Name)
// recursive call for child directories to get their content
childFiles, err := c.Get(ctx, subdirectoryPath, branch, optFns...)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • You could provide a maxFiles and maxDepth options.
  • You could accept a func(files *gitprovider.CommitFile) that would let user count the number of files and exit early.

@ranatrk ranatrk requested a review from souleb June 15, 2022 10:20
github/client_repository_file.go Outdated Show resolved Hide resolved
github/client_repository_file.go Outdated Show resolved Hide resolved
github/client_repository_file.go Outdated Show resolved Hide resolved
github/client_repository_file.go Outdated Show resolved Hide resolved
github/client_repository_file.go Outdated Show resolved Hide resolved
@ranatrk ranatrk requested a review from souleb June 15, 2022 17:32
github/client_repository_file.go Outdated Show resolved Hide resolved
github/client_repository_file.go Outdated Show resolved Hide resolved
github/integration_test.go Outdated Show resolved Hide resolved
@ranatrk ranatrk requested a review from souleb June 16, 2022 12:07
@ranatrk ranatrk marked this pull request as draft June 28, 2022 12:19
@ranatrk ranatrk changed the title If applied, this commit will add options including recursive functionality to File Client Get If applied, this will add options including recursive functionality to File Client Get in Gitlab, and add Tree Client with create,get,and list functionalities Jul 17, 2022
@ranatrk ranatrk changed the title If applied, this will add options including recursive functionality to File Client Get in Gitlab, and add Tree Client with create,get,and list functionalities If applied, this will add options including recursive functionality to File Client Get(Gitlab), and add Tree Client with create,get,and list functionalities Jul 17, 2022
@ranatrk ranatrk force-pushed the main branch 2 times, most recently from 5ae2be2 to b92b5fc Compare July 17, 2022 17:41
@ranatrk
Copy link
Contributor Author

ranatrk commented Jul 17, 2022

PR updated to only add recursive option in gitlab where it is passed to the gitlab API call so no recursive API calls are performed.
Tree Client is added which can be used to list all the files in the tree, and so can be used to know the number of files in a directory before performing all the calls that could be expensive.
And so the user will then choose to retrieve these files contents if needed with the cost known before hand not in the middle of the recursive call

@ranatrk ranatrk marked this pull request as ready for review July 17, 2022 18:48
return &responseTreeInfo, nil
}

// Get returns a tree
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Get returns a tree
// Get returns a single tree using the SHA1 value for that tree.
//
// The number of files is limited to 100,000 after which `Truncated` will be set to true on the returned
// TreeInfo.

return nil, err
}

treeEntries := make([]*gitprovider.TreeEntry, 0)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would benefit from using len(githubTree.Entries) as the size, because you know up front how many entries there are?

return treeEntries, nil
}

func createTreeEntry(githubTreeEntry github.TreeEntry) *gitprovider.TreeEntry {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't used?

It looks like it could be used in both Get and List?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

initially it was for that reason, but the treeEntries in both Get and List are of different types. I will simply remove it.

github/integration_test.go Outdated Show resolved Hide resolved
github/integration_test.go Outdated Show resolved Hide resolved
gitprovider/options.go Show resolved Hide resolved

func (opts *FilesGetOptions) ApplyFilesGetOptions(target *FilesGetOptions) {
// Go through each field in opts, and apply it to target if set
if opts.Recursive == true {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could simplify this to...

target.Recursive = opts.Recursive

Because if target.Recursive is true then it'll be set to true, otherwise it would be set to false?

gitprovider/resources.go Show resolved Hide resolved
gitprovider/types_repository.go Show resolved Hide resolved
gitprovider/types_repository.go Show resolved Hide resolved
gitprovider/resources.go Outdated Show resolved Hide resolved
gitprovider/resources.go Outdated Show resolved Hide resolved
gitprovider/resources.go Show resolved Hide resolved
@@ -36,11 +36,18 @@ type FileClient struct {
}

// Get fetches and returns the contents of a file from a given branch and path
func (c *FileClient) Get(_ context.Context, path, branch string) ([]*gitprovider.CommitFile, error) {
func (c *FileClient) Get(ctx context.Context, path, branch string, optFns ...gitprovider.FilesGetOption) ([]*gitprovider.CommitFile, error) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

get you change description here too?

@@ -34,13 +34,18 @@ type FileClient struct {
ref gitprovider.RepositoryRef
}

// Get fetches and returns the contents of a file from a given branch and path
func (c *FileClient) Get(ctx context.Context, path, branch string) ([]*gitprovider.CommitFile, error) {
// Get fetches and returns the contents of a file or directory from a given branch and path with possible options of FilesGetOption
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After reading the API, It says it will always returns a directory content if the path reference a directory. So I think that the description should say that we return the content of a file, because we expect a directory path, right? We should also have a statement of what path do we expect (file or directory or both).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will update it with the content either being a file content given a file path, or content of files in a directory given the directory path.

github/client_repository_file.go Show resolved Hide resolved
ref gitprovider.RepositoryRef
}

// Create creates,updates,deletes a tree
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does it do that?

If true, the function name is wrong.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An example of its usage is in the Commit Create implementation here. The tree can be updated with additions.
In that case, the create itself could be removed from the tree implementation as the commit create can be used instead to add, update, delete content from a tree. What do you think @souleb?

github/client_repository_tree.go Show resolved Hide resolved

// List files (blob) in a tree
func (c *TreeClient) List(ctx context.Context, sha string, recursive bool) ([]*gitprovider.TreeEntry, error) {
return nil, fmt.Errorf("error listing tree items %s. not implemented in gitlab yet", sha)
Copy link
Collaborator

@foot foot Aug 8, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GitLab forces pagination on this API and limits to 100 per_page, however it does allow you to start at a subpath of the repo: https://docs.gitlab.com/ee/api/repositories.html

Proposal

In other places in this repository we auto-paginate results so we could follow that pattern here too.

Notes

  • For our particular case of deleting all the files in a subpath that should run on a cluster, the worst case could be 10000+ files, which is 100 pagination requests and will probably timeout. Asking the user to manually remove those files would be the workaround and seems reasonable. In the future we could add an ...Options param to this API to allow more control over pagination for other users of this library with other use cases.
  • I would guess the average case would be more on the order of < 100 files which we could handle in a single request or two.
  • As we can scope down to a subpath when requesting tree-entries it won't be the entire repo, just the path which makes things a bit better.
  • As Gitlab does not have a tree api for doing cleverer manipulations, the only other options to perform recursive deletion operations efficiently for gitlab seems to be:
    1. Grab file archive (limited to 5 requests / minute on gitlab.com)
    2. Git clone.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In other places in this repository we auto-paginate results so we could follow that pattern here too.

This is the main reason I recommend against using go-git-providers, it has no control over pages being loaded.

This pattern is probably a DoS risk, because you can't control how many pages might be loaded for any resource.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • with the current limitation mentioned in this issue and sticking to the limit of 100 files when gitlab and giving a warning of this limitation so that if there are more files in the specific directory they are to be deleted manually, is this a possible temporary solution?
  • how about the number of pages loaded for a resource to be limited with a max_pages flag passed

"strings"

"github.com/fluxcd/go-git-providers/gitprovider"
"github.com/google/go-github/v42/github"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems to be a clash here, might have to merge in main to your fork and update this to v45 now I think.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated, thanks

ranatrk and others added 9 commits August 15, 2022 11:44
…d recursive options in Files Get

Signed-off-by: Rana Tarek Hassan <[email protected]>
…ursive calls in Files Get

Add more test cases for recursive, maxDepth, maxFiles options in Files Get

Signed-off-by: Rana Tarek Hassan <[email protected]>
Add create,get, and list functionalities to tree client of github

not implemented in gitlab and stash clients

Signed-off-by: Rana Tarek Hassan <[email protected]>
LICENSE typo

Co-authored-by: Kevin McDermott <[email protected]>
Signed-off-by: Rana Tarek Hassan <[email protected]>
Co-authored-by: souleb <[email protected]>
Signed-off-by: Rana Tarek Hassan <[email protected]>
It will add go docs based on suggestions from code review

Signed-off-by: Rana Tarek Hassan <[email protected]>
…n gitlab client

It will add path parameter used in github tree list to optionally filter files in a certain path

Signed-off-by: Rana Tarek Hassan <[email protected]>
Copy link
Collaborator

@foot foot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking really good!

We should have a simple test for tree.Create I think. But alternatively we could remove that interface for now and do it in another PR one day if you're keen to get this landed. wdyt?

gitlab/integration_test.go Show resolved Hide resolved
Expect(treeEntries).To(HaveLen(3))
for ind, treeEntry := range treeEntries {
Expect(treeEntry.Path).To(Equal(*files[ind].Path))
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

}

// Create creates,updates,deletes a tree
func (c *TreeClient) Create(ctx context.Context, tree *gitprovider.TreeInfo) (*gitprovider.TreeInfo, error) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't really have any tests for this function. Would that be easy to write? The other option would be to remove the Create for now as we don't really use it and make this a more Read-only (get/list) PR. wdyt?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think making it Read-only get/list is a good option, because the functionality needed so far from the create is implemented using the commit client where the tree is updated with the new tree structure

Copy link
Collaborator

@foot foot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💯

Awesome job, thanks for being patient with all the back and forth on this one.

Very clean ✨ and very useful functionality to have!

Copy link
Member

@souleb souleb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@souleb souleb merged commit dd5c65f into fluxcd:main Aug 23, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants