diff --git a/Gopkg.lock b/Gopkg.lock index 5784ab4..b833823 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -84,7 +84,7 @@ "plugin/logger", "plugin/secret" ] - revision = "d00b5c24549e5676acefedc4d5ef115471bf48eb" + revision = "a02047a0b54dae0f23b511367c12c528606b599e" [[projects]] branch = "master" diff --git a/drone/encrypt/encrypt.go b/drone/encrypt/encrypt.go index 3f65951..0ee624c 100644 --- a/drone/encrypt/encrypt.go +++ b/drone/encrypt/encrypt.go @@ -20,7 +20,11 @@ var Command = cli.Command{ Flags: []cli.Flag{ cli.BoolFlag{ Name: "allow-pull-request", - Usage: "permit access to pull requests", + Usage: "permit read access to pull requests", + }, + cli.BoolFlag{ + Name: "allow-push-on-pull-request", + Usage: "permit write access to pull requests (e.g. allow docker push)", }, }, } @@ -47,8 +51,9 @@ func encryptSecret(c *cli.Context) error { } secret := &drone.Secret{ - Data: plaintext, - Pull: c.Bool("allow-pull-request"), + Data: plaintext, + PullRequest: c.Bool("allow-pull-request"), + PullRequestPush: c.Bool("allow-push-on-pull-request"), } encrypted, err := client.Encrypt(owner, name, secret) if err != nil { diff --git a/drone/main.go b/drone/main.go index 120dc3d..8fd970c 100644 --- a/drone/main.go +++ b/drone/main.go @@ -15,6 +15,7 @@ import ( "github.com/drone/drone-cli/drone/log" "github.com/drone/drone-cli/drone/plugins" "github.com/drone/drone-cli/drone/repo" + "github.com/drone/drone-cli/drone/secret" "github.com/drone/drone-cli/drone/server" "github.com/drone/drone-cli/drone/sign" "github.com/drone/drone-cli/drone/user" @@ -77,6 +78,7 @@ func main() { info.Command, repo.Command, user.Command, + secret.Command, server.Command, autoscale.Command, format.Command, diff --git a/drone/secret/secret.go b/drone/secret/secret.go new file mode 100644 index 0000000..fc1ee23 --- /dev/null +++ b/drone/secret/secret.go @@ -0,0 +1,16 @@ +package secret + +import "github.com/urfave/cli" + +// Command exports the secret command. +var Command = cli.Command{ + Name: "secret", + Usage: "manage secrets", + Subcommands: []cli.Command{ + secretCreateCmd, + secretDeleteCmd, + secretUpdateCmd, + secretInfoCmd, + secretListCmd, + }, +} diff --git a/drone/secret/secret_add.go b/drone/secret/secret_add.go new file mode 100644 index 0000000..f538889 --- /dev/null +++ b/drone/secret/secret_add.go @@ -0,0 +1,72 @@ +package secret + +import ( + "io/ioutil" + "strings" + + "github.com/drone/drone-cli/drone/internal" + "github.com/drone/drone-go/drone" + + "github.com/urfave/cli" +) + +var secretCreateCmd = cli.Command{ + Name: "add", + Usage: "adds a secret", + ArgsUsage: "[repo/name]", + Action: secretCreate, + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "repository", + Usage: "repository name (e.g. octocat/hello-world)", + }, + cli.StringFlag{ + Name: "name", + Usage: "secret name", + }, + cli.StringFlag{ + Name: "data", + Usage: "secret value", + }, + cli.BoolFlag{ + Name: "allow-pull-request", + Usage: "permit read access to pull requests", + }, + cli.BoolFlag{ + Name: "allow-push-on-pull-request", + Usage: "permit write access to pull requests (e.g. allow docker push)", + }, + }, +} + +func secretCreate(c *cli.Context) error { + reponame := c.String("repository") + if reponame == "" { + reponame = c.Args().First() + } + owner, name, err := internal.ParseRepo(reponame) + if err != nil { + return err + } + client, err := internal.NewClient(c) + if err != nil { + return err + } + secret := &drone.Secret{ + Name: c.String("name"), + Data: c.String("data"), + PullRequest: c.Bool("allow-pull-request"), + PullRequestPush: c.Bool("allow-push-on-pull-request"), + } + + if strings.HasPrefix(secret.Data, "@") { + path := strings.TrimPrefix(secret.Data, "@") + out, ferr := ioutil.ReadFile(path) + if ferr != nil { + return ferr + } + secret.Data = string(out) + } + _, err = client.SecretCreate(owner, name, secret) + return err +} diff --git a/drone/secret/secret_info.go b/drone/secret/secret_info.go new file mode 100644 index 0000000..1caef17 --- /dev/null +++ b/drone/secret/secret_info.go @@ -0,0 +1,61 @@ +package secret + +import ( + "html/template" + "os" + + "github.com/urfave/cli" + + "github.com/drone/drone-cli/drone/internal" +) + +var secretInfoCmd = cli.Command{ + Name: "info", + Usage: "display secret info", + ArgsUsage: "[repo/name]", + Action: secretInfo, + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "repository", + Usage: "repository name (e.g. octocat/hello-world)", + }, + cli.StringFlag{ + Name: "name", + Usage: "secret name", + }, + cli.StringFlag{ + Name: "format", + Usage: "format output", + Value: tmplSecretList, + Hidden: true, + }, + }, +} + +func secretInfo(c *cli.Context) error { + var ( + secretName = c.String("name") + repoName = c.String("repository") + format = c.String("format") + "\n" + ) + if repoName == "" { + repoName = c.Args().First() + } + owner, name, err := internal.ParseRepo(repoName) + if err != nil { + return err + } + client, err := internal.NewClient(c) + if err != nil { + return err + } + secret, err := client.Secret(owner, name, secretName) + if err != nil { + return err + } + tmpl, err := template.New("_").Parse(format) + if err != nil { + return err + } + return tmpl.Execute(os.Stdout, secret) +} diff --git a/drone/secret/secret_list.go b/drone/secret/secret_list.go new file mode 100644 index 0000000..9137a79 --- /dev/null +++ b/drone/secret/secret_list.go @@ -0,0 +1,65 @@ +package secret + +import ( + "html/template" + "os" + + "github.com/urfave/cli" + + "github.com/drone/drone-cli/drone/internal" +) + +var secretListCmd = cli.Command{ + Name: "ls", + Usage: "list secrets", + ArgsUsage: "[repo/name]", + Action: secretList, + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "repository", + Usage: "repository name (e.g. octocat/hello-world)", + }, + cli.StringFlag{ + Name: "format", + Usage: "format output", + Value: tmplSecretList, + Hidden: true, + }, + }, +} + +func secretList(c *cli.Context) error { + var ( + format = c.String("format") + "\n" + reponame = c.String("repository") + ) + if reponame == "" { + reponame = c.Args().First() + } + owner, name, err := internal.ParseRepo(reponame) + if err != nil { + return err + } + client, err := internal.NewClient(c) + if err != nil { + return err + } + list, err := client.SecretList(owner, name) + if err != nil { + return err + } + tmpl, err := template.New("_").Parse(format) + if err != nil { + return err + } + for _, secret := range list { + tmpl.Execute(os.Stdout, secret) + } + return nil +} + +// template for secret list items +var tmplSecretList = "\x1b[33m{{ .Name }} \x1b[0m" + ` +Pull Request Read: {{ .PullRequest }} +Pull Request Write: {{ .PullRequestPush }} +` diff --git a/drone/secret/secret_rm.go b/drone/secret/secret_rm.go new file mode 100644 index 0000000..338fce4 --- /dev/null +++ b/drone/secret/secret_rm.go @@ -0,0 +1,43 @@ +package secret + +import ( + "github.com/urfave/cli" + + "github.com/drone/drone-cli/drone/internal" +) + +var secretDeleteCmd = cli.Command{ + Name: "rm", + Usage: "remove a secret", + ArgsUsage: "[repo/name]", + Action: secretDelete, + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "repository", + Usage: "repository name (e.g. octocat/hello-world)", + }, + cli.StringFlag{ + Name: "name", + Usage: "secret name", + }, + }, +} + +func secretDelete(c *cli.Context) error { + var ( + secret = c.String("name") + reponame = c.String("repository") + ) + if reponame == "" { + reponame = c.Args().First() + } + owner, name, err := internal.ParseRepo(reponame) + if err != nil { + return err + } + client, err := internal.NewClient(c) + if err != nil { + return err + } + return client.SecretDelete(owner, name, secret) +} diff --git a/drone/secret/secret_set.go b/drone/secret/secret_set.go new file mode 100644 index 0000000..14c16b0 --- /dev/null +++ b/drone/secret/secret_set.go @@ -0,0 +1,71 @@ +package secret + +import ( + "io/ioutil" + "strings" + + "github.com/drone/drone-cli/drone/internal" + "github.com/drone/drone-go/drone" + + "github.com/urfave/cli" +) + +var secretUpdateCmd = cli.Command{ + Name: "update", + Usage: "update a secret", + ArgsUsage: "[repo/name]", + Action: secretUpdate, + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "repository", + Usage: "repository name (e.g. octocat/hello-world)", + }, + cli.StringFlag{ + Name: "name", + Usage: "secret name", + }, + cli.StringFlag{ + Name: "data", + Usage: "secret value", + }, + cli.BoolFlag{ + Name: "allow-pull-request", + Usage: "permit read access to pull requests", + }, + cli.BoolFlag{ + Name: "allow-push-on-pull-request", + Usage: "permit write access to pull requests (e.g. allow docker push)", + }, + }, +} + +func secretUpdate(c *cli.Context) error { + reponame := c.String("repository") + if reponame == "" { + reponame = c.Args().First() + } + owner, name, err := internal.ParseRepo(reponame) + if err != nil { + return err + } + client, err := internal.NewClient(c) + if err != nil { + return err + } + secret := &drone.Secret{ + Name: c.String("name"), + Data: c.String("data"), + PullRequest: c.Bool("allow-pull-request"), + PullRequestPush: c.Bool("allow-push-on-pull-request"), + } + if strings.HasPrefix(secret.Data, "@") { + path := strings.TrimPrefix(secret.Data, "@") + out, ferr := ioutil.ReadFile(path) + if ferr != nil { + return ferr + } + secret.Data = string(out) + } + _, err = client.SecretUpdate(owner, name, secret) + return err +} diff --git a/vendor/github.com/drone/drone-go/drone/client.go b/vendor/github.com/drone/drone-go/drone/client.go index 517d2d0..7f1033d 100644 --- a/vendor/github.com/drone/drone-go/drone/client.go +++ b/vendor/github.com/drone/drone-go/drone/client.go @@ -333,6 +333,44 @@ func (c *client) Encrypt(owner, name string, secret *Secret) (string, error) { return out.Data, err } +// Secret returns a secret by name. +func (c *client) Secret(owner, name, secret string) (*Secret, error) { + out := new(Secret) + uri := fmt.Sprintf(pathRepoSecret, c.addr, owner, name, secret) + err := c.get(uri, out) + return out, err +} + +// SecretList returns a list of all repository secrets. +func (c *client) SecretList(owner string, name string) ([]*Secret, error) { + var out []*Secret + uri := fmt.Sprintf(pathRepoSecrets, c.addr, owner, name) + err := c.get(uri, &out) + return out, err +} + +// SecretCreate creates a secret. +func (c *client) SecretCreate(owner, name string, in *Secret) (*Secret, error) { + out := new(Secret) + uri := fmt.Sprintf(pathRepoSecrets, c.addr, owner, name) + err := c.post(uri, in, out) + return out, err +} + +// SecretUpdate updates a secret. +func (c *client) SecretUpdate(owner, name string, in *Secret) (*Secret, error) { + out := new(Secret) + uri := fmt.Sprintf(pathRepoSecret, c.addr, owner, name, in.Name) + err := c.patch(uri, in, out) + return out, err +} + +// SecretDelete deletes a secret. +func (c *client) SecretDelete(owner, name, secret string) error { + uri := fmt.Sprintf(pathRepoSecret, c.addr, owner, name, secret) + return c.delete(uri) +} + // Cron returns a cronjob by name. func (c *client) Cron(owner, name, cron string) (*Cron, error) { out := new(Cron) @@ -349,6 +387,20 @@ func (c *client) CronList(owner string, name string) ([]*Cron, error) { return out, err } +// CronCreate creates a cronjob. +func (c *client) CronCreate(owner, name string, in *Cron) (*Cron, error) { + out := new(Cron) + uri := fmt.Sprintf(pathCrons, c.addr, owner, name) + err := c.post(uri, in, out) + return out, err +} + +// CronDelete deletes a cronjob. +func (c *client) CronDelete(owner, name, cron string) error { + uri := fmt.Sprintf(pathCron, c.addr, owner, name, cron) + return c.delete(uri) +} + // CronEnable ensables a cronjob. func (c *client) CronEnable(owner, name, cron string) error { uri := fmt.Sprintf(pathCron, c.addr, owner, name, cron) diff --git a/vendor/github.com/drone/drone-go/drone/interface.go b/vendor/github.com/drone/drone-go/drone/interface.go index 04fe652..ab900ce 100644 --- a/vendor/github.com/drone/drone-go/drone/interface.go +++ b/vendor/github.com/drone/drone-go/drone/interface.go @@ -114,12 +114,33 @@ type Client interface { // LogsPurge purges the build logs for the specified step. LogsPurge(owner, name string, build, stage, step int) error + // Secret returns a secret by name. + Secret(owner, name, secret string) (*Secret, error) + + // SecretList returns a list of all repository secrets. + SecretList(owner, name string) ([]*Secret, error) + + // SecretCreate creates a registry. + SecretCreate(owner, name string, secret *Secret) (*Secret, error) + + // SecretUpdate updates a registry. + SecretUpdate(owner, name string, secret *Secret) (*Secret, error) + + // SecretDelete deletes a secret. + SecretDelete(owner, name, secret string) error + // Cron returns a cronjob by name. Cron(owner, name, cron string) (*Cron, error) // CronList returns a list of all repository cronjobs. CronList(owner string, name string) ([]*Cron, error) + // CronCreate creates a cronjob. + CronCreate(owner, name string, in *Cron) (*Cron, error) + + // CronDelete deletes a cronjob. + CronDelete(owner, name, cron string) error + // CronEnable enables a cronjob. CronEnable(owner, name, cron string) error diff --git a/vendor/github.com/drone/drone-go/drone/types.go b/vendor/github.com/drone/drone-go/drone/types.go index 79fb65c..6e35c1d 100644 --- a/vendor/github.com/drone/drone-go/drone/types.go +++ b/vendor/github.com/drone/drone-go/drone/types.go @@ -161,10 +161,14 @@ type ( // Secret represents a secret variable, such as a password or token. Secret struct { - Name string `json:"name,omitempty"` - Data string `json:"data,omitempty"` - Pull bool `json:"pull,omitempty"` - Fork bool `json:"fork,omitempty"` + Name string `json:"name,omitempty"` + Data string `json:"data,omitempty"` + PullRequest bool `json:"pull_request,omitempty"` + PullRequestPush bool `json:"pull_request_push,omitempty"` + + // Deprecated. + Pull bool `json:"pull,omitempty"` + Fork bool `json:"fork,omitempty"` } // Server represents a server node.