Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

enhance scaffold #39

Merged
merged 2 commits into from
Sep 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 18 additions & 8 deletions internal/entity/repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ func ReadRepositories(fs afero.Fs, archivedDirname string, teamDirname string, t
continue
}
if !strings.HasSuffix(entry.Name(), ".yaml") {
warning = append(warning, fmt.Errorf("File %s doesn't have a .yaml extension", entry.Name()))
continue
}
repo, err := NewRepository(fs, filepath.Join(archivedDirname, entry.Name()))
Expand Down Expand Up @@ -118,9 +119,18 @@ func ReadRepositories(fs afero.Fs, archivedDirname string, teamDirname string, t
if err := repo.Validate(filepath.Join(teamDirname, team.Name(), sube.Name()), teams, externalUsers, false); err != nil {
errors = append(errors, err)
} else {
teamname := team.Name()
repo.Owner = &teamname
repos[repo.Metadata.Name] = repo
// check if the repository doesn't already exists
if _, exist := repos[repo.Metadata.Name]; exist {
existing := filepath.Join(archivedDirname, repo.Metadata.Name)
if repos[repo.Metadata.Name].Owner != nil {
existing = filepath.Join(teamDirname, *repos[repo.Metadata.Name].Owner, repo.Metadata.Name)
}
errors = append(errors, fmt.Errorf("Repository %s defined in 2 places (check %s and %s)", repo.Metadata.Name, filepath.Join(teamDirname, team.Name(), sube.Name()), existing))
} else {
teamname := team.Name()
repo.Owner = &teamname
repos[repo.Metadata.Name] = repo
}
}
}
}
Expand All @@ -134,15 +144,15 @@ func ReadRepositories(fs afero.Fs, archivedDirname string, teamDirname string, t
func (r *Repository) Validate(filename string, teams map[string]*Team, externalUsers map[string]*User, archived bool) error {

if r.ApiVersion != "v1" {
return fmt.Errorf("invalid apiVersion: %s for repository filename %s", r.ApiVersion, filename)
return fmt.Errorf("invalid apiVersion: %s (check repository filename %s)", r.ApiVersion, filename)
}

if r.Kind != "Repository" {
return fmt.Errorf("invalid kind: %s for repository filename %s", r.Kind, filename)
return fmt.Errorf("invalid kind: %s (check repository filename %s)", r.Kind, filename)
}

if r.Metadata.Name == "" {
return fmt.Errorf("metadata.name is empty for repository filename %s", filename)
return fmt.Errorf("metadata.name is empty (check repository filename %s)", filename)
}

filename = filepath.Base(filename)
Expand All @@ -152,12 +162,12 @@ func (r *Repository) Validate(filename string, teams map[string]*Team, externalU

for _, writer := range r.Data.Writers {
if _, ok := teams[writer]; !ok {
return fmt.Errorf("invalid writer: %s doesn't exist in repository filename %s", writer, filename)
return fmt.Errorf("invalid writer: %s doesn't exist (check repository filename %s)", writer, filename)
}
}
for _, reader := range r.Data.Readers {
if _, ok := teams[reader]; !ok {
return fmt.Errorf("invalid reader: %s doesn't exist in repository filename %s", reader, filename)
return fmt.Errorf("invalid reader: %s doesn't exist (check repository filename %s)", reader, filename)
}
}

Expand Down
93 changes: 93 additions & 0 deletions internal/scaffold.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,10 @@ func (s *Scaffold) generate(fs afero.Fs, rootpath string, adminteam string) erro
return fmt.Errorf("Error creating the .github/workflows/pr.yaml file: %v", err)
}

if err := s.generateReadme(fs, rootpath); err != nil {
return fmt.Errorf("Error creating the README.md file: %v", err)
}

return nil
}

Expand Down Expand Up @@ -319,6 +323,8 @@ usersync:
}

