Add more CLI commands
This commit is contained in:
parent
475d424f32
commit
8ceafff09b
21 changed files with 995 additions and 107 deletions
|
|
@ -12,8 +12,14 @@ import (
|
|||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func NewClient(name string, cfg config.Manager) *Client {
|
||||
func NewClient(name string, cfg config.Manager, debug bool) *Client {
|
||||
cli := resty.New()
|
||||
if cfg.Token != "" {
|
||||
cli = cli.SetAuthToken(cfg.Token)
|
||||
}
|
||||
cli = cli.
|
||||
SetHeader("Accept", "application/json").
|
||||
SetDebug(debug)
|
||||
return &Client{
|
||||
ManagerName: name,
|
||||
Config: cfg,
|
||||
|
|
@ -45,7 +51,6 @@ func (c *Client) InitManager(url string, param params.NewUserParams) (params.Use
|
|||
|
||||
var response params.User
|
||||
resp, err := c.client.R().
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetBody(body).
|
||||
SetResult(&response).
|
||||
Post(url)
|
||||
|
|
@ -69,17 +74,210 @@ func (c *Client) Login(url string, param params.PasswordLoginParams) (string, er
|
|||
|
||||
var response params.JWTResponse
|
||||
resp, err := c.client.R().
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetBody(body).
|
||||
SetResult(&response).
|
||||
Post(url)
|
||||
if err != nil {
|
||||
if err != nil || resp.IsError() {
|
||||
apiErr, decErr := c.decodeAPIError(resp.Body())
|
||||
if decErr != nil {
|
||||
return "", errors.Wrap(err, "sending request")
|
||||
return "", errors.Wrap(decErr, "sending request")
|
||||
}
|
||||
return "", fmt.Errorf("error running init: %s", apiErr.Details)
|
||||
return "", fmt.Errorf("error performing login: %s", apiErr.Details)
|
||||
}
|
||||
|
||||
return response.Token, nil
|
||||
}
|
||||
|
||||
func (c *Client) ListCredentials() ([]params.GithubCredentials, error) {
|
||||
var ghCreds []params.GithubCredentials
|
||||
url := fmt.Sprintf("%s/api/v1/credentials", c.Config.BaseURL)
|
||||
resp, err := c.client.R().
|
||||
SetResult(&ghCreds).
|
||||
Get(url)
|
||||
if err != nil || resp.IsError() {
|
||||
apiErr, decErr := c.decodeAPIError(resp.Body())
|
||||
if decErr != nil {
|
||||
return nil, errors.Wrap(decErr, "sending request")
|
||||
}
|
||||
return nil, fmt.Errorf("error fetching credentials: %s", apiErr.Details)
|
||||
}
|
||||
return ghCreds, nil
|
||||
}
|
||||
|
||||
func (c *Client) ListProviders() ([]params.Provider, error) {
|
||||
var providers []params.Provider
|
||||
url := fmt.Sprintf("%s/api/v1/providers", c.Config.BaseURL)
|
||||
resp, err := c.client.R().
|
||||
SetResult(&providers).
|
||||
Get(url)
|
||||
if err != nil || resp.IsError() {
|
||||
apiErr, decErr := c.decodeAPIError(resp.Body())
|
||||
if decErr != nil {
|
||||
return nil, errors.Wrap(decErr, "sending request")
|
||||
}
|
||||
return nil, fmt.Errorf("error fetching providers: %s", apiErr.Details)
|
||||
}
|
||||
return providers, nil
|
||||
}
|
||||
|
||||
func (c *Client) ListRepositories() ([]params.Repository, error) {
|
||||
var repos []params.Repository
|
||||
url := fmt.Sprintf("%s/api/v1/repositories", c.Config.BaseURL)
|
||||
resp, err := c.client.R().
|
||||
SetResult(&repos).
|
||||
Get(url)
|
||||
if err != nil || resp.IsError() {
|
||||
apiErr, decErr := c.decodeAPIError(resp.Body())
|
||||
if decErr != nil {
|
||||
return nil, errors.Wrap(decErr, "sending request")
|
||||
}
|
||||
return nil, fmt.Errorf("error fetching repos: %s", apiErr.Details)
|
||||
}
|
||||
return repos, nil
|
||||
}
|
||||
|
||||
func (c *Client) CreateRepository(param params.CreateRepoParams) (params.Repository, error) {
|
||||
var response params.Repository
|
||||
url := fmt.Sprintf("%s/api/v1/repositories", c.Config.BaseURL)
|
||||
|
||||
body, err := json.Marshal(param)
|
||||
if err != nil {
|
||||
return params.Repository{}, err
|
||||
}
|
||||
resp, err := c.client.R().
|
||||
SetBody(body).
|
||||
SetResult(&response).
|
||||
Post(url)
|
||||
if err != nil || resp.IsError() {
|
||||
apiErr, decErr := c.decodeAPIError(resp.Body())
|
||||
if decErr != nil {
|
||||
return response, errors.Wrap(decErr, "sending request")
|
||||
}
|
||||
return response, fmt.Errorf("error performing login: %s", apiErr.Details)
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (c *Client) GetRepository(repoID string) (params.Repository, error) {
|
||||
var response params.Repository
|
||||
url := fmt.Sprintf("%s/api/v1/repositories/%s", c.Config.BaseURL, repoID)
|
||||
resp, err := c.client.R().
|
||||
SetResult(&response).
|
||||
Get(url)
|
||||
if err != nil || resp.IsError() {
|
||||
apiErr, decErr := c.decodeAPIError(resp.Body())
|
||||
if decErr != nil {
|
||||
return response, errors.Wrap(decErr, "sending request")
|
||||
}
|
||||
return response, fmt.Errorf("error fetching repos: %s", apiErr.Details)
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (c *Client) DeleteRepository(repoID string) error {
|
||||
url := fmt.Sprintf("%s/api/v1/repositories/%s", c.Config.BaseURL, repoID)
|
||||
resp, err := c.client.R().
|
||||
Delete(url)
|
||||
if err != nil || resp.IsError() {
|
||||
apiErr, decErr := c.decodeAPIError(resp.Body())
|
||||
if decErr != nil {
|
||||
return errors.Wrap(decErr, "sending request")
|
||||
}
|
||||
return fmt.Errorf("error fetching repos: %s", apiErr.Details)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) CreateRepoPool(repoID string, param params.CreatePoolParams) (params.Pool, error) {
|
||||
url := fmt.Sprintf("%s/api/v1/repositories/%s/pools", c.Config.BaseURL, repoID)
|
||||
|
||||
var response params.Pool
|
||||
body, err := json.Marshal(param)
|
||||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
resp, err := c.client.R().
|
||||
SetBody(body).
|
||||
SetResult(&response).
|
||||
Post(url)
|
||||
if err != nil || resp.IsError() {
|
||||
apiErr, decErr := c.decodeAPIError(resp.Body())
|
||||
if decErr != nil {
|
||||
return response, errors.Wrap(decErr, "sending request")
|
||||
}
|
||||
return response, fmt.Errorf("error performing login: %s", apiErr.Details)
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (c *Client) ListRepoPools(repoID string) ([]params.Pool, error) {
|
||||
url := fmt.Sprintf("%s/api/v1/repositories/%s/pools", c.Config.BaseURL, repoID)
|
||||
|
||||
var response []params.Pool
|
||||
resp, err := c.client.R().
|
||||
SetResult(&response).
|
||||
Get(url)
|
||||
if err != nil || resp.IsError() {
|
||||
apiErr, decErr := c.decodeAPIError(resp.Body())
|
||||
if decErr != nil {
|
||||
return response, errors.Wrap(decErr, "sending request")
|
||||
}
|
||||
return response, fmt.Errorf("error performing login: %s", apiErr.Details)
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (c *Client) GetRepoPool(repoID, poolID string) (params.Pool, error) {
|
||||
url := fmt.Sprintf("%s/api/v1/repositories/%s/pools/%s", c.Config.BaseURL, repoID, poolID)
|
||||
|
||||
var response params.Pool
|
||||
resp, err := c.client.R().
|
||||
SetResult(&response).
|
||||
Get(url)
|
||||
if err != nil || resp.IsError() {
|
||||
apiErr, decErr := c.decodeAPIError(resp.Body())
|
||||
if decErr != nil {
|
||||
return response, errors.Wrap(decErr, "sending request")
|
||||
}
|
||||
return response, fmt.Errorf("error performing login: %s", apiErr.Details)
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (c *Client) DeleteRepoPool(repoID, poolID string) error {
|
||||
url := fmt.Sprintf("%s/api/v1/repositories/%s/pools/%s", c.Config.BaseURL, repoID, poolID)
|
||||
|
||||
resp, err := c.client.R().
|
||||
Delete(url)
|
||||
|
||||
if err != nil || resp.IsError() {
|
||||
apiErr, decErr := c.decodeAPIError(resp.Body())
|
||||
if decErr != nil {
|
||||
return errors.Wrap(decErr, "sending request")
|
||||
}
|
||||
return fmt.Errorf("error performing login: %s", apiErr.Details)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) UpdateRepoPool(repoID, poolID string, param params.UpdatePoolParams) (params.Pool, error) {
|
||||
url := fmt.Sprintf("%s/api/v1/repositories/%s/pools/%s", c.Config.BaseURL, repoID, poolID)
|
||||
|
||||
var response params.Pool
|
||||
body, err := json.Marshal(param)
|
||||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
resp, err := c.client.R().
|
||||
SetBody(body).
|
||||
SetResult(&response).
|
||||
Put(url)
|
||||
if err != nil || resp.IsError() {
|
||||
apiErr, decErr := c.decodeAPIError(resp.Body())
|
||||
if decErr != nil {
|
||||
return response, errors.Wrap(decErr, "sending request")
|
||||
}
|
||||
return response, fmt.Errorf("error performing login: %s", apiErr.Details)
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
|
|
|||
63
cmd/run-cli/cmd/credentials.go
Normal file
63
cmd/run-cli/cmd/credentials.go
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
Copyright © 2022 NAME HERE <EMAIL ADDRESS>
|
||||
|
||||
*/
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runner-manager/params"
|
||||
|
||||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// credentialsCmd represents the credentials command
|
||||
var credentialsCmd = &cobra.Command{
|
||||
Use: "credentials",
|
||||
Aliases: []string{"creds"},
|
||||
Short: "List configured credentials",
|
||||
Long: `List all available credentials configured in the service
|
||||
config file.
|
||||
|
||||
Currently, github personal tokens are configured statically in the config file
|
||||
of the runner-manager service. This command lists the names of those credentials,
|
||||
which in turn can be used to define pools of runners withing repositories.`,
|
||||
Run: nil,
|
||||
}
|
||||
|
||||
func init() {
|
||||
credentialsCmd.AddCommand(
|
||||
&cobra.Command{
|
||||
Use: "list",
|
||||
Aliases: []string{"ls"},
|
||||
Short: "List configured github credentials",
|
||||
Long: `List the names of the github personal access tokens availabe to the runner-manager.`,
|
||||
SilenceUsage: true,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if needsInit {
|
||||
return needsInitError
|
||||
}
|
||||
|
||||
creds, err := cli.ListCredentials()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
formatGithubCredentials(creds)
|
||||
return nil
|
||||
},
|
||||
})
|
||||
|
||||
rootCmd.AddCommand(credentialsCmd)
|
||||
}
|
||||
|
||||
func formatGithubCredentials(creds []params.GithubCredentials) {
|
||||
t := table.NewWriter()
|
||||
header := table.Row{"Name", "Description"}
|
||||
t.AppendHeader(header)
|
||||
for _, val := range creds {
|
||||
t.AppendRow(table.Row{val.Name, val.Description})
|
||||
t.AppendSeparator()
|
||||
}
|
||||
fmt.Println(t.Render())
|
||||
}
|
||||
|
|
@ -6,7 +6,9 @@ package cmd
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"runner-manager/cmd/run-cli/common"
|
||||
"runner-manager/cmd/run-cli/config"
|
||||
"runner-manager/params"
|
||||
|
||||
|
|
@ -33,18 +35,24 @@ run-cli login --name=dev --url=https://runner.example.com --username=admin --pas
|
|||
`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if cfg != nil {
|
||||
if cfg.HasManager(loginName) {
|
||||
return fmt.Errorf("a manager with name %s already exists in your local config", loginName)
|
||||
if cfg.HasManager(loginProfileName) {
|
||||
return fmt.Errorf("a manager with name %s already exists in your local config", loginProfileName)
|
||||
}
|
||||
}
|
||||
|
||||
if err := promptUnsetInitVariables(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
newUser := params.NewUserParams{
|
||||
Username: loginUserName,
|
||||
Password: loginPassword,
|
||||
FullName: loginFullName,
|
||||
Email: loginEmail,
|
||||
}
|
||||
response, err := cli.InitManager(loginURL, newUser)
|
||||
|
||||
url := strings.TrimSuffix(loginURL, "/")
|
||||
response, err := cli.InitManager(url, newUser)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "initializing manager")
|
||||
}
|
||||
|
|
@ -54,25 +62,18 @@ run-cli login --name=dev --url=https://runner.example.com --username=admin --pas
|
|||
Password: loginPassword,
|
||||
}
|
||||
|
||||
token, err := cli.Login(loginURL, loginParams)
|
||||
token, err := cli.Login(url, loginParams)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "authenticating")
|
||||
}
|
||||
|
||||
if cfg == nil {
|
||||
// we're creating a new config
|
||||
cfg = &config.Config{
|
||||
Managers: []config.Manager{},
|
||||
}
|
||||
}
|
||||
|
||||
cfg.Managers = append(cfg.Managers, config.Manager{
|
||||
Name: loginName,
|
||||
BaseURL: loginURL,
|
||||
Name: loginProfileName,
|
||||
BaseURL: url,
|
||||
Token: token,
|
||||
})
|
||||
|
||||
cfg.ActiveManager = loginName
|
||||
cfg.ActiveManager = loginProfileName
|
||||
|
||||
if err := cfg.SaveConfig(); err != nil {
|
||||
return errors.Wrap(err, "saving config")
|
||||
|
|
@ -83,21 +84,42 @@ run-cli login --name=dev --url=https://runner.example.com --username=admin --pas
|
|||
},
|
||||
}
|
||||
|
||||
func promptUnsetInitVariables() error {
|
||||
var err error
|
||||
if loginUserName == "" {
|
||||
loginUserName, err = common.PromptString("Username")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if loginEmail == "" {
|
||||
loginEmail, err = common.PromptString("Email")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if loginPassword == "" {
|
||||
loginPassword, err = common.PromptPassword("Password")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(initCmd)
|
||||
|
||||
initCmd.Flags().StringVarP(&loginName, "name", "n", "", "A name for this runner manager")
|
||||
initCmd.Flags().StringVarP(&loginProfileName, "name", "n", "", "A name for this runner manager")
|
||||
initCmd.Flags().StringVarP(&loginURL, "url", "a", "", "The base URL for the runner manager API")
|
||||
initCmd.Flags().StringVarP(&loginUserName, "username", "u", "", "The desired administrative username")
|
||||
initCmd.Flags().StringVarP(&loginPassword, "password", "p", "", "The admin password")
|
||||
initCmd.Flags().StringVarP(&loginFullName, "full-name", "f", "", "Full name of the user")
|
||||
initCmd.Flags().StringVarP(&loginEmail, "email", "e", "", "Email address")
|
||||
initCmd.Flags().StringVarP(&loginFullName, "full-name", "f", "", "Full name of the user")
|
||||
initCmd.Flags().StringVarP(&loginPassword, "password", "p", "", "The admin password")
|
||||
initCmd.MarkFlagRequired("name")
|
||||
initCmd.MarkFlagRequired("url")
|
||||
initCmd.MarkFlagRequired("username")
|
||||
initCmd.MarkFlagRequired("password")
|
||||
initCmd.MarkFlagRequired("full-name")
|
||||
initCmd.MarkFlagRequired("email")
|
||||
}
|
||||
|
||||
func renderUserTable(user params.User) {
|
||||
|
|
|
|||
|
|
@ -6,17 +6,21 @@ package cmd
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"runner-manager/cmd/run-cli/common"
|
||||
"runner-manager/cmd/run-cli/config"
|
||||
"runner-manager/params"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var (
|
||||
loginName string
|
||||
loginURL string
|
||||
loginPassword string
|
||||
loginUserName string
|
||||
loginFullName string
|
||||
loginEmail string
|
||||
loginProfileName string
|
||||
loginURL string
|
||||
loginPassword string
|
||||
loginUserName string
|
||||
loginFullName string
|
||||
loginEmail string
|
||||
)
|
||||
|
||||
// loginCmd represents the login command
|
||||
|
|
@ -27,16 +31,68 @@ var loginCmd = &cobra.Command{
|
|||
Long: `Performs login for this machine on a remote runner-manager:
|
||||
|
||||
run-cli login --name=dev --url=https://runner.example.com`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
fmt.Printf("--> %v %v\n", cfg, configErr)
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if cfg != nil {
|
||||
if cfg.HasManager(loginProfileName) {
|
||||
return fmt.Errorf("a manager with name %s already exists in your local config", loginProfileName)
|
||||
}
|
||||
}
|
||||
|
||||
if err := promptUnsetLoginVariables(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
url := strings.TrimSuffix(loginURL, "/")
|
||||
loginParams := params.PasswordLoginParams{
|
||||
Username: loginUserName,
|
||||
Password: loginPassword,
|
||||
}
|
||||
|
||||
resp, err := cli.Login(url, loginParams)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cfg.Managers = append(cfg.Managers, config.Manager{
|
||||
Name: loginProfileName,
|
||||
BaseURL: url,
|
||||
Token: resp,
|
||||
})
|
||||
cfg.ActiveManager = loginProfileName
|
||||
|
||||
if err := cfg.SaveConfig(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
func promptUnsetLoginVariables() error {
|
||||
var err error
|
||||
if loginUserName == "" {
|
||||
loginUserName, err = common.PromptString("Username")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if loginPassword == "" {
|
||||
loginPassword, err = common.PromptPassword("Password")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(loginCmd)
|
||||
|
||||
loginCmd.Flags().StringVarP(&loginName, "name", "n", "", "A name for this runner manager")
|
||||
loginCmd.Flags().StringVarP(&loginProfileName, "name", "n", "", "A name for this runner manager")
|
||||
loginCmd.Flags().StringVarP(&loginURL, "url", "a", "", "The base URL for the runner manager API")
|
||||
loginCmd.Flags().StringVarP(&loginUserName, "username", "u", "", "Username to log in as")
|
||||
loginCmd.Flags().StringVarP(&loginPassword, "password", "p", "", "The user passowrd")
|
||||
|
||||
loginCmd.MarkFlagRequired("name")
|
||||
loginCmd.MarkFlagRequired("url")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import (
|
|||
var organizationCmd = &cobra.Command{
|
||||
Use: "organization",
|
||||
SilenceUsage: true,
|
||||
Aliases: []string{"org"},
|
||||
Short: "A brief description of your command",
|
||||
Long: `A longer description that spans multiple lines and likely contains examples
|
||||
and usage of using your command. For example:
|
||||
|
|
|
|||
|
|
@ -6,7 +6,9 @@ package cmd
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"runner-manager/params"
|
||||
|
||||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
|
|
@ -32,10 +34,29 @@ func init() {
|
|||
Long: `List all cloud providers configured with the service.`,
|
||||
SilenceUsage: true,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
fmt.Println("provider list called")
|
||||
return fmt.Errorf("I failed :(")
|
||||
if needsInit {
|
||||
return needsInitError
|
||||
}
|
||||
|
||||
providers, err := cli.ListProviders()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
formatProviders(providers)
|
||||
return nil
|
||||
},
|
||||
})
|
||||
|
||||
rootCmd.AddCommand(providerCmd)
|
||||
}
|
||||
|
||||
func formatProviders(providers []params.Provider) {
|
||||
t := table.NewWriter()
|
||||
header := table.Row{"Name", "Description"}
|
||||
t.AppendHeader(header)
|
||||
for _, val := range providers {
|
||||
t.AppendRow(table.Row{val.Name, val.ProviderType})
|
||||
t.AppendSeparator()
|
||||
}
|
||||
fmt.Println(t.Render())
|
||||
}
|
||||
|
|
|
|||
309
cmd/run-cli/cmd/repo_pool.go
Normal file
309
cmd/run-cli/cmd/repo_pool.go
Normal file
|
|
@ -0,0 +1,309 @@
|
|||
/*
|
||||
Copyright © 2022 NAME HERE <EMAIL ADDRESS>
|
||||
|
||||
*/
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runner-manager/config"
|
||||
"runner-manager/params"
|
||||
"strings"
|
||||
|
||||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var (
|
||||
poolProvider string
|
||||
poolMaxRunners uint
|
||||
poolMinIdleRunners uint
|
||||
poolImage string
|
||||
poolFlavor string
|
||||
poolOSType string
|
||||
poolOSArch string
|
||||
poolTags string
|
||||
poolEnabled bool
|
||||
)
|
||||
|
||||
// repoPoolCmd represents the pool command
|
||||
var repoPoolCmd = &cobra.Command{
|
||||
Use: "pool",
|
||||
SilenceUsage: true,
|
||||
Aliases: []string{"pools"},
|
||||
Short: "Manage pools",
|
||||
Long: `Manage pools for a repository.
|
||||
|
||||
Repositories and organizations can define multiple pools with different
|
||||
characteristics, which in turn will spawn github self hosted runners on
|
||||
compute instances that reflect those characteristics.
|
||||
|
||||
For example, one pool can define a runner with tags "GPU,ML" which will
|
||||
spin up instances with access to a GPU, on the desired provider.`,
|
||||
Run: nil,
|
||||
}
|
||||
|
||||
var poolAddCmd = &cobra.Command{
|
||||
Use: "add",
|
||||
Aliases: []string{"create"},
|
||||
Short: "Add pool",
|
||||
Long: `Add a new pool repository to the manager.`,
|
||||
SilenceUsage: true,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if needsInit {
|
||||
return needsInitError
|
||||
}
|
||||
|
||||
if len(args) == 0 {
|
||||
return fmt.Errorf("requires a repository ID")
|
||||
}
|
||||
|
||||
if len(args) > 1 {
|
||||
return fmt.Errorf("too many arguments")
|
||||
}
|
||||
|
||||
tags := strings.Split(poolTags, ",")
|
||||
newPoolParams := params.CreatePoolParams{
|
||||
ProviderName: poolProvider,
|
||||
MaxRunners: poolMaxRunners,
|
||||
MinIdleRunners: poolMinIdleRunners,
|
||||
Image: poolImage,
|
||||
Flavor: poolFlavor,
|
||||
OSType: config.OSType(poolOSType),
|
||||
OSArch: config.OSArch(poolOSArch),
|
||||
Tags: tags,
|
||||
Enabled: poolEnabled,
|
||||
}
|
||||
if err := newPoolParams.Validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
pool, err := cli.CreateRepoPool(args[0], newPoolParams)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
formatOnePool(pool)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var poolListCmd = &cobra.Command{
|
||||
Use: "list",
|
||||
Aliases: []string{"ls"},
|
||||
Short: "List repository pools",
|
||||
Long: `List all configured pools for a given repository.`,
|
||||
SilenceUsage: true,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if needsInit {
|
||||
return needsInitError
|
||||
}
|
||||
|
||||
if len(args) == 0 {
|
||||
return fmt.Errorf("requires a repository ID")
|
||||
}
|
||||
|
||||
if len(args) > 1 {
|
||||
return fmt.Errorf("too many arguments")
|
||||
}
|
||||
|
||||
pools, err := cli.ListRepoPools(args[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
formatPools(pools)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var poolShowCmd = &cobra.Command{
|
||||
Use: "show",
|
||||
Short: "Show details for one pool",
|
||||
Long: `Displays detailed information about a single pool.`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if needsInit {
|
||||
return needsInitError
|
||||
}
|
||||
|
||||
if len(args) < 2 || len(args) > 2 {
|
||||
return fmt.Errorf("command requires repoID and poolID")
|
||||
}
|
||||
|
||||
pool, err := cli.GetRepoPool(args[0], args[1])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
formatOnePool(pool)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var poolDeleteCmd = &cobra.Command{
|
||||
Use: "delete",
|
||||
Aliases: []string{"remove", "rm", "del"},
|
||||
Short: "Removes one pool",
|
||||
Long: `Delete one repository pool from the manager.`,
|
||||
SilenceUsage: true,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if needsInit {
|
||||
return needsInitError
|
||||
}
|
||||
if len(args) < 2 || len(args) > 2 {
|
||||
return fmt.Errorf("command requires repoID and poolID")
|
||||
}
|
||||
|
||||
if err := cli.DeleteRepoPool(args[0], args[1]); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var poolUpdateCmd = &cobra.Command{
|
||||
Use: "update",
|
||||
Short: "Update one pool",
|
||||
Long: `Updates pool characteristics.
|
||||
|
||||
This command updates the pool characteristics. Runners already created prior to updating
|
||||
the pool, will not be recreated. IF they no longer suit your needs, you will need to
|
||||
explicitly remove them using the runner delete command.
|
||||
`,
|
||||
SilenceUsage: true,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if needsInit {
|
||||
return needsInitError
|
||||
}
|
||||
|
||||
if len(args) < 2 || len(args) > 2 {
|
||||
return fmt.Errorf("command requires repoID and poolID")
|
||||
}
|
||||
|
||||
poolUpdateParams := params.UpdatePoolParams{}
|
||||
|
||||
if cmd.Flags().Changed("image") {
|
||||
poolUpdateParams.Image = poolImage
|
||||
}
|
||||
|
||||
if cmd.Flags().Changed("flavor") {
|
||||
poolUpdateParams.Flavor = poolFlavor
|
||||
}
|
||||
|
||||
if cmd.Flags().Changed("tags") {
|
||||
poolUpdateParams.Tags = strings.Split(poolTags, ",")
|
||||
}
|
||||
|
||||
if cmd.Flags().Changed("os-type") {
|
||||
poolUpdateParams.OSType = config.OSType(poolOSType)
|
||||
}
|
||||
|
||||
if cmd.Flags().Changed("os-arch") {
|
||||
poolUpdateParams.OSArch = config.OSArch(poolOSArch)
|
||||
}
|
||||
|
||||
if cmd.Flags().Changed("max-runners") {
|
||||
poolUpdateParams.MaxRunners = &poolMaxRunners
|
||||
}
|
||||
|
||||
if cmd.Flags().Changed("min-idle-runners") {
|
||||
poolUpdateParams.MinIdleRunners = &poolMinIdleRunners
|
||||
}
|
||||
|
||||
if cmd.Flags().Changed("enabled") {
|
||||
poolUpdateParams.Enabled = &poolEnabled
|
||||
}
|
||||
|
||||
pool, err := cli.UpdateRepoPool(args[0], args[1], poolUpdateParams)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
formatOnePool(pool)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
poolAddCmd.Flags().StringVar(&poolProvider, "provider-name", "", "The name of the provider where runners will be created.")
|
||||
poolAddCmd.Flags().StringVar(&poolImage, "image", "", "The provider-specific image name to use for runners in this pool.")
|
||||
poolAddCmd.Flags().StringVar(&poolFlavor, "flavor", "", "The flavor to use for this runner.")
|
||||
poolAddCmd.Flags().StringVar(&poolTags, "tags", "", "A comma separated list of tags to assign to this runner.")
|
||||
poolAddCmd.Flags().StringVar(&poolOSType, "os-type", "linux", "Operating system type (windows, linux, etc).")
|
||||
poolAddCmd.Flags().StringVar(&poolOSArch, "os-arch", "amd64", "Operating system architecture (amd64, arm, etc).")
|
||||
poolAddCmd.Flags().UintVar(&poolMaxRunners, "max-runners", 5, "The maximum number of runner this pool will create.")
|
||||
poolAddCmd.Flags().UintVar(&poolMinIdleRunners, "min-idle-runners", 1, "Attempt to maintain a minimum of idle self-hosted runners of this type.")
|
||||
poolAddCmd.Flags().BoolVar(&poolEnabled, "enabled", false, "Enable this pool.")
|
||||
poolAddCmd.MarkFlagRequired("provider-name")
|
||||
poolAddCmd.MarkFlagRequired("image")
|
||||
poolAddCmd.MarkFlagRequired("flavor")
|
||||
poolAddCmd.MarkFlagRequired("tags")
|
||||
|
||||
poolUpdateCmd.Flags().StringVar(&poolImage, "image", "", "The provider-specific image name to use for runners in this pool.")
|
||||
poolUpdateCmd.Flags().StringVar(&poolFlavor, "flavor", "", "The flavor to use for this runner.")
|
||||
poolUpdateCmd.Flags().StringVar(&poolTags, "tags", "", "A comma separated list of tags to assign to this runner.")
|
||||
poolUpdateCmd.Flags().StringVar(&poolOSType, "os-type", "linux", "Operating system type (windows, linux, etc).")
|
||||
poolUpdateCmd.Flags().StringVar(&poolOSArch, "os-arch", "amd64", "Operating system architecture (amd64, arm, etc).")
|
||||
poolUpdateCmd.Flags().UintVar(&poolMaxRunners, "max-runners", 5, "The maximum number of runner this pool will create.")
|
||||
poolUpdateCmd.Flags().UintVar(&poolMinIdleRunners, "min-idle-runners", 1, "Attempt to maintain a minimum of idle self-hosted runners of this type.")
|
||||
poolUpdateCmd.Flags().BoolVar(&poolEnabled, "enabled", false, "Enable this pool.")
|
||||
|
||||
repoPoolCmd.AddCommand(
|
||||
poolListCmd,
|
||||
poolAddCmd,
|
||||
poolShowCmd,
|
||||
poolDeleteCmd,
|
||||
poolUpdateCmd,
|
||||
)
|
||||
|
||||
repositoryCmd.AddCommand(repoPoolCmd)
|
||||
}
|
||||
|
||||
func formatPools(pools []params.Pool) {
|
||||
t := table.NewWriter()
|
||||
header := table.Row{"ID", "Image", "Flavor", "Tags", "Enabled"}
|
||||
t.AppendHeader(header)
|
||||
|
||||
for _, pool := range pools {
|
||||
tags := []string{}
|
||||
for _, tag := range pool.Tags {
|
||||
tags = append(tags, tag.Name)
|
||||
}
|
||||
t.AppendRow(table.Row{pool.ID, pool.Image, pool.Flavor, strings.Join(tags, " "), pool.Enabled})
|
||||
t.AppendSeparator()
|
||||
}
|
||||
fmt.Println(t.Render())
|
||||
}
|
||||
|
||||
func formatOnePool(pool params.Pool) {
|
||||
t := table.NewWriter()
|
||||
rowConfigAutoMerge := table.RowConfig{AutoMerge: true}
|
||||
|
||||
header := table.Row{"Field", "Value"}
|
||||
|
||||
tags := []string{}
|
||||
for _, tag := range pool.Tags {
|
||||
tags = append(tags, tag.Name)
|
||||
}
|
||||
|
||||
t.AppendHeader(header)
|
||||
t.AppendRow(table.Row{"ID", pool.ID})
|
||||
t.AppendRow(table.Row{"Provider Name", pool.ProviderName})
|
||||
t.AppendRow(table.Row{"Image", pool.Image})
|
||||
t.AppendRow(table.Row{"Flavor", pool.Flavor})
|
||||
t.AppendRow(table.Row{"OS Type", pool.OSType})
|
||||
t.AppendRow(table.Row{"OS Architecture", pool.OSArch})
|
||||
t.AppendRow(table.Row{"Max Runners", pool.MaxRunners})
|
||||
t.AppendRow(table.Row{"Min Idle Runners", pool.MinIdleRunners})
|
||||
t.AppendRow(table.Row{"Tags", strings.Join(tags, ", ")})
|
||||
t.AppendRow(table.Row{"Enabled", pool.Enabled})
|
||||
|
||||
if len(pool.Instances) > 0 {
|
||||
for _, instance := range pool.Instances {
|
||||
t.AppendRow(table.Row{"Instances", fmt.Sprintf("%s (%s)", instance.Name, instance.ID)}, rowConfigAutoMerge)
|
||||
}
|
||||
}
|
||||
|
||||
t.SetColumnConfigs([]table.ColumnConfig{
|
||||
{Number: 1, AutoMerge: true},
|
||||
{Number: 2, AutoMerge: true},
|
||||
})
|
||||
fmt.Println(t.Render())
|
||||
}
|
||||
|
|
@ -6,36 +6,170 @@ package cmd
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"runner-manager/params"
|
||||
|
||||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var (
|
||||
repoOwner string
|
||||
repoName string
|
||||
repoWebhookSecret string
|
||||
repoCreds string
|
||||
)
|
||||
|
||||
// repositoryCmd represents the repository command
|
||||
var repositoryCmd = &cobra.Command{
|
||||
Use: "repository",
|
||||
Aliases: []string{"repo"},
|
||||
SilenceUsage: true,
|
||||
Short: "A brief description of your command",
|
||||
Long: `A longer description that spans multiple lines and likely contains examples
|
||||
and usage of using your command. For example:
|
||||
Short: "Manage repositories",
|
||||
Long: `Add, remove or update repositories for which we manage
|
||||
self hosted runners.
|
||||
|
||||
Cobra is a CLI library for Go that empowers applications.
|
||||
This application is a tool to generate the needed files
|
||||
to quickly create a Cobra application.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
fmt.Println("repository called")
|
||||
This command allows you to define a new repository or manage an existing
|
||||
repository for which the runner-manager maintains pools of self hosted runners.`,
|
||||
Run: nil,
|
||||
}
|
||||
|
||||
var repoAddCmd = &cobra.Command{
|
||||
Use: "add",
|
||||
Aliases: []string{"create"},
|
||||
Short: "Add repository",
|
||||
Long: `Add a new repository to the manager.`,
|
||||
SilenceUsage: true,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if needsInit {
|
||||
return needsInitError
|
||||
}
|
||||
|
||||
newRepoReq := params.CreateRepoParams{
|
||||
Owner: repoOwner,
|
||||
Name: repoName,
|
||||
WebhookSecret: repoWebhookSecret,
|
||||
CredentialsName: repoCreds,
|
||||
}
|
||||
repo, err := cli.CreateRepository(newRepoReq)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
formatOneRepository(repo)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var repoListCmd = &cobra.Command{
|
||||
Use: "list",
|
||||
Aliases: []string{"ls"},
|
||||
Short: "List repositories",
|
||||
Long: `List all configured respositories that are currently managed.`,
|
||||
SilenceUsage: true,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if needsInit {
|
||||
return needsInitError
|
||||
}
|
||||
|
||||
repos, err := cli.ListRepositories()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
formatRepositories(repos)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var repoShowCmd = &cobra.Command{
|
||||
Use: "show",
|
||||
Short: "Show details for one repository",
|
||||
Long: `Displays detailed information about a single repository.`,
|
||||
SilenceUsage: true,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if needsInit {
|
||||
return needsInitError
|
||||
}
|
||||
if len(args) == 0 {
|
||||
return fmt.Errorf("requires a repository ID")
|
||||
}
|
||||
if len(args) > 1 {
|
||||
return fmt.Errorf("too many arguments")
|
||||
}
|
||||
repo, err := cli.GetRepository(args[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
formatOneRepository(repo)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var repoDeleteCmd = &cobra.Command{
|
||||
Use: "delete",
|
||||
Aliases: []string{"remove", "rm", "del"},
|
||||
Short: "Removes one repository",
|
||||
Long: `Delete one repository from the manager.`,
|
||||
SilenceUsage: true,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if needsInit {
|
||||
return needsInitError
|
||||
}
|
||||
if len(args) == 0 {
|
||||
return fmt.Errorf("requires a repository ID")
|
||||
}
|
||||
if len(args) > 1 {
|
||||
return fmt.Errorf("too many arguments")
|
||||
}
|
||||
if err := cli.DeleteRepository(args[0]); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
||||
repoAddCmd.Flags().StringVar(&repoOwner, "owner", "", "The owner of this repository")
|
||||
repoAddCmd.Flags().StringVar(&repoName, "name", "", "The name of the repository")
|
||||
repoAddCmd.Flags().StringVar(&repoWebhookSecret, "webhook-secret", "", "The webhook secret for this repository")
|
||||
repoAddCmd.Flags().StringVar(&repoCreds, "credentials", "", "Credentials name. See credentials list.")
|
||||
repoAddCmd.MarkFlagRequired("credentials")
|
||||
repoAddCmd.MarkFlagRequired("owner")
|
||||
repoAddCmd.MarkFlagRequired("name")
|
||||
|
||||
repositoryCmd.AddCommand(
|
||||
repoListCmd,
|
||||
repoAddCmd,
|
||||
repoShowCmd,
|
||||
repoDeleteCmd,
|
||||
)
|
||||
|
||||
rootCmd.AddCommand(repositoryCmd)
|
||||
|
||||
// Here you will define your flags and configuration settings.
|
||||
|
||||
// Cobra supports Persistent Flags which will work for this command
|
||||
// and all subcommands, e.g.:
|
||||
// repositoryCmd.PersistentFlags().String("foo", "", "A help for foo")
|
||||
|
||||
// Cobra supports local flags which will only run when this command
|
||||
// is called directly, e.g.:
|
||||
// repositoryCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
|
||||
}
|
||||
|
||||
func formatRepositories(repos []params.Repository) {
|
||||
t := table.NewWriter()
|
||||
header := table.Row{"ID", "Owner", "Name", "Credentials name"}
|
||||
t.AppendHeader(header)
|
||||
for _, val := range repos {
|
||||
t.AppendRow(table.Row{val.ID, val.Owner, val.Name, val.CredentialsName})
|
||||
t.AppendSeparator()
|
||||
}
|
||||
fmt.Println(t.Render())
|
||||
}
|
||||
|
||||
func formatOneRepository(repo params.Repository) {
|
||||
t := table.NewWriter()
|
||||
header := table.Row{"Field", "Value"}
|
||||
t.AppendHeader(header)
|
||||
t.AppendRow(table.Row{"ID", repo.ID})
|
||||
t.AppendRow(table.Row{"Owner", repo.Owner})
|
||||
t.AppendRow(table.Row{"Name", repo.Name})
|
||||
t.AppendRow(table.Row{"Credentials", repo.CredentialsName})
|
||||
|
||||
if len(repo.Pools) > 0 {
|
||||
for _, pool := range repo.Pools {
|
||||
t.AppendRow(table.Row{"Pools", pool.ID})
|
||||
}
|
||||
}
|
||||
fmt.Println(t.Render())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ Copyright © 2022 NAME HERE <EMAIL ADDRESS>
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"runner-manager/cmd/run-cli/client"
|
||||
"runner-manager/cmd/run-cli/config"
|
||||
|
|
@ -12,11 +13,15 @@ import (
|
|||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var cfg *config.Config
|
||||
var mgr config.Manager
|
||||
var configErr error
|
||||
var cli *client.Client
|
||||
var active string
|
||||
var (
|
||||
cfg *config.Config
|
||||
mgr config.Manager
|
||||
cli *client.Client
|
||||
active string
|
||||
needsInit bool
|
||||
debug bool
|
||||
needsInitError = fmt.Errorf("Please log into a runner-manager first")
|
||||
)
|
||||
|
||||
// rootCmd represents the base command when called without any subcommands
|
||||
var rootCmd = &cobra.Command{
|
||||
|
|
@ -28,6 +33,7 @@ var rootCmd = &cobra.Command{
|
|||
// Execute adds all child commands to the root command and sets flags appropriately.
|
||||
// This is called by main.main(). It only needs to happen once to the rootCmd.
|
||||
func Execute() {
|
||||
rootCmd.PersistentFlags().BoolVar(&debug, "debug", false, "Enable debug on all API calls")
|
||||
cobra.OnInitialize(initConfig)
|
||||
|
||||
err := rootCmd.Execute()
|
||||
|
|
@ -37,9 +43,21 @@ func Execute() {
|
|||
}
|
||||
|
||||
func initConfig() {
|
||||
cfg, configErr = config.LoadConfig()
|
||||
if configErr == nil {
|
||||
mgr, _ = cfg.GetActiveConfig()
|
||||
var err error
|
||||
cfg, err = config.LoadConfig()
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to load config: %s", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
cli = client.NewClient(active, mgr)
|
||||
if len(cfg.Managers) == 0 {
|
||||
// config is empty.
|
||||
needsInit = true
|
||||
} else {
|
||||
mgr, err = cfg.GetActiveConfig()
|
||||
if err != nil {
|
||||
mgr = cfg.Managers[0]
|
||||
}
|
||||
active = mgr.Name
|
||||
}
|
||||
cli = client.NewClient(active, mgr, debug)
|
||||
}
|
||||
|
|
|
|||
53
cmd/run-cli/common/common.go
Normal file
53
cmd/run-cli/common/common.go
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/manifoldco/promptui"
|
||||
"github.com/nbutton23/zxcvbn-go"
|
||||
)
|
||||
|
||||
func PromptPassword(label string) (string, error) {
|
||||
if label == "" {
|
||||
label = "Password"
|
||||
}
|
||||
validate := func(input string) error {
|
||||
passwordStenght := zxcvbn.PasswordStrength(input, nil)
|
||||
if passwordStenght.Score < 4 {
|
||||
return errors.New("password is too weak")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
prompt := promptui.Prompt{
|
||||
Label: label,
|
||||
Validate: validate,
|
||||
Mask: '*',
|
||||
}
|
||||
result, err := prompt.Run()
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func PromptString(label string) (string, error) {
|
||||
validate := func(input string) error {
|
||||
if len(input) == 0 {
|
||||
return errors.New("empty input not allowed")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
prompt := promptui.Prompt{
|
||||
Label: label,
|
||||
Validate: validate,
|
||||
}
|
||||
result, err := prompt.Run()
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
|
@ -35,6 +35,14 @@ func LoadConfig() (*Config, error) {
|
|||
return nil, errors.Wrap(err, "fetching config")
|
||||
}
|
||||
|
||||
if _, err := os.Stat(cfgFile); err != nil {
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
// return empty config
|
||||
return &Config{}, nil
|
||||
}
|
||||
return nil, errors.Wrap(err, "accessing config file")
|
||||
}
|
||||
|
||||
var config Config
|
||||
if _, err := toml.DecodeFile(cfgFile, &config); err != nil {
|
||||
return nil, errors.Wrap(err, "decoding toml")
|
||||
|
|
|
|||
|
|
@ -4,16 +4,17 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func getHomeDir() (string, error) {
|
||||
home := os.Getenv("HOME")
|
||||
home, err := os.UserHomeDir()
|
||||
|
||||
if home == "" {
|
||||
return "", fmt.Errorf("failed to get home folder")
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "fetching home dir")
|
||||
}
|
||||
|
||||
return filepath.Join(home, ".local", "share", DefaultAppFolder), nil
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue