diff --git a/build/release.yml b/build/release.yml index bc86666c..557387a3 100644 --- a/build/release.yml +++ b/build/release.yml @@ -25,7 +25,7 @@ steps: - cf_export GO111MODULE=on - cf_export GOCACHE=/codefresh/volume/gocache # change gopath to codefresh shared volume - cf_export GOPATH=/codefresh/volume/gopath - - cf_export PATH=$PATH:/codefresh/volume/gopath/bin + - cf_export PATH=$PATH:/codefresh/volume/gopath/bin:/home/linuxbrew/.linuxbrew/bin:/home/linuxbrew/.linuxbrew/sbin # must have brew in path for final brew step - (echo "${{CF_BRANCH}}" | grep -Eq '^v(\d+\.)?(\d+\.)?(\*|\d+)$') && echo on release branch || (echo not on release branch && exit 1) - (gh release view ${{CF_BRANCH}} --repo ${{CF_REPO_OWNER}}/${{CF_REPO_NAME}}) && (echo release with the same name already exists && exit 1) || echo no release with the name ${{CF_BRANCH}}, we can continue! when: @@ -182,7 +182,7 @@ steps: environment: - HOMEBREW_GITHUB_API_TOKEN=${{GITHUB_TOKEN}} commands: - - brew bump-formula-pr --strict --no-browser --tag ${{CF_BRANCH_TAG_NORMALIZED}} --revision ${{CF_REVISION}} --fork-org codefresh-io + - brew bump-formula-pr --strict --no-browse --tag ${{CF_BRANCH_TAG_NORMALIZED}} --revision ${{CF_REVISION}} --fork-org codefresh-io when: steps: - name: create_release diff --git a/cmd/commands/app.go b/cmd/commands/app.go index 88c7d91b..20cd28ef 100644 --- a/cmd/commands/app.go +++ b/cmd/commands/app.go @@ -24,9 +24,10 @@ import ( type ( AppCreateOptions struct { - CloneOpts *git.CloneOptions - ProjectName string - AppOpts *application.CreateOptions + CloneOpts *git.CloneOptions + AppsCloneOpts *git.CloneOptions + ProjectName string + AppOpts *application.CreateOptions } AppDeleteOptions struct { @@ -65,8 +66,9 @@ func NewAppCommand() *cobra.Command { func NewAppCreateCommand(cloneOpts *git.CloneOptions) *cobra.Command { var ( - appOpts *application.CreateOptions - projectName string + appsCloneOpts *git.CloneOptions + appOpts *application.CreateOptions + projectName string ) cmd := &cobra.Command{ @@ -86,11 +88,26 @@ func NewAppCreateCommand(cloneOpts *git.CloneOptions) *cobra.Command { # using the --type flag (kustomize|dir) is optional. If it is ommitted, will clone # the --app repository, and infer the type automatically. -# Create a new application from kustomization in a remote repository +# Create a new application from kustomization in a remote repository (will reference the HEAD revision) + + app create --app github.com/some_org/some_repo/manifests --project project_name + +# Reference a specific git commit hash: + + app create --app github.com/some_org/some_repo/manifests?sha= --project project_name + +# Reference a specific git tag: - app create --app github.com/some_org/some_repo/manifests?ref=v1.2.3 --project project_name + app create --app github.com/some_org/some_repo/manifests?tag= --project project_name + +# Reference a specific git branch: + + app create --app github.com/some_org/some_repo/manifests?ref= --project project_name `), - PreRun: func(cmd *cobra.Command, args []string) { cloneOpts.Parse() }, + PreRun: func(_ *cobra.Command, _ []string) { + cloneOpts.Parse() + appsCloneOpts.Parse() + }, RunE: func(cmd *cobra.Command, args []string) error { if len(args) < 1 { log.G().Fatal("must enter application name") @@ -98,14 +115,16 @@ func NewAppCreateCommand(cloneOpts *git.CloneOptions) *cobra.Command { appOpts.AppName = args[0] return RunAppCreate(cmd.Context(), &AppCreateOptions{ - CloneOpts: cloneOpts, - ProjectName: projectName, - AppOpts: appOpts, + CloneOpts: cloneOpts, + AppsCloneOpts: appsCloneOpts, + ProjectName: projectName, + AppOpts: appOpts, }) }, } cmd.Flags().StringVarP(&projectName, "project", "p", "", "Project name") + appsCloneOpts = git.AddFlags(cmd, memfs.New(), "apps") appOpts = application.AddFlags(cmd) die(cmd.MarkFlagRequired("app")) @@ -120,16 +139,35 @@ func RunAppCreate(ctx context.Context, opts *AppCreateOptions) error { return err } + var ( + appsRepo git.Repository + appsfs fs.FS + ) + + if opts.AppsCloneOpts.Repo != "" { + if opts.AppsCloneOpts.Auth.Password == "" { + opts.AppsCloneOpts.Auth.Password = opts.CloneOpts.Auth.Password + } + + appsRepo, appsfs, err = clone(ctx, opts.AppsCloneOpts) + if err != nil { + return err + } + } else { + opts.AppsCloneOpts = opts.CloneOpts + appsRepo, appsfs = r, repofs + } + if err = setAppOptsDefaults(ctx, repofs, opts); err != nil { return err } - app, err := opts.AppOpts.Parse(opts.ProjectName, opts.CloneOpts.URL(), opts.CloneOpts.Revision(), opts.CloneOpts.Path()) + app, err := parseApp(opts.AppOpts, opts.ProjectName, opts.CloneOpts.URL(), opts.CloneOpts.Revision(), opts.CloneOpts.Path()) if err != nil { return fmt.Errorf("failed to parse application from flags: %v", err) } - if err = app.CreateFiles(repofs, opts.ProjectName); err != nil { + if err = app.CreateFiles(repofs, appsfs, opts.ProjectName); err != nil { if errors.Is(err, application.ErrAppAlreadyInstalledOnProject) { return fmt.Errorf("application '%s' already exists in project '%s': %w", app.Name(), opts.ProjectName, err) } @@ -137,6 +175,13 @@ func RunAppCreate(ctx context.Context, opts *AppCreateOptions) error { return err } + if opts.AppsCloneOpts != opts.CloneOpts { + log.G().Info("committing changes to apps repo...") + if err = appsRepo.Persist(ctx, &git.PushOptions{CommitMsg: getCommitMsg(opts, appsfs)}); err != nil { + return fmt.Errorf("failed to push to apps repo: %w", err) + } + } + log.G().Info("committing changes to gitops repo...") if err = r.Persist(ctx, &git.PushOptions{CommitMsg: getCommitMsg(opts, repofs)}); err != nil { return fmt.Errorf("failed to push to gitops repo: %w", err) @@ -147,7 +192,7 @@ func RunAppCreate(ctx context.Context, opts *AppCreateOptions) error { return nil } -func setAppOptsDefaults(ctx context.Context, repofs fs.FS, opts *AppCreateOptions) error { +var setAppOptsDefaults = func(ctx context.Context, repofs fs.FS, opts *AppCreateOptions) error { var err error if opts.AppOpts.DestServer == store.Default.DestServer || opts.AppOpts.DestServer == "" { @@ -189,6 +234,10 @@ func setAppOptsDefaults(ctx context.Context, repofs fs.FS, opts *AppCreateOption return nil } +var parseApp = func(appOpts *application.CreateOptions, projectName, repoURL, targetRevision, repoRoot string) (application.Application, error) { + return appOpts.Parse(projectName, repoURL, targetRevision, repoRoot) +} + func getProjectDestServer(repofs fs.FS, projectName string) (string, error) { path := repofs.Join(store.Default.ProjectsDir, projectName+".yaml") p := &argocdv1alpha1.AppProject{} @@ -227,7 +276,7 @@ func NewAppListCommand(cloneOpts *git.CloneOptions) *cobra.Command { app list `), - PreRun: func(cmd *cobra.Command, args []string) { cloneOpts.Parse() }, + PreRun: func(_ *cobra.Command, _ []string) { cloneOpts.Parse() }, RunE: func(cmd *cobra.Command, args []string) error { if len(args) < 1 { log.G().Fatal("must enter a project name") @@ -311,7 +360,7 @@ func NewAppDeleteCommand(cloneOpts *git.CloneOptions) *cobra.Command { app delete --project `), - PreRun: func(cmd *cobra.Command, args []string) { cloneOpts.Parse() }, + PreRun: func(_ *cobra.Command, _ []string) { cloneOpts.Parse() }, RunE: func(cmd *cobra.Command, args []string) error { if len(args) < 1 { log.G().Fatal("must enter application name") diff --git a/cmd/commands/app_test.go b/cmd/commands/app_test.go index 2030f000..ac5986d7 100644 --- a/cmd/commands/app_test.go +++ b/cmd/commands/app_test.go @@ -2,13 +2,13 @@ package commands import ( "context" - "encoding/json" "fmt" "path/filepath" "strings" "testing" "github.com/argoproj-labs/argocd-autopilot/pkg/application" + appmocks "github.com/argoproj-labs/argocd-autopilot/pkg/application/mocks" "github.com/argoproj-labs/argocd-autopilot/pkg/fs" fsmocks "github.com/argoproj-labs/argocd-autopilot/pkg/fs/mocks" "github.com/argoproj-labs/argocd-autopilot/pkg/git" @@ -23,6 +23,260 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) +func TestRunAppCreate(t *testing.T) { + tests := map[string]struct { + appsRepo string + wantErr string + prepareRepo func() (git.Repository, fs.FS, error) + clone func(t *testing.T, cloneOpts *git.CloneOptions) (git.Repository, fs.FS, error) + setAppOptsDefaultsErr error + parseApp func() (application.Application, error) + assertFn func(t *testing.T, gitopsRepo git.Repository, appsRepo git.Repository) + }{ + "Should fail when clone fails": { + wantErr: "some error", + prepareRepo: func() (git.Repository, fs.FS, error) { + return nil, nil, fmt.Errorf("some error") + }, + }, + "Should fail if srcClone fails": { + appsRepo: "https://github.com/owner/other_name", + wantErr: "some error", + prepareRepo: func() (git.Repository, fs.FS, error) { + return nil, nil, nil + }, + clone: func(t *testing.T, cloneOpts *git.CloneOptions) (git.Repository, fs.FS, error) { + return nil, nil, fmt.Errorf("some error") + }, + }, + "Should use cloneOpts password for srcCloneOpts, if required": { + appsRepo: "https://github.com/owner/other_name/path?ref=branch", + wantErr: "some error", + prepareRepo: func() (git.Repository, fs.FS, error) { + return nil, nil, nil + }, + clone: func(t *testing.T, cloneOpts *git.CloneOptions) (git.Repository, fs.FS, error) { + assert.Equal(t, "https://github.com/owner/other_name.git", cloneOpts.URL()) + assert.Equal(t, "branch", cloneOpts.Revision()) + assert.Equal(t, "path", cloneOpts.Path()) + assert.Equal(t, "password", cloneOpts.Auth.Password) + return nil, nil, fmt.Errorf("some error") + }, + }, + "Should fail if setAppOptsDefaults fails": { + wantErr: "some error", + prepareRepo: func() (git.Repository, fs.FS, error) { + return nil, nil, nil + }, + setAppOptsDefaultsErr: fmt.Errorf("some error"), + }, + "Should fail if app parse fails": { + wantErr: "failed to parse application from flags: some error", + prepareRepo: func() (git.Repository, fs.FS, error) { + return nil, nil, nil + }, + parseApp: func() (application.Application, error) { + return nil, fmt.Errorf("some error") + }, + }, + "Should fail if app already exist in project": { + wantErr: fmt.Errorf("application 'app' already exists in project 'project': %w", application.ErrAppAlreadyInstalledOnProject).Error(), + prepareRepo: func() (git.Repository, fs.FS, error) { + memfs := memfs.New() + _ = memfs.MkdirAll(filepath.Join(store.Default.AppsDir, "app", store.Default.OverlaysDir, "project"), 0666) + mockRepo := &gitmocks.Repository{} + return mockRepo, fs.Create(memfs), nil + }, + parseApp: func() (application.Application, error) { + app := &appmocks.Application{} + app.On("Name").Return("app") + app.On("CreateFiles", mock.Anything, mock.Anything, "project").Return(application.ErrAppAlreadyInstalledOnProject) + return app, nil + }, + }, + "Should fail if file creation fails": { + wantErr: "some error", + prepareRepo: func() (git.Repository, fs.FS, error) { + memfs := memfs.New() + _ = memfs.MkdirAll(filepath.Join(store.Default.AppsDir, "app", store.Default.OverlaysDir, "project"), 0666) + mockRepo := &gitmocks.Repository{} + return mockRepo, fs.Create(memfs), nil + }, + parseApp: func() (application.Application, error) { + app := &appmocks.Application{} + app.On("Name").Return("app") + app.On("CreateFiles", mock.Anything, mock.Anything, "project").Return(fmt.Errorf("some error")) + return app, nil + }, + }, + "Should fail if commiting to appsRepo fails": { + appsRepo: "https://github.com/owner/other_name", + wantErr: "failed to push to apps repo: some error", + prepareRepo: func() (git.Repository, fs.FS, error) { + memfs := memfs.New() + _ = memfs.MkdirAll(filepath.Join(store.Default.AppsDir, "app", store.Default.OverlaysDir, "project"), 0666) + mockRepo := &gitmocks.Repository{} + return mockRepo, fs.Create(memfs), nil + }, + clone: func(_ *testing.T, _ *git.CloneOptions) (git.Repository, fs.FS, error) { + mockRepo := &gitmocks.Repository{} + mockRepo.On("Persist", mock.Anything, &git.PushOptions{ + CommitMsg: "installed app 'app' on project 'project' installation-path: '/'", + }).Return(fmt.Errorf("some error")) + return mockRepo, fs.Create(memfs.New()), nil + }, + parseApp: func() (application.Application, error) { + app := &appmocks.Application{} + app.On("Name").Return("app") + app.On("CreateFiles", mock.Anything, mock.Anything, "project").Return(nil) + return app, nil + }, + }, + "Should fail if commiting to gitops repo fails": { + wantErr: "failed to push to gitops repo: some error", + prepareRepo: func() (git.Repository, fs.FS, error) { + memfs := memfs.New() + _ = memfs.MkdirAll(filepath.Join(store.Default.AppsDir, "app", store.Default.OverlaysDir, "project"), 0666) + mockRepo := &gitmocks.Repository{} + mockRepo.On("Persist", mock.Anything, &git.PushOptions{ + CommitMsg: "installed app 'app' on project 'project' installation-path: '/'", + }).Return(fmt.Errorf("some error")) + return mockRepo, fs.Create(memfs), nil + }, + parseApp: func() (application.Application, error) { + app := &appmocks.Application{} + app.On("Name").Return("app") + app.On("CreateFiles", mock.Anything, mock.Anything, "project").Return(nil) + return app, nil + }, + }, + "Should Persist to both repos, if required": { + appsRepo: "https://github.com/owner/other_name", + wantErr: "failed to push to gitops repo: some error", + prepareRepo: func() (git.Repository, fs.FS, error) { + memfs := memfs.New() + _ = memfs.MkdirAll(filepath.Join(store.Default.AppsDir, "app", store.Default.OverlaysDir, "project"), 0666) + mockRepo := &gitmocks.Repository{} + mockRepo.On("Persist", mock.Anything, &git.PushOptions{ + CommitMsg: "installed app 'app' on project 'project' installation-path: '/'", + }).Return(nil) + return mockRepo, fs.Create(memfs), nil + }, + clone: func(_ *testing.T, _ *git.CloneOptions) (git.Repository, fs.FS, error) { + mockRepo := &gitmocks.Repository{} + mockRepo.On("Persist", mock.Anything, &git.PushOptions{ + CommitMsg: "installed app 'app' on project 'project' installation-path: '/'", + }).Return(nil) + return mockRepo, fs.Create(memfs.New()), nil + }, + parseApp: func() (application.Application, error) { + app := &appmocks.Application{} + app.On("Name").Return("app") + app.On("CreateFiles", mock.Anything, mock.Anything, "project").Return(nil) + return app, nil + }, + assertFn: func(t *testing.T, gitopsRepo git.Repository, appsRepo git.Repository) { + gitopsRepo.(*gitmocks.Repository).AssertExpectations(t) + appsRepo.(*gitmocks.Repository).AssertExpectations(t) + }, + }, + "Should Persist to a single repo, if required": { + wantErr: "failed to push to gitops repo: some error", + prepareRepo: func() (git.Repository, fs.FS, error) { + memfs := memfs.New() + _ = memfs.MkdirAll(filepath.Join(store.Default.AppsDir, "app", store.Default.OverlaysDir, "project"), 0666) + mockRepo := &gitmocks.Repository{} + mockRepo.On("Persist", mock.Anything, &git.PushOptions{ + CommitMsg: "installed app 'app' on project 'project' installation-path: '/'", + }).Return(nil) + return mockRepo, fs.Create(memfs), nil + }, + parseApp: func() (application.Application, error) { + app := &appmocks.Application{} + app.On("Name").Return("app") + app.On("CreateFiles", mock.Anything, mock.Anything, "project").Return(nil) + return app, nil + }, + assertFn: func(t *testing.T, gitopsRepo git.Repository, appsRepo git.Repository) { + assert.Nil(t, appsRepo) + gitopsRepo.(*gitmocks.Repository).AssertExpectations(t) + }, + }, + } + origPrepareRepo, origClone, origSetAppOptsDefault, origAppParse := prepareRepo, clone, setAppOptsDefaults, parseApp + defer func() { + prepareRepo = origPrepareRepo + clone = origClone + setAppOptsDefaults = origSetAppOptsDefault + parseApp = origAppParse + }() + + for name, tt := range tests { + t.Run(name, func(t *testing.T) { + var ( + gitopsRepo git.Repository + appsRepo git.Repository + ) + + prepareRepo = func(_ context.Context, _ *git.CloneOptions, _ string) (git.Repository, fs.FS, error) { + var ( + repofs fs.FS + err error + ) + gitopsRepo, repofs, err = tt.prepareRepo() + return gitopsRepo, repofs, err + } + clone = func(_ context.Context, cloneOpts *git.CloneOptions) (git.Repository, fs.FS, error) { + var ( + repofs fs.FS + err error + ) + appsRepo, repofs, err = tt.clone(t, cloneOpts) + return appsRepo, repofs, err + } + setAppOptsDefaults = func(_ context.Context, _ fs.FS, _ *AppCreateOptions) error { + return tt.setAppOptsDefaultsErr + } + parseApp = func(_ *application.CreateOptions, _, _, _, _ string) (application.Application, error) { + return tt.parseApp() + } + opts := &AppCreateOptions{ + CloneOpts: &git.CloneOptions{ + Repo: "https://github.com/owner/name", + Auth: git.Auth{ + Password: "password", + }, + }, + AppsCloneOpts: &git.CloneOptions{ + Repo: tt.appsRepo, + }, + ProjectName: "project", + AppOpts: &application.CreateOptions{ + AppName: "app", + AppType: application.AppTypeDirectory, + AppSpecifier: "https://github.com/owner/name/manifests", + }, + } + + opts.CloneOpts.Parse() + opts.AppsCloneOpts.Parse() + if err := RunAppCreate(context.Background(), opts); err != nil { + if tt.wantErr != "" { + assert.EqualError(t, err, tt.wantErr) + } else { + t.Errorf("RunAppCreate() error = %v", err) + } + + return + } + + if tt.assertFn != nil { + tt.assertFn(t, gitopsRepo, appsRepo) + } + }) + } +} + func Test_getCommitMsg(t *testing.T) { tests := map[string]struct { appName string @@ -74,8 +328,7 @@ func Test_getConfigFileFromPath(t *testing.T) { appName: "test", beforeFn: func(repofs fs.FS, appName string) fs.FS { conf := application.Config{AppName: appName} - b, _ := json.Marshal(&conf) - _ = billyUtils.WriteFile(repofs, fmt.Sprintf("%s/config.json", appName), b, 0666) + _ = repofs.WriteJson(fmt.Sprintf("%s/config.json", appName), conf) return repofs }, assertFn: func(t *testing.T, conf *application.Config) { @@ -165,7 +418,7 @@ func TestRunAppDelete(t *testing.T) { memfs := memfs.New() _ = memfs.MkdirAll(filepath.Join(store.Default.AppsDir, "app", store.Default.OverlaysDir, "project"), 0666) mockRepo := &gitmocks.Repository{} - mockRepo.On("Persist", mock.AnythingOfType("*context.emptyCtx"), &git.PushOptions{ + mockRepo.On("Persist", mock.Anything, &git.PushOptions{ CommitMsg: "Deleted app 'app'", }).Return(nil) return mockRepo, fs.Create(memfs), nil @@ -209,7 +462,7 @@ func TestRunAppDelete(t *testing.T) { _ = memfs.MkdirAll(filepath.Join(store.Default.AppsDir, "app", store.Default.OverlaysDir, "project"), 0666) _ = memfs.MkdirAll(filepath.Join(store.Default.AppsDir, "app", store.Default.OverlaysDir, "project2"), 0666) mockRepo := &gitmocks.Repository{} - mockRepo.On("Persist", mock.AnythingOfType("*context.emptyCtx"), &git.PushOptions{ + mockRepo.On("Persist", mock.Anything, &git.PushOptions{ CommitMsg: "Deleted app 'app' from project 'project'", }).Return(nil) return mockRepo, fs.Create(memfs), nil @@ -227,7 +480,7 @@ func TestRunAppDelete(t *testing.T) { memfs := memfs.New() _ = memfs.MkdirAll(filepath.Join(store.Default.AppsDir, "app", store.Default.OverlaysDir, "project"), 0666) mockRepo := &gitmocks.Repository{} - mockRepo.On("Persist", mock.AnythingOfType("*context.emptyCtx"), &git.PushOptions{ + mockRepo.On("Persist", mock.Anything, &git.PushOptions{ CommitMsg: "Deleted app 'app'", }).Return(nil) return mockRepo, fs.Create(memfs), nil @@ -245,7 +498,7 @@ func TestRunAppDelete(t *testing.T) { _ = memfs.MkdirAll(filepath.Join(store.Default.AppsDir, "app", "project"), 0666) _ = memfs.MkdirAll(filepath.Join(store.Default.AppsDir, "app", "project2"), 0666) mockRepo := &gitmocks.Repository{} - mockRepo.On("Persist", mock.AnythingOfType("*context.emptyCtx"), &git.PushOptions{ + mockRepo.On("Persist", mock.Anything, &git.PushOptions{ CommitMsg: "Deleted app 'app' from project 'project'", }).Return(nil) return mockRepo, fs.Create(memfs), nil @@ -263,7 +516,7 @@ func TestRunAppDelete(t *testing.T) { memfs := memfs.New() _ = memfs.MkdirAll(filepath.Join(store.Default.AppsDir, "app", "project"), 0666) mockRepo := &gitmocks.Repository{} - mockRepo.On("Persist", mock.AnythingOfType("*context.emptyCtx"), &git.PushOptions{ + mockRepo.On("Persist", mock.Anything, &git.PushOptions{ CommitMsg: "Deleted app 'app'", }).Return(nil) return mockRepo, fs.Create(memfs), nil @@ -281,7 +534,7 @@ func TestRunAppDelete(t *testing.T) { memfs := memfs.New() _ = memfs.MkdirAll(filepath.Join(store.Default.AppsDir, "app", store.Default.OverlaysDir, "project"), 0666) mockRepo := &gitmocks.Repository{} - mockRepo.On("Persist", mock.AnythingOfType("*context.emptyCtx"), &git.PushOptions{ + mockRepo.On("Persist", mock.Anything, &git.PushOptions{ CommitMsg: "Deleted app 'app'", }).Return(fmt.Errorf("some error")) return mockRepo, fs.Create(memfs), nil diff --git a/cmd/commands/project.go b/cmd/commands/project.go index 2151aa4b..632b6def 100644 --- a/cmd/commands/project.go +++ b/cmd/commands/project.go @@ -104,7 +104,7 @@ func NewProjectCreateCommand(cloneOpts *git.CloneOptions) *cobra.Command { project create `), - PreRun: func(cmd *cobra.Command, args []string) { cloneOpts.Parse() }, + PreRun: func(_ *cobra.Command, _ []string) { cloneOpts.Parse() }, RunE: func(cmd *cobra.Command, args []string) error { if len(args) < 1 { log.G().Fatal("must enter project name") @@ -339,7 +339,7 @@ func NewProjectListCommand(cloneOpts *git.CloneOptions) *cobra.Command { project list `), - PreRun: func(cmd *cobra.Command, args []string) { cloneOpts.Parse() }, + PreRun: func(_ *cobra.Command, _ []string) { cloneOpts.Parse() }, RunE: func(cmd *cobra.Command, args []string) error { return RunProjectList(cmd.Context(), &ProjectListOptions{ CloneOpts: cloneOpts, @@ -407,7 +407,7 @@ func NewProjectDeleteCommand(cloneOpts *git.CloneOptions) *cobra.Command { project delete `), - PreRun: func(cmd *cobra.Command, args []string) { cloneOpts.Parse() }, + PreRun: func(_ *cobra.Command, _ []string) { cloneOpts.Parse() }, RunE: func(cmd *cobra.Command, args []string) error { if len(args) < 1 { log.G().Fatal("must enter project name") diff --git a/cmd/commands/repo.go b/cmd/commands/repo.go index d74d5d89..15b63ce8 100644 --- a/cmd/commands/repo.go +++ b/cmd/commands/repo.go @@ -664,9 +664,10 @@ func createBootstrapKustomization(namespace, repoURL, appSpecifier string) (*kus } func createCreds(repoUrl string) ([]byte, error) { - creds := []argocdsettings.Repository{ + host, _, _, _, _ := git.ParseGitUrl(repoUrl) + creds := []argocdsettings.RepositoryCredentials{ { - URL: repoUrl, + URL: host, UsernameSecret: &v1.SecretKeySelector{ LocalObjectReference: v1.LocalObjectReference{ Name: "autopilot-secret", diff --git a/docs/commands/argocd-autopilot_application_create.md b/docs/commands/argocd-autopilot_application_create.md index f75f5173..70dbdc91 100644 --- a/docs/commands/argocd-autopilot_application_create.md +++ b/docs/commands/argocd-autopilot_application_create.md @@ -23,9 +23,21 @@ argocd-autopilot application create [APP_NAME] [flags] # using the --type flag (kustomize|dir) is optional. If it is ommitted, argocd-autopilot will clone # the --app repository, and infer the type automatically. -# Create a new application from kustomization in a remote repository +# Create a new application from kustomization in a remote repository (will reference the HEAD revision) - argocd-autopilot app create --app github.com/some_org/some_repo/manifests?ref=v1.2.3 --project project_name + argocd-autopilot app create --app github.com/some_org/some_repo/manifests --project project_name + +# Reference a specific git commit hash: + + argocd-autopilot app create --app github.com/some_org/some_repo/manifests?sha= --project project_name + +# Reference a specific git tag: + + argocd-autopilot app create --app github.com/some_org/some_repo/manifests?tag= --project project_name + +# Reference a specific git branch: + + argocd-autopilot app create --app github.com/some_org/some_repo/manifests?ref= --project project_name ``` @@ -33,6 +45,8 @@ argocd-autopilot application create [APP_NAME] [flags] ``` --app string The application specifier (e.g. github.com/argoproj/argo-workflows/manifests/cluster-install/?ref=v3.0.3) + --apps-git-token string Your git provider api token [APPS_GIT_TOKEN] + --apps-repo string Repository URL [APPS_GIT_REPO] --dest-namespace string K8s target namespace (overrides the namespace specified in the kustomization.yaml) --dest-server string K8s cluster URL (e.g. https://kubernetes.default.svc) (default "https://kubernetes.default.svc") -h, --help help for create diff --git a/go.mod b/go.mod index b592e92f..b4fb33e9 100644 --- a/go.mod +++ b/go.mod @@ -11,13 +11,12 @@ require ( github.com/go-git/go-billy/v5 v5.3.1 github.com/go-git/go-git/v5 v5.4.1 github.com/gobuffalo/packr v1.30.1 - github.com/google/go-github/v34 v34.0.0 + github.com/google/go-github/v35 v35.3.0 github.com/sirupsen/logrus v1.8.1 github.com/spf13/cobra v1.1.3 github.com/spf13/pflag v1.0.5 github.com/spf13/viper v1.7.1 github.com/stretchr/testify v1.7.0 - gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect k8s.io/api v0.21.1 k8s.io/apimachinery v0.21.1 k8s.io/cli-runtime v0.21.1 diff --git a/go.sum b/go.sum index ded4b4ad..9826f229 100644 --- a/go.sum +++ b/go.sum @@ -406,12 +406,13 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-github/v29 v29.0.2 h1:opYN6Wc7DOz7Ku3Oh4l7prmkOMwEcQxpFtxdU8N8Pts= github.com/google/go-github/v29 v29.0.2/go.mod h1:CHKiKKPHJ0REzfwc14QMklvtHwCveD0PxlMjLlzAM5E= -github.com/google/go-github/v34 v34.0.0 h1:/siYFImY8KwGc5QD1gaPf+f8QX6tLwxNIco2RkYxoFA= -github.com/google/go-github/v34 v34.0.0/go.mod h1:w/2qlrXUfty+lbyO6tatnzIw97v1CM+/jZcwXMDiPQQ= +github.com/google/go-github/v35 v35.3.0 h1:fU+WBzuukn0VssbayTT+Zo3/ESKX9JYWjbZTLOTEyho= +github.com/google/go-github/v35 v35.3.0/go.mod h1:yWB7uCcVWaUbUP74Aq3whuMySRMatyRmq5U9FTNlbio= github.com/google/go-jsonnet v0.17.0 h1:/9NIEfhK1NQRKl3sP2536b2+x5HnZMdql7x3yK/l8JY= github.com/google/go-jsonnet v0.17.0/go.mod h1:sOcuej3UW1vpPTZOr8L7RQimqai1a57bt5j22LzGZCw= github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= @@ -1240,9 +1241,8 @@ gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ= gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= diff --git a/pkg/application/application.go b/pkg/application/application.go index 10a35e9a..0db354e9 100644 --- a/pkg/application/application.go +++ b/pkg/application/application.go @@ -51,7 +51,7 @@ type ( Application interface { Name() string - CreateFiles(repofs fs.FS, projectName string) error + CreateFiles(repofs fs.FS, appsfs fs.FS, projectName string) error } Config struct { @@ -288,48 +288,55 @@ func newKustApp(o *CreateOptions, projectName, repoURL, targetRevision, repoRoot return app, nil } -func (app *kustApp) CreateFiles(repofs fs.FS, projectName string) error { - return kustCreateFiles(app, repofs, projectName) +func (app *kustApp) CreateFiles(repofs fs.FS, appsfs fs.FS, projectName string) error { + return kustCreateFiles(app, repofs, appsfs, projectName) } -func kustCreateFiles(app *kustApp, repofs fs.FS, projectName string) error { +func kustCreateFiles(app *kustApp, repofs fs.FS, appsfs fs.FS, projectName string) error { var err error // create Base - appPath := repofs.Join(store.Default.AppsDir, app.Name()) - basePath := repofs.Join(appPath, "base") - baseKustomizationPath := repofs.Join(basePath, "kustomization.yaml") + appPath := appsfs.Join(store.Default.AppsDir, app.Name()) + basePath := appsfs.Join(appPath, "base") + baseKustomizationPath := appsfs.Join(basePath, "kustomization.yaml") - // check if base is in the same filesystem - if repofs.ExistsOrDie(baseKustomizationPath) { + // check if app is in the same filesystem + if appsfs.ExistsOrDie(appPath) { // check if the bases are the same - log.G().Debug("application base with the same name exists, checking for collisions") - if collision, err := checkBaseCollision(repofs, baseKustomizationPath, app.base); err != nil { + log.G().Debug("application with the same name exists, checking for collisions") + if collision, err := checkBaseCollision(appsfs, baseKustomizationPath, app.base); err != nil { return err } else if collision { return ErrAppCollisionWithExistingBase } + } else if appsfs != repofs && repofs.ExistsOrDie(appPath) { + appRepo, err := getAppRepo(repofs, app.Name()) + if err != nil { + return fmt.Errorf("Failed getting app repo: %v", err) + } + + return fmt.Errorf("an application with the same name already exists in '%s', consider choosing a different name", appRepo) } - if err = repofs.WriteYamls(baseKustomizationPath, app.base); err != nil { + if err = appsfs.WriteYamls(baseKustomizationPath, app.base); err != nil { return err } // create Overlay - overlayPath := repofs.Join(appPath, "overlays", projectName) - overlayKustomizationPath := repofs.Join(overlayPath, "kustomization.yaml") - if repofs.ExistsOrDie(overlayKustomizationPath) { + overlayPath := appsfs.Join(appPath, "overlays", projectName) + overlayKustomizationPath := appsfs.Join(overlayPath, "kustomization.yaml") + if appsfs.ExistsOrDie(overlayKustomizationPath) { return ErrAppAlreadyInstalledOnProject } - if err = repofs.WriteYamls(overlayKustomizationPath, app.overlay); err != nil { + if err = appsfs.WriteYamls(overlayKustomizationPath, app.overlay); err != nil { return err } // create manifests - only used in flat installation mode if app.manifests != nil { - manifestsPath := repofs.Join(basePath, "install.yaml") - if _, err = writeFile(repofs, manifestsPath, "manifests", app.manifests); err != nil { + manifestsPath := appsfs.Join(basePath, "install.yaml") + if _, err = writeFile(appsfs, manifestsPath, "manifests", app.manifests); err != nil { return err } } @@ -346,6 +353,10 @@ func kustCreateFiles(app *kustApp, repofs fs.FS, projectName string) error { } configPath := repofs.Join(overlayPath, "config.json") + if repofs != appsfs { + configPath = repofs.Join(appPath, projectName, "config.json") + } + config, err := json.Marshal(app.config) if err != nil { return fmt.Errorf("failed to marshal app config.json: %w", err) @@ -405,7 +416,7 @@ func newDirApp(opts *CreateOptions) *dirApp { return app } -func (app *dirApp) CreateFiles(repofs fs.FS, projectName string) error { +func (app *dirApp) CreateFiles(repofs fs.FS, appsfs fs.FS, projectName string) error { basePath := repofs.Join(store.Default.AppsDir, app.opts.AppName, projectName) configPath := repofs.Join(basePath, "config.json") config, err := json.Marshal(app.config) @@ -523,3 +534,17 @@ var generateManifests = func(k *kusttypes.Kustomization) ([]byte, error) { return res.AsYaml() } + +var getAppRepo = func(repofs fs.FS, appName string) (string, error) { + overlays, err := billyUtils.Glob(repofs, repofs.Join(store.Default.AppsDir, appName, store.Default.OverlaysDir, "**", "config.json")) + if err != nil { + return "", err + } + + if len(overlays) == 0 { + return "", fmt.Errorf("Application '%s' has no overlays", appName) + } + + c := &Config{} + return c.SrcRepoURL, repofs.ReadJson(overlays[0], c) +} diff --git a/pkg/application/application_test.go b/pkg/application/application_test.go index d00718e7..f87a8f40 100644 --- a/pkg/application/application_test.go +++ b/pkg/application/application_test.go @@ -2,7 +2,7 @@ package application import ( "encoding/json" - "fmt" + "errors" "io/ioutil" "path/filepath" "reflect" @@ -220,7 +220,7 @@ func Test_writeFile(t *testing.T) { }, beforeFn: func(_ fs.FS) fs.FS { mfs := &fsmocks.FS{} - mfs.On("CheckExistsOrWrite", mock.Anything, mock.Anything).Return(false, fmt.Errorf("error")) + mfs.On("CheckExistsOrWrite", mock.Anything, mock.Anything).Return(false, errors.New("error")) mfs.On("Root").Return("/") mfs.On("Join", mock.Anything, mock.Anything).Return("/foo/bar") return mfs @@ -251,11 +251,11 @@ func Test_writeFile(t *testing.T) { func Test_kustCreateFiles(t *testing.T) { tests := map[string]struct { - beforeFn func() (app *kustApp, repofs fs.FS, projectName string) - assertFn func(t *testing.T, repofs fs.FS, err error) + beforeFn func() (app *kustApp, repofs fs.FS, appsfs fs.FS, projectName string) + assertFn func(t *testing.T, repofs fs.FS, appsfs fs.FS, err error) }{ "Should create all files for a simple application": { - beforeFn: func() (*kustApp, fs.FS, string) { + beforeFn: func() (*kustApp, fs.FS, fs.FS, string) { app := &kustApp{ baseApp: baseApp{ opts: &CreateOptions{ @@ -265,9 +265,9 @@ func Test_kustCreateFiles(t *testing.T) { }, } repofs := fs.Create(memfs.New()) - return app, repofs, "project" + return app, repofs, repofs, "project" }, - assertFn: func(t *testing.T, repofs fs.FS, err error) { + assertFn: func(t *testing.T, repofs fs.FS, _ fs.FS, err error) { assert.NoError(t, err) assert.True(t, repofs.ExistsOrDie(repofs.Join(store.Default.AppsDir, "app", store.Default.OverlaysDir, "project", "config.json")), "overlay config should exist") @@ -277,8 +277,37 @@ func Test_kustCreateFiles(t *testing.T) { assert.False(t, repofs.ExistsOrDie(repofs.Join(store.Default.AppsDir, "app", store.Default.OverlaysDir, "project", "namespace.yaml")), "overlay namespace should not exist") }, }, + "Should create all files in two separate filesystems": { + beforeFn: func() (*kustApp, fs.FS, fs.FS, string) { + app := &kustApp{ + baseApp: baseApp{ + opts: &CreateOptions{ + AppName: "app", + DestServer: store.Default.DestServer, + }, + }, + } + repofs := fs.Create(memfs.New()) + appsfs := fs.Create(memfs.New()) + return app, repofs, appsfs, "project" + }, + assertFn: func(t *testing.T, repofs fs.FS, appsfs fs.FS, err error) { + assert.NoError(t, err) + + // in repofs - only config.json + assert.True(t, repofs.ExistsOrDie(repofs.Join(store.Default.AppsDir, "app", "project", "config.json")), "app config should exist in repofs") + assert.False(t, repofs.ExistsOrDie(repofs.Join(store.Default.AppsDir, "app", store.Default.BaseDir)), "base directory should not exist in repofs") + assert.False(t, repofs.ExistsOrDie(repofs.Join(store.Default.AppsDir, "app", store.Default.OverlaysDir)), "overlay directory should not exist in repofs") + + // in appsfs - only base and overlay kustomization + assert.True(t, appsfs.ExistsOrDie(appsfs.Join(store.Default.AppsDir, "app", store.Default.BaseDir, "kustomization.yaml")), "base kustomization should exist in appsfs") + assert.False(t, appsfs.ExistsOrDie(appsfs.Join(store.Default.AppsDir, "app", store.Default.BaseDir, "install.yaml")), "install file should not exist in appsfs") + assert.True(t, appsfs.ExistsOrDie(appsfs.Join(store.Default.AppsDir, "app", store.Default.OverlaysDir, "project", "kustomization.yaml")), "overlay kustomization should exist in appsfs") + assert.False(t, appsfs.ExistsOrDie(appsfs.Join(store.Default.AppsDir, "app", store.Default.OverlaysDir, "project", "namespace.yaml")), "overlay namespace should not exist in appsfs") + }, + }, "Should create install.yaml when manifests exist": { - beforeFn: func() (*kustApp, fs.FS, string) { + beforeFn: func() (*kustApp, fs.FS, fs.FS, string) { app := &kustApp{ baseApp: baseApp{ opts: &CreateOptions{ @@ -289,9 +318,9 @@ func Test_kustCreateFiles(t *testing.T) { manifests: []byte("some manifests"), } repofs := fs.Create(memfs.New()) - return app, repofs, "project" + return app, repofs, repofs, "project" }, - assertFn: func(t *testing.T, repofs fs.FS, err error) { + assertFn: func(t *testing.T, repofs fs.FS, _ fs.FS, err error) { assert.NoError(t, err) installFile := repofs.Join(store.Default.AppsDir, "app", store.Default.BaseDir, "install.yaml") assert.True(t, repofs.ExistsOrDie(installFile), "install file should exist") @@ -300,7 +329,7 @@ func Test_kustCreateFiles(t *testing.T) { }, }, "Should create namespace.yaml on the correct cluster resources directory when needed": { - beforeFn: func() (*kustApp, fs.FS, string) { + beforeFn: func() (*kustApp, fs.FS, fs.FS, string) { app := &kustApp{ baseApp: baseApp{ opts: &CreateOptions{ @@ -311,9 +340,9 @@ func Test_kustCreateFiles(t *testing.T) { namespace: kube.GenerateNamespace("foo"), } repofs := fs.Create(memfs.New()) - return app, repofs, "project" + return app, repofs, repofs, "project" }, - assertFn: func(t *testing.T, repofs fs.FS, err error) { + assertFn: func(t *testing.T, repofs fs.FS, _ fs.FS, err error) { assert.NoError(t, err) path := repofs.Join(store.Default.BootsrtrapDir, store.Default.ClusterResourcesDir, store.Default.ClusterContextName, "foo-ns.yaml") ns, err := repofs.ReadFile(path) @@ -324,7 +353,7 @@ func Test_kustCreateFiles(t *testing.T) { }, }, "Should fail if trying to install an application with destServer that is not configured yet": { - beforeFn: func() (*kustApp, fs.FS, string) { + beforeFn: func() (*kustApp, fs.FS, fs.FS, string) { app := &kustApp{ baseApp: baseApp{ opts: &CreateOptions{ @@ -334,14 +363,14 @@ func Test_kustCreateFiles(t *testing.T) { }, } repofs := fs.Create(memfs.New()) - return app, repofs, "project" + return app, repofs, repofs, "project" }, - assertFn: func(t *testing.T, _ fs.FS, err error) { + assertFn: func(t *testing.T, _ fs.FS, _ fs.FS, err error) { assert.Error(t, err, "cluster 'foo' is not configured yet, you need to create a project that uses this cluster first") }, }, "Should fail when base kustomization is different from kustRes": { - beforeFn: func() (*kustApp, fs.FS, string) { + beforeFn: func() (*kustApp, fs.FS, fs.FS, string) { app := &kustApp{ baseApp: baseApp{ opts: &CreateOptions{ @@ -357,7 +386,6 @@ func Test_kustCreateFiles(t *testing.T) { Resources: []string{"github.com/owner/repo?ref=v1.2.3"}, }, } - repofs := fs.Create(memfs.New()) origBase := &kusttypes.Kustomization{ TypeMeta: kusttypes.TypeMeta{ APIVersion: kusttypes.KustomizationVersion, @@ -365,15 +393,30 @@ func Test_kustCreateFiles(t *testing.T) { }, Resources: []string{"github.com/owner/different_repo?ref=v1.2.3"}, } + repofs := fs.Create(memfs.New()) _ = repofs.WriteYamls(repofs.Join(store.Default.AppsDir, "app", store.Default.BaseDir, "kustomization.yaml"), origBase) - return app, repofs, "project" + return app, repofs, repofs, "project" }, - assertFn: func(t *testing.T, _ fs.FS, err error) { + assertFn: func(t *testing.T, _ fs.FS, _ fs.FS, err error) { assert.ErrorIs(t, err, ErrAppCollisionWithExistingBase) }, }, "Should fail when overlay already exists": { - beforeFn: func() (*kustApp, fs.FS, string) { + beforeFn: func() (*kustApp, fs.FS, fs.FS, string) { + origBase := &kusttypes.Kustomization{ + TypeMeta: kusttypes.TypeMeta{ + APIVersion: kusttypes.KustomizationVersion, + Kind: kusttypes.KustomizationKind, + }, + Resources: []string{"github.com/owner/different_repo?ref=v1.2.3"}, + } + overlay := &kusttypes.Kustomization{ + TypeMeta: kusttypes.TypeMeta{ + APIVersion: kusttypes.KustomizationVersion, + Kind: kusttypes.KustomizationKind, + }, + Resources: []string{"../../base"}, + } app := &kustApp{ baseApp: baseApp{ opts: &CreateOptions{ @@ -381,29 +424,64 @@ func Test_kustCreateFiles(t *testing.T) { DestServer: store.Default.DestServer, }, }, + base: origBase, } repofs := fs.Create(memfs.New()) - origBase := &kusttypes.Kustomization{ - TypeMeta: kusttypes.TypeMeta{ - APIVersion: kusttypes.KustomizationVersion, - Kind: kusttypes.KustomizationKind, + _ = repofs.WriteYamls(repofs.Join(store.Default.AppsDir, "app", store.Default.BaseDir, "kustomization.yaml"), origBase) + _ = repofs.WriteYamls(repofs.Join(store.Default.AppsDir, "app", store.Default.OverlaysDir, "project", "kustomization.yaml"), overlay) + return app, repofs, repofs, "project" + }, + assertFn: func(t *testing.T, _ fs.FS, _ fs.FS, err error) { + assert.ErrorIs(t, err, ErrAppAlreadyInstalledOnProject) + }, + }, + "Should fail when failing to find original appRepo": { + beforeFn: func() (*kustApp, fs.FS, fs.FS, string) { + app := &kustApp{ + baseApp: baseApp{ + opts: &CreateOptions{ + AppName: "app", + }, }, - Resources: []string{"github.com/owner/different_repo?ref=v1.2.3"}, } - _ = repofs.WriteYamls(repofs.Join(store.Default.AppsDir, "app", store.Default.OverlaysDir, "project", "kustomization.yaml"), origBase) - return app, repofs, "project" + repofs := fs.Create(memfs.New()) + appsfs := fs.Create(memfs.New()) + _ = repofs.MkdirAll(repofs.Join(store.Default.AppsDir, "app"), 0666) + return app, repofs, appsfs, "project" }, - assertFn: func(t *testing.T, _ fs.FS, err error) { - assert.ErrorIs(t, err, ErrAppAlreadyInstalledOnProject) + assertFn: func(t *testing.T, _ fs.FS, _ fs.FS, err error) { + assert.EqualError(t, err, "Failed getting app repo: Application 'app' has no overlays") + }, + }, + "Should fail when app exists on another repo": { + beforeFn: func() (*kustApp, fs.FS, fs.FS, string) { + app := &kustApp{ + baseApp: baseApp{ + opts: &CreateOptions{ + AppName: "app", + }, + }, + } + config := &Config{ + AppName: "app", + SrcRepoURL: "https://github.com/owner/other_name.git", + } + repofs := fs.Create(memfs.New()) + appsfs := fs.Create(memfs.New()) + _ = repofs.WriteJson(repofs.Join(store.Default.AppsDir, "app", store.Default.OverlaysDir, "project", "config.json"), config) + return app, repofs, appsfs, "project" + }, + assertFn: func(t *testing.T, _ fs.FS, _ fs.FS, err error) { + assert.EqualError(t, err, "an application with the same name already exists in 'https://github.com/owner/other_name.git', consider choosing a different name") }, }, } for tname, tt := range tests { t.Run(tname, func(t *testing.T) { - app, repofs, projectName := tt.beforeFn() + app, repofs, appsfs, projectName := tt.beforeFn() bootstrapMockFS(t, repofs) - err := app.CreateFiles(repofs, projectName) - tt.assertFn(t, repofs, err) + err := app.CreateFiles(repofs, appsfs, projectName) + tt.assertFn(t, repofs, appsfs, err) }) } } @@ -691,7 +769,7 @@ func Test_dirApp_CreateFiles(t *testing.T) { t.Run(tname, func(t *testing.T) { repofs := fs.Create(memfs.New()) bootstrapMockFS(t, repofs) - tt.assertFn(t, repofs, tt.app.CreateFiles(repofs, tt.projectName)) + tt.assertFn(t, repofs, tt.app.CreateFiles(repofs, repofs, tt.projectName)) }) } } diff --git a/pkg/application/mocks/application.go b/pkg/application/mocks/application.go index 0289e719..8112bd96 100644 --- a/pkg/application/mocks/application.go +++ b/pkg/application/mocks/application.go @@ -12,13 +12,13 @@ type Application struct { mock.Mock } -// CreateFiles provides a mock function with given fields: repofs, projectName -func (_m *Application) CreateFiles(repofs fs.FS, projectName string) error { - ret := _m.Called(repofs, projectName) +// CreateFiles provides a mock function with given fields: repofs, appsfs, projectName +func (_m *Application) CreateFiles(repofs fs.FS, appsfs fs.FS, projectName string) error { + ret := _m.Called(repofs, appsfs, projectName) var r0 error - if rf, ok := ret.Get(0).(func(fs.FS, string) error); ok { - r0 = rf(repofs, projectName) + if rf, ok := ret.Get(0).(func(fs.FS, fs.FS, string) error); ok { + r0 = rf(repofs, appsfs, projectName) } else { r0 = ret.Error(0) } diff --git a/pkg/fs/fs.go b/pkg/fs/fs.go index 90b4d964..0c7bb744 100644 --- a/pkg/fs/fs.go +++ b/pkg/fs/fs.go @@ -1,6 +1,7 @@ package fs import ( + "encoding/json" "fmt" "io/ioutil" "os" @@ -31,8 +32,14 @@ type FS interface { // ReadYamls reads the file data as yaml into o ReadYamls(filename string, o ...interface{}) error - // WriteYamls write the data as yaml into the file + // WriteYamls writes the data as yaml into the file WriteYamls(filename string, o ...interface{}) error + + // ReadJson reads the file data as json into o + ReadJson(filename string, o interface{}) error + + // WriteJson writes the data as json into the file + WriteJson(filename string, o interface{}) error } type fsimpl struct { @@ -126,3 +133,21 @@ func (fs *fsimpl) WriteYamls(filename string, o ...interface{}) error { data := util.JoinManifests(yamls...) return billyUtils.WriteFile(fs, filename, data, 0666) } + +func (fs *fsimpl) ReadJson(filename string, o interface{}) error { + data, err := fs.ReadFile(filename) + if err != nil { + return err + } + + return json.Unmarshal(data, o) +} + +func (fs *fsimpl) WriteJson(filename string, o interface{}) error { + data, err := json.Marshal(o) + if err != nil { + return err + } + + return billyUtils.WriteFile(fs, filename, data, 0666) +} diff --git a/pkg/fs/fs_test.go b/pkg/fs/fs_test.go index b97fc019..799c963a 100644 --- a/pkg/fs/fs_test.go +++ b/pkg/fs/fs_test.go @@ -1,6 +1,7 @@ package fs import ( + "encoding/json" "fmt" "os" "reflect" @@ -19,6 +20,12 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) +type ( + SampleJson struct { + Name string `json:"name"` + } +) + func TestCreate(t *testing.T) { tests := map[string]struct { bfs billy.Filesystem @@ -501,3 +508,94 @@ func Test_fsimpl_WriteYamls(t *testing.T) { }) } } + +func Test_fsimpl_ReadJson(t *testing.T) { + tests := map[string]struct { + o interface{} + wantErr string + beforeFn func() FS + assertFn func(*testing.T, interface{}) + }{ + "Should read a simple json file": { + o: &SampleJson{}, + beforeFn: func() FS { + j, _ := json.Marshal(&SampleJson{ + Name: "name", + }) + memfs := memfs.New() + _ = billyUtils.WriteFile(memfs, "filename", j, 0666) + return Create(memfs) + }, + assertFn: func(t *testing.T, o interface{}) { + j := o.(*SampleJson) + assert.Equal(t, "name", j.Name) + }, + }, + "Should fail if file does not exist": { + o: &SampleJson{}, + wantErr: os.ErrNotExist.Error(), + beforeFn: func() FS { + memfs := memfs.New() + return Create(memfs) + }, + }, + } + for name, tt := range tests { + t.Run(name, func(t *testing.T) { + fs := tt.beforeFn() + if err := fs.ReadJson("filename", tt.o); err != nil { + if tt.wantErr != "" { + assert.EqualError(t, err, tt.wantErr) + } else { + t.Errorf("ReadYamls() error = %v", err) + } + + return + } + + if tt.assertFn != nil { + tt.assertFn(t, tt.o) + } + }) + } +} + +func Test_fsimpl_WriteJson(t *testing.T) { + tests := map[string]struct { + o interface{} + wantErr string + assertFn func(*testing.T, FS) + }{ + "Should write a simple file": { + o: &SampleJson{ + Name: "name", + }, + assertFn: func(t *testing.T, fs FS) { + data, err := fs.ReadFile("filename") + assert.NoError(t, err) + j := &SampleJson{} + err = yaml.Unmarshal(data, j) + assert.NoError(t, err) + assert.Equal(t, "name", j.Name) + }, + }, + } + for name, tt := range tests { + t.Run(name, func(t *testing.T) { + fs := Create(memfs.New()) + if err := fs.WriteJson("filename", tt.o); err != nil { + if tt.wantErr != "" { + assert.EqualError(t, err, tt.wantErr) + } else { + t.Errorf("WriteYamls() error = %v", err) + } + + return + } + + if tt.assertFn != nil { + tt.assertFn(t, fs) + } + }) + } +} diff --git a/pkg/fs/mocks/fs.go b/pkg/fs/mocks/fs.go index bd75842d..e964cdeb 100644 --- a/pkg/fs/mocks/fs.go +++ b/pkg/fs/mocks/fs.go @@ -266,6 +266,20 @@ func (_m *FS) ReadFile(filename string) ([]byte, error) { return r0, r1 } +// ReadJson provides a mock function with given fields: filename, o +func (_m *FS) ReadJson(filename string, o interface{}) error { + ret := _m.Called(filename, o) + + var r0 error + if rf, ok := ret.Get(0).(func(string, interface{}) error); ok { + r0 = rf(filename, o) + } else { + r0 = ret.Error(0) + } + + return r0 +} + // ReadYamls provides a mock function with given fields: filename, o func (_m *FS) ReadYamls(filename string, o ...interface{}) error { var _ca []interface{} @@ -406,6 +420,20 @@ func (_m *FS) TempFile(dir string, prefix string) (billy.File, error) { return r0, r1 } +// WriteJson provides a mock function with given fields: filename, o +func (_m *FS) WriteJson(filename string, o interface{}) error { + ret := _m.Called(filename, o) + + var r0 error + if rf, ok := ret.Get(0).(func(string, interface{}) error); ok { + r0 = rf(filename, o) + } else { + r0 = ret.Error(0) + } + + return r0 +} + // WriteYamls provides a mock function with given fields: filename, o func (_m *FS) WriteYamls(filename string, o ...interface{}) error { var _ca []interface{} diff --git a/pkg/git/github/mocks/organizations.go b/pkg/git/github/mocks/organizations.go new file mode 100644 index 00000000..3427e8fe --- /dev/null +++ b/pkg/git/github/mocks/organizations.go @@ -0,0 +1,1200 @@ +// Code generated by mockery (devel). DO NOT EDIT. + +package mocks + +import ( + context "context" + + github "github.com/google/go-github/v35/github" + + mock "github.com/stretchr/testify/mock" +) + +// Organizations is an autogenerated mock type for the Organizations type +type Organizations struct { + mock.Mock +} + +// BlockUser provides a mock function with given fields: _a0, _a1, _a2 +func (_m *Organizations) BlockUser(_a0 context.Context, _a1 string, _a2 string) (*github.Response, error) { + ret := _m.Called(_a0, _a1, _a2) + + var r0 *github.Response + if rf, ok := ret.Get(0).(func(context.Context, string, string) *github.Response); ok { + r0 = rf(_a0, _a1, _a2) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*github.Response) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, string, string) error); ok { + r1 = rf(_a0, _a1, _a2) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ConcealMembership provides a mock function with given fields: _a0, _a1, _a2 +func (_m *Organizations) ConcealMembership(_a0 context.Context, _a1 string, _a2 string) (*github.Response, error) { + ret := _m.Called(_a0, _a1, _a2) + + var r0 *github.Response + if rf, ok := ret.Get(0).(func(context.Context, string, string) *github.Response); ok { + r0 = rf(_a0, _a1, _a2) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*github.Response) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, string, string) error); ok { + r1 = rf(_a0, _a1, _a2) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ConvertMemberToOutsideCollaborator provides a mock function with given fields: _a0, _a1, _a2 +func (_m *Organizations) ConvertMemberToOutsideCollaborator(_a0 context.Context, _a1 string, _a2 string) (*github.Response, error) { + ret := _m.Called(_a0, _a1, _a2) + + var r0 *github.Response + if rf, ok := ret.Get(0).(func(context.Context, string, string) *github.Response); ok { + r0 = rf(_a0, _a1, _a2) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*github.Response) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, string, string) error); ok { + r1 = rf(_a0, _a1, _a2) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// CreateHook provides a mock function with given fields: _a0, _a1, _a2 +func (_m *Organizations) CreateHook(_a0 context.Context, _a1 string, _a2 *github.Hook) (*github.Hook, *github.Response, error) { + ret := _m.Called(_a0, _a1, _a2) + + var r0 *github.Hook + if rf, ok := ret.Get(0).(func(context.Context, string, *github.Hook) *github.Hook); ok { + r0 = rf(_a0, _a1, _a2) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*github.Hook) + } + } + + var r1 *github.Response + if rf, ok := ret.Get(1).(func(context.Context, string, *github.Hook) *github.Response); ok { + r1 = rf(_a0, _a1, _a2) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(*github.Response) + } + } + + var r2 error + if rf, ok := ret.Get(2).(func(context.Context, string, *github.Hook) error); ok { + r2 = rf(_a0, _a1, _a2) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + +// CreateOrgInvitation provides a mock function with given fields: _a0, _a1, _a2 +func (_m *Organizations) CreateOrgInvitation(_a0 context.Context, _a1 string, _a2 *github.CreateOrgInvitationOptions) (*github.Invitation, *github.Response, error) { + ret := _m.Called(_a0, _a1, _a2) + + var r0 *github.Invitation + if rf, ok := ret.Get(0).(func(context.Context, string, *github.CreateOrgInvitationOptions) *github.Invitation); ok { + r0 = rf(_a0, _a1, _a2) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*github.Invitation) + } + } + + var r1 *github.Response + if rf, ok := ret.Get(1).(func(context.Context, string, *github.CreateOrgInvitationOptions) *github.Response); ok { + r1 = rf(_a0, _a1, _a2) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(*github.Response) + } + } + + var r2 error + if rf, ok := ret.Get(2).(func(context.Context, string, *github.CreateOrgInvitationOptions) error); ok { + r2 = rf(_a0, _a1, _a2) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + +// CreateProject provides a mock function with given fields: _a0, _a1, _a2 +func (_m *Organizations) CreateProject(_a0 context.Context, _a1 string, _a2 *github.ProjectOptions) (*github.Project, *github.Response, error) { + ret := _m.Called(_a0, _a1, _a2) + + var r0 *github.Project + if rf, ok := ret.Get(0).(func(context.Context, string, *github.ProjectOptions) *github.Project); ok { + r0 = rf(_a0, _a1, _a2) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*github.Project) + } + } + + var r1 *github.Response + if rf, ok := ret.Get(1).(func(context.Context, string, *github.ProjectOptions) *github.Response); ok { + r1 = rf(_a0, _a1, _a2) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(*github.Response) + } + } + + var r2 error + if rf, ok := ret.Get(2).(func(context.Context, string, *github.ProjectOptions) error); ok { + r2 = rf(_a0, _a1, _a2) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + +// DeleteHook provides a mock function with given fields: _a0, _a1, _a2 +func (_m *Organizations) DeleteHook(_a0 context.Context, _a1 string, _a2 int64) (*github.Response, error) { + ret := _m.Called(_a0, _a1, _a2) + + var r0 *github.Response + if rf, ok := ret.Get(0).(func(context.Context, string, int64) *github.Response); ok { + r0 = rf(_a0, _a1, _a2) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*github.Response) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, string, int64) error); ok { + r1 = rf(_a0, _a1, _a2) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Edit provides a mock function with given fields: _a0, _a1, _a2 +func (_m *Organizations) Edit(_a0 context.Context, _a1 string, _a2 *github.Organization) (*github.Organization, *github.Response, error) { + ret := _m.Called(_a0, _a1, _a2) + + var r0 *github.Organization + if rf, ok := ret.Get(0).(func(context.Context, string, *github.Organization) *github.Organization); ok { + r0 = rf(_a0, _a1, _a2) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*github.Organization) + } + } + + var r1 *github.Response + if rf, ok := ret.Get(1).(func(context.Context, string, *github.Organization) *github.Response); ok { + r1 = rf(_a0, _a1, _a2) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(*github.Response) + } + } + + var r2 error + if rf, ok := ret.Get(2).(func(context.Context, string, *github.Organization) error); ok { + r2 = rf(_a0, _a1, _a2) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + +// EditActionsAllowed provides a mock function with given fields: _a0, _a1, _a2 +func (_m *Organizations) EditActionsAllowed(_a0 context.Context, _a1 string, _a2 github.ActionsAllowed) (*github.ActionsAllowed, *github.Response, error) { + ret := _m.Called(_a0, _a1, _a2) + + var r0 *github.ActionsAllowed + if rf, ok := ret.Get(0).(func(context.Context, string, github.ActionsAllowed) *github.ActionsAllowed); ok { + r0 = rf(_a0, _a1, _a2) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*github.ActionsAllowed) + } + } + + var r1 *github.Response + if rf, ok := ret.Get(1).(func(context.Context, string, github.ActionsAllowed) *github.Response); ok { + r1 = rf(_a0, _a1, _a2) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(*github.Response) + } + } + + var r2 error + if rf, ok := ret.Get(2).(func(context.Context, string, github.ActionsAllowed) error); ok { + r2 = rf(_a0, _a1, _a2) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + +// EditActionsPermissions provides a mock function with given fields: _a0, _a1, _a2 +func (_m *Organizations) EditActionsPermissions(_a0 context.Context, _a1 string, _a2 github.ActionsPermissions) (*github.ActionsPermissions, *github.Response, error) { + ret := _m.Called(_a0, _a1, _a2) + + var r0 *github.ActionsPermissions + if rf, ok := ret.Get(0).(func(context.Context, string, github.ActionsPermissions) *github.ActionsPermissions); ok { + r0 = rf(_a0, _a1, _a2) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*github.ActionsPermissions) + } + } + + var r1 *github.Response + if rf, ok := ret.Get(1).(func(context.Context, string, github.ActionsPermissions) *github.Response); ok { + r1 = rf(_a0, _a1, _a2) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(*github.Response) + } + } + + var r2 error + if rf, ok := ret.Get(2).(func(context.Context, string, github.ActionsPermissions) error); ok { + r2 = rf(_a0, _a1, _a2) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + +// EditHook provides a mock function with given fields: _a0, _a1, _a2, _a3 +func (_m *Organizations) EditHook(_a0 context.Context, _a1 string, _a2 int64, _a3 *github.Hook) (*github.Hook, *github.Response, error) { + ret := _m.Called(_a0, _a1, _a2, _a3) + + var r0 *github.Hook + if rf, ok := ret.Get(0).(func(context.Context, string, int64, *github.Hook) *github.Hook); ok { + r0 = rf(_a0, _a1, _a2, _a3) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*github.Hook) + } + } + + var r1 *github.Response + if rf, ok := ret.Get(1).(func(context.Context, string, int64, *github.Hook) *github.Response); ok { + r1 = rf(_a0, _a1, _a2, _a3) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(*github.Response) + } + } + + var r2 error + if rf, ok := ret.Get(2).(func(context.Context, string, int64, *github.Hook) error); ok { + r2 = rf(_a0, _a1, _a2, _a3) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + +// EditOrgMembership provides a mock function with given fields: _a0, _a1, _a2, _a3 +func (_m *Organizations) EditOrgMembership(_a0 context.Context, _a1 string, _a2 string, _a3 *github.Membership) (*github.Membership, *github.Response, error) { + ret := _m.Called(_a0, _a1, _a2, _a3) + + var r0 *github.Membership + if rf, ok := ret.Get(0).(func(context.Context, string, string, *github.Membership) *github.Membership); ok { + r0 = rf(_a0, _a1, _a2, _a3) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*github.Membership) + } + } + + var r1 *github.Response + if rf, ok := ret.Get(1).(func(context.Context, string, string, *github.Membership) *github.Response); ok { + r1 = rf(_a0, _a1, _a2, _a3) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(*github.Response) + } + } + + var r2 error + if rf, ok := ret.Get(2).(func(context.Context, string, string, *github.Membership) error); ok { + r2 = rf(_a0, _a1, _a2, _a3) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + +// Get provides a mock function with given fields: _a0, _a1 +func (_m *Organizations) Get(_a0 context.Context, _a1 string) (*github.Organization, *github.Response, error) { + ret := _m.Called(_a0, _a1) + + var r0 *github.Organization + if rf, ok := ret.Get(0).(func(context.Context, string) *github.Organization); ok { + r0 = rf(_a0, _a1) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*github.Organization) + } + } + + var r1 *github.Response + if rf, ok := ret.Get(1).(func(context.Context, string) *github.Response); ok { + r1 = rf(_a0, _a1) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(*github.Response) + } + } + + var r2 error + if rf, ok := ret.Get(2).(func(context.Context, string) error); ok { + r2 = rf(_a0, _a1) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + +// GetActionsAllowed provides a mock function with given fields: _a0, _a1 +func (_m *Organizations) GetActionsAllowed(_a0 context.Context, _a1 string) (*github.ActionsAllowed, *github.Response, error) { + ret := _m.Called(_a0, _a1) + + var r0 *github.ActionsAllowed + if rf, ok := ret.Get(0).(func(context.Context, string) *github.ActionsAllowed); ok { + r0 = rf(_a0, _a1) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*github.ActionsAllowed) + } + } + + var r1 *github.Response + if rf, ok := ret.Get(1).(func(context.Context, string) *github.Response); ok { + r1 = rf(_a0, _a1) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(*github.Response) + } + } + + var r2 error + if rf, ok := ret.Get(2).(func(context.Context, string) error); ok { + r2 = rf(_a0, _a1) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + +// GetActionsPermissions provides a mock function with given fields: _a0, _a1 +func (_m *Organizations) GetActionsPermissions(_a0 context.Context, _a1 string) (*github.ActionsPermissions, *github.Response, error) { + ret := _m.Called(_a0, _a1) + + var r0 *github.ActionsPermissions + if rf, ok := ret.Get(0).(func(context.Context, string) *github.ActionsPermissions); ok { + r0 = rf(_a0, _a1) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*github.ActionsPermissions) + } + } + + var r1 *github.Response + if rf, ok := ret.Get(1).(func(context.Context, string) *github.Response); ok { + r1 = rf(_a0, _a1) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(*github.Response) + } + } + + var r2 error + if rf, ok := ret.Get(2).(func(context.Context, string) error); ok { + r2 = rf(_a0, _a1) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + +// GetAuditLog provides a mock function with given fields: _a0, _a1, _a2 +func (_m *Organizations) GetAuditLog(_a0 context.Context, _a1 string, _a2 *github.GetAuditLogOptions) ([]*github.AuditEntry, *github.Response, error) { + ret := _m.Called(_a0, _a1, _a2) + + var r0 []*github.AuditEntry + if rf, ok := ret.Get(0).(func(context.Context, string, *github.GetAuditLogOptions) []*github.AuditEntry); ok { + r0 = rf(_a0, _a1, _a2) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*github.AuditEntry) + } + } + + var r1 *github.Response + if rf, ok := ret.Get(1).(func(context.Context, string, *github.GetAuditLogOptions) *github.Response); ok { + r1 = rf(_a0, _a1, _a2) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(*github.Response) + } + } + + var r2 error + if rf, ok := ret.Get(2).(func(context.Context, string, *github.GetAuditLogOptions) error); ok { + r2 = rf(_a0, _a1, _a2) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + +// GetByID provides a mock function with given fields: _a0, _a1 +func (_m *Organizations) GetByID(_a0 context.Context, _a1 int64) (*github.Organization, *github.Response, error) { + ret := _m.Called(_a0, _a1) + + var r0 *github.Organization + if rf, ok := ret.Get(0).(func(context.Context, int64) *github.Organization); ok { + r0 = rf(_a0, _a1) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*github.Organization) + } + } + + var r1 *github.Response + if rf, ok := ret.Get(1).(func(context.Context, int64) *github.Response); ok { + r1 = rf(_a0, _a1) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(*github.Response) + } + } + + var r2 error + if rf, ok := ret.Get(2).(func(context.Context, int64) error); ok { + r2 = rf(_a0, _a1) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + +// GetHook provides a mock function with given fields: _a0, _a1, _a2 +func (_m *Organizations) GetHook(_a0 context.Context, _a1 string, _a2 int64) (*github.Hook, *github.Response, error) { + ret := _m.Called(_a0, _a1, _a2) + + var r0 *github.Hook + if rf, ok := ret.Get(0).(func(context.Context, string, int64) *github.Hook); ok { + r0 = rf(_a0, _a1, _a2) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*github.Hook) + } + } + + var r1 *github.Response + if rf, ok := ret.Get(1).(func(context.Context, string, int64) *github.Response); ok { + r1 = rf(_a0, _a1, _a2) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(*github.Response) + } + } + + var r2 error + if rf, ok := ret.Get(2).(func(context.Context, string, int64) error); ok { + r2 = rf(_a0, _a1, _a2) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + +// GetOrgMembership provides a mock function with given fields: _a0, _a1, _a2 +func (_m *Organizations) GetOrgMembership(_a0 context.Context, _a1 string, _a2 string) (*github.Membership, *github.Response, error) { + ret := _m.Called(_a0, _a1, _a2) + + var r0 *github.Membership + if rf, ok := ret.Get(0).(func(context.Context, string, string) *github.Membership); ok { + r0 = rf(_a0, _a1, _a2) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*github.Membership) + } + } + + var r1 *github.Response + if rf, ok := ret.Get(1).(func(context.Context, string, string) *github.Response); ok { + r1 = rf(_a0, _a1, _a2) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(*github.Response) + } + } + + var r2 error + if rf, ok := ret.Get(2).(func(context.Context, string, string) error); ok { + r2 = rf(_a0, _a1, _a2) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + +// IsBlocked provides a mock function with given fields: _a0, _a1, _a2 +func (_m *Organizations) IsBlocked(_a0 context.Context, _a1 string, _a2 string) (bool, *github.Response, error) { + ret := _m.Called(_a0, _a1, _a2) + + var r0 bool + if rf, ok := ret.Get(0).(func(context.Context, string, string) bool); ok { + r0 = rf(_a0, _a1, _a2) + } else { + r0 = ret.Get(0).(bool) + } + + var r1 *github.Response + if rf, ok := ret.Get(1).(func(context.Context, string, string) *github.Response); ok { + r1 = rf(_a0, _a1, _a2) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(*github.Response) + } + } + + var r2 error + if rf, ok := ret.Get(2).(func(context.Context, string, string) error); ok { + r2 = rf(_a0, _a1, _a2) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + +// IsMember provides a mock function with given fields: _a0, _a1, _a2 +func (_m *Organizations) IsMember(_a0 context.Context, _a1 string, _a2 string) (bool, *github.Response, error) { + ret := _m.Called(_a0, _a1, _a2) + + var r0 bool + if rf, ok := ret.Get(0).(func(context.Context, string, string) bool); ok { + r0 = rf(_a0, _a1, _a2) + } else { + r0 = ret.Get(0).(bool) + } + + var r1 *github.Response + if rf, ok := ret.Get(1).(func(context.Context, string, string) *github.Response); ok { + r1 = rf(_a0, _a1, _a2) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(*github.Response) + } + } + + var r2 error + if rf, ok := ret.Get(2).(func(context.Context, string, string) error); ok { + r2 = rf(_a0, _a1, _a2) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + +// IsPublicMember provides a mock function with given fields: _a0, _a1, _a2 +func (_m *Organizations) IsPublicMember(_a0 context.Context, _a1 string, _a2 string) (bool, *github.Response, error) { + ret := _m.Called(_a0, _a1, _a2) + + var r0 bool + if rf, ok := ret.Get(0).(func(context.Context, string, string) bool); ok { + r0 = rf(_a0, _a1, _a2) + } else { + r0 = ret.Get(0).(bool) + } + + var r1 *github.Response + if rf, ok := ret.Get(1).(func(context.Context, string, string) *github.Response); ok { + r1 = rf(_a0, _a1, _a2) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(*github.Response) + } + } + + var r2 error + if rf, ok := ret.Get(2).(func(context.Context, string, string) error); ok { + r2 = rf(_a0, _a1, _a2) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + +// List provides a mock function with given fields: _a0, _a1, _a2 +func (_m *Organizations) List(_a0 context.Context, _a1 string, _a2 *github.ListOptions) ([]*github.Organization, *github.Response, error) { + ret := _m.Called(_a0, _a1, _a2) + + var r0 []*github.Organization + if rf, ok := ret.Get(0).(func(context.Context, string, *github.ListOptions) []*github.Organization); ok { + r0 = rf(_a0, _a1, _a2) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*github.Organization) + } + } + + var r1 *github.Response + if rf, ok := ret.Get(1).(func(context.Context, string, *github.ListOptions) *github.Response); ok { + r1 = rf(_a0, _a1, _a2) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(*github.Response) + } + } + + var r2 error + if rf, ok := ret.Get(2).(func(context.Context, string, *github.ListOptions) error); ok { + r2 = rf(_a0, _a1, _a2) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + +// ListAll provides a mock function with given fields: _a0, _a1 +func (_m *Organizations) ListAll(_a0 context.Context, _a1 *github.OrganizationsListOptions) ([]*github.Organization, *github.Response, error) { + ret := _m.Called(_a0, _a1) + + var r0 []*github.Organization + if rf, ok := ret.Get(0).(func(context.Context, *github.OrganizationsListOptions) []*github.Organization); ok { + r0 = rf(_a0, _a1) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*github.Organization) + } + } + + var r1 *github.Response + if rf, ok := ret.Get(1).(func(context.Context, *github.OrganizationsListOptions) *github.Response); ok { + r1 = rf(_a0, _a1) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(*github.Response) + } + } + + var r2 error + if rf, ok := ret.Get(2).(func(context.Context, *github.OrganizationsListOptions) error); ok { + r2 = rf(_a0, _a1) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + +// ListBlockedUsers provides a mock function with given fields: _a0, _a1, _a2 +func (_m *Organizations) ListBlockedUsers(_a0 context.Context, _a1 string, _a2 *github.ListOptions) ([]*github.User, *github.Response, error) { + ret := _m.Called(_a0, _a1, _a2) + + var r0 []*github.User + if rf, ok := ret.Get(0).(func(context.Context, string, *github.ListOptions) []*github.User); ok { + r0 = rf(_a0, _a1, _a2) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*github.User) + } + } + + var r1 *github.Response + if rf, ok := ret.Get(1).(func(context.Context, string, *github.ListOptions) *github.Response); ok { + r1 = rf(_a0, _a1, _a2) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(*github.Response) + } + } + + var r2 error + if rf, ok := ret.Get(2).(func(context.Context, string, *github.ListOptions) error); ok { + r2 = rf(_a0, _a1, _a2) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + +// ListFailedOrgInvitations provides a mock function with given fields: _a0, _a1, _a2 +func (_m *Organizations) ListFailedOrgInvitations(_a0 context.Context, _a1 string, _a2 *github.ListOptions) ([]*github.Invitation, *github.Response, error) { + ret := _m.Called(_a0, _a1, _a2) + + var r0 []*github.Invitation + if rf, ok := ret.Get(0).(func(context.Context, string, *github.ListOptions) []*github.Invitation); ok { + r0 = rf(_a0, _a1, _a2) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*github.Invitation) + } + } + + var r1 *github.Response + if rf, ok := ret.Get(1).(func(context.Context, string, *github.ListOptions) *github.Response); ok { + r1 = rf(_a0, _a1, _a2) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(*github.Response) + } + } + + var r2 error + if rf, ok := ret.Get(2).(func(context.Context, string, *github.ListOptions) error); ok { + r2 = rf(_a0, _a1, _a2) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + +// ListHooks provides a mock function with given fields: _a0, _a1, _a2 +func (_m *Organizations) ListHooks(_a0 context.Context, _a1 string, _a2 *github.ListOptions) ([]*github.Hook, *github.Response, error) { + ret := _m.Called(_a0, _a1, _a2) + + var r0 []*github.Hook + if rf, ok := ret.Get(0).(func(context.Context, string, *github.ListOptions) []*github.Hook); ok { + r0 = rf(_a0, _a1, _a2) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*github.Hook) + } + } + + var r1 *github.Response + if rf, ok := ret.Get(1).(func(context.Context, string, *github.ListOptions) *github.Response); ok { + r1 = rf(_a0, _a1, _a2) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(*github.Response) + } + } + + var r2 error + if rf, ok := ret.Get(2).(func(context.Context, string, *github.ListOptions) error); ok { + r2 = rf(_a0, _a1, _a2) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + +// ListInstallations provides a mock function with given fields: _a0, _a1, _a2 +func (_m *Organizations) ListInstallations(_a0 context.Context, _a1 string, _a2 *github.ListOptions) (*github.OrganizationInstallations, *github.Response, error) { + ret := _m.Called(_a0, _a1, _a2) + + var r0 *github.OrganizationInstallations + if rf, ok := ret.Get(0).(func(context.Context, string, *github.ListOptions) *github.OrganizationInstallations); ok { + r0 = rf(_a0, _a1, _a2) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*github.OrganizationInstallations) + } + } + + var r1 *github.Response + if rf, ok := ret.Get(1).(func(context.Context, string, *github.ListOptions) *github.Response); ok { + r1 = rf(_a0, _a1, _a2) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(*github.Response) + } + } + + var r2 error + if rf, ok := ret.Get(2).(func(context.Context, string, *github.ListOptions) error); ok { + r2 = rf(_a0, _a1, _a2) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + +// ListMembers provides a mock function with given fields: _a0, _a1, _a2 +func (_m *Organizations) ListMembers(_a0 context.Context, _a1 string, _a2 *github.ListMembersOptions) ([]*github.User, *github.Response, error) { + ret := _m.Called(_a0, _a1, _a2) + + var r0 []*github.User + if rf, ok := ret.Get(0).(func(context.Context, string, *github.ListMembersOptions) []*github.User); ok { + r0 = rf(_a0, _a1, _a2) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*github.User) + } + } + + var r1 *github.Response + if rf, ok := ret.Get(1).(func(context.Context, string, *github.ListMembersOptions) *github.Response); ok { + r1 = rf(_a0, _a1, _a2) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(*github.Response) + } + } + + var r2 error + if rf, ok := ret.Get(2).(func(context.Context, string, *github.ListMembersOptions) error); ok { + r2 = rf(_a0, _a1, _a2) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + +// ListOrgInvitationTeams provides a mock function with given fields: _a0, _a1, _a2, _a3 +func (_m *Organizations) ListOrgInvitationTeams(_a0 context.Context, _a1 string, _a2 string, _a3 *github.ListOptions) ([]*github.Team, *github.Response, error) { + ret := _m.Called(_a0, _a1, _a2, _a3) + + var r0 []*github.Team + if rf, ok := ret.Get(0).(func(context.Context, string, string, *github.ListOptions) []*github.Team); ok { + r0 = rf(_a0, _a1, _a2, _a3) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*github.Team) + } + } + + var r1 *github.Response + if rf, ok := ret.Get(1).(func(context.Context, string, string, *github.ListOptions) *github.Response); ok { + r1 = rf(_a0, _a1, _a2, _a3) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(*github.Response) + } + } + + var r2 error + if rf, ok := ret.Get(2).(func(context.Context, string, string, *github.ListOptions) error); ok { + r2 = rf(_a0, _a1, _a2, _a3) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + +// ListOrgMemberships provides a mock function with given fields: _a0, _a1 +func (_m *Organizations) ListOrgMemberships(_a0 context.Context, _a1 *github.ListOrgMembershipsOptions) ([]*github.Membership, *github.Response, error) { + ret := _m.Called(_a0, _a1) + + var r0 []*github.Membership + if rf, ok := ret.Get(0).(func(context.Context, *github.ListOrgMembershipsOptions) []*github.Membership); ok { + r0 = rf(_a0, _a1) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*github.Membership) + } + } + + var r1 *github.Response + if rf, ok := ret.Get(1).(func(context.Context, *github.ListOrgMembershipsOptions) *github.Response); ok { + r1 = rf(_a0, _a1) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(*github.Response) + } + } + + var r2 error + if rf, ok := ret.Get(2).(func(context.Context, *github.ListOrgMembershipsOptions) error); ok { + r2 = rf(_a0, _a1) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + +// ListOutsideCollaborators provides a mock function with given fields: _a0, _a1, _a2 +func (_m *Organizations) ListOutsideCollaborators(_a0 context.Context, _a1 string, _a2 *github.ListOutsideCollaboratorsOptions) ([]*github.User, *github.Response, error) { + ret := _m.Called(_a0, _a1, _a2) + + var r0 []*github.User + if rf, ok := ret.Get(0).(func(context.Context, string, *github.ListOutsideCollaboratorsOptions) []*github.User); ok { + r0 = rf(_a0, _a1, _a2) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*github.User) + } + } + + var r1 *github.Response + if rf, ok := ret.Get(1).(func(context.Context, string, *github.ListOutsideCollaboratorsOptions) *github.Response); ok { + r1 = rf(_a0, _a1, _a2) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(*github.Response) + } + } + + var r2 error + if rf, ok := ret.Get(2).(func(context.Context, string, *github.ListOutsideCollaboratorsOptions) error); ok { + r2 = rf(_a0, _a1, _a2) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + +// ListPendingOrgInvitations provides a mock function with given fields: _a0, _a1, _a2 +func (_m *Organizations) ListPendingOrgInvitations(_a0 context.Context, _a1 string, _a2 *github.ListOptions) ([]*github.Invitation, *github.Response, error) { + ret := _m.Called(_a0, _a1, _a2) + + var r0 []*github.Invitation + if rf, ok := ret.Get(0).(func(context.Context, string, *github.ListOptions) []*github.Invitation); ok { + r0 = rf(_a0, _a1, _a2) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*github.Invitation) + } + } + + var r1 *github.Response + if rf, ok := ret.Get(1).(func(context.Context, string, *github.ListOptions) *github.Response); ok { + r1 = rf(_a0, _a1, _a2) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(*github.Response) + } + } + + var r2 error + if rf, ok := ret.Get(2).(func(context.Context, string, *github.ListOptions) error); ok { + r2 = rf(_a0, _a1, _a2) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + +// ListProjects provides a mock function with given fields: _a0, _a1, _a2 +func (_m *Organizations) ListProjects(_a0 context.Context, _a1 string, _a2 *github.ProjectListOptions) ([]*github.Project, *github.Response, error) { + ret := _m.Called(_a0, _a1, _a2) + + var r0 []*github.Project + if rf, ok := ret.Get(0).(func(context.Context, string, *github.ProjectListOptions) []*github.Project); ok { + r0 = rf(_a0, _a1, _a2) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*github.Project) + } + } + + var r1 *github.Response + if rf, ok := ret.Get(1).(func(context.Context, string, *github.ProjectListOptions) *github.Response); ok { + r1 = rf(_a0, _a1, _a2) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(*github.Response) + } + } + + var r2 error + if rf, ok := ret.Get(2).(func(context.Context, string, *github.ProjectListOptions) error); ok { + r2 = rf(_a0, _a1, _a2) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + +// PingHook provides a mock function with given fields: _a0, _a1, _a2 +func (_m *Organizations) PingHook(_a0 context.Context, _a1 string, _a2 int64) (*github.Response, error) { + ret := _m.Called(_a0, _a1, _a2) + + var r0 *github.Response + if rf, ok := ret.Get(0).(func(context.Context, string, int64) *github.Response); ok { + r0 = rf(_a0, _a1, _a2) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*github.Response) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, string, int64) error); ok { + r1 = rf(_a0, _a1, _a2) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// PublicizeMembership provides a mock function with given fields: _a0, _a1, _a2 +func (_m *Organizations) PublicizeMembership(_a0 context.Context, _a1 string, _a2 string) (*github.Response, error) { + ret := _m.Called(_a0, _a1, _a2) + + var r0 *github.Response + if rf, ok := ret.Get(0).(func(context.Context, string, string) *github.Response); ok { + r0 = rf(_a0, _a1, _a2) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*github.Response) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, string, string) error); ok { + r1 = rf(_a0, _a1, _a2) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// RemoveMember provides a mock function with given fields: _a0, _a1, _a2 +func (_m *Organizations) RemoveMember(_a0 context.Context, _a1 string, _a2 string) (*github.Response, error) { + ret := _m.Called(_a0, _a1, _a2) + + var r0 *github.Response + if rf, ok := ret.Get(0).(func(context.Context, string, string) *github.Response); ok { + r0 = rf(_a0, _a1, _a2) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*github.Response) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, string, string) error); ok { + r1 = rf(_a0, _a1, _a2) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// RemoveOrgMembership provides a mock function with given fields: _a0, _a1, _a2 +func (_m *Organizations) RemoveOrgMembership(_a0 context.Context, _a1 string, _a2 string) (*github.Response, error) { + ret := _m.Called(_a0, _a1, _a2) + + var r0 *github.Response + if rf, ok := ret.Get(0).(func(context.Context, string, string) *github.Response); ok { + r0 = rf(_a0, _a1, _a2) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*github.Response) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, string, string) error); ok { + r1 = rf(_a0, _a1, _a2) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// RemoveOutsideCollaborator provides a mock function with given fields: _a0, _a1, _a2 +func (_m *Organizations) RemoveOutsideCollaborator(_a0 context.Context, _a1 string, _a2 string) (*github.Response, error) { + ret := _m.Called(_a0, _a1, _a2) + + var r0 *github.Response + if rf, ok := ret.Get(0).(func(context.Context, string, string) *github.Response); ok { + r0 = rf(_a0, _a1, _a2) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*github.Response) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, string, string) error); ok { + r1 = rf(_a0, _a1, _a2) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UnblockUser provides a mock function with given fields: _a0, _a1, _a2 +func (_m *Organizations) UnblockUser(_a0 context.Context, _a1 string, _a2 string) (*github.Response, error) { + ret := _m.Called(_a0, _a1, _a2) + + var r0 *github.Response + if rf, ok := ret.Get(0).(func(context.Context, string, string) *github.Response); ok { + r0 = rf(_a0, _a1, _a2) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*github.Response) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, string, string) error); ok { + r1 = rf(_a0, _a1, _a2) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} diff --git a/pkg/git/github/mocks/repositories.go b/pkg/git/github/mocks/repositories.go index a4de23ea..83d269f8 100644 --- a/pkg/git/github/mocks/repositories.go +++ b/pkg/git/github/mocks/repositories.go @@ -5,7 +5,7 @@ package mocks import ( context "context" - github "github.com/google/go-github/v34/github" + github "github.com/google/go-github/v35/github" http "net/http" @@ -565,6 +565,38 @@ func (_m *Repositories) CreateStatus(_a0 context.Context, _a1 string, _a2 string return r0, r1, r2 } +// CreateUpdateEnvironment provides a mock function with given fields: _a0, _a1, _a2, _a3, _a4 +func (_m *Repositories) CreateUpdateEnvironment(_a0 context.Context, _a1 string, _a2 string, _a3 string, _a4 *github.CreateUpdateEnvironment) (*github.Environment, *github.Response, error) { + ret := _m.Called(_a0, _a1, _a2, _a3, _a4) + + var r0 *github.Environment + if rf, ok := ret.Get(0).(func(context.Context, string, string, string, *github.CreateUpdateEnvironment) *github.Environment); ok { + r0 = rf(_a0, _a1, _a2, _a3, _a4) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*github.Environment) + } + } + + var r1 *github.Response + if rf, ok := ret.Get(1).(func(context.Context, string, string, string, *github.CreateUpdateEnvironment) *github.Response); ok { + r1 = rf(_a0, _a1, _a2, _a3, _a4) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(*github.Response) + } + } + + var r2 error + if rf, ok := ret.Get(2).(func(context.Context, string, string, string, *github.CreateUpdateEnvironment) error); ok { + r2 = rf(_a0, _a1, _a2, _a3, _a4) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + // Delete provides a mock function with given fields: _a0, _a1, _a2 func (_m *Repositories) Delete(_a0 context.Context, _a1 string, _a2 string) (*github.Response, error) { ret := _m.Called(_a0, _a1, _a2) @@ -634,6 +666,29 @@ func (_m *Repositories) DeleteDeployment(_a0 context.Context, _a1 string, _a2 st return r0, r1 } +// DeleteEnvironment provides a mock function with given fields: _a0, _a1, _a2, _a3 +func (_m *Repositories) DeleteEnvironment(_a0 context.Context, _a1 string, _a2 string, _a3 string) (*github.Response, error) { + ret := _m.Called(_a0, _a1, _a2, _a3) + + var r0 *github.Response + if rf, ok := ret.Get(0).(func(context.Context, string, string, string) *github.Response); ok { + r0 = rf(_a0, _a1, _a2, _a3) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*github.Response) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, string, string, string) error); ok { + r1 = rf(_a0, _a1, _a2, _a3) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // DeleteFile provides a mock function with given fields: _a0, _a1, _a2, _a3, _a4 func (_m *Repositories) DeleteFile(_a0 context.Context, _a1 string, _a2 string, _a3 string, _a4 *github.RepositoryContentFileOptions) (*github.RepositoryContentResponse, *github.Response, error) { ret := _m.Called(_a0, _a1, _a2, _a3, _a4) @@ -1763,6 +1818,38 @@ func (_m *Repositories) GetDeploymentStatus(_a0 context.Context, _a1 string, _a2 return r0, r1, r2 } +// GetEnvironment provides a mock function with given fields: _a0, _a1, _a2, _a3 +func (_m *Repositories) GetEnvironment(_a0 context.Context, _a1 string, _a2 string, _a3 string) (*github.Environment, *github.Response, error) { + ret := _m.Called(_a0, _a1, _a2, _a3) + + var r0 *github.Environment + if rf, ok := ret.Get(0).(func(context.Context, string, string, string) *github.Environment); ok { + r0 = rf(_a0, _a1, _a2, _a3) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*github.Environment) + } + } + + var r1 *github.Response + if rf, ok := ret.Get(1).(func(context.Context, string, string, string) *github.Response); ok { + r1 = rf(_a0, _a1, _a2, _a3) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(*github.Response) + } + } + + var r2 error + if rf, ok := ret.Get(2).(func(context.Context, string, string, string) error); ok { + r2 = rf(_a0, _a1, _a2, _a3) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + // GetHook provides a mock function with given fields: _a0, _a1, _a2, _a3 func (_m *Repositories) GetHook(_a0 context.Context, _a1 string, _a2 string, _a3 int64) (*github.Hook, *github.Response, error) { ret := _m.Called(_a0, _a1, _a2, _a3) @@ -2879,6 +2966,38 @@ func (_m *Repositories) ListDeployments(_a0 context.Context, _a1 string, _a2 str return r0, r1, r2 } +// ListEnvironments provides a mock function with given fields: _a0, _a1, _a2 +func (_m *Repositories) ListEnvironments(_a0 context.Context, _a1 string, _a2 string) (*github.EnvResponse, *github.Response, error) { + ret := _m.Called(_a0, _a1, _a2) + + var r0 *github.EnvResponse + if rf, ok := ret.Get(0).(func(context.Context, string, string) *github.EnvResponse); ok { + r0 = rf(_a0, _a1, _a2) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*github.EnvResponse) + } + } + + var r1 *github.Response + if rf, ok := ret.Get(1).(func(context.Context, string, string) *github.Response); ok { + r1 = rf(_a0, _a1, _a2) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(*github.Response) + } + } + + var r2 error + if rf, ok := ret.Get(2).(func(context.Context, string, string) error); ok { + r2 = rf(_a0, _a1, _a2) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + // ListForks provides a mock function with given fields: _a0, _a1, _a2, _a3 func (_m *Repositories) ListForks(_a0 context.Context, _a1 string, _a2 string, _a3 *github.RepositoryListForksOptions) ([]*github.Repository, *github.Response, error) { ret := _m.Called(_a0, _a1, _a2, _a3) diff --git a/pkg/git/github/mocks/users.go b/pkg/git/github/mocks/users.go index 557461e4..df4e0b8c 100644 --- a/pkg/git/github/mocks/users.go +++ b/pkg/git/github/mocks/users.go @@ -5,7 +5,7 @@ package mocks import ( context "context" - github "github.com/google/go-github/v34/github" + github "github.com/google/go-github/v35/github" mock "github.com/stretchr/testify/mock" ) diff --git a/pkg/git/github/repos.go b/pkg/git/github/repos.go index 6952b5f2..4ec3c844 100644 --- a/pkg/git/github/repos.go +++ b/pkg/git/github/repos.go @@ -4,14 +4,14 @@ package github import ( "context" - "github.com/google/go-github/v34/github" + "github.com/google/go-github/v35/github" "io" "net/http" "net/url" "os" ) -// Repositories is an interface generated for "github.com/google/go-github/v34/github.RepositoriesService". +// Repositories is an interface generated for "github.com/google/go-github/v35/github.RepositoriesService". type Repositories interface { AddAdminEnforcement(context.Context, string, string, string) (*github.AdminEnforcement, *github.Response, error) AddAppRestrictions(context.Context, string, string, string, []string) ([]*github.App, *github.Response, error) @@ -30,9 +30,11 @@ type Repositories interface { CreateProject(context.Context, string, string, *github.ProjectOptions) (*github.Project, *github.Response, error) CreateRelease(context.Context, string, string, *github.RepositoryRelease) (*github.RepositoryRelease, *github.Response, error) CreateStatus(context.Context, string, string, string, *github.RepoStatus) (*github.RepoStatus, *github.Response, error) + CreateUpdateEnvironment(context.Context, string, string, string, *github.CreateUpdateEnvironment) (*github.Environment, *github.Response, error) Delete(context.Context, string, string) (*github.Response, error) DeleteComment(context.Context, string, string, int64) (*github.Response, error) DeleteDeployment(context.Context, string, string, int64) (*github.Response, error) + DeleteEnvironment(context.Context, string, string, string) (*github.Response, error) DeleteFile(context.Context, string, string, string, *github.RepositoryContentFileOptions) (*github.RepositoryContentResponse, *github.Response, error) DeleteHook(context.Context, string, string, int64) (*github.Response, error) DeleteInvitation(context.Context, string, string, int64) (*github.Response, error) @@ -71,6 +73,7 @@ type Repositories interface { GetContents(context.Context, string, string, string, *github.RepositoryContentGetOptions) (*github.RepositoryContent, []*github.RepositoryContent, *github.Response, error) GetDeployment(context.Context, string, string, int64) (*github.Deployment, *github.Response, error) GetDeploymentStatus(context.Context, string, string, int64, int64) (*github.DeploymentStatus, *github.Response, error) + GetEnvironment(context.Context, string, string, string) (*github.Environment, *github.Response, error) GetHook(context.Context, string, string, int64) (*github.Hook, *github.Response, error) GetKey(context.Context, string, string, int64) (*github.Key, *github.Response, error) GetLatestPagesBuild(context.Context, string, string) (*github.PagesBuild, *github.Response, error) @@ -106,6 +109,7 @@ type Repositories interface { ListContributorsStats(context.Context, string, string) ([]*github.ContributorStats, *github.Response, error) ListDeploymentStatuses(context.Context, string, string, int64, *github.ListOptions) ([]*github.DeploymentStatus, *github.Response, error) ListDeployments(context.Context, string, string, *github.DeploymentsListOptions) ([]*github.Deployment, *github.Response, error) + ListEnvironments(context.Context, string, string) (*github.EnvResponse, *github.Response, error) ListForks(context.Context, string, string, *github.RepositoryListForksOptions) ([]*github.Repository, *github.Response, error) ListHooks(context.Context, string, string, *github.ListOptions) ([]*github.Hook, *github.Response, error) ListInvitations(context.Context, string, string, *github.ListOptions) ([]*github.RepositoryInvitation, *github.Response, error) diff --git a/pkg/git/github/users.go b/pkg/git/github/users.go index 91892b53..30675564 100644 --- a/pkg/git/github/users.go +++ b/pkg/git/github/users.go @@ -4,10 +4,10 @@ package github import ( "context" - "github.com/google/go-github/v34/github" + "github.com/google/go-github/v35/github" ) -// Users is an interface generated for "github.com/google/go-github/v34/github.UsersService". +// Users is an interface generated for "github.com/google/go-github/v35/github.UsersService". type Users interface { AcceptInvitation(context.Context, int64) (*github.Response, error) AddEmails(context.Context, []string) ([]*github.UserEmail, *github.Response, error) diff --git a/pkg/git/provider_github.go b/pkg/git/provider_github.go index 99260e38..ff645ac9 100644 --- a/pkg/git/provider_github.go +++ b/pkg/git/provider_github.go @@ -7,7 +7,7 @@ import ( g "github.com/argoproj-labs/argocd-autopilot/pkg/git/github" - gh "github.com/google/go-github/v34/github" + gh "github.com/google/go-github/v35/github" ) //go:generate mockery --dir github --all --output github/mocks --case snake @@ -56,7 +56,7 @@ func (g *github) CreateRepository(ctx context.Context, opts *CreateRepoOptions) if res.StatusCode == 401 { return "", ErrAuthenticationFailed(err) } - + return "", err } diff --git a/pkg/git/provider_github_test.go b/pkg/git/provider_github_test.go index 03b145e5..6d4762c9 100644 --- a/pkg/git/provider_github_test.go +++ b/pkg/git/provider_github_test.go @@ -8,7 +8,7 @@ import ( "github.com/argoproj-labs/argocd-autopilot/pkg/git/github/mocks" - gh "github.com/google/go-github/v34/github" + gh "github.com/google/go-github/v35/github" "github.com/stretchr/testify/assert" ) diff --git a/pkg/git/repository.go b/pkg/git/repository.go index 7d3dc6f7..ccbd6122 100644 --- a/pkg/git/repository.go +++ b/pkg/git/repository.go @@ -122,7 +122,7 @@ func (o *CloneOptions) Parse() { suffix string ) - host, orgRepo, o.path, o.revision, suffix = parseGitUrl(o.Repo) + host, orgRepo, o.path, o.revision, suffix = ParseGitUrl(o.Repo) o.url = host + orgRepo + suffix } @@ -288,10 +288,16 @@ func getAuth(auth Auth) transport.AuthMethod { } } -// From strings like git@github.com:someOrg/someRepo.git or -// https://github.com/someOrg/someRepo?ref=someHash, extract -// the parts. -func parseGitUrl(n string) (host, orgRepo, path, ref, gitSuff string) { +// ParseGitUrl returns the different parts of the repo url +// example: "https://github.com/owner/name/repo/path?ref=branch" +// host: "https://github.com" +// orgRepo: "owner/name" +// path: "path" +// ref: "refs/heads/branch" +// gitSuff: ".git" +// For tags use "?tag=" +// For specific git commit sha use "?sha=" +func ParseGitUrl(n string) (host, orgRepo, path, ref, gitSuff string) { if strings.Contains(n, gitDelimiter) { index := strings.Index(n, gitDelimiter) // Adding _git/ to host diff --git a/pkg/git/repospec_test.go b/pkg/git/repospec_test.go index 53f8678c..3ad4c518 100644 --- a/pkg/git/repospec_test.go +++ b/pkg/git/repospec_test.go @@ -57,7 +57,7 @@ func TestNewRepoSpecFromUrl(t *testing.T) { for _, pathName := range pathNames { for _, hrefArg := range hrefArgs { uri := makeUrl(hostRaw, orgRepo, pathName, hrefArg) - host, org, path, ref, _ := parseGitUrl(uri) + host, org, path, ref, _ := ParseGitUrl(uri) if host != hostSpec { bad = append(bad, []string{"host", uri, host, hostSpec}) } @@ -143,7 +143,7 @@ func TestNewRepoSpecFromUrl_CloneSpecs(t *testing.T) { }, } for _, testcase := range testcases { - host, orgRepo, path, ref, suffix := parseGitUrl(testcase.input) + host, orgRepo, path, ref, suffix := ParseGitUrl(testcase.input) cloneSpec := host + orgRepo + suffix if cloneSpec != testcase.cloneSpec { t.Errorf("CloneSpec expected to be %v, but got %v on %s", testcase.cloneSpec, cloneSpec, testcase.input)