func (s *Scaffold) generateGithubAction(fs afero.Fs, rootpath string) error {
fs.MkdirAll(path.Join(rootpath, ".github", "workflows"), 0755)

workflow := `
name: Validate structure

Expand All @@ -344,6 +350,93 @@ jobs:
return nil
}

func (s *Scaffold) generateReadme(fs afero.Fs, rootpath string) error {
readme := `
# teams

This repository manage the Github organization through [Goliac](https://github.com/alayacare/goliac) application

## Create a repository

On a given team subdirectory you can create a repository definition via a yaml file (like ` + "`" + `/teams/foobar/awesome-repository.yaml` + "`" + `):

` + "```" + `
apiVersion: v1
kind: Repository
metadata:
name: awesome-repository
` + "```" + `

This will create a ` + "`" + `awesome-repository` + "`" + ` repository under your organization, that will be
- private by default
- writable by all owners/members of this team (in our example ` + "`" + `foobar` + "`" + `)

You can of course tweak that:

` + "```" + `
apiVersion: v1
kind: Repository
metadata:
name: awesome-repository
data:
public: true
writers:
- anotherteamA
- anotherteamB
readers:
- anotherteamC
- anotherteamD
` + "```" + `

In this last example:
- the repository is now publci
- other teams have write (` + "`" + `notherteamA` + "`" + `, ` + "`" + `anotherteamB` + "`" + `) or read (` + "`" + `anotherteamC` + "`" + `, ` + "`" + `anotherteamD` + "`" + `) access

### Create a new team

If you want to create a new team (like ` + "`" + `foobar` + "`" + `), you need to create a PR with a ` + "`" + `/teams/foobar/team.yaml` + "`" + ` file:

` + "```" + `
apiVersion: v1
kind: Team
metadata:
name: foobar
data:
owners:
- user1
- user2
members:
- user3
- user4
` + "```" + `

The users defined there are in 2 different categories
- members: are part of the team (and will be writer on all repositories of the team)
- owners: are part of the team (and will be writer on all repositories of the team) AMD can approve PR in the ` + "`" + `foobar` + "`" + ` teams repository (when you want to change a team definition, or when you want to create/update a repository definition)

The users name used are the one defined in the ` + "`" + `/users` + "`" + ` sub directories (like ` + "`" + `alice` + "`" + `)

### Archive a repository

You can archive a repository, by a PR that
- move the yaml repository file into the ` + "`" + `/archived` + "`" + ` directory
- and chage the repository definition like
` + "```" + `
apiVersion: v1
kind: Repository
metadata:
name: awesome-repository
data:
archived: true
` + "```" + `

`
if err := writeFile(path.Join(rootpath, "README.md"), []byte(readme), fs); err != nil {
return err
}
return nil
}

func writeFile(filename string, content []byte, fs afero.Fs) error {
file, err := fs.Create(filename)
if err == nil {
Expand Down
18 changes: 17 additions & 1 deletion internal/scaffold_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ func TestScaffoldUnit(t *testing.T) {
assert.Equal(t, true, found)
})

t.Run("happy path: test users SAML", func(t *testing.T) {
t.Run("happy path: test users SAML", func(t *testing.T) {
fs := afero.NewMemMapFs()
// MockGithubClient doesn't support concurrent access

Expand Down Expand Up @@ -211,6 +211,22 @@ func TestScaffoldUnit(t *testing.T) {
assert.Equal(t, true, found)
})

t.Run("happy path: test github action", func(t *testing.T) {
fs := afero.NewMemMapFs()
// MockGithubClient doesn't support concurrent access

scaffold := &Scaffold{
remote: NewScaffoldGoliacRemoteMock(),
loadUsersFromGithubOrgSaml: LoadGithubSamlUsersMock,
}

err := scaffold.generateGithubAction(fs, "/")
assert.Nil(t, err)

found, err := afero.Exists(fs, "/.github/workflows/pr.yaml")
assert.Nil(t, err)
assert.Equal(t, true, found)
})
}
func TestScaffoldFull(t *testing.T) {

Expand Down