Documentation
¶
Overview ¶
Package reposetup is the front half of the repository set-up engine: it reads a team file of giantswarm/github, validates the entries about to create a repository against the repositories schema and the creation rules, derives the template each repository is scaffolded from and returns the dry-run value the clients render — the team-file entry as it would be written, the implied template, the verdict of the name check and the guard notices.
The package is imported by `devctl repo validate` (and `repo create`), by the validation workflow of giantswarm/github and by giantswarm-repo-manager's validate_repository tool, so validation and rendering exist once. It keeps no CLI state: every input is a value of Request, every output a value of Result, and GitHub is reached only through the small FileGetter and RepositoryGetter interfaces, which *githubclient.Client satisfies.
Declarations ¶
A TeamFile is one repositories/<team>.yaml. Its team is the file's base name (repositories/team-bumblebee.yaml → team-bumblebee), the GitHub team slug CODEOWNERS names for the file; an entry needs no team field. Each Declaration is one entry, kept as the YAML node it was read from so the rendered entry keeps the author's key order and comments.
Rules ¶
Every entry named in Request.Names (all entries when nil) is treated as a repository the reconciler creates and must satisfy, on top of the schema:
- gen.flavours and gen.language are set; gen.ci.generate defaults to true and is written into the rendered entry;
- the name is lowercase and free on GitHub — an existing repository or a redirect from a renamed one counts as taken;
- the chart-name convention holds where a chart exists (the app or cluster-app flavour): the repository is named after its chart, without an -app suffix, and gen.ci.chartName, when set, equals the name;
- a template exists for the language: language node is refused until giantswarm/template-node ships.
Every refusal is a Problem naming the field in dotted form (gen.ci.chartName, gen.flavours[1]).
Templates ¶
DeriveTemplate maps a declaration to its template without a template field: language go → giantswarm/template; language generic with the app flavour → giantswarm/template-app; the customer flavour or component type, the languages python and kyverno-policy, and generic repositories without a chart → the minimal scaffold (README, LICENSE, DCO, SECURITY.md, CODEOWNERS, .gitignore plus the generated files). Rendering the scaffold is the engine's back half.
Guards ¶
Two notices bound the machine approval of a creation-only pull request: an author outside the owning team (and outside FallbackTeam, the CODEOWNERS fallback) keeps the team's review, and more than MaxMachineApprovedEntries added entries get a person. Author and membership are inputs; the package resolves neither.
Schema ¶
FetchSchema reads the schema from giantswarm/github main so a run follows the schema as it is; EmbeddedSchema is the copy shipped with this devctl version, for tests and as the fallback when GitHub cannot be reached. Result.Schema says which one a result was validated against.
Index ¶
- Constants
- func IsEntryNotFound(err error) bool
- func IsInvalidConfig(err error) bool
- func IsInvalidSchema(err error) bool
- func IsInvalidTeamFile(err error) bool
- func IsTemplateUnavailable(err error) bool
- func TeamOf(path string) string
- type Declaration
- type Entry
- type FileGetter
- type GitHubNameChecker
- type NameCheck
- type NameChecker
- type Notice
- type NoticeKind
- type Problem
- type RepositoryGetter
- type Request
- type Result
- type Schema
- type SchemaOrigin
- type TeamFile
- type Template
- type Validator
- type Verdict
Constants ¶
const ( SchemaRepositoryOwner = "giantswarm" SchemaRepository = "github" SchemaPath = ".github/repositories.schema.json" SchemaRef = "main" )
Where the live schema lives: giantswarm/github's main branch.
const ( // DefaultOwner is the GitHub organisation repositories are created in. DefaultOwner = "giantswarm" // FallbackTeam is the CODEOWNERS fallback owner of giantswarm/github; its // members may add entries to any team file without the team's review. FallbackTeam = "team-planeteers" // MaxMachineApprovedEntries is how many entries a creation-only pull // request may add and still be approved by the machine; more is a // migration and gets a person. MaxMachineApprovedEntries = 3 )
Variables ¶
This section is empty.
Functions ¶
func IsEntryNotFound ¶
IsEntryNotFound asserts entryNotFoundError: a requested entry name is not in the team file.
func IsInvalidConfig ¶
IsInvalidConfig asserts invalidConfigError.
func IsInvalidSchema ¶
IsInvalidSchema asserts invalidSchemaError: the repositories schema does not parse or compile.
func IsInvalidTeamFile ¶
IsInvalidTeamFile asserts invalidTeamFileError: the team file is not a YAML list of mappings.
func IsTemplateUnavailable ¶
IsTemplateUnavailable asserts templateUnavailableError: the declaration needs a template that does not exist yet.
Types ¶
type Declaration ¶
type Declaration struct {
// Name is the entry's name field; empty when the entry has none.
Name string
// contains filtered or unexported fields
}
Declaration is one entry of a team file: the desired state of one repository. It keeps the YAML node it was read from, so a rendered entry preserves the author's key order and comments.
func (Declaration) Instance ¶
func (d Declaration) Instance() (any, error)
Instance returns the entry as a JSON-compatible value (maps, slices, strings, json.Number, bools), the shape the schema validates.
func (Declaration) YAML ¶
func (d Declaration) YAML() (string, error)
YAML renders the entry as it stands in a team file: one list item.
type Entry ¶
type Entry struct {
// Name of the repository.
Name string `json:"name"`
// Rendered is the entry as it would be written to the team file, defaults
// applied (gen.ci.generate: true), as a one-item YAML list.
Rendered string `json:"rendered"`
// Template the repository would be scaffolded from; empty when the
// declaration does not derive one.
Template Template `json:"template,omitempty"`
// NameCheck is the verdict of the GitHub name check.
NameCheck NameCheck `json:"nameCheck"`
// Problems are the refusals, each naming the field. Empty when accepted.
Problems []Problem `json:"problems,omitempty"`
// Accepted is true when the entry has no problems.
Accepted bool `json:"accepted"`
}
Entry is the dry run of one declaration.
type FileGetter ¶
type FileGetter interface {
GetFile(ctx context.Context, owner, repo, path, ref string) (githubclient.RepositoryFile, error)
}
FileGetter reads one file of a GitHub repository at a ref. *githubclient.Client is a FileGetter.
type GitHubNameChecker ¶
type GitHubNameChecker struct {
Repositories RepositoryGetter
}
GitHubNameChecker checks names against GitHub. GitHub answers a request for a renamed repository with a redirect the client follows, so the repository that comes back under a different full name is the redirect — and the old name is taken as much as an existing one.
func (GitHubNameChecker) CheckName ¶
CheckName implements NameChecker.
type NameChecker ¶
type NameChecker interface {
CheckName(ctx context.Context, owner, name string) (NameCheck, error)
}
NameChecker says whether owner/name is free on GitHub.
type Notice ¶
type Notice struct {
Kind NoticeKind `json:"kind"`
Message string `json:"message"`
}
Notice is a guard notice about the change as a whole. A notice does not refuse; it tells the author what review the change will get.
type NoticeKind ¶
type NoticeKind string
NoticeKind classifies a guard notice.
const ( // NoticeTeamReview: the author is outside the owning team and the // fallback team, so the team's review is required. NoticeTeamReview NoticeKind = "team-review" // NoticeBatchReview: more than MaxMachineApprovedEntries entries are // added, so a person reviews. NoticeBatchReview NoticeKind = "batch-review" // NoticeNamesUnchecked: no NameChecker was configured. NoticeNamesUnchecked NoticeKind = "names-unchecked" )
type Problem ¶
Problem is one refusal: the field in dotted form (gen.ci.chartName, gen.flavours[1]; "(entry)" for the entry as a whole) and why.
type RepositoryGetter ¶
type RepositoryGetter interface {
GetRepository(ctx context.Context, owner, repo string) (*github.Repository, error)
}
RepositoryGetter reads one repository. *githubclient.Client is one; a missing repository is reported through githubclient.IsNotFound or a go-github 404.
type Request ¶
type Request struct {
// TeamFile the entries live in; its team is the owning team. Required.
TeamFile *TeamFile
// Names of the entries being added, the ones the creation rules apply
// to. Nil means every entry of the file.
Names []string
// Author is the GitHub login of the person opening the change; empty
// when unknown, which skips the team guard.
Author string
// AuthorTeams are the GitHub team slugs the author is a member of
// (team-bumblebee; a giantswarm/ or @giantswarm/ prefix is accepted).
AuthorTeams []string
}
Request is one validation: the team file, the entries being added and the author of the change.
type Result ¶
type Result struct {
// Team that owns the entries: the team file's.
Team string `json:"team"`
// Schema the entries were validated against.
Schema SchemaOrigin `json:"schema"`
// Entries in request order.
Entries []Entry `json:"entries"`
// Notices are the guard notices that apply to the change as a whole.
Notices []Notice `json:"notices,omitempty"`
// Accepted is true when every entry is accepted.
Accepted bool `json:"accepted"`
}
Result is the dry-run value: what the reconciler would do with the entries and why it would refuse. `devctl repo validate` prints it as JSON, giantswarm-repo-manager's validate_repository returns it and the validation workflow renders it.
type Schema ¶
type Schema struct {
Origin SchemaOrigin
// contains filtered or unexported fields
}
Schema is a compiled repositories schema: the JSON schema of the team files, whose top level is the list of entries.
func CompileSchema ¶
func CompileSchema(doc []byte, origin SchemaOrigin) (*Schema, error)
CompileSchema compiles a repositories schema document.
func EmbeddedSchema ¶
EmbeddedSchema compiles the schema copy shipped with this devctl version. It carries the fields of the repository set-up plan (description, visibility, lifecycle archived) and is the fallback when GitHub cannot be reached.
func FetchSchema ¶
func FetchSchema(ctx context.Context, files FileGetter) (*Schema, error)
FetchSchema reads and compiles the schema from giantswarm/github main, so a validation follows the schema as it is today.
type SchemaOrigin ¶
type SchemaOrigin string
SchemaOrigin says where the schema a result was validated against came from.
const ( // SchemaOriginGitHub is the schema as fetched from giantswarm/github main. SchemaOriginGitHub SchemaOrigin = "giantswarm/github@main" // SchemaOriginEmbedded is the copy shipped with this devctl version. SchemaOriginEmbedded SchemaOrigin = "embedded" // SchemaOriginFile is a schema read from a local file. SchemaOriginFile SchemaOrigin = "file" )
type TeamFile ¶
type TeamFile struct {
// Team is the GitHub team slug the file belongs to: the file's base name
// without extension (repositories/team-bumblebee.yaml → team-bumblebee).
Team string
// Path is where the file was read from; empty for a parsed reader.
Path string
// Entries in file order.
Entries []Declaration
}
TeamFile is one repositories/<team>.yaml of giantswarm/github: the team that owns every entry in it and the entries themselves.
func ParseTeamFile ¶
ParseTeamFile parses a team file for the named team. The file is a YAML list of mappings; an empty file is a team with no entries.
func ReadTeamFile ¶
ReadTeamFile reads a team file; the team is TeamOf the path.
type Template ¶
type Template string
Template is what a created repository is scaffolded from: a template repository of the giantswarm organisation, or the minimal scaffold.
const ( // TemplateGo is giantswarm/template, the Go service and CLI template. TemplateGo Template = "giantswarm/template" // TemplateChart is giantswarm/template-app, the chart-only template. TemplateChart Template = "giantswarm/template-app" // TemplateMinimal is the minimal scaffold: README, LICENSE, DCO, // SECURITY.md, CODEOWNERS and .gitignore plus the generated files. TemplateMinimal Template = "minimal" )
func DeriveTemplate ¶
DeriveTemplate returns the template a declaration is scaffolded from. There is no template field: the component type, flavours and language decide. Language go → TemplateGo; language generic with the app flavour → TemplateChart; the customer flavour or component type, the languages python and kyverno-policy, and a generic repository without a chart → TemplateMinimal. Language node has no template yet and is refused with an error IsTemplateUnavailable asserts. An unknown flavour or language is refused with the error gen.NewFlavour or gen.NewLanguage returns.
func (Template) Repository ¶
Repository is the owner/name of the template repository, or empty for the minimal scaffold.
type Validator ¶
type Validator struct {
// Schema the entries are validated against. Required.
Schema *Schema
// Names checks whether a repository name is free on GitHub. Optional:
// without one every name is reported unchecked and the result says so.
Names NameChecker
// Owner is the GitHub organisation; DefaultOwner when empty.
Owner string
}
Validator validates the entries of a team file that are about to create repositories and returns the dry-run value.
type Verdict ¶
type Verdict string
Verdict is the outcome of a repository name check on GitHub.
const ( // VerdictFree: no repository of that name, no redirect to another one. VerdictFree Verdict = "free" // VerdictTaken: a repository exists under the name, or the name redirects // to a renamed repository. VerdictTaken Verdict = "taken" // VerdictUnchecked: the name was not checked (no GitHub client, or the // name is invalid anyway). VerdictUnchecked Verdict = "unchecked" )