Skip to content
2 changes: 1 addition & 1 deletion cmd/gitlabMirrorCreateCmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ var gitlabMirrorCreateCmd = &cobra.Command{
Long: `Enable the mirroring for one or more existing Gitlab projects.
Each project can be given as numeric ID or as full path.
The destination project is created on the mirror instance when missing.
Projects that already have a mirror are refused: use "update mirroring" for those.`,
Projects that already have a mirror are refused: use "mirror update" for those.`,
Example: `
Enable the mirroring for a project
opsi gitlab mirror create corporate/wiki/beliven-wiki
Expand Down
8 changes: 8 additions & 0 deletions cmd/gitlabProjectCreateCmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ var gitlabProjectCreateCmd = &cobra.Command{

---

Create a project with name "Nightly" with the default weekly pipeline schedule
opsi gitlab project create Nightly -c

---

Create a project with name "Anonymous" disabling shared runners
opsi gitlab project create Anonymous -r

Expand All @@ -58,6 +63,7 @@ var gitlabProjectCreateCmd = &cobra.Command{
mirror, _ := cmd.Flags().GetBool("mirror")
sharedRunners, _ := cmd.Flags().GetBool("sharedrunners")
visibility, _ := cmd.Flags().GetString("visibility")
schedule, _ := cmd.Flags().GetBool("schedule")

// Slugify the name if the pathname flag
// for the project is not provided
Expand All @@ -73,6 +79,7 @@ var gitlabProjectCreateCmd = &cobra.Command{
DefaultBranch: defaultBranch,
Mirror: mirror,
SharedRunners: sharedRunners,
Schedule: schedule,
Group: group,
}

Expand All @@ -95,6 +102,7 @@ func init() {
gitlabProjectCreateCmd.Flags().StringP("branch-default", "b", "main", "the default main branch. Possible values are master or main")
gitlabProjectCreateCmd.Flags().BoolP("mirror", "m", false, "Enable or disable the mirroring repo. Default is false")
gitlabProjectCreateCmd.Flags().BoolP("sharedrunners", "r", false, "Enable or disable the shared runners. Default is true")
gitlabProjectCreateCmd.Flags().BoolP("schedule", "c", false, "Create the default weekly pipeline schedule, on the least used slot. Default is false")
gitlabProjectCreateCmd.Flags().StringP("visibility", "i", "", "Set the visibility of the project. Allowed values are private, public, internal")

// Mark group as required
Expand Down
17 changes: 17 additions & 0 deletions cmd/gitlabScheduleCmd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package cmd

import (
"github.com/spf13/cobra"
)

var gitlabScheduleCmd = &cobra.Command{
Use: "schedule {verb}",
Args: cobra.ExactArgs(1),
Short: "Manage the default pipeline schedule of Gitlab projects",
Long: "Manage the default pipeline schedule of Gitlab projects",
Run: func(cmd *cobra.Command, args []string) {},
}

func init() {
gitlabCmd.AddCommand(gitlabScheduleCmd)
}
47 changes: 47 additions & 0 deletions cmd/gitlabScheduleCreateCmd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package cmd

import (
"fmt"
"os"

"github.com/spf13/cobra"
)

var gitlabScheduleCreateCmd = &cobra.Command{
Use: "create {project_id}...",
Args: cobra.MinimumNArgs(1),
Short: "Create the default pipeline schedule for existing Gitlab projects",
Long: `Create the default pipeline schedule for one or more existing Gitlab projects.
Each project is given by its numeric ID.

The schedule is weekly, on a working day between 09:00 and 18:00 (Europe/Rome).
The day and the time are chosen among the ones used by the fewest schedules,
so the pipelines are spread instead of starting all together.
It runs on the "main" branch when the project has it, otherwise on the default branch.
Projects that already have an active schedule are left alone.`,
Example: `
Create the schedule for a project
opsi gitlab schedule create 239

---

Create the schedule for more projects
opsi gitlab schedule create 239 1234
`,
Run: func(cmd *cobra.Command, args []string) {
ids, err := projectIDs(args)
if err != nil {
fmt.Println(err)
os.Exit(1)
}

if err := gitlab.CreateSchedule(ids); err != nil {
fmt.Println(err)
os.Exit(1)
}
},
}

func init() {
gitlabScheduleCmd.AddCommand(gitlabScheduleCreateCmd)
}
63 changes: 63 additions & 0 deletions cmd/gitlabScheduleUpdateCmd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package cmd

import (
"fmt"
"opsi/helpers"
"os"

"github.com/spf13/cobra"
)

var gitlabScheduleUpdateCmd = &cobra.Command{
Use: "update [project_id]...",
Args: cobra.ArbitraryArgs,
Short: "Fix the pipeline schedules of Gitlab projects that lost their owner",
Long: `Fix the active pipeline schedules of Gitlab projects that lost their owner.
Each project is given by its numeric ID. Without any project,
all the projects are checked.

A schedule runs as its owner, so it stops when the owner is blocked, deactivated
or removed. This command gives those schedules to the user of the token.
The schedules of an active user are left alone, as cron, branch and description.
Nothing is created or activated: projects without an active schedule are ignored.
Use --dry-run to only see the schedules, with their owner.`,
Example: `
Show the active schedules of all the projects and who owns them
opsi gitlab schedule update --dry-run

---

Fix the schedules of more projects
opsi gitlab schedule update 239 1234

---

Fix the schedules of all the projects
opsi gitlab schedule update
`,
Run: func(cmd *cobra.Command, args []string) {
ids, err := projectIDs(args)
if err != nil {
fmt.Println(err)
os.Exit(1)
}

// Confirm the action (a dry run changes nothing)
dryRun, _ := cmd.Flags().GetBool("dry-run")
force, _ := cmd.Flags().GetBool("force")
if !force && !dryRun {
helpers.Confirm()
}

if err := gitlab.UpdateSchedule(ids, dryRun); err != nil {
fmt.Println(err)
os.Exit(1)
}
},
}

func init() {
gitlabScheduleCmd.AddCommand(gitlabScheduleUpdateCmd)
gitlabScheduleUpdateCmd.Flags().Bool("dry-run", false, "Only show the schedules and their owner")
gitlabScheduleUpdateCmd.Flags().BoolP("force", "f", false, "Not ask confirmation")
}
21 changes: 21 additions & 0 deletions cmd/projectIDs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package cmd

import (
"fmt"
"strconv"
)

// projectIDs converts the arguments of a command into Gitlab project IDs.
func projectIDs(args []string) ([]int, error) {
ids := make([]int, 0, len(args))

for _, arg := range args {
id, err := strconv.Atoi(arg)
if err != nil || id <= 0 {
return nil, fmt.Errorf("%q is not a valid project ID", arg)
}
ids = append(ids, id)
}

return ids, nil
}
3 changes: 3 additions & 0 deletions scopes/gitlab/defs.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ type Gitlab interface {
UpdateMirroring() error
ListGroupAccessTokens(int) error
CreateMirror([]string) error
CreateSchedule([]int) error
UpdateSchedule([]int, bool) error
UpdateCleanUpPolicy(string) error
}

Expand Down Expand Up @@ -216,6 +218,7 @@ type ProjectRequest struct {
DefaultBranch string
Mirror bool
SharedRunners bool
Schedule bool
Group int
}

Expand Down
17 changes: 16 additions & 1 deletion scopes/gitlab/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ func (g *gitlab) createMirrorForProject(ref string) (string, error) {
return project.PathWithNamespace, err
}
if hasMirror {
return project.PathWithNamespace, errors.New("the project already has a mirror, use `opsi gitlab update mirroring`")
return project.PathWithNamespace, errors.New("the project already has a mirror, use `opsi gitlab mirror update`")
}

// Create the destination project only when it is not there yet.
Expand Down Expand Up @@ -694,6 +694,21 @@ func (g *gitlab) CreateProject(options ProjectRequest) (int, error) {
g.enableMirrorForProject(project.ID, options.Path)
}

// If requested, schedule the pipeline on the least used slot
if options.Schedule {
load, err := g.loadSchedules()
if err != nil {
return 0, err
}

description, err := g.createSchedule(project.ID, project.PathWithNamespace, options.DefaultBranch, load)
if err != nil {
return 0, fmt.Errorf("cannot create the schedule: %w", err)
}

fmt.Printf("Schedule created: %s\n", description)
}

return project.ID, nil
}

Expand Down
Loading
Loading