diff --git a/CHANGELOG.md b/CHANGELOG.md index a1741d1..354c408 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## Goliac v0.15.3 + +- allow public,internal,private repository visibility +- allow to restrict public repositories via global rules + ## Goliac v0.15.2 - refactor error handling diff --git a/browser/goliac-ui/src/components/RepositoriesApp.vue b/browser/goliac-ui/src/components/RepositoriesApp.vue index b5a5d8e..19b2b8c 100644 --- a/browser/goliac-ui/src/components/RepositoriesApp.vue +++ b/browser/goliac-ui/src/components/RepositoriesApp.vue @@ -16,7 +16,7 @@ :default-sort="{ prop: 'name', order: 'descending' }" > - + diff --git a/browser/goliac-ui/src/components/RepositoryApp.vue b/browser/goliac-ui/src/components/RepositoryApp.vue index b7ba851..d3d1e2f 100644 --- a/browser/goliac-ui/src/components/RepositoryApp.vue +++ b/browser/goliac-ui/src/components/RepositoryApp.vue @@ -15,8 +15,8 @@
- Public : - {{ repository.public}} + Visibility : + {{ repository.visibility}}
Archived : diff --git a/docs/api_docs/bundle.yaml b/docs/api_docs/bundle.yaml index 8a13d25..40fc133 100644 --- a/docs/api_docs/bundle.yaml +++ b/docs/api_docs/bundle.yaml @@ -321,8 +321,8 @@ definitions: name: type: string x-isnullable: false - public: - type: boolean + visibility: + type: string x-isnullable: false x-omitempty: false autoMergeAllowed: @@ -351,8 +351,8 @@ definitions: name: type: string x-isnullable: false - public: - type: boolean + visibility: + type: string x-isnullable: false x-omitempty: false archived: diff --git a/docs/installation.md b/docs/installation.md index 9d086f4..ee247ae 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -138,6 +138,15 @@ destructive_operations: teams: false # can Goliac remove teams not listed in this repository users: false # can Goliac remove users not listed in this repository rulesets: false # can Goliac remove rulesets not listed in this repository + +usersync: + plugin: noop # noop, fromgithubsaml, shellscript + +#visibility_rules: +# forbid_public_repositories: true # if you want to forbid public repositories +# forbid_public_repositories_exclusions: # if you want to allow some public repositories +# - goliac-teams +# - repo_public.* ``` and you can configure different ruleset in the `/rulesets` directory like diff --git a/docs/quick_start.md b/docs/quick_start.md index 2cb2ced..d3bc6c8 100644 --- a/docs/quick_start.md +++ b/docs/quick_start.md @@ -158,7 +158,7 @@ apiVersion: v1 kind: Repository name: myrepository spec: - public: false + visibility: private EOF git add myrepository.yaml diff --git a/docs/usage.md b/docs/usage.md index 114f461..da5bf57 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -49,7 +49,7 @@ apiVersion: v1 kind: Repository name: awesome-repository spec: - public: true + visibility: public allow_auto_merge: true delete_branch_on_merge: true allow_update_branch: true @@ -77,7 +77,7 @@ apiVersion: v1 kind: Repository name: awesome-repository spec: - public: true + visibility: public ... renameTo: anotherName ``` @@ -99,7 +99,7 @@ apiVersion: v1 kind: Repository name: awesome-repository spec: - public: true + visibility: public ... rulesets: - name: myruleset @@ -118,7 +118,7 @@ apiVersion: v1 kind: Repository name: awesome-repository spec: - public: true + visibility: public ... rulesets: - name: myruleset @@ -141,7 +141,7 @@ apiVersion: v1 kind: Repository name: awesome-repository spec: - public: true + visibility: public ... rulesets: - name: myruleset @@ -168,7 +168,7 @@ apiVersion: v1 kind: Repository name: awesome-repository spec: - public: true + visibility: public ... branch_protections: - pattern: master @@ -195,7 +195,7 @@ apiVersion: v1 kind: Repository name: awesome-repository spec: - public: true + visibility: public ... branch_protections: - pattern: master diff --git a/internal/config/repo_config.go b/internal/config/repo_config.go index 0017fd7..c344e30 100644 --- a/internal/config/repo_config.go +++ b/internal/config/repo_config.go @@ -25,6 +25,11 @@ type RepositoryConfig struct { AllowDestructiveUsers bool `yaml:"users"` AllowDestructiveRulesets bool `yaml:"rulesets"` } `yaml:"destructive_operations"` + + VisibilityRules struct { + ForbidPublicRepositories bool `yaml:"forbid_public_repositories"` + ForbidPublicRepositoriesExclusions []string `yaml:"forbid_public_repositories_exclusions"` + } `yaml:"visibility_rules"` } // set default values diff --git a/internal/engine/goliac_reconciliator.go b/internal/engine/goliac_reconciliator.go index db7eeb9..3ed2f8b 100644 --- a/internal/engine/goliac_reconciliator.go +++ b/internal/engine/goliac_reconciliator.go @@ -30,16 +30,18 @@ type GoliacReconciliator interface { } type GoliacReconciliatorImpl struct { - executor ReconciliatorExecutor - repoconfig *config.RepositoryConfig - unmanaged *UnmanagedResources + executor ReconciliatorExecutor + reconciliatorFilter ReconciliatorFilter + repoconfig *config.RepositoryConfig + unmanaged *UnmanagedResources } -func NewGoliacReconciliatorImpl(executor ReconciliatorExecutor, repoconfig *config.RepositoryConfig) GoliacReconciliator { +func NewGoliacReconciliatorImpl(isEntreprise bool, executor ReconciliatorExecutor, repoconfig *config.RepositoryConfig) GoliacReconciliator { return &GoliacReconciliatorImpl{ - executor: executor, - repoconfig: repoconfig, - unmanaged: nil, + executor: executor, + reconciliatorFilter: NewReconciliatorFilter(isEntreprise, repoconfig), + repoconfig: repoconfig, + unmanaged: nil, } } @@ -350,6 +352,7 @@ func (r *GoliacReconciliatorImpl) reconciliateTeams(ctx context.Context, errorCo } type GithubRepoComparable struct { + Visibility string BoolProperties map[string]bool Writers []string Readers []string @@ -395,6 +398,7 @@ func (r *GoliacReconciliatorImpl) reconciliateRepositories(ctx context.Context, ghRepos := remote.Repositories() for k, v := range ghRepos { repo := &GithubRepoComparable{ + Visibility: v.Visibility, BoolProperties: map[string]bool{}, Writers: []string{}, Readers: []string{}, @@ -444,7 +448,7 @@ func (r *GoliacReconciliatorImpl) reconciliateRepositories(ctx context.Context, teamsRepo.Name = teamsreponame teamsRepo.Spec.Writers = []string{r.repoconfig.AdminTeam} teamsRepo.Spec.Readers = []string{} - teamsRepo.Spec.IsPublic = false + teamsRepo.Spec.Visibility = "private" teamsRepo.Spec.DeleteBranchOnMerge = true // cf goliac.go:L231-252 bp := entity.RepositoryBranchProtection{ @@ -544,14 +548,14 @@ func (r *GoliacReconciliatorImpl) reconciliateRepositories(ctx context.Context, branchprotections[bp.Pattern] = &branchprotection } - lRepos[utils.GithubAnsiString(reponame)] = &GithubRepoComparable{ + lRepos[utils.GithubAnsiString(reponame)] = r.reconciliatorFilter.RepositoryFilter(reponame, &GithubRepoComparable{ BoolProperties: map[string]bool{ - "private": !lRepo.Spec.IsPublic, "archived": lRepo.Archived, "allow_auto_merge": lRepo.Spec.AllowAutoMerge, "delete_branch_on_merge": lRepo.Spec.DeleteBranchOnMerge, "allow_update_branch": lRepo.Spec.AllowUpdateBranch, }, + Visibility: lRepo.Spec.Visibility, Readers: readers, Writers: writers, ExternalUserReaders: eReaders, @@ -559,7 +563,7 @@ func (r *GoliacReconciliatorImpl) reconciliateRepositories(ctx context.Context, InternalUsers: []string{}, Rulesets: rulesets, BranchProtections: branchprotections, - } + }) } // now we compare local (slugTeams) and remote (rTeams) @@ -613,6 +617,10 @@ func (r *GoliacReconciliatorImpl) reconciliateRepositories(ctx context.Context, } } + if lRepo.Visibility != rRepo.Visibility { + return false + } + if res, _, _ := entity.StringArrayEquivalent(lRepo.Readers, rRepo.Readers); !res { return false } @@ -640,9 +648,12 @@ func (r *GoliacReconciliatorImpl) reconciliateRepositories(ctx context.Context, // reconciliate repositories boolean properties for lk, lv := range lRepo.BoolProperties { if rv, ok := rRepo.BoolProperties[lk]; !ok || rv != lv { - r.UpdateRepositoryUpdateBoolProperty(ctx, errorCollector, dryrun, remote, reponame, lk, lv) + r.UpdateRepositoryUpdateProperty(ctx, errorCollector, dryrun, remote, reponame, lk, lv) } } + if lRepo.Visibility != rRepo.Visibility { + r.UpdateRepositoryUpdateProperty(ctx, errorCollector, dryrun, remote, reponame, "visibility", lRepo.Visibility) + } if res, readToRemove, readToAdd := entity.StringArrayEquivalent(lRepo.Readers, rRepo.Readers); !res { for _, teamSlug := range readToAdd { @@ -717,11 +728,11 @@ func (r *GoliacReconciliatorImpl) reconciliateRepositories(ctx context.Context, // if the repo was just archived in a previous commit and we "resume it" if aRepo, ok := toArchive[reponame]; ok { delete(toArchive, reponame) - r.UpdateRepositoryUpdateBoolProperty(ctx, errorCollector, dryrun, remote, reponame, "archived", false) + r.UpdateRepositoryUpdateProperty(ctx, errorCollector, dryrun, remote, reponame, "archived", false) // calling onChanged to update the repository permissions onChanged(reponame, aRepo, rRepo) } else { - r.CreateRepository(ctx, errorCollector, dryrun, remote, reponame, reponame, lRepo.Writers, lRepo.Readers, lRepo.BoolProperties) + r.CreateRepository(ctx, errorCollector, dryrun, remote, reponame, reponame, lRepo.Visibility, lRepo.Writers, lRepo.Readers, lRepo.BoolProperties) } } @@ -731,7 +742,7 @@ func (r *GoliacReconciliatorImpl) reconciliateRepositories(ctx context.Context, // but if we have ArchiveOnDelete... if r.repoconfig.ArchiveOnDelete { if r.repoconfig.DestructiveOperations.AllowDestructiveRepositories { - r.UpdateRepositoryUpdateBoolProperty(ctx, errorCollector, dryrun, remote, reponame, "archived", true) + r.UpdateRepositoryUpdateProperty(ctx, errorCollector, dryrun, remote, reponame, "archived", true) toArchive[reponame] = rRepo } else { r.unmanaged.Repositories[reponame] = true @@ -975,11 +986,11 @@ func (r *GoliacReconciliatorImpl) DeleteTeam(ctx context.Context, errorCollector r.unmanaged.Teams[teamslug] = true } } -func (r *GoliacReconciliatorImpl) CreateRepository(ctx context.Context, errorCollector *observability.ErrorCollection, dryrun bool, remote *MutableGoliacRemoteImpl, reponame string, descrition string, writers []string, readers []string, boolProperties map[string]bool) { +func (r *GoliacReconciliatorImpl) CreateRepository(ctx context.Context, errorCollector *observability.ErrorCollection, dryrun bool, remote *MutableGoliacRemoteImpl, reponame string, descrition string, visibility string, writers []string, readers []string, boolProperties map[string]bool) { logrus.WithFields(map[string]interface{}{"dryrun": dryrun, "command": "create_repository"}).Infof("repositoryname: %s, readers: %s, writers: %s, boolProperties: %v", reponame, strings.Join(readers, ","), strings.Join(writers, ","), boolProperties) - remote.CreateRepository(reponame, reponame, writers, readers, boolProperties) + remote.CreateRepository(reponame, reponame, visibility, writers, readers, boolProperties) if r.executor != nil { - r.executor.CreateRepository(ctx, errorCollector, dryrun, reponame, reponame, writers, readers, boolProperties) + r.executor.CreateRepository(ctx, errorCollector, dryrun, reponame, reponame, visibility, writers, readers, boolProperties) } } func (r *GoliacReconciliatorImpl) UpdateRepositoryAddTeamAccess(ctx context.Context, errorCollector *observability.ErrorCollection, dryrun bool, remote *MutableGoliacRemoteImpl, reponame string, teamslug string, permission string) { @@ -1025,11 +1036,11 @@ func (r *GoliacReconciliatorImpl) RenameRepository(ctx context.Context, errorCol } } -func (r *GoliacReconciliatorImpl) UpdateRepositoryUpdateBoolProperty(ctx context.Context, errorCollector *observability.ErrorCollection, dryrun bool, remote *MutableGoliacRemoteImpl, reponame string, propertyName string, propertyValue bool) { +func (r *GoliacReconciliatorImpl) UpdateRepositoryUpdateProperty(ctx context.Context, errorCollector *observability.ErrorCollection, dryrun bool, remote *MutableGoliacRemoteImpl, reponame string, propertyName string, propertyValue interface{}) { logrus.WithFields(map[string]interface{}{"dryrun": dryrun, "command": "update_repository_update_bool_property"}).Infof("repositoryname: %s %s:%v", reponame, propertyName, propertyValue) - remote.UpdateRepositoryUpdateBoolProperty(reponame, propertyName, propertyValue) + remote.UpdateRepositoryUpdateProperty(reponame, propertyName, propertyValue) if r.executor != nil { - r.executor.UpdateRepositoryUpdateBoolProperty(ctx, errorCollector, dryrun, reponame, propertyName, propertyValue) + r.executor.UpdateRepositoryUpdateProperty(ctx, errorCollector, dryrun, reponame, propertyName, propertyValue) } } func (r *GoliacReconciliatorImpl) AddRuleset(ctx context.Context, errorCollector *observability.ErrorCollection, dryrun bool, ruleset *GithubRuleSet) { diff --git a/internal/engine/goliac_reconciliator_filter.go b/internal/engine/goliac_reconciliator_filter.go new file mode 100644 index 0000000..22af9b3 --- /dev/null +++ b/internal/engine/goliac_reconciliator_filter.go @@ -0,0 +1,52 @@ +package engine + +import ( + "regexp" + + "github.com/goliac-project/goliac/internal/config" +) + +type ReconciliatorFilter interface { + RepositoryFilter(reponame string, repo *GithubRepoComparable) *GithubRepoComparable +} + +type ReconciliatorFilterImpl struct { + isEnterprise bool + ForbidPublicRepositories bool + ForbidPulicRepostitoriesExcept []*regexp.Regexp +} + +func NewReconciliatorFilter(isEnterprise bool, config *config.RepositoryConfig) *ReconciliatorFilterImpl { + exclude := []*regexp.Regexp{} + if config != nil { + for _, pattern := range config.VisibilityRules.ForbidPublicRepositoriesExclusions { + exclude = append(exclude, regexp.MustCompile("^"+pattern+"$")) + } + } + + return &ReconciliatorFilterImpl{ + isEnterprise: isEnterprise, + ForbidPublicRepositories: config.VisibilityRules.ForbidPublicRepositories, + ForbidPulicRepostitoriesExcept: exclude, + } +} + +func (r *ReconciliatorFilterImpl) RepositoryFilter(reponame string, repo *GithubRepoComparable) *GithubRepoComparable { + if !r.isEnterprise { + if repo.Visibility == "internal" { + repo.Visibility = "private" + } + } + + if r.ForbidPublicRepositories { + for _, exclude := range r.ForbidPulicRepostitoriesExcept { + if exclude.MatchString(reponame) { + return repo + } + } + if repo.Visibility == "public" { + repo.Visibility = "private" + } + } + return repo +} diff --git a/internal/engine/goliac_reconciliator_filter_test.go b/internal/engine/goliac_reconciliator_filter_test.go new file mode 100644 index 0000000..9fe8a19 --- /dev/null +++ b/internal/engine/goliac_reconciliator_filter_test.go @@ -0,0 +1,82 @@ +package engine + +import ( + "testing" + + "github.com/goliac-project/goliac/internal/config" + "github.com/stretchr/testify/assert" +) + +func TestReconciliatorFilterImpl_RepositoryFilter(t *testing.T) { + t.Run("happy path: public allowed", func(t *testing.T) { + config := &config.RepositoryConfig{} + + filter := NewReconciliatorFilter(true, config) + repo := &GithubRepoComparable{ + Visibility: "internal", + } + repo = filter.RepositoryFilter("repo", repo) + assert.Equal(t, "internal", repo.Visibility) + + repo = &GithubRepoComparable{ + Visibility: "public", + } + + repo = filter.RepositoryFilter("repo", repo) + assert.Equal(t, "public", repo.Visibility) + }) + + t.Run("happy path: public forbidden", func(t *testing.T) { + config := &config.RepositoryConfig{} + config.VisibilityRules.ForbidPublicRepositories = true + + filter := NewReconciliatorFilter(true, config) + repo := &GithubRepoComparable{ + Visibility: "internal", + } + repo = filter.RepositoryFilter("repo", repo) + assert.Equal(t, "internal", repo.Visibility) + + repo = &GithubRepoComparable{ + Visibility: "public", + } + repo = filter.RepositoryFilter("repo", repo) + assert.Equal(t, "private", repo.Visibility) + }) + + t.Run("happy path: not enterprise", func(t *testing.T) { + config := &config.RepositoryConfig{} + + filter := NewReconciliatorFilter(false, config) + repo := &GithubRepoComparable{ + Visibility: "internal", + } + repo = filter.RepositoryFilter("repo", repo) + assert.Equal(t, "private", repo.Visibility) + + repo = &GithubRepoComparable{ + Visibility: "public", + } + repo = filter.RepositoryFilter("repo", repo) + assert.Equal(t, "public", repo.Visibility) + }) + + t.Run("happy path: forbidden except", func(t *testing.T) { + config := &config.RepositoryConfig{} + config.VisibilityRules.ForbidPublicRepositories = true + config.VisibilityRules.ForbidPublicRepositoriesExclusions = []string{"repo"} + + filter := NewReconciliatorFilter(true, config) + repo := &GithubRepoComparable{ + Visibility: "public", + } + repo = filter.RepositoryFilter("repo", repo) + assert.Equal(t, "public", repo.Visibility) + + repo = &GithubRepoComparable{ + Visibility: "public", + } + repo = filter.RepositoryFilter("repo2", repo) + assert.Equal(t, "private", repo.Visibility) + }) +} diff --git a/internal/engine/goliac_reconciliator_test.go b/internal/engine/goliac_reconciliator_test.go index 66ed8ce..66edce7 100644 --- a/internal/engine/goliac_reconciliator_test.go +++ b/internal/engine/goliac_reconciliator_test.go @@ -218,7 +218,7 @@ func (r *ReconciliatorListenerRecorder) UpdateTeamSetParent(ctx context.Context, func (r *ReconciliatorListenerRecorder) DeleteTeam(ctx context.Context, errorCollector *observability.ErrorCollection, dryrun bool, teamslug string) { r.TeamDeleted[teamslug] = true } -func (r *ReconciliatorListenerRecorder) CreateRepository(ctx context.Context, errorCollector *observability.ErrorCollection, dryrun bool, reponame string, descrition string, writers []string, readers []string, boolProperties map[string]bool) { +func (r *ReconciliatorListenerRecorder) CreateRepository(ctx context.Context, errorCollector *observability.ErrorCollection, dryrun bool, reponame string, descrition string, visibility string, writers []string, readers []string, boolProperties map[string]bool) { r.RepositoryCreated[reponame] = true } func (r *ReconciliatorListenerRecorder) UpdateRepositoryAddTeamAccess(ctx context.Context, errorCollector *observability.ErrorCollection, dryrun bool, reponame string, teamslug string, permission string) { @@ -236,7 +236,7 @@ func (r *ReconciliatorListenerRecorder) DeleteRepository(ctx context.Context, er func (r *ReconciliatorListenerRecorder) RenameRepository(ctx context.Context, errorCollector *observability.ErrorCollection, dryrun bool, reponame string, newname string) { r.RepositoriesRenamed[reponame] = true } -func (r *ReconciliatorListenerRecorder) UpdateRepositoryUpdateBoolProperty(ctx context.Context, errorCollector *observability.ErrorCollection, dryrun bool, reponame string, propertyName string, propertyValue bool) { +func (r *ReconciliatorListenerRecorder) UpdateRepositoryUpdateProperty(ctx context.Context, errorCollector *observability.ErrorCollection, dryrun bool, reponame string, propertyName string, propertyValue interface{}) { r.RepositoriesUpdatePrivate[reponame] = true } func (r *ReconciliatorListenerRecorder) UpdateRepositorySetExternalUser(ctx context.Context, errorCollector *observability.ErrorCollection, dryrun bool, reponame string, githubid string, permission string) { @@ -319,7 +319,7 @@ func TestReconciliationTeam(t *testing.T) { repoconf := config.RepositoryConfig{} - r := NewGoliacReconciliatorImpl(recorder, &repoconf) + r := NewGoliacReconciliatorImpl(false, recorder, &repoconf) local := GoliacLocalMock{ users: make(map[string]*entity.User), @@ -365,7 +365,7 @@ func TestReconciliationTeam(t *testing.T) { repoconf := config.RepositoryConfig{} - r := NewGoliacReconciliatorImpl(recorder, &repoconf) + r := NewGoliacReconciliatorImpl(false, recorder, &repoconf) local := GoliacLocalMock{ users: make(map[string]*entity.User), @@ -411,7 +411,7 @@ func TestReconciliationTeam(t *testing.T) { repoconf := config.RepositoryConfig{} - r := NewGoliacReconciliatorImpl(recorder, &repoconf) + r := NewGoliacReconciliatorImpl(false, recorder, &repoconf) local := GoliacLocalMock{ users: make(map[string]*entity.User), @@ -475,7 +475,7 @@ func TestReconciliationTeam(t *testing.T) { repoconf := config.RepositoryConfig{} - r := NewGoliacReconciliatorImpl(recorder, &repoconf) + r := NewGoliacReconciliatorImpl(false, recorder, &repoconf) local := GoliacLocalMock{ users: make(map[string]*entity.User), @@ -544,7 +544,7 @@ func TestReconciliationTeam(t *testing.T) { EveryoneTeamEnabled: true, } - r := NewGoliacReconciliatorImpl(recorder, &repoconf) + r := NewGoliacReconciliatorImpl(false, recorder, &repoconf) local := GoliacLocalMock{ users: make(map[string]*entity.User), @@ -592,7 +592,7 @@ func TestReconciliationTeam(t *testing.T) { repoconf := config.RepositoryConfig{} - r := NewGoliacReconciliatorImpl(recorder, &repoconf) + r := NewGoliacReconciliatorImpl(false, recorder, &repoconf) local := GoliacLocalMock{ users: make(map[string]*entity.User), @@ -629,7 +629,7 @@ func TestReconciliationTeam(t *testing.T) { repoconf := config.RepositoryConfig{} - r := NewGoliacReconciliatorImpl(recorder, &repoconf) + r := NewGoliacReconciliatorImpl(false, recorder, &repoconf) local := GoliacLocalMock{ users: make(map[string]*entity.User), @@ -705,7 +705,7 @@ func TestReconciliationTeam(t *testing.T) { repoconf := config.RepositoryConfig{} - r := NewGoliacReconciliatorImpl(recorder, &repoconf) + r := NewGoliacReconciliatorImpl(false, recorder, &repoconf) local := GoliacLocalMock{ users: make(map[string]*entity.User), @@ -784,7 +784,7 @@ func TestReconciliationTeam(t *testing.T) { recorder := NewReconciliatorListenerRecorder() repoconfig := &config.RepositoryConfig{} repoconfig.DestructiveOperations.AllowDestructiveTeams = true - r := NewGoliacReconciliatorImpl(recorder, repoconfig) + r := NewGoliacReconciliatorImpl(false, recorder, repoconfig) local := GoliacLocalMock{ users: make(map[string]*entity.User), teams: make(map[string]*entity.Team), @@ -821,7 +821,7 @@ func TestReconciliationRepo(t *testing.T) { recorder := NewReconciliatorListenerRecorder() repoconf := config.RepositoryConfig{} - r := NewGoliacReconciliatorImpl(recorder, &repoconf) + r := NewGoliacReconciliatorImpl(false, recorder, &repoconf) local := GoliacLocalMock{ users: make(map[string]*entity.User), @@ -862,7 +862,7 @@ func TestReconciliationRepo(t *testing.T) { repoconf := config.RepositoryConfig{} - r := NewGoliacReconciliatorImpl(recorder, &repoconf) + r := NewGoliacReconciliatorImpl(false, recorder, &repoconf) local := GoliacLocalMock{ users: make(map[string]*entity.User), @@ -917,7 +917,7 @@ func TestReconciliationRepo(t *testing.T) { repoconf := config.RepositoryConfig{} - r := NewGoliacReconciliatorImpl(recorder, &repoconf) + r := NewGoliacReconciliatorImpl(false, recorder, &repoconf) local := GoliacLocalMock{ users: make(map[string]*entity.User), @@ -990,7 +990,7 @@ func TestReconciliationRepo(t *testing.T) { EveryoneTeamEnabled: true, } - r := NewGoliacReconciliatorImpl(recorder, &repoconf) + r := NewGoliacReconciliatorImpl(false, recorder, &repoconf) local := GoliacLocalMock{ users: make(map[string]*entity.User), @@ -1062,7 +1062,7 @@ func TestReconciliationRepo(t *testing.T) { repoconf := config.RepositoryConfig{} - r := NewGoliacReconciliatorImpl(recorder, &repoconf) + r := NewGoliacReconciliatorImpl(false, recorder, &repoconf) local := GoliacLocalMock{ users: make(map[string]*entity.User), @@ -1145,7 +1145,7 @@ func TestReconciliationRepo(t *testing.T) { repoconf := config.RepositoryConfig{} - r := NewGoliacReconciliatorImpl(recorder, &repoconf) + r := NewGoliacReconciliatorImpl(false, recorder, &repoconf) local := GoliacLocalMock{ users: make(map[string]*entity.User), @@ -1233,7 +1233,7 @@ func TestReconciliationRepo(t *testing.T) { repoconf := config.RepositoryConfig{} - r := NewGoliacReconciliatorImpl(recorder, &repoconf) + r := NewGoliacReconciliatorImpl(false, recorder, &repoconf) local := GoliacLocalMock{ users: make(map[string]*entity.User), @@ -1311,7 +1311,7 @@ func TestReconciliationRepo(t *testing.T) { repoconf := config.RepositoryConfig{} - r := NewGoliacReconciliatorImpl(recorder, &repoconf) + r := NewGoliacReconciliatorImpl(false, recorder, &repoconf) local := GoliacLocalMock{ users: make(map[string]*entity.User), @@ -1391,7 +1391,7 @@ func TestReconciliationRepo(t *testing.T) { recorder := NewReconciliatorListenerRecorder() repoconf := config.RepositoryConfig{} - r := NewGoliacReconciliatorImpl(recorder, &repoconf) + r := NewGoliacReconciliatorImpl(false, recorder, &repoconf) local := GoliacLocalMock{ users: make(map[string]*entity.User), @@ -1466,7 +1466,7 @@ func TestReconciliationRepo(t *testing.T) { recorder := NewReconciliatorListenerRecorder() repoconf := config.RepositoryConfig{} - r := NewGoliacReconciliatorImpl(recorder, &repoconf) + r := NewGoliacReconciliatorImpl(false, recorder, &repoconf) local := GoliacLocalMock{ users: make(map[string]*entity.User), @@ -1543,7 +1543,7 @@ func TestReconciliationRepo(t *testing.T) { repoconf := config.RepositoryConfig{} - r := NewGoliacReconciliatorImpl(recorder, &repoconf) + r := NewGoliacReconciliatorImpl(false, recorder, &repoconf) local := GoliacLocalMock{ users: make(map[string]*entity.User), @@ -1623,7 +1623,7 @@ func TestReconciliationRepo(t *testing.T) { repoconf := config.RepositoryConfig{} - r := NewGoliacReconciliatorImpl(recorder, &repoconf) + r := NewGoliacReconciliatorImpl(false, recorder, &repoconf) local := GoliacLocalMock{ users: make(map[string]*entity.User), @@ -1700,7 +1700,7 @@ func TestReconciliationRepo(t *testing.T) { repoconf := config.RepositoryConfig{} - r := NewGoliacReconciliatorImpl(recorder, &repoconf) + r := NewGoliacReconciliatorImpl(false, recorder, &repoconf) local := GoliacLocalMock{ users: make(map[string]*entity.User), @@ -1784,7 +1784,7 @@ func TestReconciliationRepo(t *testing.T) { repoconf := config.RepositoryConfig{} - r := NewGoliacReconciliatorImpl(recorder, &repoconf) + r := NewGoliacReconciliatorImpl(false, recorder, &repoconf) local := GoliacLocalMock{ users: make(map[string]*entity.User), @@ -1820,7 +1820,7 @@ func TestReconciliationRepo(t *testing.T) { ArchiveOnDelete: true, } repoconfig.DestructiveOperations.AllowDestructiveRepositories = true - r := NewGoliacReconciliatorImpl(recorder, repoconfig) + r := NewGoliacReconciliatorImpl(false, recorder, repoconfig) local := GoliacLocalMock{ users: make(map[string]*entity.User), @@ -1859,7 +1859,7 @@ func TestReconciliationRepo(t *testing.T) { ArchiveOnDelete: false, } repoconfig.DestructiveOperations.AllowDestructiveRepositories = true - r := NewGoliacReconciliatorImpl(recorder, repoconfig) + r := NewGoliacReconciliatorImpl(false, recorder, repoconfig) local := GoliacLocalMock{ users: make(map[string]*entity.User), @@ -1899,7 +1899,7 @@ func TestReconciliationRepo(t *testing.T) { ArchiveOnDelete: false, } repoconfig.DestructiveOperations.AllowDestructiveRepositories = true - r := NewGoliacReconciliatorImpl(recorder, repoconfig) + r := NewGoliacReconciliatorImpl(false, recorder, repoconfig) local := GoliacLocalMock{ users: make(map[string]*entity.User), @@ -2022,7 +2022,7 @@ func TestReconciliationRulesets(t *testing.T) { recorder := NewReconciliatorListenerRecorder() repoconf := config.RepositoryConfig{} - r := NewGoliacReconciliatorImpl(recorder, &repoconf) + r := NewGoliacReconciliatorImpl(false, recorder, &repoconf) local := GoliacLocalMock{ users: make(map[string]*entity.User), @@ -2079,7 +2079,7 @@ func TestReconciliationRulesets(t *testing.T) { Ruleset: "new", }) - r := NewGoliacReconciliatorImpl(recorder, &repoconf) + r := NewGoliacReconciliatorImpl(false, recorder, &repoconf) local := GoliacLocalMock{ users: make(map[string]*entity.User), @@ -2136,7 +2136,7 @@ func TestReconciliationRulesets(t *testing.T) { Ruleset: "update", }) - r := NewGoliacReconciliatorImpl(recorder, &repoconf) + r := NewGoliacReconciliatorImpl(false, recorder, &repoconf) local := GoliacLocalMock{ users: make(map[string]*entity.User), @@ -2195,7 +2195,7 @@ func TestReconciliationRulesets(t *testing.T) { } repoconf.DestructiveOperations.AllowDestructiveRulesets = true - r := NewGoliacReconciliatorImpl(recorder, &repoconf) + r := NewGoliacReconciliatorImpl(false, recorder, &repoconf) local := GoliacLocalMock{ users: make(map[string]*entity.User), @@ -2240,7 +2240,7 @@ func TestReconciliationRepoRulesets(t *testing.T) { repoconf := config.RepositoryConfig{} - r := NewGoliacReconciliatorImpl(recorder, &repoconf) + r := NewGoliacReconciliatorImpl(false, recorder, &repoconf) local := GoliacLocalMock{ users: make(map[string]*entity.User), @@ -2337,7 +2337,7 @@ func TestReconciliationRepoRulesets(t *testing.T) { repoconf := config.RepositoryConfig{} - r := NewGoliacReconciliatorImpl(recorder, &repoconf) + r := NewGoliacReconciliatorImpl(false, recorder, &repoconf) local := GoliacLocalMock{ users: make(map[string]*entity.User), @@ -2428,7 +2428,7 @@ func TestReconciliationRepoBranchProtection(t *testing.T) { repoconf := config.RepositoryConfig{} - r := NewGoliacReconciliatorImpl(recorder, &repoconf) + r := NewGoliacReconciliatorImpl(false, recorder, &repoconf) local := GoliacLocalMock{ users: make(map[string]*entity.User), @@ -2515,7 +2515,7 @@ func TestReconciliationRepoBranchProtection(t *testing.T) { repoconf := config.RepositoryConfig{} - r := NewGoliacReconciliatorImpl(recorder, &repoconf) + r := NewGoliacReconciliatorImpl(false, recorder, &repoconf) local := GoliacLocalMock{ users: make(map[string]*entity.User), diff --git a/internal/engine/mutable_remote.go b/internal/engine/mutable_remote.go index 00432c4..a8f7e0b 100644 --- a/internal/engine/mutable_remote.go +++ b/internal/engine/mutable_remote.go @@ -169,9 +169,10 @@ func (m *MutableGoliacRemoteImpl) DeleteTeam(teamslug string) { delete(m.teamRepos, teamslug) } } -func (m *MutableGoliacRemoteImpl) CreateRepository(reponame string, descrition string, writers []string, readers []string, boolProperties map[string]bool) { +func (m *MutableGoliacRemoteImpl) CreateRepository(reponame string, descrition string, visibility string, writers []string, readers []string, boolProperties map[string]bool) { r := GithubRepository{ Name: reponame, + Visibility: visibility, BoolProperties: boolProperties, ExternalUsers: map[string]string{}, } @@ -226,15 +227,19 @@ func (m *MutableGoliacRemoteImpl) RenameRepository(reponame string, newname stri /* UpdateRepositoryUpdateBoolProperty is used for -- private -- archived -- allow_auto_merge -- delete_branch_on_merge -- allow_update_branch +- visibility (string) +- archived (bool) +- allow_auto_merge (bool) +- delete_branch_on_merge (bool) +- allow_update_branch (bool) */ -func (m *MutableGoliacRemoteImpl) UpdateRepositoryUpdateBoolProperty(reponame string, propertyName string, propertyValue bool) { +func (m *MutableGoliacRemoteImpl) UpdateRepositoryUpdateProperty(reponame string, propertyName string, propertyValue interface{}) { if r, ok := m.repositories[reponame]; ok { - r.BoolProperties[propertyName] = propertyValue + if propertyName == "visibility" { + r.Visibility = propertyValue.(string) + } else { + r.BoolProperties[propertyName] = propertyValue.(bool) + } } } func (m *MutableGoliacRemoteImpl) UpdateRepositorySetExternalUser(reponame string, collaboatorGithubId string, permission string) { diff --git a/internal/engine/reconciliator_executor.go b/internal/engine/reconciliator_executor.go index 106ea58..691a96b 100644 --- a/internal/engine/reconciliator_executor.go +++ b/internal/engine/reconciliator_executor.go @@ -17,8 +17,8 @@ type ReconciliatorExecutor interface { UpdateTeamSetParent(ctx context.Context, errorCollector *observability.ErrorCollection, dryrun bool, teamslug string, parentTeam *int) DeleteTeam(ctx context.Context, errorCollector *observability.ErrorCollection, dryrun bool, teamslug string) - CreateRepository(ctx context.Context, errorCollector *observability.ErrorCollection, dryrun bool, reponame string, descrition string, writers []string, readers []string, boolProperties map[string]bool) - UpdateRepositoryUpdateBoolProperty(ctx context.Context, errorCollector *observability.ErrorCollection, dryrun bool, reponame string, propertyName string, propertyValue bool) + CreateRepository(ctx context.Context, errorCollector *observability.ErrorCollection, dryrun bool, reponame string, descrition string, visibility string, writers []string, readers []string, boolProperties map[string]bool) + UpdateRepositoryUpdateProperty(ctx context.Context, errorCollector *observability.ErrorCollection, dryrun bool, reponame string, propertyName string, propertyValue interface{}) UpdateRepositoryAddTeamAccess(ctx context.Context, errorCollector *observability.ErrorCollection, dryrun bool, reponame string, teamslug string, permission string) // permission can be "pull", "push", or "admin" which correspond to read, write, and admin access. UpdateRepositoryUpdateTeamAccess(ctx context.Context, errorCollector *observability.ErrorCollection, dryrun bool, reponame string, teamslug string, permission string) // permission can be "pull", "push", or "admin" which correspond to read, write, and admin access. UpdateRepositoryRemoveTeamAccess(ctx context.Context, errorCollector *observability.ErrorCollection, dryrun bool, reponame string, teamslug string) diff --git a/internal/engine/remote.go b/internal/engine/remote.go index dadbe32..5a6787c 100644 --- a/internal/engine/remote.go +++ b/internal/engine/remote.go @@ -61,7 +61,8 @@ type GithubRepository struct { Name string Id int RefId string - BoolProperties map[string]bool // archived, private, allow_auto_merge, delete_branch_on_merge, allow_update_branch + Visibility string // public, internal, private + BoolProperties map[string]bool // archived, allow_auto_merge, delete_branch_on_merge, allow_update_branch ExternalUsers map[string]string // [githubid]permission InternalUsers map[string]string // [githubid]permission RuleSets map[string]*GithubRuleSet // [name]ruleset @@ -493,7 +494,7 @@ query listAllReposInOrg($orgLogin: String!, $endCursor: String) { id databaseId isArchived - isPrivate + visibility autoMergeAllowed deleteBranchOnMerge allowUpdateBranch @@ -608,7 +609,7 @@ type GraplQLRepositories struct { Id string DatabaseId int IsArchived bool - IsPrivate bool + Visibility string AutoMergeAllowed bool DeleteBranchOnMerge bool AllowUpdateBranch bool @@ -683,12 +684,12 @@ func (g *GoliacRemoteImpl) loadRepositories(ctx context.Context) (map[string]*Gi for _, c := range gResult.Data.Organization.Repositories.Nodes { repo := &GithubRepository{ - Name: c.Name, - Id: c.DatabaseId, - RefId: c.Id, + Name: c.Name, + Id: c.DatabaseId, + RefId: c.Id, + Visibility: strings.ToLower(c.Visibility), BoolProperties: map[string]bool{ "archived": c.IsArchived, - "private": c.IsPrivate, "allow_auto_merge": c.AutoMergeAllowed, "delete_branch_on_merge": c.DeleteBranchOnMerge, "allow_update_branch": c.AllowUpdateBranch, @@ -2360,14 +2361,13 @@ type CreateRepositoryResponse struct { /* boolProperties are: -- private - archived - allow_auto_merge - delete_branch_on_merge - allow_update_branch - ... */ -func (g *GoliacRemoteImpl) CreateRepository(ctx context.Context, errorCollector *observability.ErrorCollection, dryrun bool, reponame string, description string, writers []string, readers []string, boolProperties map[string]bool) { +func (g *GoliacRemoteImpl) CreateRepository(ctx context.Context, errorCollector *observability.ErrorCollection, dryrun bool, reponame string, description string, visibility string, writers []string, readers []string, boolProperties map[string]bool) { repoId := 0 repoRefId := reponame // create repository @@ -2376,6 +2376,7 @@ func (g *GoliacRemoteImpl) CreateRepository(ctx context.Context, errorCollector props := map[string]interface{}{ "name": reponame, "description": description, + "visibility": visibility, } for k, v := range boolProperties { props[k] = v @@ -2409,6 +2410,7 @@ func (g *GoliacRemoteImpl) CreateRepository(ctx context.Context, errorCollector Name: reponame, Id: repoId, RefId: repoRefId, + Visibility: visibility, BoolProperties: boolProperties, } g.repositories[reponame] = newRepo @@ -2558,13 +2560,13 @@ func (g *GoliacRemoteImpl) UpdateRepositoryRemoveTeamAccess(ctx context.Context, /* Used for -- private -- allow_auto_merge -- delete_branch_on_merge -- allow_update_branch -- archived +- visibility (string) +- allow_auto_merge (bool) +- delete_branch_on_merge (bool) +- allow_update_branch (bool) +- archived (bool) */ -func (g *GoliacRemoteImpl) UpdateRepositoryUpdateBoolProperty(ctx context.Context, errorCollector *observability.ErrorCollection, dryrun bool, reponame string, propertyName string, propertyValue bool) { +func (g *GoliacRemoteImpl) UpdateRepositoryUpdateProperty(ctx context.Context, errorCollector *observability.ErrorCollection, dryrun bool, reponame string, propertyName string, propertyValue interface{}) { // https://docs.github.com/en/rest/repos/repos?apiVersion=2022-11-28#update-a-repository if !dryrun { body, err := g.client.CallRestAPI( @@ -2581,7 +2583,11 @@ func (g *GoliacRemoteImpl) UpdateRepositoryUpdateBoolProperty(ctx context.Contex } if repo, ok := g.repositories[reponame]; ok { - repo.BoolProperties[propertyName] = propertyValue + if propertyName == "visibility" { + repo.Visibility = propertyValue.(string) + } else { + repo.BoolProperties[propertyName] = propertyValue.(bool) + } } } diff --git a/internal/engine/remote_test.go b/internal/engine/remote_test.go index 5bf9c3d..7fb7328 100644 --- a/internal/engine/remote_test.go +++ b/internal/engine/remote_test.go @@ -103,7 +103,7 @@ func (m *MockGithubClient) reposNodes(first, after string, args ast.ArgumentList searchName, _ := hasChild("name", children) searchArchived, _ := hasChild("isArchived", children) - searchPrivate, _ := hasChild("isPrivate", children) + searchPrivate, _ := hasChild("visibility", children) index := iAfter totalCount := 0 @@ -120,7 +120,11 @@ func (m *MockGithubClient) reposNodes(first, after string, args ast.ArgumentList block["isArchived"] = index%3 == 0 // let's pretend each 3 repo is an archive repo } if searchPrivate { - block["isPrivate"] = index%10 == 0 // let's pretend each 10 repo is a private repo + if index%10 == 0 { // let's pretend each 10 repo is a private repo + block["visibility"] = "private" + } else { + block["visibility"] = "public" + } } index++ if index > maxToFake { // let's pretend we have maxToFake repos @@ -447,8 +451,8 @@ func TestRemoteRepository(t *testing.T) { assert.Equal(t, 133, len(repositories)) assert.Equal(t, false, repositories["repo_1"].BoolProperties["archived"]) assert.Equal(t, true, repositories["repo_3"].BoolProperties["archived"]) - assert.Equal(t, false, repositories["repo_1"].BoolProperties["private"]) - assert.Equal(t, true, repositories["repo_10"].BoolProperties["private"]) + assert.Equal(t, "public", repositories["repo_1"].Visibility) + assert.Equal(t, "private", repositories["repo_10"].Visibility) }) t.Run("happy path: load remote teams", func(t *testing.T) { // MockGithubClient doesn't support concurrent access diff --git a/internal/entity/repository.go b/internal/entity/repository.go index dcdcd40..f594785 100644 --- a/internal/entity/repository.go +++ b/internal/entity/repository.go @@ -18,7 +18,7 @@ type Repository struct { Readers []string `yaml:"readers,omitempty"` ExternalUserReaders []string `yaml:"externalUserReaders,omitempty"` ExternalUserWriters []string `yaml:"externalUserWriters,omitempty"` - IsPublic bool `yaml:"public,omitempty"` + Visibility string `yaml:"visibility,omitempty"` AllowAutoMerge bool `yaml:"allow_auto_merge,omitempty"` DeleteBranchOnMerge bool `yaml:"delete_branch_on_merge,omitempty"` AllowUpdateBranch bool `yaml:"allow_update_branch,omitempty"` @@ -64,6 +64,7 @@ func NewRepository(fs billy.Filesystem, filename string) (*Repository, error) { } repository := &Repository{} + repository.Spec.Visibility = "private" // default visibility err = yaml.Unmarshal(filecontent, repository) if err != nil { return nil, err @@ -204,6 +205,11 @@ func (r *Repository) Validate(filename string, teams map[string]*Team, externalU return fmt.Errorf("invalid name: %s for repository filename %s", r.Name, filename) } + visibility := r.Spec.Visibility + if visibility != "public" && visibility != "private" && visibility != "internal" { + return fmt.Errorf("invalid visibility: %s for repository filename %s", visibility, filename) + } + for _, writer := range r.Spec.Writers { if _, ok := teams[writer]; !ok { return fmt.Errorf("invalid writer: %s doesn't exist (check repository filename %s)", writer, filename) diff --git a/internal/github_batch_executor.go b/internal/github_batch_executor.go index 36c784e..42710a2 100644 --- a/internal/github_batch_executor.go +++ b/internal/github_batch_executor.go @@ -118,12 +118,13 @@ func (g *GithubBatchExecutor) DeleteTeam(ctx context.Context, errorCollector *ob }) } -func (g *GithubBatchExecutor) CreateRepository(ctx context.Context, errorCollector *observability.ErrorCollection, dryrun bool, reponame string, description string, writers []string, readers []string, boolProperties map[string]bool) { +func (g *GithubBatchExecutor) CreateRepository(ctx context.Context, errorCollector *observability.ErrorCollection, dryrun bool, reponame string, description string, visibility string, writers []string, readers []string, boolProperties map[string]bool) { g.commands = append(g.commands, &GithubCommandCreateRepository{ client: g.client, dryrun: dryrun, reponame: reponame, description: description, + visibility: visibility, readers: readers, writers: writers, boolProperties: boolProperties, @@ -159,8 +160,8 @@ func (g *GithubBatchExecutor) UpdateRepositoryRemoveTeamAccess(ctx context.Conte }) } -func (g *GithubBatchExecutor) UpdateRepositoryUpdateBoolProperty(ctx context.Context, errorCollector *observability.ErrorCollection, dryrun bool, reponame string, propertyName string, propertyValue bool) { - g.commands = append(g.commands, &GithubCommandUpdateRepositoryUpdateBoolProperty{ +func (g *GithubBatchExecutor) UpdateRepositoryUpdateProperty(ctx context.Context, errorCollector *observability.ErrorCollection, dryrun bool, reponame string, propertyName string, propertyValue interface{}) { + g.commands = append(g.commands, &GithubCommandUpdateRepositoryUpdateProperty{ client: g.client, dryrun: dryrun, reponame: reponame, @@ -322,13 +323,14 @@ type GithubCommandCreateRepository struct { dryrun bool reponame string description string + visibility string writers []string readers []string boolProperties map[string]bool } func (g *GithubCommandCreateRepository) Apply(ctx context.Context, errorCollector *observability.ErrorCollection) { - g.client.CreateRepository(ctx, errorCollector, g.dryrun, g.reponame, g.description, g.writers, g.readers, g.boolProperties) + g.client.CreateRepository(ctx, errorCollector, g.dryrun, g.reponame, g.description, g.visibility, g.writers, g.readers, g.boolProperties) } type GithubCommandCreateTeam struct { @@ -454,16 +456,16 @@ func (g *GithubCommandUpdateRepositoryRemoveInternalUser) Apply(ctx context.Cont g.client.UpdateRepositoryRemoveInternalUser(ctx, errorCollector, g.dryrun, g.reponame, g.githubid) } -type GithubCommandUpdateRepositoryUpdateBoolProperty struct { +type GithubCommandUpdateRepositoryUpdateProperty struct { client engine.ReconciliatorExecutor dryrun bool reponame string propertyName string - propertyValue bool + propertyValue interface{} } -func (g *GithubCommandUpdateRepositoryUpdateBoolProperty) Apply(ctx context.Context, errorCollector *observability.ErrorCollection) { - g.client.UpdateRepositoryUpdateBoolProperty(ctx, errorCollector, g.dryrun, g.reponame, g.propertyName, g.propertyValue) +func (g *GithubCommandUpdateRepositoryUpdateProperty) Apply(ctx context.Context, errorCollector *observability.ErrorCollection) { + g.client.UpdateRepositoryUpdateProperty(ctx, errorCollector, g.dryrun, g.reponame, g.propertyName, g.propertyValue) } type GithubCommandUpdateTeamAddMember struct { diff --git a/internal/goliac.go b/internal/goliac.go index da42b59..4a10326 100644 --- a/internal/goliac.go +++ b/internal/goliac.go @@ -338,7 +338,7 @@ func (g *GoliacImpl) applyCommitsToGithub(ctx context.Context, errorCollector *o var unmanaged *engine.UnmanagedResources ga := NewGithubBatchExecutor(g.remote, g.repoconfig.MaxChangesets) - reconciliator := engine.NewGoliacReconciliatorImpl(ga, g.repoconfig) + reconciliator := engine.NewGoliacReconciliatorImpl(g.remote.IsEnterprise(), ga, g.repoconfig) commit, err := g.local.GetHeadCommit() if err != nil { diff --git a/internal/goliac_server.go b/internal/goliac_server.go index c85d720..a4e0aa3 100644 --- a/internal/goliac_server.go +++ b/internal/goliac_server.go @@ -134,9 +134,9 @@ func (g *GoliacServerImpl) GetRepositories(app.GetRepositoriesParams) middleware for _, r := range local.Repositories() { repo := models.Repository{ - Name: r.Name, - Public: r.Spec.IsPublic, - Archived: r.Archived, + Name: r.Name, + Visibility: r.Spec.Visibility, + Archived: r.Archived, } repositories = append(repositories, &repo) } @@ -198,7 +198,7 @@ func (g *GoliacServerImpl) GetRepository(params app.GetRepositoryParams) middlew repositoryDetails := models.RepositoryDetails{ Name: repository.Name, - Public: repository.Spec.IsPublic, + Visibility: repository.Spec.Visibility, AutoMergeAllowed: repository.Spec.AllowAutoMerge, DeleteBranchOnMerge: repository.Spec.DeleteBranchOnMerge, AllowUpdateBranch: repository.Spec.AllowUpdateBranch, @@ -295,7 +295,7 @@ func (g *GoliacServerImpl) GetTeam(params app.GetTeamParams) middleware.Responde r := models.Repository{ Name: reponame, Archived: repo.Archived, - Public: repo.Spec.IsPublic, + Visibility: repo.Spec.Visibility, AutoMergeAllowed: repo.Spec.AllowAutoMerge, DeleteBranchOnMerge: repo.Spec.DeleteBranchOnMerge, AllowUpdateBranch: repo.Spec.AllowUpdateBranch, @@ -432,18 +432,18 @@ func (g *GoliacServerImpl) GetCollaborator(params app.GetCollaboratorParams) mid for _, r := range repo.Spec.ExternalUserReaders { if r == params.CollaboratorID { collaboratordetails.Repositories = append(collaboratordetails.Repositories, &models.Repository{ - Name: repo.Name, - Public: repo.Spec.IsPublic, - Archived: repo.Archived, + Name: repo.Name, + Visibility: repo.Spec.Visibility, + Archived: repo.Archived, }) } } for _, r := range repo.Spec.ExternalUserWriters { if r == params.CollaboratorID { collaboratordetails.Repositories = append(collaboratordetails.Repositories, &models.Repository{ - Name: repo.Name, - Public: repo.Spec.IsPublic, - Archived: repo.Archived, + Name: repo.Name, + Visibility: repo.Spec.Visibility, + Archived: repo.Archived, }) } } @@ -547,9 +547,9 @@ func (g *GoliacServerImpl) GetUser(params app.GetUserParams) middleware.Responde for _, r := range userRepos { repo := models.Repository{ - Name: r.Name, - Public: r.Spec.IsPublic, - Archived: r.Archived, + Name: r.Name, + Visibility: r.Spec.Visibility, + Archived: r.Archived, } userdetails.Repositories = append(userdetails.Repositories, &repo) } diff --git a/internal/goliac_test.go b/internal/goliac_test.go index b71b1f9..aad14d4 100644 --- a/internal/goliac_test.go +++ b/internal/goliac_test.go @@ -434,12 +434,12 @@ func (e *GoliacRemoteExecutorMock) Teams(ctx context.Context, current bool) map[ func (e *GoliacRemoteExecutorMock) Repositories(ctx context.Context) map[string]*engine.GithubRepository { return map[string]*engine.GithubRepository{ "src": { - Name: "src", // this is the "teams" repository - Id: 0, - RefId: "MDEwOlJlcG9zaXRvcnkaMTMxNjExOQ==", + Name: "src", // this is the "teams" repository + Id: 0, + RefId: "MDEwOlJlcG9zaXRvcnkaMTMxNjExOQ==", + Visibility: "private", BoolProperties: map[string]bool{ "archived": false, - "private": true, "allow_auto_merge": false, "delete_branch_on_merge": true, "allow_update_branch": false, @@ -458,12 +458,12 @@ func (e *GoliacRemoteExecutorMock) Repositories(ctx context.Context) map[string] }, }, "repo1": { - Name: "repo1", - Id: 1, - RefId: "MDEwOlJlcG9zaXRvcnkaMTMxNjExOQ==", + Name: "repo1", + Id: 1, + RefId: "MDEwOlJlcG9zaXRvcnkaMTMxNjExOQ==", + Visibility: "private", BoolProperties: map[string]bool{ "archived": false, - "private": true, "allow_auto_merge": false, "delete_branch_on_merge": false, "allow_update_branch": false, @@ -471,12 +471,12 @@ func (e *GoliacRemoteExecutorMock) Repositories(ctx context.Context) map[string] ExternalUsers: map[string]string{}, }, "repo2": { - Name: "repo2", - Id: 2, - RefId: "MDEwOlJlcG9zaXRvcnkaNTcwNDA4Ng==", + Name: "repo2", + Id: 2, + RefId: "MDEwOlJlcG9zaXRvcnkaNTcwNDA4Ng==", + Visibility: "private", BoolProperties: map[string]bool{ "archived": false, - "private": true, "allow_auto_merge": false, "delete_branch_on_merge": false, "allow_update_branch": false, @@ -586,12 +586,12 @@ func (e *GoliacRemoteExecutorMock) DeleteTeam(ctx context.Context, errorCollecto e.nbChanges++ } -func (e *GoliacRemoteExecutorMock) CreateRepository(ctx context.Context, errorCollector *observability.ErrorCollection, dryrun bool, reponame string, descrition string, writers []string, readers []string, boolProperties map[string]bool) { - fmt.Println("*** CreateRepository", reponame, descrition, writers, readers, boolProperties) +func (e *GoliacRemoteExecutorMock) CreateRepository(ctx context.Context, errorCollector *observability.ErrorCollection, dryrun bool, reponame string, descrition string, visibility string, writers []string, readers []string, boolProperties map[string]bool) { + fmt.Println("*** CreateRepository", reponame, descrition, visibility, writers, readers, boolProperties) e.nbChanges++ } -func (e *GoliacRemoteExecutorMock) UpdateRepositoryUpdateBoolProperty(ctx context.Context, errorCollector *observability.ErrorCollection, dryrun bool, reponame string, propertyName string, propertyValue bool) { - fmt.Println("*** UpdateRepositoryUpdateBoolProperty", reponame, propertyName, propertyValue) +func (e *GoliacRemoteExecutorMock) UpdateRepositoryUpdateProperty(ctx context.Context, errorCollector *observability.ErrorCollection, dryrun bool, reponame string, propertyName string, propertyValue interface{}) { + fmt.Println("*** UpdateRepositoryUpdateProperty", reponame, propertyName, propertyValue) e.nbChanges++ } func (e *GoliacRemoteExecutorMock) UpdateRepositoryAddTeamAccess(ctx context.Context, errorCollector *observability.ErrorCollection, dryrun bool, reponame string, teamslug string, permission string) { diff --git a/internal/scaffold.go b/internal/scaffold.go index defcc2d..cf8caf3 100644 --- a/internal/scaffold.go +++ b/internal/scaffold.go @@ -273,7 +273,7 @@ func (s *Scaffold) generateTeams(ctx context.Context, fs billy.Filesystem, teams if rRepo, ok := rRepos[r]; ok { // basic repository properties - lRepo.Spec.IsPublic = rRepo.BoolProperties["public"] + lRepo.Spec.Visibility = rRepo.Visibility lRepo.Spec.AllowAutoMerge = rRepo.BoolProperties["allow_auto_merge"] lRepo.Spec.DeleteBranchOnMerge = rRepo.BoolProperties["delete_branch_on_merge"] lRepo.Spec.AllowUpdateBranch = rRepo.BoolProperties["allow_update_branch"] @@ -530,6 +530,10 @@ destructive_operations: usersync: plugin: %s + +#visibility_rules: +# forbid_public_repositories: false +# forbid_public_repositories_exclusions: [] # reponame or regexp `, adminteam, userplugin) if err := writeFile(filepath.Join(rootpath, "goliac.yaml"), []byte(conf), fs); err != nil { return err diff --git a/swagger/index.yaml b/swagger/index.yaml index 5691858..9253c9d 100644 --- a/swagger/index.yaml +++ b/swagger/index.yaml @@ -112,8 +112,8 @@ definitions: name: type: string x-isnullable: false - public: - type: boolean + visibility: + type: string x-isnullable: false x-omitempty: false autoMergeAllowed: @@ -145,8 +145,8 @@ definitions: name: type: string x-isnullable: false - public: - type: boolean + visibility: + type: string x-isnullable: false x-omitempty: false archived: diff --git a/swagger_gen/models/repository.go b/swagger_gen/models/repository.go index 683aa14..c8bda54 100644 --- a/swagger_gen/models/repository.go +++ b/swagger_gen/models/repository.go @@ -32,8 +32,8 @@ type Repository struct { // name Name string `json:"name,omitempty"` - // public - Public bool `json:"public"` + // visibility + Visibility string `json:"visibility"` } // Validate validates this repository diff --git a/swagger_gen/models/repository_details.go b/swagger_gen/models/repository_details.go index cd23db5..39cd1bc 100644 --- a/swagger_gen/models/repository_details.go +++ b/swagger_gen/models/repository_details.go @@ -38,11 +38,11 @@ type RepositoryDetails struct { // name Name string `json:"name,omitempty"` - // public - Public bool `json:"public"` - // teams Teams []*RepositoryDetailsTeamsItems0 `json:"teams"` + + // visibility + Visibility string `json:"visibility"` } // Validate validates this repository details diff --git a/swagger_gen/restapi/embedded_spec.go b/swagger_gen/restapi/embedded_spec.go index fecbf18..b48c523 100644 --- a/swagger_gen/restapi/embedded_spec.go +++ b/swagger_gen/restapi/embedded_spec.go @@ -484,8 +484,8 @@ func init() { "type": "string", "x-isnullable": false }, - "public": { - "type": "boolean", + "visibility": { + "type": "string", "x-isnullable": false, "x-omitempty": false } @@ -534,11 +534,6 @@ func init() { "type": "string", "x-isnullable": false }, - "public": { - "type": "boolean", - "x-isnullable": false, - "x-omitempty": false - }, "teams": { "type": "array", "items": { @@ -554,6 +549,11 @@ func init() { } } } + }, + "visibility": { + "type": "string", + "x-isnullable": false, + "x-omitempty": false } } }, @@ -1352,8 +1352,8 @@ func init() { "type": "string", "x-isnullable": false }, - "public": { - "type": "boolean", + "visibility": { + "type": "string", "x-isnullable": false, "x-omitempty": false } @@ -1392,16 +1392,16 @@ func init() { "type": "string", "x-isnullable": false }, - "public": { - "type": "boolean", - "x-isnullable": false, - "x-omitempty": false - }, "teams": { "type": "array", "items": { "$ref": "#/definitions/RepositoryDetailsTeamsItems0" } + }, + "visibility": { + "type": "string", + "x-isnullable": false, + "x-omitempty": false } } },