Skip to content
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
7 changes: 5 additions & 2 deletions cmd/addgroupguests.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,10 @@ Example:
return
}

courseConfig := config.GetCourseConfig(courseName)
courseConfig, err := config.GetCourseConfig(courseName)
if err != nil {
er(err)
}
subgroupPath := config.GetCourseSubgroupPath(courseName)

fmt.Printf("Course: %s\n", courseName)
Expand Down Expand Up @@ -64,7 +67,7 @@ Example:
fmt.Scanln() //nolint:errcheck

c := gitlab.NewClient()
err := c.AddGroupGuests(courseName)
err = c.AddGroupGuests(courseName)
if err != nil {
fmt.Printf("Error: %v\n", err)
return
Expand Down
5 changes: 4 additions & 1 deletion cmd/archive.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@ var (
Long: `Archive or unarchive repositories.`,
Args: cobra.MinimumNArgs(2), //nolint:gomnd
Run: func(cmd *cobra.Command, args []string) {
assignmentConfig := config.GetAssignmentConfig(args[0], args[1], args[2:]...)
assignmentConfig, err := config.GetAssignmentConfig(args[0], args[1], args[2:]...)
if err != nil {
er(err)
}
assignmentConfig.Show()
fmt.Println(aurora.Magenta("Config okay? Press 'Enter' to continue or 'Ctrl-C' to stop ..."))
fmt.Scanln() //nolint:errcheck
Expand Down
5 changes: 4 additions & 1 deletion cmd/check.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@ var checkCmd = &cobra.Command{
viper.Set("show-success", true)
c := gitlab.NewClient()
if len(args) == 1 {
cfg := config.GetCourseConfig(args[0])
cfg, err := config.GetCourseConfig(args[0])
if err != nil {
er(err)
}
if cfg != nil {
c.CheckCourse(cfg)
}
Expand Down
5 changes: 4 additions & 1 deletion cmd/clone.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@ var (
You can specify students or groups in order to clone only for these.`,
Args: cobra.MinimumNArgs(2), //nolint:gomnd
Run: func(cmd *cobra.Command, args []string) {
assignmentConfig := config.GetAssignmentConfig(args[0], args[1], args[2:]...)
assignmentConfig, err := config.GetAssignmentConfig(args[0], args[1], args[2:]...)
if err != nil {
er(err)
}
if len(Branch) > 0 {
assignmentConfig.SetBranch(Branch)
}
Expand Down
5 changes: 4 additions & 1 deletion cmd/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@ var deleteCmd = &cobra.Command{
You can specify students or groups in order to delete only for these.`,
Args: cobra.MinimumNArgs(2), //nolint:gomnd
Run: func(cmd *cobra.Command, args []string) {
assignmentConfig := config.GetAssignmentConfig(args[0], args[1], args[2:]...)
assignmentConfig, err := config.GetAssignmentConfig(args[0], args[1], args[2:]...)
if err != nil {
er(err)
}
assignmentConfig.Show()
fmt.Println(aurora.Magenta("Do you really want to delete the repos? Press 'Enter' to continue or 'Ctrl-C' to stop ..."))
fmt.Scanln() //nolint:errcheck
Expand Down
5 changes: 4 additions & 1 deletion cmd/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@ You can specify students or groups in order to generate only for these.
A student needs to exist on GitLab, a group needs to exist in the configuration file.`,
Args: cobra.MinimumNArgs(2), //nolint:gomnd
Run: func(cmd *cobra.Command, args []string) {
assignmentConfig := config.GetAssignmentConfig(args[0], args[1], args[2:]...)
assignmentConfig, err := config.GetAssignmentConfig(args[0], args[1], args[2:]...)
if err != nil {
er(err)
}
assignmentConfig.Show()
fmt.Println(aurora.Magenta("Config okay? Press 'Enter' to continue or 'Ctrl-C' to stop ..."))
fmt.Scanln() //nolint:errcheck
Expand Down
5 changes: 4 additions & 1 deletion cmd/protect.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@ var (
Long: `Protect branch for exisiting repositories.`,
Args: cobra.MinimumNArgs(2), //nolint:gomnd
Run: func(cmd *cobra.Command, args []string) {
assignmentConfig := config.GetAssignmentConfig(args[0], args[1], args[2:]...)
assignmentConfig, err := config.GetAssignmentConfig(args[0], args[1], args[2:]...)
if err != nil {
er(err)
}
if len(ProtectBranch) > 0 {
assignmentConfig.SetProtectToBranch(ProtectBranch)
}
Expand Down
7 changes: 5 additions & 2 deletions cmd/push.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,15 @@ var pushCmd = &cobra.Command{
course := args[0]
assignment := args[1]
branchname := args[2]
assignmentConfig := config.GetAssignmentConfig(course, assignment, args[3:]...)
assignmentConfig, err := config.GetAssignmentConfig(course, assignment, args[3:]...)
if err != nil {
er(err)
}
assignmentConfig.Show()
fmt.Println(aurora.Magenta("Config okay? Press 'Enter' to continue or 'Ctrl-C' to stop ..."))
fmt.Scanln() //nolint:errcheck
c := gitlab.NewClient()
err := c.Push(assignmentConfig, branchname)
err = c.Push(assignmentConfig, branchname)
if err != nil {
fmt.Printf("error: %s", err.Error())
}
Expand Down
5 changes: 4 additions & 1 deletion cmd/report.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,10 @@ var (
}
return
}
assignmentConfig := config.GetAssignmentConfig(args[0], args[1], args[2:]...)
assignmentConfig, err := config.GetAssignmentConfig(args[0], args[1], args[2:]...)
if err != nil {
er(err)
}
c := gitlab.NewClient()
if Json {
c.ReportJSON(assignmentConfig, output)
Expand Down
5 changes: 4 additions & 1 deletion cmd/setaccess.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@ var (
Long: `Set access level for exisiting repositories.`,
Args: cobra.MinimumNArgs(2), //nolint:gomnd
Run: func(cmd *cobra.Command, args []string) {
assignmentConfig := config.GetAssignmentConfig(args[0], args[1], args[2:]...)
assignmentConfig, err := config.GetAssignmentConfig(args[0], args[1], args[2:]...)
if err != nil {
er(err)
}
if len(Level) > 0 {
assignmentConfig.SetAccessLevel(Level)
}
Expand Down
5 changes: 4 additions & 1 deletion cmd/show.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@ var showConfigCmd = &cobra.Command{
Long: `Show config of an assignment`,
Args: cobra.MinimumNArgs(2),
Run: func(cmd *cobra.Command, args []string) {
cfg := config.GetAssignmentConfig(args[0], args[1], args[2:]...)
cfg, err := config.GetAssignmentConfig(args[0], args[1], args[2:]...)
if err != nil {
er(err)
}
cfg.Show()
},
}
5 changes: 4 additions & 1 deletion cmd/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,10 @@ var updateCmd = &cobra.Command{
Use only with fresh, i.e. untouched, repositories.`,
Args: cobra.MinimumNArgs(2), //nolint:gomnd
Run: func(cmd *cobra.Command, args []string) {
assignmentConfig := config.GetAssignmentConfig(args[0], args[1], args[2:]...)
assignmentConfig, err := config.GetAssignmentConfig(args[0], args[1], args[2:]...)
if err != nil {
er(err)
}
assignmentConfig.Show()
fmt.Println(aurora.Red("Heads up! Use only with untouched projects."))
fmt.Println(aurora.Magenta("Config okay? Press 'Enter' to continue or 'Ctrl-C' to stop ..."))
Expand Down
5 changes: 4 additions & 1 deletion cmd/urls.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ var (
if len(args) == 1 {
config.GetCourseURL(args[0])
} else {
assignmentConfig := config.GetAssignmentConfig(args[0], args[1], args[2:]...)
assignmentConfig, err := config.GetAssignmentConfig(args[0], args[1], args[2:]...)
if err != nil {
er(err)
}
if startercode {
assignmentConfig.StartercodeURL()
} else {
Expand Down
91 changes: 54 additions & 37 deletions config/assignment.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,41 +5,35 @@ import (
"strings"

"github.com/go-viper/mapstructure/v2"
"github.com/rs/zerolog/log"
"github.com/spf13/viper"
)

func GetCourseURL(course string) {
fmt.Printf("%s/%s\n", viper.GetString("gitlab.host"), coursePath(course))
}

func GetAssignmentConfig(course, assignment string, onlyForStudentsOrGroups ...string) *AssignmentConfig {
func GetAssignmentConfig(course, assignment string, onlyForStudentsOrGroups ...string) (*AssignmentConfig, error) {
if !viper.IsSet(course) {
log.Fatal().
Str("course", course).
Msg("configuration for course not found")
return nil, fmt.Errorf("configuration for course %s not found", course)
}

if !viper.IsSet(course + "." + assignment) {
log.Fatal().
Str("course", course).
Str("assignment", assignment).
Msg("configuration for assignment not found")
return nil, fmt.Errorf("course %s: configuration for assignment %s not found", course, assignment)
}

// Abstract assignments are bases for `extends` only and must not be used
// directly. Checked before resolving inheritance so the (own) flag is read,
// not an inherited one.
if assignmentIsAbstract(course, assignment) {
log.Fatal().
Str("course", course).
Str("assignment", assignment).
Msg("assignment is abstract (a base for 'extends') and cannot be used directly")
return nil, fmt.Errorf("course %s: assignment %s is abstract (a base for 'extends') and cannot be used directly",
course, assignment)
}

// Resolve `extends` inheritance before reading any fields so the rest of
// the config loading sees the merged, effective configuration.
resolveAssignmentInheritance(course, assignment)
if err := resolveAssignmentInheritance(course, assignment); err != nil {
return nil, err
}

assignmentKey := course + "." + assignment
per := per(assignmentKey)
Expand All @@ -53,8 +47,14 @@ func GetAssignmentConfig(course, assignment string, onlyForStudentsOrGroups ...s
containerRegistry = true
}

starter := startercode(assignmentKey)
branchRules := branches(assignmentKey, starter)
starter, err := startercode(assignmentKey)
if err != nil {
return nil, err
}
branchRules, err := branches(assignmentKey, starter)
if err != nil {
return nil, err
}
defaultCloneBranch := defaultBranch(branchRules, "main")

deferredBranches := make(map[string]*DeferredBranch)
Expand Down Expand Up @@ -93,6 +93,15 @@ func GetAssignmentConfig(course, assignment string, onlyForStudentsOrGroups ...s
}
}

mr, err := mergeRequest(assignmentKey)
if err != nil {
return nil, err
}
seed, err := seeder(assignmentKey)
if err != nil {
return nil, err
}

assignmentConfig := &AssignmentConfig{
Course: course,
Name: assignment,
Expand All @@ -109,19 +118,19 @@ func GetAssignmentConfig(course, assignment string, onlyForStudentsOrGroups ...s
Description: description(assignmentKey),
ContainerRegistry: containerRegistry,
AccessLevel: accessLevel(assignmentKey),
MergeRequest: mergeRequest(assignmentKey),
MergeRequest: mr,
Branches: branchRules,
Issues: issues(assignmentKey),
Students: students(per, course, assignment, onlyForStudentsOrGroups...),
Groups: groups(per, course, assignment, onlyForStudentsOrGroups...),
Startercode: starter,
Clone: clone(assignmentKey, defaultCloneBranch),
Release: release,
Seeder: seeder(assignmentKey),
Seeder: seed,
DeferredBranches: deferredBranches,
}

return assignmentConfig
return assignmentConfig, nil
}

// Using email addresses instead of usernames/user-id's results in @ in the student's name.
Expand Down Expand Up @@ -215,7 +224,7 @@ func description(assignmentKey string) string {
return description
}

func mergeRequest(assignmentKey string) *MergeRequest {
func mergeRequest(assignmentKey string) (*MergeRequest, error) {
mergeMethod := MergeCommit
switch viper.GetString(assignmentKey + ".mergeRequest.mergeMethod") {
case "semi_linear":
Expand All @@ -238,28 +247,37 @@ func mergeRequest(assignmentKey string) *MergeRequest {
squashOption = SquashDefaultOff
}

approvals, err := mergeRequestApprovals(assignmentKey)
if err != nil {
return nil, err
}
settings, err := mergeRequestApprovalSettings(assignmentKey)
if err != nil {
return nil, err
}

return &MergeRequest{
MergeMethod: mergeMethod,
SquashOption: squashOption,
PipelineMustSucceed: viper.GetBool(assignmentKey + ".mergeRequest.pipeline"),
SkippedPipelinesAreSuccessful: viper.GetBool(assignmentKey + ".mergeRequest.skippedPipelinesAreSuccessful"),
AllThreadsMustBeResolved: viper.GetBool(assignmentKey + ".mergeRequest.allThreadsMustBeResolved"),
StatusChecksMustSucceed: viper.GetBool(assignmentKey + ".mergeRequest.statusChecksMustSucceed"),
Approvals: mergeRequestApprovals(assignmentKey),
ApprovalSettings: mergeRequestApprovalSettings(assignmentKey),
}
Approvals: approvals,
ApprovalSettings: settings,
}, nil
}

func mergeRequestApprovals(assignmentKey string) []MergeRequestApprovalRule {
func mergeRequestApprovals(assignmentKey string) ([]MergeRequestApprovalRule, error) {
raw := viper.Get(assignmentKey + ".mergeRequest.approvals")
raw = extractMergeRequestApprovalRulesRaw(raw)
raw = normalizeMergeRequestApprovalConfigKeys(raw)
if raw == nil {
return nil
return nil, nil
}

if containsLegacyApprovalUsersKey(raw) {
log.Fatal().Str("assignmentKey", assignmentKey).Msg("mergeRequest.approvals.rules[].users is no longer supported; use usernames")
return nil, fmt.Errorf("%s: mergeRequest.approvals.rules[].users is no longer supported; use usernames", assignmentKey)
}

var configured []MergeRequestApprovalRule
Expand All @@ -269,10 +287,10 @@ func mergeRequestApprovals(assignmentKey string) []MergeRequestApprovalRule {
WeaklyTypedInput: true,
})
if err != nil {
log.Fatal().Err(err).Str("assignmentKey", assignmentKey).Msg("cannot create mergeRequest.approvals decoder")
return nil, fmt.Errorf("%s: cannot create mergeRequest.approvals decoder: %w", assignmentKey, err)
}
if err := decoder.Decode(raw); err != nil {
log.Fatal().Err(err).Str("assignmentKey", assignmentKey).Msg("cannot parse mergeRequest.approvals config")
return nil, fmt.Errorf("%s: cannot parse mergeRequest.approvals config: %w", assignmentKey, err)
}

normalized := make([]MergeRequestApprovalRule, 0, len(configured))
Expand Down Expand Up @@ -327,7 +345,7 @@ func mergeRequestApprovals(assignmentKey string) []MergeRequestApprovalRule {
normalized = append(normalized, rule)
}

return normalized
return normalized, nil
}

func extractMergeRequestApprovalRulesRaw(value any) any {
Expand Down Expand Up @@ -374,10 +392,10 @@ func containsLegacyApprovalUsersKey(value any) bool {
}
}

func mergeRequestApprovalSettings(assignmentKey string) *MergeRequestApprovalSettings {
func mergeRequestApprovalSettings(assignmentKey string) (*MergeRequestApprovalSettings, error) {
prefix := assignmentKey + ".mergeRequest.approvals.settings"
if !viper.IsSet(prefix) {
return nil
return nil, nil
}

settings := &MergeRequestApprovalSettings{}
Expand All @@ -404,10 +422,9 @@ func mergeRequestApprovalSettings(assignmentKey string) *MergeRequestApprovalSet
case ApprovalKeepApprovals, ApprovalRemoveAllApprovals, ApprovalRemoveCodeOwnerApprovalsIfFilesChanged:
settings.WhenCommitAdded = &v
default:
log.Fatal().
Str("assignmentKey", assignmentKey).
Str("whenCommitAdded", string(v)).
Msg("invalid mergeRequest.approvals.settings.whenCommitAdded")
return nil, fmt.Errorf("%s: invalid mergeRequest.approvals.settings.whenCommitAdded %q, must be one of %s, %s, %s",
assignmentKey, v,
ApprovalKeepApprovals, ApprovalRemoveAllApprovals, ApprovalRemoveCodeOwnerApprovalsIfFilesChanged)
}
}

Expand All @@ -416,10 +433,10 @@ func mergeRequestApprovalSettings(assignmentKey string) *MergeRequestApprovalSet
settings.PreventEditingApprovalRulesInMergeRequests == nil &&
settings.RequireUserReauthenticationToApprove == nil &&
settings.WhenCommitAdded == nil {
return nil
return nil, nil
}

return settings
return settings, nil
}

func normalizeMergeRequestApprovalConfigKeys(value any) any {
Expand Down
Loading
Loading