Skip to content

Commit

Permalink
Merge pull request goliac-project#12 from AlayaCare/repo_visibility
Browse files Browse the repository at this point in the history
Repo visibility
  • Loading branch information
nzin-alayacare authored Feb 24, 2025
2 parents d513bfc + a07e238 commit 70807fd
Show file tree
Hide file tree
Showing 26 changed files with 357 additions and 166 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down
2 changes: 1 addition & 1 deletion browser/goliac-ui/src/components/RepositoriesApp.vue
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
:default-sort="{ prop: 'name', order: 'descending' }"
>
<el-table-column prop="name" align="left" label="Repository name" sortable />
<el-table-column prop="public" align="left" label="Public" sortable />
<el-table-column prop="visibility" align="left" label="Visibility" sortable />
<el-table-column prop="archived" align="left" label="Archived" sortable />
</el-table>
</el-row>
Expand Down
4 changes: 2 additions & 2 deletions browser/goliac-ui/src/components/RepositoryApp.vue
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@
</div>
</template>
<div class="flex-container">
<el-text>Public : </el-text>
<el-text>{{ repository.public}}</el-text>
<el-text>Visibility : </el-text>
<el-text>{{ repository.visibility}}</el-text>
</div>
<div class="flex-container">
<el-text>Archived : </el-text>
Expand Down
8 changes: 4 additions & 4 deletions docs/api_docs/bundle.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -321,8 +321,8 @@ definitions:
name:
type: string
x-isnullable: false
public:
type: boolean
visibility:
type: string
x-isnullable: false
x-omitempty: false
autoMergeAllowed:
Expand Down Expand Up @@ -351,8 +351,8 @@ definitions:
name:
type: string
x-isnullable: false
public:
type: boolean
visibility:
type: string
x-isnullable: false
x-omitempty: false
archived:
Expand Down
9 changes: 9 additions & 0 deletions docs/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion docs/quick_start.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ apiVersion: v1
kind: Repository
name: myrepository
spec:
public: false
visibility: private
EOF

git add myrepository.yaml
Expand Down
14 changes: 7 additions & 7 deletions docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -77,7 +77,7 @@ apiVersion: v1
kind: Repository
name: awesome-repository
spec:
public: true
visibility: public
...
renameTo: anotherName
```
Expand All @@ -99,7 +99,7 @@ apiVersion: v1
kind: Repository
name: awesome-repository
spec:
public: true
visibility: public
...
rulesets:
- name: myruleset
Expand All @@ -118,7 +118,7 @@ apiVersion: v1
kind: Repository
name: awesome-repository
spec:
public: true
visibility: public
...
rulesets:
- name: myruleset
Expand All @@ -141,7 +141,7 @@ apiVersion: v1
kind: Repository
name: awesome-repository
spec:
public: true
visibility: public
...
rulesets:
- name: myruleset
Expand All @@ -168,7 +168,7 @@ apiVersion: v1
kind: Repository
name: awesome-repository
spec:
public: true
visibility: public
...
branch_protections:
- pattern: master
Expand All @@ -195,7 +195,7 @@ apiVersion: v1
kind: Repository
name: awesome-repository
spec:
public: true
visibility: public
...
branch_protections:
- pattern: master
Expand Down
5 changes: 5 additions & 0 deletions internal/config/repo_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
53 changes: 32 additions & 21 deletions internal/engine/goliac_reconciliator.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
}

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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{},
Expand Down Expand Up @@ -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{
Expand Down Expand Up @@ -544,22 +548,22 @@ 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,
ExternalUserWriters: eWriters,
InternalUsers: []string{},
Rulesets: rulesets,
BranchProtections: branchprotections,
}
})
}

// now we compare local (slugTeams) and remote (rTeams)
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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)
}
}

Expand All @@ -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
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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) {
Expand Down
52 changes: 52 additions & 0 deletions internal/engine/goliac_reconciliator_filter.go
Original file line number Diff line number Diff line change
@@ -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
}
Loading

0 comments on commit 70807fd

Please sign in to comment.