diff --git a/cli/admin/admin.go b/cli/admin/admin.go index 351efc0d5..f96c5fd0b 100644 --- a/cli/admin/admin.go +++ b/cli/admin/admin.go @@ -17,14 +17,20 @@ package admin import ( "github.com/urfave/cli/v3" + "go.woodpecker-ci.org/woodpecker/v2/cli/admin/loglevel" "go.woodpecker-ci.org/woodpecker/v2/cli/admin/registry" + "go.woodpecker-ci.org/woodpecker/v2/cli/admin/secret" + "go.woodpecker-ci.org/woodpecker/v2/cli/admin/user" ) // Command exports the admin command set. var Command = &cli.Command{ Name: "admin", - Usage: "administer server settings", + Usage: "manage server settings", Commands: []*cli.Command{ + secret.Command, registry.Command, + user.Command, + loglevel.Command, }, } diff --git a/cli/loglevel/loglevel.go b/cli/admin/loglevel/loglevel.go similarity index 100% rename from cli/loglevel/loglevel.go rename to cli/admin/loglevel/loglevel.go diff --git a/cli/admin/secret/secret.go b/cli/admin/secret/secret.go new file mode 100644 index 000000000..ffd466f94 --- /dev/null +++ b/cli/admin/secret/secret.go @@ -0,0 +1,32 @@ +// Copyright 2023 Woodpecker Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package secret + +import ( + "github.com/urfave/cli/v3" +) + +// Command exports the secret command. +var Command = &cli.Command{ + Name: "secret", + Usage: "manage global secrets", + Commands: []*cli.Command{ + secretCreateCmd, + secretDeleteCmd, + secretUpdateCmd, + secretInfoCmd, + secretListCmd, + }, +} diff --git a/cli/admin/secret/secret_add.go b/cli/admin/secret/secret_add.go new file mode 100644 index 000000000..f2cf88c8c --- /dev/null +++ b/cli/admin/secret/secret_add.go @@ -0,0 +1,82 @@ +// Copyright 2023 Woodpecker Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package secret + +import ( + "context" + "os" + "strings" + + "github.com/urfave/cli/v3" + + "go.woodpecker-ci.org/woodpecker/v2/cli/internal" + "go.woodpecker-ci.org/woodpecker/v2/woodpecker-go/woodpecker" +) + +var secretCreateCmd = &cli.Command{ + Name: "add", + Usage: "adds a secret", + ArgsUsage: "[repo-id|repo-full-name]", + Action: secretCreate, + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "value", + Usage: "secret value", + }, + &cli.StringSliceFlag{ + Name: "event", + Usage: "secret limited to these events", + }, + &cli.StringSliceFlag{ + Name: "image", + Usage: "secret limited to these images", + }, + }, +} + +func secretCreate(ctx context.Context, c *cli.Command) error { + client, err := internal.NewClient(ctx, c) + if err != nil { + return err + } + + secret := &woodpecker.Secret{ + Name: strings.ToLower(c.String("name")), + Value: c.String("value"), + Images: c.StringSlice("image"), + Events: c.StringSlice("event"), + } + if len(secret.Events) == 0 { + secret.Events = defaultSecretEvents + } + if strings.HasPrefix(secret.Value, "@") { + path := strings.TrimPrefix(secret.Value, "@") + out, err := os.ReadFile(path) + if err != nil { + return err + } + secret.Value = string(out) + } + + _, err = client.GlobalSecretCreate(secret) + return err +} + +var defaultSecretEvents = []string{ + woodpecker.EventPush, + woodpecker.EventTag, + woodpecker.EventRelease, + woodpecker.EventDeploy, +} diff --git a/cli/admin/secret/secret_info.go b/cli/admin/secret/secret_info.go new file mode 100644 index 000000000..7efdacc25 --- /dev/null +++ b/cli/admin/secret/secret_info.go @@ -0,0 +1,68 @@ +// Copyright 2023 Woodpecker Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package secret + +import ( + "context" + "fmt" + "html/template" + "os" + + "github.com/urfave/cli/v3" + + "go.woodpecker-ci.org/woodpecker/v2/cli/common" + "go.woodpecker-ci.org/woodpecker/v2/cli/internal" +) + +var secretInfoCmd = &cli.Command{ + Name: "info", + Usage: "display secret info", + ArgsUsage: "[repo-id|repo-full-name]", + Action: secretInfo, + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "name", + Usage: "secret name", + }, + common.FormatFlag(tmplSecretList, true), + }, +} + +func secretInfo(ctx context.Context, c *cli.Command) error { + var ( + secretName = c.String("name") + format = c.String("format") + "\n" + ) + + if secretName == "" { + return fmt.Errorf("secret name is missing") + } + + client, err := internal.NewClient(ctx, c) + if err != nil { + return err + } + + secret, err := client.GlobalSecret(secretName) + if err != nil { + return err + } + + tmpl, err := template.New("_").Funcs(secretFuncMap).Parse(format) + if err != nil { + return err + } + return tmpl.Execute(os.Stdout, secret) +} diff --git a/cli/secret/secret_list.go b/cli/admin/secret/secret_list.go similarity index 79% rename from cli/secret/secret_list.go rename to cli/admin/secret/secret_list.go index 7c971631d..d8d7374f0 100644 --- a/cli/secret/secret_list.go +++ b/cli/admin/secret/secret_list.go @@ -33,12 +33,6 @@ var secretListCmd = &cli.Command{ ArgsUsage: "[repo-id|repo-full-name]", Action: secretList, Flags: []cli.Flag{ - &cli.BoolFlag{ - Name: "global", - Usage: "global secret", - }, - common.OrgFlag, - common.RepoFlag, common.FormatFlag(tmplSecretList, true), }, } @@ -51,30 +45,11 @@ func secretList(ctx context.Context, c *cli.Command) error { return err } - global, orgID, repoID, err := parseTargetArgs(client, c) - if err != nil { - return err - } - opt := woodpecker.SecretListOptions{} - var list []*woodpecker.Secret - switch { - case global: - list, err = client.GlobalSecretList(opt) - if err != nil { - return err - } - case orgID != -1: - list, err = client.OrgSecretList(orgID, opt) - if err != nil { - return err - } - default: - list, err = client.SecretList(repoID, opt) - if err != nil { - return err - } + list, err := client.GlobalSecretList(opt) + if err != nil { + return err } tmpl, err := template.New("_").Funcs(secretFuncMap).Parse(format) diff --git a/cli/admin/secret/secret_rm.go b/cli/admin/secret/secret_rm.go new file mode 100644 index 000000000..f919d6b9d --- /dev/null +++ b/cli/admin/secret/secret_rm.go @@ -0,0 +1,47 @@ +// Copyright 2023 Woodpecker Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package secret + +import ( + "context" + + "github.com/urfave/cli/v3" + + "go.woodpecker-ci.org/woodpecker/v2/cli/internal" +) + +var secretDeleteCmd = &cli.Command{ + Name: "rm", + Usage: "remove a secret", + ArgsUsage: "[repo-id|repo-full-name]", + Action: secretDelete, + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "name", + Usage: "secret name", + }, + }, +} + +func secretDelete(ctx context.Context, c *cli.Command) error { + secretName := c.String("name") + + client, err := internal.NewClient(ctx, c) + if err != nil { + return err + } + + return client.GlobalSecretDelete(secretName) +} diff --git a/cli/admin/secret/secret_set.go b/cli/admin/secret/secret_set.go new file mode 100644 index 000000000..452826115 --- /dev/null +++ b/cli/admin/secret/secret_set.go @@ -0,0 +1,76 @@ +// Copyright 2023 Woodpecker Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package secret + +import ( + "context" + "os" + "strings" + + "github.com/urfave/cli/v3" + + "go.woodpecker-ci.org/woodpecker/v2/cli/internal" + "go.woodpecker-ci.org/woodpecker/v2/woodpecker-go/woodpecker" +) + +var secretUpdateCmd = &cli.Command{ + Name: "update", + Usage: "update a secret", + ArgsUsage: "[repo-id|repo-full-name]", + Action: secretUpdate, + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "name", + Usage: "secret name", + }, + &cli.StringFlag{ + Name: "value", + Usage: "secret value", + }, + &cli.StringSliceFlag{ + Name: "event", + Usage: "secret limited to these events", + }, + &cli.StringSliceFlag{ + Name: "image", + Usage: "secret limited to these images", + }, + }, +} + +func secretUpdate(ctx context.Context, c *cli.Command) error { + client, err := internal.NewClient(ctx, c) + if err != nil { + return err + } + + secret := &woodpecker.Secret{ + Name: strings.ToLower(c.String("name")), + Value: c.String("value"), + Images: c.StringSlice("image"), + Events: c.StringSlice("event"), + } + if strings.HasPrefix(secret.Value, "@") { + path := strings.TrimPrefix(secret.Value, "@") + out, err := os.ReadFile(path) + if err != nil { + return err + } + secret.Value = string(out) + } + + _, err = client.GlobalSecretUpdate(secret) + return err +} diff --git a/cli/user/user.go b/cli/admin/user/user.go similarity index 100% rename from cli/user/user.go rename to cli/admin/user/user.go diff --git a/cli/user/user_add.go b/cli/admin/user/user_add.go similarity index 100% rename from cli/user/user_add.go rename to cli/admin/user/user_add.go diff --git a/cli/user/user_info.go b/cli/admin/user/user_info.go similarity index 100% rename from cli/user/user_info.go rename to cli/admin/user/user_info.go diff --git a/cli/user/user_list.go b/cli/admin/user/user_list.go similarity index 100% rename from cli/user/user_list.go rename to cli/admin/user/user_list.go diff --git a/cli/user/user_rm.go b/cli/admin/user/user_rm.go similarity index 100% rename from cli/user/user_rm.go rename to cli/admin/user/user_rm.go diff --git a/cli/org/org.go b/cli/org/org.go index c7b9cd840..2b3aacf28 100644 --- a/cli/org/org.go +++ b/cli/org/org.go @@ -18,6 +18,7 @@ import ( "github.com/urfave/cli/v3" "go.woodpecker-ci.org/woodpecker/v2/cli/org/registry" + "go.woodpecker-ci.org/woodpecker/v2/cli/org/secret" ) // Command exports the org command set. @@ -26,5 +27,6 @@ var Command = &cli.Command{ Usage: "manage organizations", Commands: []*cli.Command{ registry.Command, + secret.Command, }, } diff --git a/cli/org/secret/secret.go b/cli/org/secret/secret.go new file mode 100644 index 000000000..11206be53 --- /dev/null +++ b/cli/org/secret/secret.go @@ -0,0 +1,60 @@ +// Copyright 2023 Woodpecker Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package secret + +import ( + "strconv" + + "github.com/urfave/cli/v3" + + "go.woodpecker-ci.org/woodpecker/v2/woodpecker-go/woodpecker" +) + +// Command exports the secret command. +var Command = &cli.Command{ + Name: "secret", + Usage: "manage secrets", + Commands: []*cli.Command{ + secretCreateCmd, + secretDeleteCmd, + secretUpdateCmd, + secretInfoCmd, + secretListCmd, + }, +} + +func parseTargetArgs(client woodpecker.Client, c *cli.Command) (orgID int64, err error) { + orgIDOrName := c.String("organization") + if orgIDOrName == "" { + orgIDOrName = c.Args().First() + } + + if orgIDOrName == "" { + if err := cli.ShowSubcommandHelp(c); err != nil { + return -1, err + } + } + + if orgID, err := strconv.ParseInt(orgIDOrName, 10, 64); err == nil { + return orgID, nil + } + + org, err := client.OrgLookup(orgIDOrName) + if err != nil { + return -1, err + } + + return org.ID, nil +} diff --git a/cli/org/secret/secret_add.go b/cli/org/secret/secret_add.go new file mode 100644 index 000000000..97b901cdd --- /dev/null +++ b/cli/org/secret/secret_add.go @@ -0,0 +1,93 @@ +// Copyright 2023 Woodpecker Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package secret + +import ( + "context" + "os" + "strings" + + "github.com/urfave/cli/v3" + + "go.woodpecker-ci.org/woodpecker/v2/cli/common" + "go.woodpecker-ci.org/woodpecker/v2/cli/internal" + "go.woodpecker-ci.org/woodpecker/v2/woodpecker-go/woodpecker" +) + +var secretCreateCmd = &cli.Command{ + Name: "add", + Usage: "adds a secret", + ArgsUsage: "[repo-id|repo-full-name]", + Action: secretCreate, + Flags: []cli.Flag{ + common.OrgFlag, + &cli.StringFlag{ + Name: "name", + Usage: "secret name", + }, + &cli.StringFlag{ + Name: "value", + Usage: "secret value", + }, + &cli.StringSliceFlag{ + Name: "event", + Usage: "secret limited to these events", + }, + &cli.StringSliceFlag{ + Name: "image", + Usage: "secret limited to these images", + }, + }, +} + +func secretCreate(ctx context.Context, c *cli.Command) error { + client, err := internal.NewClient(ctx, c) + if err != nil { + return err + } + + secret := &woodpecker.Secret{ + Name: strings.ToLower(c.String("name")), + Value: c.String("value"), + Images: c.StringSlice("image"), + Events: c.StringSlice("event"), + } + if len(secret.Events) == 0 { + secret.Events = defaultSecretEvents + } + if strings.HasPrefix(secret.Value, "@") { + path := strings.TrimPrefix(secret.Value, "@") + out, err := os.ReadFile(path) + if err != nil { + return err + } + secret.Value = string(out) + } + + orgID, err := parseTargetArgs(client, c) + if err != nil { + return err + } + + _, err = client.OrgSecretCreate(orgID, secret) + return err +} + +var defaultSecretEvents = []string{ + woodpecker.EventPush, + woodpecker.EventTag, + woodpecker.EventRelease, + woodpecker.EventDeploy, +} diff --git a/cli/org/secret/secret_info.go b/cli/org/secret/secret_info.go new file mode 100644 index 000000000..8025a977f --- /dev/null +++ b/cli/org/secret/secret_info.go @@ -0,0 +1,74 @@ +// Copyright 2023 Woodpecker Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package secret + +import ( + "context" + "fmt" + "html/template" + "os" + + "github.com/urfave/cli/v3" + + "go.woodpecker-ci.org/woodpecker/v2/cli/common" + "go.woodpecker-ci.org/woodpecker/v2/cli/internal" +) + +var secretInfoCmd = &cli.Command{ + Name: "info", + Usage: "display secret info", + ArgsUsage: "[repo-id|repo-full-name]", + Action: secretInfo, + Flags: []cli.Flag{ + common.OrgFlag, + &cli.StringFlag{ + Name: "name", + Usage: "secret name", + }, + common.FormatFlag(tmplSecretList, true), + }, +} + +func secretInfo(ctx context.Context, c *cli.Command) error { + var ( + secretName = c.String("name") + format = c.String("format") + "\n" + ) + + if secretName == "" { + return fmt.Errorf("secret name is missing") + } + + client, err := internal.NewClient(ctx, c) + if err != nil { + return err + } + + orgID, err := parseTargetArgs(client, c) + if err != nil { + return err + } + + secret, err := client.OrgSecret(orgID, secretName) + if err != nil { + return err + } + + tmpl, err := template.New("_").Funcs(secretFuncMap).Parse(format) + if err != nil { + return err + } + return tmpl.Execute(os.Stdout, secret) +} diff --git a/cli/org/secret/secret_list.go b/cli/org/secret/secret_list.go new file mode 100644 index 000000000..f33aaaa8a --- /dev/null +++ b/cli/org/secret/secret_list.go @@ -0,0 +1,87 @@ +// Copyright 2023 Woodpecker Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package secret + +import ( + "context" + "html/template" + "os" + "strings" + + "github.com/urfave/cli/v3" + + "go.woodpecker-ci.org/woodpecker/v2/cli/common" + "go.woodpecker-ci.org/woodpecker/v2/cli/internal" + "go.woodpecker-ci.org/woodpecker/v2/woodpecker-go/woodpecker" +) + +var secretListCmd = &cli.Command{ + Name: "ls", + Usage: "list secrets", + ArgsUsage: "[repo-id|repo-full-name]", + Action: secretList, + Flags: []cli.Flag{ + common.OrgFlag, + common.FormatFlag(tmplSecretList, true), + }, +} + +func secretList(ctx context.Context, c *cli.Command) error { + format := c.String("format") + "\n" + + client, err := internal.NewClient(ctx, c) + if err != nil { + return err + } + + orgID, err := parseTargetArgs(client, c) + if err != nil { + return err + } + + opt := woodpecker.SecretListOptions{} + + list, err := client.OrgSecretList(orgID, opt) + if err != nil { + return err + } + + tmpl, err := template.New("_").Funcs(secretFuncMap).Parse(format) + if err != nil { + return err + } + for _, secret := range list { + if err := tmpl.Execute(os.Stdout, secret); err != nil { + return err + } + } + return nil +} + +// Template for secret list items. +var tmplSecretList = "\x1b[33m{{ .Name }} \x1b[0m" + ` +Events: {{ list .Events }} +{{- if .Images }} +Images: {{ list .Images }} +{{- else }} +Images: +{{- end }} +` + +var secretFuncMap = template.FuncMap{ + "list": func(s []string) string { + return strings.Join(s, ", ") + }, +} diff --git a/cli/org/secret/secret_rm.go b/cli/org/secret/secret_rm.go new file mode 100644 index 000000000..0b0f4db39 --- /dev/null +++ b/cli/org/secret/secret_rm.go @@ -0,0 +1,54 @@ +// Copyright 2023 Woodpecker Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package secret + +import ( + "context" + + "github.com/urfave/cli/v3" + + "go.woodpecker-ci.org/woodpecker/v2/cli/common" + "go.woodpecker-ci.org/woodpecker/v2/cli/internal" +) + +var secretDeleteCmd = &cli.Command{ + Name: "rm", + Usage: "remove a secret", + ArgsUsage: "[repo-id|repo-full-name]", + Action: secretDelete, + Flags: []cli.Flag{ + common.OrgFlag, + &cli.StringFlag{ + Name: "name", + Usage: "secret name", + }, + }, +} + +func secretDelete(ctx context.Context, c *cli.Command) error { + secretName := c.String("name") + + client, err := internal.NewClient(ctx, c) + if err != nil { + return err + } + + orgID, err := parseTargetArgs(client, c) + if err != nil { + return err + } + + return client.OrgSecretDelete(orgID, secretName) +} diff --git a/cli/org/secret/secret_set.go b/cli/org/secret/secret_set.go new file mode 100644 index 000000000..2e25655aa --- /dev/null +++ b/cli/org/secret/secret_set.go @@ -0,0 +1,83 @@ +// Copyright 2023 Woodpecker Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package secret + +import ( + "context" + "os" + "strings" + + "github.com/urfave/cli/v3" + + "go.woodpecker-ci.org/woodpecker/v2/cli/common" + "go.woodpecker-ci.org/woodpecker/v2/cli/internal" + "go.woodpecker-ci.org/woodpecker/v2/woodpecker-go/woodpecker" +) + +var secretUpdateCmd = &cli.Command{ + Name: "update", + Usage: "update a secret", + ArgsUsage: "[repo-id|repo-full-name]", + Action: secretUpdate, + Flags: []cli.Flag{ + common.OrgFlag, + &cli.StringFlag{ + Name: "name", + Usage: "secret name", + }, + &cli.StringFlag{ + Name: "value", + Usage: "secret value", + }, + &cli.StringSliceFlag{ + Name: "event", + Usage: "secret limited to these events", + }, + &cli.StringSliceFlag{ + Name: "image", + Usage: "secret limited to these images", + }, + }, +} + +func secretUpdate(ctx context.Context, c *cli.Command) error { + client, err := internal.NewClient(ctx, c) + if err != nil { + return err + } + + secret := &woodpecker.Secret{ + Name: strings.ToLower(c.String("name")), + Value: c.String("value"), + Images: c.StringSlice("image"), + Events: c.StringSlice("event"), + } + if strings.HasPrefix(secret.Value, "@") { + path := strings.TrimPrefix(secret.Value, "@") + out, err := os.ReadFile(path) + if err != nil { + return err + } + secret.Value = string(out) + } + + orgID, err := parseTargetArgs(client, c) + if err != nil { + return err + } + + _, err = client.OrgSecretUpdate(orgID, secret) + return err +} diff --git a/cli/deploy/deploy.go b/cli/pipeline/deploy/deploy.go similarity index 100% rename from cli/deploy/deploy.go rename to cli/pipeline/deploy/deploy.go diff --git a/cli/log/log.go b/cli/pipeline/log/log.go similarity index 100% rename from cli/log/log.go rename to cli/pipeline/log/log.go diff --git a/cli/log/log_purge.go b/cli/pipeline/log/log_purge.go similarity index 100% rename from cli/log/log_purge.go rename to cli/pipeline/log/log_purge.go diff --git a/cli/pipeline/pipeline.go b/cli/pipeline/pipeline.go index 5ea2a56bd..c49107652 100644 --- a/cli/pipeline/pipeline.go +++ b/cli/pipeline/pipeline.go @@ -23,6 +23,8 @@ import ( "github.com/urfave/cli/v3" "go.woodpecker-ci.org/woodpecker/v2/cli/output" + "go.woodpecker-ci.org/woodpecker/v2/cli/pipeline/deploy" + "go.woodpecker-ci.org/woodpecker/v2/cli/pipeline/log" "go.woodpecker-ci.org/woodpecker/v2/woodpecker-go/woodpecker" ) @@ -43,6 +45,8 @@ var Command = &cli.Command{ pipelineKillCmd, pipelinePsCmd, pipelineCreateCmd, + log.Command, + deploy.Command, }, } diff --git a/cli/cron/cron.go b/cli/repo/cron/cron.go similarity index 100% rename from cli/cron/cron.go rename to cli/repo/cron/cron.go diff --git a/cli/cron/cron_add.go b/cli/repo/cron/cron_add.go similarity index 100% rename from cli/cron/cron_add.go rename to cli/repo/cron/cron_add.go diff --git a/cli/cron/cron_info.go b/cli/repo/cron/cron_info.go similarity index 100% rename from cli/cron/cron_info.go rename to cli/repo/cron/cron_info.go diff --git a/cli/cron/cron_list.go b/cli/repo/cron/cron_list.go similarity index 100% rename from cli/cron/cron_list.go rename to cli/repo/cron/cron_list.go diff --git a/cli/cron/cron_rm.go b/cli/repo/cron/cron_rm.go similarity index 100% rename from cli/cron/cron_rm.go rename to cli/repo/cron/cron_rm.go diff --git a/cli/cron/cron_update.go b/cli/repo/cron/cron_update.go similarity index 100% rename from cli/cron/cron_update.go rename to cli/repo/cron/cron_update.go diff --git a/cli/repo/repo.go b/cli/repo/repo.go index 1be57743e..6eeda1ddd 100644 --- a/cli/repo/repo.go +++ b/cli/repo/repo.go @@ -17,7 +17,9 @@ package repo import ( "github.com/urfave/cli/v3" + "go.woodpecker-ci.org/woodpecker/v2/cli/repo/cron" "go.woodpecker-ci.org/woodpecker/v2/cli/repo/registry" + "go.woodpecker-ci.org/woodpecker/v2/cli/repo/secret" ) // Command exports the repository command. @@ -34,5 +36,7 @@ var Command = &cli.Command{ repoChownCmd, repoSyncCmd, registry.Command, + secret.Command, + cron.Command, }, } diff --git a/cli/secret/secret.go b/cli/repo/secret/secret.go similarity index 56% rename from cli/secret/secret.go rename to cli/repo/secret/secret.go index e44149241..1ea67ea99 100644 --- a/cli/secret/secret.go +++ b/cli/repo/secret/secret.go @@ -15,10 +15,6 @@ package secret import ( - "fmt" - "strconv" - "strings" - "github.com/urfave/cli/v3" "go.woodpecker-ci.org/woodpecker/v2/cli/internal" @@ -38,46 +34,11 @@ var Command = &cli.Command{ }, } -func parseTargetArgs(client woodpecker.Client, c *cli.Command) (global bool, orgID, repoID int64, err error) { - if c.Bool("global") { - return true, -1, -1, nil - } - +func parseTargetArgs(client woodpecker.Client, c *cli.Command) (repoID int64, err error) { repoIDOrFullName := c.String("repository") if repoIDOrFullName == "" { repoIDOrFullName = c.Args().First() } - orgIDOrName := c.String("organization") - if orgIDOrName == "" && repoIDOrFullName == "" { - if err := cli.ShowSubcommandHelp(c); err != nil { - return false, -1, -1, err - } - - return false, -1, -1, fmt.Errorf("missing arguments") - } - - if orgIDOrName != "" && repoIDOrFullName == "" { - if orgID, err := strconv.ParseInt(orgIDOrName, 10, 64); err == nil { - return false, orgID, -1, nil - } - - org, err := client.OrgLookup(orgIDOrName) - if err != nil { - return false, -1, -1, err - } - - return false, org.ID, -1, nil - } - - if orgIDOrName != "" && !strings.Contains(repoIDOrFullName, "/") { - repoIDOrFullName = orgIDOrName + "/" + repoIDOrFullName - } - - repoID, err = internal.ParseRepo(client, repoIDOrFullName) - if err != nil { - return false, -1, -1, err - } - - return false, -1, repoID, nil + return internal.ParseRepo(client, repoIDOrFullName) } diff --git a/cli/secret/secret_add.go b/cli/repo/secret/secret_add.go similarity index 87% rename from cli/secret/secret_add.go rename to cli/repo/secret/secret_add.go index 452ddc46d..ee883c064 100644 --- a/cli/secret/secret_add.go +++ b/cli/repo/secret/secret_add.go @@ -32,11 +32,6 @@ var secretCreateCmd = &cli.Command{ ArgsUsage: "[repo-id|repo-full-name]", Action: secretCreate, Flags: []cli.Flag{ - &cli.BoolFlag{ - Name: "global", - Usage: "global secret", - }, - common.OrgFlag, common.RepoFlag, &cli.StringFlag{ Name: "name", @@ -81,21 +76,11 @@ func secretCreate(ctx context.Context, c *cli.Command) error { secret.Value = string(out) } - global, orgID, repoID, err := parseTargetArgs(client, c) + repoID, err := parseTargetArgs(client, c) if err != nil { return err } - if global { - _, err = client.GlobalSecretCreate(secret) - return err - } - - if orgID != -1 { - _, err = client.OrgSecretCreate(orgID, secret) - return err - } - _, err = client.SecretCreate(repoID, secret) return err } diff --git a/cli/secret/secret_info.go b/cli/repo/secret/secret_info.go similarity index 74% rename from cli/secret/secret_info.go rename to cli/repo/secret/secret_info.go index f645741b9..fcb30db83 100644 --- a/cli/secret/secret_info.go +++ b/cli/repo/secret/secret_info.go @@ -24,7 +24,6 @@ import ( "go.woodpecker-ci.org/woodpecker/v2/cli/common" "go.woodpecker-ci.org/woodpecker/v2/cli/internal" - "go.woodpecker-ci.org/woodpecker/v2/woodpecker-go/woodpecker" ) var secretInfoCmd = &cli.Command{ @@ -33,11 +32,6 @@ var secretInfoCmd = &cli.Command{ ArgsUsage: "[repo-id|repo-full-name]", Action: secretInfo, Flags: []cli.Flag{ - &cli.BoolFlag{ - Name: "global", - Usage: "global secret", - }, - common.OrgFlag, common.RepoFlag, &cli.StringFlag{ Name: "name", @@ -62,28 +56,14 @@ func secretInfo(ctx context.Context, c *cli.Command) error { return err } - global, orgID, repoID, err := parseTargetArgs(client, c) + repoID, err := parseTargetArgs(client, c) if err != nil { return err } - var secret *woodpecker.Secret - switch { - case global: - secret, err = client.GlobalSecret(secretName) - if err != nil { - return err - } - case orgID != -1: - secret, err = client.OrgSecret(orgID, secretName) - if err != nil { - return err - } - default: - secret, err = client.Secret(repoID, secretName) - if err != nil { - return err - } + secret, err := client.Secret(repoID, secretName) + if err != nil { + return err } tmpl, err := template.New("_").Funcs(secretFuncMap).Parse(format) diff --git a/cli/repo/secret/secret_list.go b/cli/repo/secret/secret_list.go new file mode 100644 index 000000000..3ad0b23d4 --- /dev/null +++ b/cli/repo/secret/secret_list.go @@ -0,0 +1,87 @@ +// Copyright 2023 Woodpecker Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package secret + +import ( + "context" + "html/template" + "os" + "strings" + + "github.com/urfave/cli/v3" + + "go.woodpecker-ci.org/woodpecker/v2/cli/common" + "go.woodpecker-ci.org/woodpecker/v2/cli/internal" + "go.woodpecker-ci.org/woodpecker/v2/woodpecker-go/woodpecker" +) + +var secretListCmd = &cli.Command{ + Name: "ls", + Usage: "list secrets", + ArgsUsage: "[repo-id|repo-full-name]", + Action: secretList, + Flags: []cli.Flag{ + common.RepoFlag, + common.FormatFlag(tmplSecretList, true), + }, +} + +func secretList(ctx context.Context, c *cli.Command) error { + format := c.String("format") + "\n" + + client, err := internal.NewClient(ctx, c) + if err != nil { + return err + } + + repoID, err := parseTargetArgs(client, c) + if err != nil { + return err + } + + opt := woodpecker.SecretListOptions{} + + list, err := client.SecretList(repoID, opt) + if err != nil { + return err + } + + tmpl, err := template.New("_").Funcs(secretFuncMap).Parse(format) + if err != nil { + return err + } + for _, secret := range list { + if err := tmpl.Execute(os.Stdout, secret); err != nil { + return err + } + } + return nil +} + +// Template for secret list items. +var tmplSecretList = "\x1b[33m{{ .Name }} \x1b[0m" + ` +Events: {{ list .Events }} +{{- if .Images }} +Images: {{ list .Images }} +{{- else }} +Images: +{{- end }} +` + +var secretFuncMap = template.FuncMap{ + "list": func(s []string) string { + return strings.Join(s, ", ") + }, +} diff --git a/cli/secret/secret_rm.go b/cli/repo/secret/secret_rm.go similarity index 82% rename from cli/secret/secret_rm.go rename to cli/repo/secret/secret_rm.go index fa7d5b82f..aa0109e67 100644 --- a/cli/secret/secret_rm.go +++ b/cli/repo/secret/secret_rm.go @@ -29,11 +29,6 @@ var secretDeleteCmd = &cli.Command{ ArgsUsage: "[repo-id|repo-full-name]", Action: secretDelete, Flags: []cli.Flag{ - &cli.BoolFlag{ - Name: "global", - Usage: "global secret", - }, - common.OrgFlag, common.RepoFlag, &cli.StringFlag{ Name: "name", @@ -50,16 +45,10 @@ func secretDelete(ctx context.Context, c *cli.Command) error { return err } - global, orgID, repoID, err := parseTargetArgs(client, c) + repoID, err := parseTargetArgs(client, c) if err != nil { return err } - if global { - return client.GlobalSecretDelete(secretName) - } - if orgID != -1 { - return client.OrgSecretDelete(orgID, secretName) - } return client.SecretDelete(repoID, secretName) } diff --git a/cli/secret/secret_set.go b/cli/repo/secret/secret_set.go similarity index 86% rename from cli/secret/secret_set.go rename to cli/repo/secret/secret_set.go index d8c0c38ce..0b21d4b66 100644 --- a/cli/secret/secret_set.go +++ b/cli/repo/secret/secret_set.go @@ -32,11 +32,6 @@ var secretUpdateCmd = &cli.Command{ ArgsUsage: "[repo-id|repo-full-name]", Action: secretUpdate, Flags: []cli.Flag{ - &cli.BoolFlag{ - Name: "global", - Usage: "global secret", - }, - common.OrgFlag, common.RepoFlag, &cli.StringFlag{ Name: "name", @@ -78,19 +73,11 @@ func secretUpdate(ctx context.Context, c *cli.Command) error { secret.Value = string(out) } - global, orgID, repoID, err := parseTargetArgs(client, c) + repoID, err := parseTargetArgs(client, c) if err != nil { return err } - if global { - _, err = client.GlobalSecretUpdate(secret) - return err - } - if orgID != -1 { - _, err = client.OrgSecretUpdate(orgID, secret) - return err - } _, err = client.SecretUpdate(repoID, secret) return err } diff --git a/cmd/cli/app.go b/cmd/cli/app.go index 086914804..347513b1c 100644 --- a/cmd/cli/app.go +++ b/cmd/cli/app.go @@ -19,20 +19,14 @@ import ( "go.woodpecker-ci.org/woodpecker/v2/cli/admin" "go.woodpecker-ci.org/woodpecker/v2/cli/common" - "go.woodpecker-ci.org/woodpecker/v2/cli/cron" - "go.woodpecker-ci.org/woodpecker/v2/cli/deploy" "go.woodpecker-ci.org/woodpecker/v2/cli/exec" "go.woodpecker-ci.org/woodpecker/v2/cli/info" "go.woodpecker-ci.org/woodpecker/v2/cli/lint" - "go.woodpecker-ci.org/woodpecker/v2/cli/log" - "go.woodpecker-ci.org/woodpecker/v2/cli/loglevel" "go.woodpecker-ci.org/woodpecker/v2/cli/org" "go.woodpecker-ci.org/woodpecker/v2/cli/pipeline" "go.woodpecker-ci.org/woodpecker/v2/cli/repo" - "go.woodpecker-ci.org/woodpecker/v2/cli/secret" "go.woodpecker-ci.org/woodpecker/v2/cli/setup" "go.woodpecker-ci.org/woodpecker/v2/cli/update" - "go.woodpecker-ci.org/woodpecker/v2/cli/user" "go.woodpecker-ci.org/woodpecker/v2/version" ) @@ -52,15 +46,9 @@ func newApp() *cli.Command { org.Command, repo.Command, pipeline.Command, - log.Command, - deploy.Command, exec.Command, info.Command, - secret.Command, - user.Command, lint.Command, - loglevel.Command, - cron.Command, setup.Command, update.Command, }