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

cli: add scrolling to repo register, fix --repo and update the cli docs #1133

Merged
merged 3 commits into from
Oct 7, 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
2 changes: 1 addition & 1 deletion cmd/cli/app/artifact/artifact_get.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import (
var artifact_getCmd = &cobra.Command{
Use: "get",
Short: "Get artifact details",
Long: `Artifact get will get artifact details from an artifact, for a given id`,
Long: `Artifact get will get artifact details from an artifact, for a given ID`,
PreRun: func(cmd *cobra.Command, args []string) {
if err := viper.BindPFlags(cmd.Flags()); err != nil {
fmt.Fprintf(os.Stderr, "error binding flags: %s", err)
Expand Down
2 changes: 1 addition & 1 deletion cmd/cli/app/keys/keys_generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ mediator control plane for an specific project.`,

func init() {
KeysCmd.AddCommand(genKeys_listCmd)
genKeys_listCmd.Flags().StringP("project-id", "g", "", "project id to list roles for")
genKeys_listCmd.Flags().StringP("project-id", "g", "", "Project ID to list roles for")
genKeys_listCmd.Flags().StringP("output", "o", "", "Output public key to file")
genKeys_listCmd.Flags().StringP("passphrase", "p", "", "Passphrase to use for key generation")
}
4 changes: 2 additions & 2 deletions cmd/cli/app/org/org_delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ import (

var org_deleteCmd = &cobra.Command{
Use: "delete",
Short: "delete a organization within a mediator controlplane",
Short: "Delete an organization within a mediator control plane",
Long: `The medic org delete subcommand lets you delete organizations within a
mediator control plane.`,
PreRun: func(cmd *cobra.Command, args []string) {
Expand Down Expand Up @@ -69,7 +69,7 @@ mediator control plane.`,

func init() {
OrgCmd.AddCommand(org_deleteCmd)
org_deleteCmd.Flags().StringP("org-id", "o", "", "id of organization to delete")
org_deleteCmd.Flags().StringP("org-id", "o", "", "ID of organization to delete")
org_deleteCmd.Flags().BoolP("force", "f", false,
"Force deletion of organization, even if it has associated projects")
err := org_deleteCmd.MarkFlagRequired("org-id")
Expand Down
4 changes: 2 additions & 2 deletions cmd/cli/app/profile/profile_delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import (

var profile_deleteCmd = &cobra.Command{
Use: "delete",
Short: "delete a profile within a mediator controlplane",
Short: "Delete a profile within a mediator control plane",
Long: `The medic profile delete subcommand lets you delete profiles within a
mediator control plane.`,
PreRun: func(cmd *cobra.Command, args []string) {
Expand Down Expand Up @@ -64,7 +64,7 @@ mediator control plane.`,

func init() {
ProfileCmd.AddCommand(profile_deleteCmd)
profile_deleteCmd.Flags().StringP("id", "i", "", "id of profile to delete")
profile_deleteCmd.Flags().StringP("id", "i", "", "ID of profile to delete")
profile_deleteCmd.Flags().StringP("provider", "p", "github", "Provider for the profile")
err := profile_deleteCmd.MarkFlagRequired("id")
util.ExitNicelyOnError(err, "Error marking flag as required")
Expand Down
6 changes: 3 additions & 3 deletions cmd/cli/app/profile_status/profile_status_get.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,11 +106,11 @@ mediator control plane for an specific provider/project or profile id, entity ty
func init() {
ProfileStatusCmd.AddCommand(profilestatus_getCmd)
profilestatus_getCmd.Flags().StringP("provider", "p", "github", "Provider to get profile status for")
profilestatus_getCmd.Flags().StringP("project", "g", "", "project id to get profile status for")
profilestatus_getCmd.Flags().StringP("profile", "i", "", "profile name to get profile status for")
profilestatus_getCmd.Flags().StringP("project", "g", "", "Project ID to get profile status for")
profilestatus_getCmd.Flags().StringP("profile", "i", "", "Profile name to get profile status for")
profilestatus_getCmd.Flags().StringP("entity-type", "t", "",
fmt.Sprintf("the entity type to get profile status for (one of %s)", entities.KnownTypesCSV()))
profilestatus_getCmd.Flags().StringP("entity", "e", "", "entity id to get profile status for")
profilestatus_getCmd.Flags().StringP("entity", "e", "", "Entity ID to get profile status for")
profilestatus_getCmd.Flags().StringP("output", "o", app.Table, "Output format (json, yaml or table)")

// mark as required
Expand Down
4 changes: 2 additions & 2 deletions cmd/cli/app/profile_status/profile_status_list.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,8 @@ mediator control plane for an specific provider/project or profile id.`,
func init() {
ProfileStatusCmd.AddCommand(profilestatus_listCmd)
profilestatus_listCmd.Flags().StringP("provider", "p", "github", "Provider to list profile status for")
profilestatus_listCmd.Flags().StringP("project", "g", "", "project id to list profile status for")
profilestatus_listCmd.Flags().StringP("profile", "i", "", "profile name to list profile status for")
profilestatus_listCmd.Flags().StringP("project", "g", "", "Project ID to list profile status for")
profilestatus_listCmd.Flags().StringP("profile", "i", "", "Profile name to list profile status for")
profilestatus_listCmd.Flags().StringP("output", "o", app.Table, "Output format (json, yaml or table)")
profilestatus_listCmd.Flags().BoolP("detailed", "d", false, "List all profile violations")
profilestatus_listCmd.Flags().StringP("rule", "r", "", "Filter profile status list by rule")
Expand Down
4 changes: 2 additions & 2 deletions cmd/cli/app/project/group_delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ import (

var project_deleteCmd = &cobra.Command{
Use: "delete",
Short: "delete a project within a mediator controlplane",
Short: "Delete a project within a mediator control plane",
Long: `The medic project delete subcommand lets you delete projects within a
mediator control plane.`,
PreRun: func(cmd *cobra.Command, args []string) {
Expand Down Expand Up @@ -69,7 +69,7 @@ mediator control plane.`,

func init() {
ProjectCmd.AddCommand(project_deleteCmd)
project_deleteCmd.Flags().StringP("project-id", "g", "", "id of project to delete")
project_deleteCmd.Flags().StringP("project-id", "g", "", "ID of project to delete")
project_deleteCmd.Flags().BoolP("force", "f", false,
"Force deletion of project, even if it's protected or has associated roles "+
"(WARNING: removing a protected project may cause loosing mediator access)")
Expand Down
2 changes: 1 addition & 1 deletion cmd/cli/app/project/group_list.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ a mediator control plane.`,

func init() {
ProjectCmd.AddCommand(project_listCmd)
project_listCmd.Flags().StringP("org-id", "i", "", "org id to list projects for")
project_listCmd.Flags().StringP("org-id", "i", "", "Organisation ID to list projects for")
project_listCmd.Flags().StringP("output", "o", "", "Output format")
project_listCmd.Flags().Int32P("limit", "l", -1, "Limit the number of results returned")
project_listCmd.Flags().Int32P("offset", "f", 0, "Offset the results returned")
Expand Down
6 changes: 0 additions & 6 deletions cmd/cli/app/repo/repo_list.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,6 @@ var repo_listCmd = &cobra.Command{
return fmt.Errorf("only %s is supported at this time", github.Github)
}
projectID := viper.GetString("project-id")
limit := viper.GetInt32("limit")
offset := viper.GetInt32("offset")
format := viper.GetString("output")

switch format {
Expand All @@ -70,8 +68,6 @@ var repo_listCmd = &cobra.Command{
resp, err := client.ListRepositories(ctx, &pb.ListRepositoriesRequest{
Provider: provider,
ProjectId: projectID,
Limit: int32(limit),
Offset: int32(offset),
Filter: pb.RepoFilter_REPO_FILTER_SHOW_REGISTERED_ONLY,
})
if err != nil {
Expand Down Expand Up @@ -114,8 +110,6 @@ func init() {
repo_listCmd.Flags().StringP("output", "f", "", "Output format (json or yaml)")
repo_listCmd.Flags().StringP("provider", "n", "", "Name for the provider to enroll")
repo_listCmd.Flags().StringP("project-id", "g", "", "ID of the project for repo registration")
repo_listCmd.Flags().Int32P("limit", "l", 20, "Number of repos to display per page")
repo_listCmd.Flags().Int32P("offset", "o", 0, "Offset of the repos to display")
if err := repo_listCmd.MarkFlagRequired("provider"); err != nil {
fmt.Fprintf(os.Stderr, "Error marking flag as required: %s\n", err)
}
Expand Down
148 changes: 95 additions & 53 deletions cmd/cli/app/repo/repo_register.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,19 +23,24 @@ package repo

import (
"context"
"errors"
"fmt"
"os"
"strings"

"github.com/AlecAivazis/survey/v2"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"k8s.io/utils/strings/slices"

github "github.com/stacklok/mediator/internal/providers/github"
"github.com/stacklok/mediator/internal/util"
pb "github.com/stacklok/mediator/pkg/api/protobuf/go/mediator/v1"
)

var errNoRepositoriesSelected = errors.New("No repositories selected")
var cfgFlagRepos string

// repo_registerCmd represents the register command to register a repo with the
// mediator control plane
var repo_registerCmd = &cobra.Command{
Expand All @@ -55,8 +60,6 @@ var repo_registerCmd = &cobra.Command{
os.Exit(1)
}
projectID := viper.GetString("project-id")
limit := viper.GetInt32("limit")
offset := viper.GetInt32("offset")

conn, err := util.GrpcForCommand(cmd)
util.ExitNicelyOnError(err, "Error getting grpc connection")
Expand All @@ -69,77 +72,46 @@ var repo_registerCmd = &cobra.Command{
req := &pb.ListRepositoriesRequest{
Provider: provider,
ProjectId: projectID,
Limit: int32(limit),
Offset: int32(offset),
Filter: pb.RepoFilter_REPO_FILTER_SHOW_NOT_REGISTERED_ONLY,
}

// Get the list of repos
listResp, err := client.ListRepositories(ctx, req)
if err != nil {
fmt.Fprintf(os.Stderr, "Error getting list of repos: %s\n", err)
os.Exit(1)
}

var allSelectedRepos []string

repoNames := make([]string, len(listResp.Results))
repoIDs := make(map[string]int32) // Map of repo names to IDs

if len(listResp.Results) == 0 {
fmt.Fprintf(os.Stderr, "No repositories found")
_, _ = fmt.Fprintf(os.Stderr, "Error getting list of repos: %s\n", err)
os.Exit(1)
}

for i, repo := range listResp.Results {
repoNames[i] = fmt.Sprintf("%s/%s", repo.Owner, repo.Name)
repoIDs[repoNames[i]] = repo.RepoId
}

var selectedRepos []string
prompt := &survey.MultiSelect{
Message: "Select repositories to register with mediator: \n",
Options: repoNames,
PageSize: 20, // PageSize determins how many options are shown at once, restricted by limit flag
}

err = survey.AskOne(prompt, &selectedRepos)
// Get the selected repos
selectedRepos, err := getSelectedRepositories(listResp, cfgFlagRepos)
if err != nil {
fmt.Fprintf(os.Stderr, "Error getting repo selection: %s\n", err)
os.Exit(1)
}
allSelectedRepos = append(allSelectedRepos, selectedRepos...)
repoProtos := make([]*pb.Repositories, len(allSelectedRepos))

// Convert the selected repos into a slice of Repositories protobufs
for i, repo := range allSelectedRepos {
splitRepo := strings.Split(repo, "/")
if len(splitRepo) != 2 {
fmt.Fprintf(os.Stderr, "Unexpected repository name format: %s\n", repo)
os.Exit(1)
}
repoProtos[i] = &pb.Repositories{
Owner: splitRepo[0],
Name: splitRepo[1],
RepoId: repoIDs[repo], // This line is new, it sets the ID from the map
if errors.Is(err, errNoRepositoriesSelected) {
_, _ = fmt.Fprintf(os.Stderr, "%v\n", err)
} else {
_, _ = fmt.Fprintf(os.Stderr, "Error getting selected repos: %s\n", err)
}
os.Exit(1)
}

// read events from config
// Read events from config
events := viper.GetStringSlice(fmt.Sprintf("%s.events", provider))

// Construct the RegisterRepositoryRequest
request := &pb.RegisterRepositoryRequest{
Provider: provider,
Repositories: repoProtos,
Repositories: selectedRepos,
Events: events,
ProjectId: projectID,
}

// Register the repos
registerResp, err := client.RegisterRepository(context.Background(), request)
if err != nil {
fmt.Fprintf(os.Stderr, "Error registering repositories: %s\n", err)
_, _ = fmt.Fprintf(os.Stderr, "Error registering repositories: %s\n", err)
os.Exit(1)
}

// Print the registered repos
for _, repo := range registerResp.Results {
fmt.Printf("Registered repository: %s/%s\n", repo.Owner, repo.Repository)
}
Expand All @@ -149,13 +121,83 @@ var repo_registerCmd = &cobra.Command{

func init() {
RepoCmd.AddCommand(repo_registerCmd)
var reposFlag string
repo_registerCmd.Flags().StringP("provider", "n", "", "Name for the provider to enroll")
repo_registerCmd.Flags().StringP("project-id", "g", "", "ID of the project for repo registration")
repo_registerCmd.Flags().Int32P("limit", "l", 20, "Number of repos to display per page")
repo_registerCmd.Flags().Int32P("offset", "o", 0, "Offset of the repos to display")
repo_registerCmd.Flags().StringVar(&reposFlag, "repo", "", "List of key-value pairs")
repo_registerCmd.Flags().StringVar(&cfgFlagRepos, "repo", "", "List of repositories to register, i.e owner/repo,owner/repo")
if err := repo_registerCmd.MarkFlagRequired("provider"); err != nil {
fmt.Fprintf(os.Stderr, "Error marking flag as required: %s\n", err)
_, _ = fmt.Fprintf(os.Stderr, "Error marking flag as required: %s\n", err)
}
}

func getSelectedRepositories(listResp *pb.ListRepositoriesResponse, flagRepos string) ([]*pb.Repositories, error) {
// If no repos are found, exit
if len(listResp.Results) == 0 {
return nil, fmt.Errorf("no repositories found")
}

// Create a slice of strings to hold the repo names
repoNames := make([]string, len(listResp.Results))

// Map of repo names to IDs
repoIDs := make(map[string]int32)

// Populate the repoNames slice and repoIDs map
for i, repo := range listResp.Results {
repoNames[i] = fmt.Sprintf("%s/%s", repo.Owner, repo.Name)
repoIDs[repoNames[i]] = repo.RepoId
}

// Create a slice of strings to hold the selected repos
var allSelectedRepos []string

// If the --repo flag is set, use it to select repos
if flagRepos != "" {
repos := strings.Split(flagRepos, ",")
for _, repo := range repos {
if !slices.Contains(repoNames, repo) {
_, _ = fmt.Fprintf(os.Stderr, "Repository %s not found\n", repo)
continue
}
allSelectedRepos = append(allSelectedRepos, repo)
}
}

// The repo flag was empty, or no repositories matched the ones from the flag
// Prompt the user to select repos
if len(allSelectedRepos) == 0 {
var userSelectedRepos []string
prompt := &survey.MultiSelect{
Message: "Select repositories to register with Mediator: \n",
Options: repoNames,
}
// Prompt the user to select repos, defaulting to 20 per page, but scrollable
err := survey.AskOne(prompt, &userSelectedRepos, survey.WithPageSize(20))
if err != nil {
return nil, fmt.Errorf("error getting repo selection: %s", err)
}
allSelectedRepos = append(allSelectedRepos, userSelectedRepos...)
}

// If no repos were selected, exit
if len(allSelectedRepos) == 0 {
return nil, errNoRepositoriesSelected
}

// Create a slice of Repositories protobufs
protoRepos := make([]*pb.Repositories, len(allSelectedRepos))

// Convert the selected repos into a slice of Repositories protobufs
for i, repo := range allSelectedRepos {
splitRepo := strings.Split(repo, "/")
if len(splitRepo) != 2 {
_, _ = fmt.Fprintf(os.Stderr, "Unexpected repository name format: %s, skipping registration\n", repo)
continue
}
protoRepos[i] = &pb.Repositories{
Owner: splitRepo[0],
Name: splitRepo[1],
RepoId: repoIDs[repo],
}
}
return protoRepos, nil
}
4 changes: 2 additions & 2 deletions cmd/cli/app/role/role_delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ import (

var role_deleteCmd = &cobra.Command{
Use: "delete",
Short: "delete a role within a mediator controlplane",
Short: "Delete a role within a mediator control plane",
Long: `The medic role delete subcommand lets you delete roles within a
mediator control plane.`,
PreRun: func(cmd *cobra.Command, args []string) {
Expand Down Expand Up @@ -69,7 +69,7 @@ mediator control plane.`,

func init() {
RoleCmd.AddCommand(role_deleteCmd)
role_deleteCmd.Flags().Int32P("role-id", "r", 0, "id of role to delete")
role_deleteCmd.Flags().Int32P("role-id", "r", 0, "ID of role to delete")
role_deleteCmd.Flags().BoolP("force", "f", false,
"Force deletion of role, even if it's protected or has associated users "+
"(WARNING: removing a protected role may cause loosing mediator access)")
Expand Down
4 changes: 2 additions & 2 deletions cmd/cli/app/role/role_list.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,8 +145,8 @@ mediator control plane for an specific project.`,

func init() {
RoleCmd.AddCommand(role_listCmd)
role_listCmd.Flags().StringP("org-id", "i", "", "org id to list roles for")
role_listCmd.Flags().StringP("project-id", "g", "", "project id to list roles for")
role_listCmd.Flags().StringP("org-id", "i", "", "Organisation ID to list roles for")
role_listCmd.Flags().StringP("project-id", "g", "", "Project ID to list roles for")
role_listCmd.Flags().StringP("output", "o", "", "Output format (json or yaml)")
role_listCmd.Flags().Int32P("limit", "l", -1, "Limit the number of results returned")
role_listCmd.Flags().Int32P("offset", "f", 0, "Offset the results returned")
Expand Down
4 changes: 2 additions & 2 deletions cmd/cli/app/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ var (
// RootCmd represents the base command when called without any subcommands
RootCmd = &cobra.Command{
Use: "medic",
Short: "medic controls mediator via the control plane",
Short: "Medic controls mediator via the control plane",
Long: `For more information about mediator, please visit:
https://docs.stacklok.com/mediator/medic/overview.html`,
}
Expand All @@ -57,7 +57,7 @@ func init() {
RootCmd.PersistentFlags().String("grpc-host", "staging.stacklok.dev", "Server host")
RootCmd.PersistentFlags().Int("grpc-port", 443, "Server port")
RootCmd.PersistentFlags().Bool("grpc-insecure", false, "Allow establishing insecure connections")
RootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $PWD/config.yaml)")
RootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "Config file (default is $PWD/config.yaml)")
}

func initConfig() {
Expand Down
Loading