From ff01a9ff1d2d710bb2e68348b46aebe5c44e15cc Mon Sep 17 00:00:00 2001 From: Anbraten Date: Mon, 12 Jun 2023 16:07:52 -0700 Subject: [PATCH] Access repos by their ids (#1691) closes #1295 closes #648 # TODO - [x] add new routes with `:repoID` - [x] load repo in middleware using `:repoID` if present - [x] update UI routes `:owner/:name` to `:repoID` - [x] load repos using id in UI - [x] add lookup endpoint `:owner/:name` to `:repoID` - [x] redirect `:owner/:name` to `:repoID` in UI - [x] use badge with `:repoID` route in UI - [x] update `woodpecker-go` - [x] check cli - [x] add migrations / deprecation notes - [x] check if #648 got solved directly - [x] Test - [x] create repo - [x] repo pages - [x] ui redirects - [x] forge status links --- cli/common/flags.go | 2 +- cli/cron/cron_add.go | 29 +- cli/cron/cron_info.go | 23 +- cli/cron/cron_list.go | 20 +- cli/cron/cron_rm.go | 20 +- cli/cron/cron_update.go | 24 +- cli/deploy/deploy.go | 12 +- cli/internal/util.go | 18 +- cli/log/log_purge.go | 19 +- cli/pipeline/approve.go | 19 +- cli/pipeline/create.go | 12 +- cli/pipeline/decline.go | 20 +- cli/pipeline/info.go | 19 +- cli/pipeline/kill.go | 18 +- cli/pipeline/last.go | 15 +- cli/pipeline/list.go | 17 +- cli/pipeline/logs.go | 17 +- cli/pipeline/ps.go | 14 +- cli/pipeline/start.go | 15 +- cli/pipeline/stop.go | 19 +- cli/registry/registry_add.go | 20 +- cli/registry/registry_info.go | 22 +- cli/registry/registry_list.go | 20 +- cli/registry/registry_rm.go | 20 +- cli/registry/registry_set.go | 25 +- cli/repo/repo_add.go | 15 +- cli/repo/repo_chown.go | 21 +- cli/repo/repo_info.go | 22 +- cli/repo/repo_list.go | 2 +- cli/repo/repo_repair.go | 22 +- cli/repo/repo_rm.go | 19 +- cli/repo/repo_sync.go | 1 + cli/repo/repo_update.go | 15 +- cli/secret/secret.go | 32 +- cli/secret/secret_add.go | 8 +- cli/secret/secret_info.go | 8 +- cli/secret/secret_list.go | 8 +- cli/secret/secret_rm.go | 8 +- cli/secret/secret_set.go | 8 +- cmd/server/docs/docs.go | 568 ++++++------------ docs/docs/20-usage/80-badges.md | 6 +- docs/docs/91-migrations.md | 8 +- server/api/badge.go | 25 +- server/api/cron.go | 30 +- server/api/pipeline.go | 55 +- server/api/registry.go | 25 +- server/api/repo.go | 86 +-- server/api/repo_secret.go | 25 +- server/api/stream.go | 5 +- server/api/user.go | 15 +- server/forge/common/status.go | 4 +- server/forge/gitlab/gitlab.go | 6 - server/model/feed.go | 5 +- server/model/repo.go | 2 +- server/router/api.go | 13 +- server/router/middleware/session/repo.go | 43 +- server/store/datastore/feed.go | 4 +- server/store/datastore/feed_test.go | 11 +- server/store/datastore/pipeline.go | 2 +- server/store/datastore/users_test.go | 6 +- .../layout/popups/DeployPipelinePopup.vue | 7 +- .../layout/popups/ManualPipelinePopup.vue | 4 +- .../pipeline-feed/PipelineFeedItem.vue | 30 +- .../pipeline-feed/PipelineFeedSidebar.vue | 28 +- .../components/repo/pipeline/PipelineList.vue | 28 +- .../components/repo/pipeline/PipelineLog.vue | 18 +- .../components/repo/settings/ActionsTab.vue | 118 ++-- web/src/components/repo/settings/BadgeTab.vue | 11 +- web/src/components/repo/settings/CronTab.vue | 12 +- .../components/repo/settings/GeneralTab.vue | 4 +- .../repo/settings/RegistriesTab.vue | 8 +- .../components/repo/settings/SecretsTab.vue | 8 +- web/src/compositions/useEvents.ts | 4 +- web/src/compositions/useRouteBackOrDefault.ts | 13 +- web/src/lib/api/index.ts | 134 +++-- web/src/lib/api/types/pipeline.ts | 4 +- web/src/lib/api/types/repo.ts | 4 + web/src/router.ts | 232 +++---- web/src/store/pipelines.ts | 51 +- web/src/store/repos.ts | 28 +- web/src/views/RepoAdd.vue | 67 +-- web/src/views/Repos.vue | 6 +- web/src/views/ReposOwner.vue | 76 --- web/src/views/RouterView.vue | 3 + web/src/views/org/OrgRepos.vue | 57 ++ web/src/views/org/OrgSettings.vue | 56 +- web/src/views/org/OrgWrapper.vue | 51 +- web/src/views/repo/RepoBranches.vue | 2 +- web/src/views/repo/RepoDeprecatedRedirect.vue | 54 ++ web/src/views/repo/RepoPullRequests.vue | 2 +- web/src/views/repo/RepoSettings.vue | 7 +- web/src/views/repo/RepoWrapper.vue | 43 +- web/src/views/repo/pipeline/Pipeline.vue | 4 +- .../views/repo/pipeline/PipelineConfig.vue | 4 +- .../views/repo/pipeline/PipelineWrapper.vue | 17 +- woodpecker-go/woodpecker/client.go | 183 +++--- woodpecker-go/woodpecker/interface.go | 71 +-- woodpecker-go/woodpecker/types.go | 37 +- 98 files changed, 1402 insertions(+), 1676 deletions(-) delete mode 100644 web/src/views/ReposOwner.vue create mode 100644 web/src/views/RouterView.vue create mode 100644 web/src/views/org/OrgRepos.vue create mode 100644 web/src/views/repo/RepoDeprecatedRedirect.vue diff --git a/cli/common/flags.go b/cli/common/flags.go index 063c56e8d..5e7dc8e4c 100644 --- a/cli/common/flags.go +++ b/cli/common/flags.go @@ -70,5 +70,5 @@ func FormatFlag(tmpl string, hidden ...bool) *cli.StringFlag { var RepoFlag = &cli.StringFlag{ Name: "repository", Aliases: []string{"repo"}, - Usage: "repository name (e.g. octocat/hello-world)", + Usage: "repository id or full-name (e.g. 134 or octocat/hello-world)", } diff --git a/cli/cron/cron_add.go b/cli/cron/cron_add.go index c46f9fe26..b960437e9 100644 --- a/cli/cron/cron_add.go +++ b/cli/cron/cron_add.go @@ -14,7 +14,7 @@ import ( var cronCreateCmd = &cli.Command{ Name: "add", Usage: "add a cron job", - ArgsUsage: "[repo/name]", + ArgsUsage: "[repo-id|repo-full-name]", Action: cronCreate, Flags: append(common.GlobalFlags, common.RepoFlag, @@ -38,29 +38,32 @@ var cronCreateCmd = &cli.Command{ func cronCreate(c *cli.Context) error { var ( - jobName = c.String("name") - branch = c.String("branch") - schedule = c.String("schedule") - reponame = c.String("repository") - format = c.String("format") + "\n" + jobName = c.String("name") + branch = c.String("branch") + schedule = c.String("schedule") + repoIDOrFullName = 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 + if repoIDOrFullName == "" { + repoIDOrFullName = c.Args().First() } + client, err := internal.NewClient(c) if err != nil { return err } + + repoID, err := internal.ParseRepo(client, repoIDOrFullName) + if err != nil { + return err + } + cron := &woodpecker.Cron{ Name: jobName, Branch: branch, Schedule: schedule, } - cron, err = client.CronCreate(owner, name, cron) + cron, err = client.CronCreate(repoID, cron) if err != nil { return err } diff --git a/cli/cron/cron_info.go b/cli/cron/cron_info.go index 9b93ed7b5..f72265756 100644 --- a/cli/cron/cron_info.go +++ b/cli/cron/cron_info.go @@ -13,7 +13,7 @@ import ( var cronInfoCmd = &cli.Command{ Name: "info", Usage: "display info about a cron job", - ArgsUsage: "[repo/name]", + ArgsUsage: "[repo-id|repo-full-name]", Action: cronInfo, Flags: append(common.GlobalFlags, common.RepoFlag, @@ -28,22 +28,23 @@ var cronInfoCmd = &cli.Command{ func cronInfo(c *cli.Context) error { var ( - jobID = c.Int64("id") - reponame = c.String("repository") - format = c.String("format") + "\n" + jobID = c.Int64("id") + repoIDOrFullName = 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 + if repoIDOrFullName == "" { + repoIDOrFullName = c.Args().First() } client, err := internal.NewClient(c) if err != nil { return err } - cron, err := client.CronGet(owner, name, jobID) + repoID, err := internal.ParseRepo(client, repoIDOrFullName) + if err != nil { + return err + } + + cron, err := client.CronGet(repoID, jobID) if err != nil { return err } diff --git a/cli/cron/cron_list.go b/cli/cron/cron_list.go index 540ed1348..0f7f7c89c 100644 --- a/cli/cron/cron_list.go +++ b/cli/cron/cron_list.go @@ -27,7 +27,7 @@ import ( var cronListCmd = &cli.Command{ Name: "ls", Usage: "list cron jobs", - ArgsUsage: "[repo/name]", + ArgsUsage: "[repo-id|repo-full-name]", Action: cronList, Flags: append(common.GlobalFlags, common.RepoFlag, @@ -37,21 +37,21 @@ var cronListCmd = &cli.Command{ func cronList(c *cli.Context) error { var ( - format = c.String("format") + "\n" - reponame = c.String("repository") + format = c.String("format") + "\n" + repoIDOrFullName = c.String("repository") ) - if reponame == "" { - reponame = c.Args().First() - } - owner, name, err := internal.ParseRepo(reponame) - if err != nil { - return err + if repoIDOrFullName == "" { + repoIDOrFullName = c.Args().First() } client, err := internal.NewClient(c) if err != nil { return err } - list, err := client.CronList(owner, name) + repoID, err := internal.ParseRepo(client, repoIDOrFullName) + if err != nil { + return err + } + list, err := client.CronList(repoID) if err != nil { return err } diff --git a/cli/cron/cron_rm.go b/cli/cron/cron_rm.go index 65780d271..e35b574ca 100644 --- a/cli/cron/cron_rm.go +++ b/cli/cron/cron_rm.go @@ -12,7 +12,7 @@ import ( var cronDeleteCmd = &cli.Command{ Name: "rm", Usage: "remove a cron job", - ArgsUsage: "[repo/name]", + ArgsUsage: "[repo-id|repo-full-name]", Action: cronDelete, Flags: append(common.GlobalFlags, common.RepoFlag, @@ -26,21 +26,21 @@ var cronDeleteCmd = &cli.Command{ func cronDelete(c *cli.Context) error { var ( - jobID = c.Int64("id") - reponame = c.String("repository") + jobID = c.Int64("id") + repoIDOrFullName = c.String("repository") ) - if reponame == "" { - reponame = c.Args().First() - } - owner, name, err := internal.ParseRepo(reponame) - if err != nil { - return err + if repoIDOrFullName == "" { + repoIDOrFullName = c.Args().First() } client, err := internal.NewClient(c) if err != nil { return err } - err = client.CronDelete(owner, name, jobID) + repoID, err := internal.ParseRepo(client, repoIDOrFullName) + if err != nil { + return err + } + err = client.CronDelete(repoID, jobID) if err != nil { return err } diff --git a/cli/cron/cron_update.go b/cli/cron/cron_update.go index 572d0a131..50817c471 100644 --- a/cli/cron/cron_update.go +++ b/cli/cron/cron_update.go @@ -14,7 +14,7 @@ import ( var cronUpdateCmd = &cli.Command{ Name: "update", Usage: "update a cron job", - ArgsUsage: "[repo/name]", + ArgsUsage: "[repo-id|repo-full-name]", Action: cronUpdate, Flags: append(common.GlobalFlags, common.RepoFlag, @@ -41,21 +41,21 @@ var cronUpdateCmd = &cli.Command{ func cronUpdate(c *cli.Context) error { var ( - reponame = c.String("repository") - jobID = c.Int64("id") - jobName = c.String("name") - branch = c.String("branch") - schedule = c.String("schedule") - format = c.String("format") + "\n" + repoIDOrFullName = c.String("repository") + jobID = c.Int64("id") + jobName = c.String("name") + branch = c.String("branch") + schedule = c.String("schedule") + format = c.String("format") + "\n" ) - if reponame == "" { - reponame = c.Args().First() + if repoIDOrFullName == "" { + repoIDOrFullName = c.Args().First() } - owner, name, err := internal.ParseRepo(reponame) + client, err := internal.NewClient(c) if err != nil { return err } - client, err := internal.NewClient(c) + repoID, err := internal.ParseRepo(client, repoIDOrFullName) if err != nil { return err } @@ -65,7 +65,7 @@ func cronUpdate(c *cli.Context) error { Branch: branch, Schedule: schedule, } - cron, err = client.CronUpdate(owner, name, cron) + cron, err = client.CronUpdate(repoID, cron) if err != nil { return err } diff --git a/cli/deploy/deploy.go b/cli/deploy/deploy.go index f9299a352..1aebc47e8 100644 --- a/cli/deploy/deploy.go +++ b/cli/deploy/deploy.go @@ -31,7 +31,7 @@ import ( var Command = &cli.Command{ Name: "deploy", Usage: "deploy code", - ArgsUsage: " ", + ArgsUsage: " ", Action: deploy, Flags: append(common.GlobalFlags, common.FormatFlag(tmplDeployInfo), @@ -59,13 +59,13 @@ var Command = &cli.Command{ } func deploy(c *cli.Context) error { - repo := c.Args().First() - owner, name, err := internal.ParseRepo(repo) + client, err := internal.NewClient(c) if err != nil { return err } - client, err := internal.NewClient(c) + repo := c.Args().First() + repoID, err := internal.ParseRepo(client, repo) if err != nil { return err } @@ -78,7 +78,7 @@ func deploy(c *cli.Context) error { var number int if pipelineArg == "last" { // Fetch the pipeline number from the last pipeline - pipelines, berr := client.PipelineList(owner, name) + pipelines, berr := client.PipelineList(repoID) if berr != nil { return berr } @@ -113,7 +113,7 @@ func deploy(c *cli.Context) error { params := internal.ParseKeyPair(c.StringSlice("param")) - deploy, err := client.Deploy(owner, name, number, env, params) + deploy, err := client.Deploy(repoID, number, env, params) if err != nil { return err } diff --git a/cli/internal/util.go b/cli/internal/util.go index 96daeff8d..f54e029e0 100644 --- a/cli/internal/util.go +++ b/cli/internal/util.go @@ -5,6 +5,7 @@ import ( "crypto/x509" "fmt" "net/http" + "strconv" "strings" "github.com/rs/zerolog/log" @@ -76,15 +77,16 @@ func NewClient(c *cli.Context) (woodpecker.Client, error) { } // ParseRepo parses the repository owner and name from a string. -func ParseRepo(str string) (user, repo string, err error) { - parts := strings.Split(str, "/") - if len(parts) != 2 { - err = fmt.Errorf("Error: Invalid or missing repository. eg octocat/hello-world") - return +func ParseRepo(client woodpecker.Client, str string) (repoID int64, err error) { + if strings.Contains(str, "/") { + repo, err := client.RepoLookup(str) + if err != nil { + return 0, err + } + return repo.ID, nil } - user = parts[0] - repo = parts[1] - return + + return strconv.ParseInt(str, 10, 64) } // ParseKeyPair parses a key=value pair. diff --git a/cli/log/log_purge.go b/cli/log/log_purge.go index e86d335d5..851833acc 100644 --- a/cli/log/log_purge.go +++ b/cli/log/log_purge.go @@ -27,14 +27,18 @@ import ( var logPurgeCmd = &cli.Command{ Name: "purge", Usage: "purge a log", - ArgsUsage: " ", + ArgsUsage: " ", Action: logPurge, Flags: common.GlobalFlags, } func logPurge(c *cli.Context) (err error) { - repo := c.Args().First() - owner, name, err := internal.ParseRepo(repo) + client, err := internal.NewClient(c) + if err != nil { + return err + } + repoIDOrFullName := c.Args().First() + repoID, err := internal.ParseRepo(client, repoIDOrFullName) if err != nil { return err } @@ -43,16 +47,11 @@ func logPurge(c *cli.Context) (err error) { return err } - client, err := internal.NewClient(c) + err = client.LogsPurge(repoID, number) if err != nil { return err } - err = client.LogsPurge(owner, name, number) - if err != nil { - return err - } - - fmt.Printf("Purging logs for pipeline %s/%s#%d\n", owner, name, number) + fmt.Printf("Purging logs for pipeline %s#%d\n", repoIDOrFullName, number) return nil } diff --git a/cli/pipeline/approve.go b/cli/pipeline/approve.go index d2dbcd092..57ec1c05e 100644 --- a/cli/pipeline/approve.go +++ b/cli/pipeline/approve.go @@ -27,14 +27,18 @@ import ( var pipelineApproveCmd = &cli.Command{ Name: "approve", Usage: "approve a pipeline", - ArgsUsage: " ", + ArgsUsage: " ", Action: pipelineApprove, Flags: common.GlobalFlags, } func pipelineApprove(c *cli.Context) (err error) { - repo := c.Args().First() - owner, name, err := internal.ParseRepo(repo) + repoIDOrFullName := c.Args().First() + client, err := internal.NewClient(c) + if err != nil { + return err + } + repoID, err := internal.ParseRepo(client, repoIDOrFullName) if err != nil { return err } @@ -43,16 +47,11 @@ func pipelineApprove(c *cli.Context) (err error) { return err } - client, err := internal.NewClient(c) + _, err = client.PipelineApprove(repoID, number) if err != nil { return err } - _, err = client.PipelineApprove(owner, name, number) - if err != nil { - return err - } - - fmt.Printf("Approving pipeline %s/%s#%d\n", owner, name, number) + fmt.Printf("Approving pipeline %s#%d\n", repoIDOrFullName, number) return nil } diff --git a/cli/pipeline/create.go b/cli/pipeline/create.go index 25a86af9e..24ab01ce9 100644 --- a/cli/pipeline/create.go +++ b/cli/pipeline/create.go @@ -30,7 +30,7 @@ import ( var pipelineCreateCmd = &cli.Command{ Name: "create", Usage: "create new pipeline", - ArgsUsage: "", + ArgsUsage: "", Action: pipelineCreate, Flags: append(common.GlobalFlags, common.FormatFlag(tmplPipelineList), @@ -47,14 +47,12 @@ var pipelineCreateCmd = &cli.Command{ } func pipelineCreate(c *cli.Context) error { - repo := c.Args().First() - - owner, name, err := internal.ParseRepo(repo) + repoIDOrFullName := c.Args().First() + client, err := internal.NewClient(c) if err != nil { return err } - - client, err := internal.NewClient(c) + repoID, err := internal.ParseRepo(client, repoIDOrFullName) if err != nil { return err } @@ -74,7 +72,7 @@ func pipelineCreate(c *cli.Context) error { Variables: variables, } - pipeline, err := client.PipelineCreate(owner, name, options) + pipeline, err := client.PipelineCreate(repoID, options) if err != nil { return err } diff --git a/cli/pipeline/decline.go b/cli/pipeline/decline.go index 2335d4925..b1fdd9c01 100644 --- a/cli/pipeline/decline.go +++ b/cli/pipeline/decline.go @@ -27,32 +27,32 @@ import ( var pipelineDeclineCmd = &cli.Command{ Name: "decline", Usage: "decline a pipeline", - ArgsUsage: " ", + ArgsUsage: " ", Action: pipelineDecline, Flags: common.GlobalFlags, } func pipelineDecline(c *cli.Context) (err error) { - repo := c.Args().First() - owner, name, err := internal.ParseRepo(repo) + repoIDOrFullName := c.Args().First() + client, err := internal.NewClient(c) if err != nil { return err } + repoID, err := internal.ParseRepo(client, repoIDOrFullName) + if err != nil { + return err + } + number, err := strconv.Atoi(c.Args().Get(1)) if err != nil { return err } - client, err := internal.NewClient(c) + _, err = client.PipelineDecline(repoID, number) if err != nil { return err } - _, err = client.PipelineDecline(owner, name, number) - if err != nil { - return err - } - - fmt.Printf("Declining pipeline %s/%s#%d\n", owner, name, number) + fmt.Printf("Declining pipeline %s#%d\n", repoIDOrFullName, number) return nil } diff --git a/cli/pipeline/info.go b/cli/pipeline/info.go index 9c6aaea90..43594747c 100644 --- a/cli/pipeline/info.go +++ b/cli/pipeline/info.go @@ -28,7 +28,7 @@ import ( var pipelineInfoCmd = &cli.Command{ Name: "info", Usage: "show pipeline details", - ArgsUsage: " [pipeline]", + ArgsUsage: " [pipeline]", Action: pipelineInfo, Flags: append(common.GlobalFlags, common.FormatFlag(tmplPipelineInfo), @@ -36,22 +36,21 @@ var pipelineInfoCmd = &cli.Command{ } func pipelineInfo(c *cli.Context) error { - repo := c.Args().First() - owner, name, err := internal.ParseRepo(repo) + repoIDOrFullName := c.Args().First() + client, err := internal.NewClient(c) + if err != nil { + return err + } + repoID, err := internal.ParseRepo(client, repoIDOrFullName) if err != nil { return err } pipelineArg := c.Args().Get(1) - client, err := internal.NewClient(c) - if err != nil { - return err - } - var number int if pipelineArg == "last" || len(pipelineArg) == 0 { // Fetch the pipeline number from the last pipeline - pipeline, err := client.PipelineLast(owner, name, "") + pipeline, err := client.PipelineLast(repoID, "") if err != nil { return err } @@ -63,7 +62,7 @@ func pipelineInfo(c *cli.Context) error { } } - pipeline, err := client.Pipeline(owner, name, number) + pipeline, err := client.Pipeline(repoID, number) if err != nil { return err } diff --git a/cli/pipeline/kill.go b/cli/pipeline/kill.go index a9ddf97b3..a732f2086 100644 --- a/cli/pipeline/kill.go +++ b/cli/pipeline/kill.go @@ -27,33 +27,33 @@ import ( var pipelineKillCmd = &cli.Command{ Name: "kill", Usage: "force kill a pipeline", - ArgsUsage: " ", + ArgsUsage: " ", Action: pipelineKill, Hidden: true, Flags: common.GlobalFlags, } func pipelineKill(c *cli.Context) (err error) { - repo := c.Args().First() - owner, name, err := internal.ParseRepo(repo) - if err != nil { - return err - } number, err := strconv.Atoi(c.Args().Get(1)) if err != nil { return err } + repoIDOrFullName := c.Args().First() client, err := internal.NewClient(c) if err != nil { return err } - - err = client.PipelineKill(owner, name, number) + repoID, err := internal.ParseRepo(client, repoIDOrFullName) if err != nil { return err } - fmt.Printf("Force killing pipeline %s/%s#%d\n", owner, name, number) + err = client.PipelineKill(repoID, number) + if err != nil { + return err + } + + fmt.Printf("Force killing pipeline %s#%d\n", repoIDOrFullName, number) return nil } diff --git a/cli/pipeline/last.go b/cli/pipeline/last.go index 52625719f..7f84fd0b5 100644 --- a/cli/pipeline/last.go +++ b/cli/pipeline/last.go @@ -27,7 +27,7 @@ import ( var pipelineLastCmd = &cli.Command{ Name: "last", Usage: "show latest pipeline details", - ArgsUsage: "", + ArgsUsage: "", Action: pipelineLast, Flags: append(common.GlobalFlags, common.FormatFlag(tmplPipelineInfo), @@ -40,18 +40,17 @@ var pipelineLastCmd = &cli.Command{ } func pipelineLast(c *cli.Context) error { - repo := c.Args().First() - owner, name, err := internal.ParseRepo(repo) - if err != nil { - return err - } - + repoIDOrFullName := c.Args().First() client, err := internal.NewClient(c) if err != nil { return err } + repoID, err := internal.ParseRepo(client, repoIDOrFullName) + if err != nil { + return err + } - pipeline, err := client.PipelineLast(owner, name, c.String("branch")) + pipeline, err := client.PipelineLast(repoID, c.String("branch")) if err != nil { return err } diff --git a/cli/pipeline/list.go b/cli/pipeline/list.go index 0650b4152..693b87238 100644 --- a/cli/pipeline/list.go +++ b/cli/pipeline/list.go @@ -27,7 +27,7 @@ import ( var pipelineListCmd = &cli.Command{ Name: "ls", Usage: "show pipeline history", - ArgsUsage: "", + ArgsUsage: "", Action: pipelineList, Flags: append(common.GlobalFlags, common.FormatFlag(tmplPipelineList), @@ -52,18 +52,17 @@ var pipelineListCmd = &cli.Command{ } func pipelineList(c *cli.Context) error { - repo := c.Args().First() - owner, name, err := internal.ParseRepo(repo) - if err != nil { - return err - } - + repoIDOrFullName := c.Args().First() client, err := internal.NewClient(c) if err != nil { return err } + repoID, err := internal.ParseRepo(client, repoIDOrFullName) + if err != nil { + return err + } - pipelines, err := client.PipelineList(owner, name) + pipelines, err := client.PipelineList(repoID) if err != nil { return err } @@ -101,7 +100,7 @@ func pipelineList(c *cli.Context) error { } // template for pipeline list information -var tmplPipelineList = "\x1b[33mBuild #{{ .Number }} \x1b[0m" + ` +var tmplPipelineList = "\x1b[33mPipeline #{{ .Number }} \x1b[0m" + ` Status: {{ .Status }} Event: {{ .Event }} Commit: {{ .Commit }} diff --git a/cli/pipeline/logs.go b/cli/pipeline/logs.go index a54b06998..2da28a6a4 100644 --- a/cli/pipeline/logs.go +++ b/cli/pipeline/logs.go @@ -27,14 +27,18 @@ import ( var pipelineLogsCmd = &cli.Command{ Name: "logs", Usage: "show pipeline logs", - ArgsUsage: " [pipeline] [stepID]", + ArgsUsage: " [pipeline] [stepID]", Action: pipelineLogs, Flags: common.GlobalFlags, } func pipelineLogs(c *cli.Context) error { - repo := c.Args().First() - owner, name, err := internal.ParseRepo(repo) + repoIDOrFullName := c.Args().First() + client, err := internal.NewClient(c) + if err != nil { + return err + } + repoID, err := internal.ParseRepo(client, repoIDOrFullName) if err != nil { return err } @@ -49,12 +53,7 @@ func pipelineLogs(c *cli.Context) error { return err } - client, err := internal.NewClient(c) - if err != nil { - return err - } - - logs, err := client.StepLogEntries(owner, name, number, step) + logs, err := client.StepLogEntries(repoID, number, step) if err != nil { return err } diff --git a/cli/pipeline/ps.go b/cli/pipeline/ps.go index 6e7745cb1..f77fd2ccd 100644 --- a/cli/pipeline/ps.go +++ b/cli/pipeline/ps.go @@ -28,7 +28,7 @@ import ( var pipelinePsCmd = &cli.Command{ Name: "ps", Usage: "show pipeline steps", - ArgsUsage: " [pipeline]", + ArgsUsage: " [pipeline]", Action: pipelinePs, Flags: append(common.GlobalFlags, common.FormatFlag(tmplPipelinePs), @@ -36,14 +36,12 @@ var pipelinePsCmd = &cli.Command{ } func pipelinePs(c *cli.Context) error { - repo := c.Args().First() - - owner, name, err := internal.ParseRepo(repo) + repoIDOrFullName := c.Args().First() + client, err := internal.NewClient(c) if err != nil { return err } - - client, err := internal.NewClient(c) + repoID, err := internal.ParseRepo(client, repoIDOrFullName) if err != nil { return err } @@ -53,7 +51,7 @@ func pipelinePs(c *cli.Context) error { if pipelineArg == "last" || len(pipelineArg) == 0 { // Fetch the pipeline number from the last pipeline - pipeline, err := client.PipelineLast(owner, name, "") + pipeline, err := client.PipelineLast(repoID, "") if err != nil { return err } @@ -66,7 +64,7 @@ func pipelinePs(c *cli.Context) error { } } - pipeline, err := client.Pipeline(owner, name, number) + pipeline, err := client.Pipeline(repoID, number) if err != nil { return err } diff --git a/cli/pipeline/start.go b/cli/pipeline/start.go index a289f546d..d4eee4fd6 100644 --- a/cli/pipeline/start.go +++ b/cli/pipeline/start.go @@ -28,7 +28,7 @@ import ( var pipelineStartCmd = &cli.Command{ Name: "start", Usage: "start a pipeline", - ArgsUsage: " [pipeline]", + ArgsUsage: " [pipeline]", Action: pipelineStart, Flags: append(common.GlobalFlags, &cli.StringSliceFlag{ @@ -40,13 +40,12 @@ var pipelineStartCmd = &cli.Command{ } func pipelineStart(c *cli.Context) (err error) { - repo := c.Args().First() - owner, name, err := internal.ParseRepo(repo) + repoIDOrFullName := c.Args().First() + client, err := internal.NewClient(c) if err != nil { return err } - - client, err := internal.NewClient(c) + repoID, err := internal.ParseRepo(client, repoIDOrFullName) if err != nil { return err } @@ -55,7 +54,7 @@ func pipelineStart(c *cli.Context) (err error) { var number int if pipelineArg == "last" { // Fetch the pipeline number from the last pipeline - pipeline, err := client.PipelineLast(owner, name, "") + pipeline, err := client.PipelineLast(repoID, "") if err != nil { return err } @@ -72,11 +71,11 @@ func pipelineStart(c *cli.Context) (err error) { params := internal.ParseKeyPair(c.StringSlice("param")) - pipeline, err := client.PipelineStart(owner, name, number, params) + pipeline, err := client.PipelineStart(repoID, number, params) if err != nil { return err } - fmt.Printf("Starting pipeline %s/%s#%d\n", owner, name, pipeline.Number) + fmt.Printf("Starting pipeline %s#%d\n", repoIDOrFullName, pipeline.Number) return nil } diff --git a/cli/pipeline/stop.go b/cli/pipeline/stop.go index c71d4cad5..55a4d8cea 100644 --- a/cli/pipeline/stop.go +++ b/cli/pipeline/stop.go @@ -27,14 +27,18 @@ import ( var pipelineStopCmd = &cli.Command{ Name: "stop", Usage: "stop a pipeline", - ArgsUsage: " [pipeline]", + ArgsUsage: " [pipeline]", Flags: common.GlobalFlags, Action: pipelineStop, } func pipelineStop(c *cli.Context) (err error) { - repo := c.Args().First() - owner, name, err := internal.ParseRepo(repo) + repoIDOrFullName := c.Args().First() + client, err := internal.NewClient(c) + if err != nil { + return err + } + repoID, err := internal.ParseRepo(client, repoIDOrFullName) if err != nil { return err } @@ -43,16 +47,11 @@ func pipelineStop(c *cli.Context) (err error) { return err } - client, err := internal.NewClient(c) + err = client.PipelineStop(repoID, number) if err != nil { return err } - err = client.PipelineStop(owner, name, number) - if err != nil { - return err - } - - fmt.Printf("Stopping pipeline %s/%s#%d\n", owner, name, number) + fmt.Printf("Stopping pipeline %s#%d\n", repoIDOrFullName, number) return nil } diff --git a/cli/registry/registry_add.go b/cli/registry/registry_add.go index ceae1143c..f19f309cc 100644 --- a/cli/registry/registry_add.go +++ b/cli/registry/registry_add.go @@ -14,7 +14,7 @@ import ( var registryCreateCmd = &cli.Command{ Name: "add", Usage: "adds a registry", - ArgsUsage: "[repo/name]", + ArgsUsage: "[repo-id|repo-full-name]", Action: registryCreate, Flags: append(common.GlobalFlags, common.RepoFlag, @@ -36,19 +36,19 @@ var registryCreateCmd = &cli.Command{ func registryCreate(c *cli.Context) error { var ( - hostname = c.String("hostname") - username = c.String("username") - password = c.String("password") - reponame = c.String("repository") + hostname = c.String("hostname") + username = c.String("username") + password = c.String("password") + repoIDOrFullName = c.String("repository") ) - if reponame == "" { - reponame = c.Args().First() + if repoIDOrFullName == "" { + repoIDOrFullName = c.Args().First() } - owner, name, err := internal.ParseRepo(reponame) + client, err := internal.NewClient(c) if err != nil { return err } - client, err := internal.NewClient(c) + repoID, err := internal.ParseRepo(client, repoIDOrFullName) if err != nil { return err } @@ -65,7 +65,7 @@ func registryCreate(c *cli.Context) error { } registry.Password = string(out) } - _, err = client.RegistryCreate(owner, name, registry) + _, err = client.RegistryCreate(repoID, registry) if err != nil { return err } diff --git a/cli/registry/registry_info.go b/cli/registry/registry_info.go index 19f0ea19e..4fbbfdfe7 100644 --- a/cli/registry/registry_info.go +++ b/cli/registry/registry_info.go @@ -13,7 +13,7 @@ import ( var registryInfoCmd = &cli.Command{ Name: "info", Usage: "display registry info", - ArgsUsage: "[repo/name]", + ArgsUsage: "[repo-id|repo-full-name]", Action: registryInfo, Flags: append(common.GlobalFlags, common.RepoFlag, @@ -28,22 +28,22 @@ var registryInfoCmd = &cli.Command{ func registryInfo(c *cli.Context) error { var ( - hostname = c.String("hostname") - reponame = c.String("repository") - format = c.String("format") + "\n" + hostname = c.String("hostname") + repoIDOrFullName = 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 + if repoIDOrFullName == "" { + repoIDOrFullName = c.Args().First() } client, err := internal.NewClient(c) if err != nil { return err } - registry, err := client.Registry(owner, name, hostname) + repoID, err := internal.ParseRepo(client, repoIDOrFullName) + if err != nil { + return err + } + registry, err := client.Registry(repoID, hostname) if err != nil { return err } diff --git a/cli/registry/registry_list.go b/cli/registry/registry_list.go index f8077b87e..93042dfe7 100644 --- a/cli/registry/registry_list.go +++ b/cli/registry/registry_list.go @@ -27,7 +27,7 @@ import ( var registryListCmd = &cli.Command{ Name: "ls", Usage: "list registries", - ArgsUsage: "[repo/name]", + ArgsUsage: "[repo-id|repo-full-name]", Action: registryList, Flags: append(common.GlobalFlags, common.RepoFlag, @@ -37,21 +37,21 @@ var registryListCmd = &cli.Command{ func registryList(c *cli.Context) error { var ( - format = c.String("format") + "\n" - reponame = c.String("repository") + format = c.String("format") + "\n" + repoIDOrFullName = c.String("repository") ) - if reponame == "" { - reponame = c.Args().First() - } - owner, name, err := internal.ParseRepo(reponame) - if err != nil { - return err + if repoIDOrFullName == "" { + repoIDOrFullName = c.Args().First() } client, err := internal.NewClient(c) if err != nil { return err } - list, err := client.RegistryList(owner, name) + repoID, err := internal.ParseRepo(client, repoIDOrFullName) + if err != nil { + return err + } + list, err := client.RegistryList(repoID) if err != nil { return err } diff --git a/cli/registry/registry_rm.go b/cli/registry/registry_rm.go index 3cef16cc6..4bd945094 100644 --- a/cli/registry/registry_rm.go +++ b/cli/registry/registry_rm.go @@ -10,7 +10,7 @@ import ( var registryDeleteCmd = &cli.Command{ Name: "rm", Usage: "remove a registry", - ArgsUsage: "[repo/name]", + ArgsUsage: "[repo-id|repo-full-name]", Action: registryDelete, Flags: append(common.GlobalFlags, common.RepoFlag, @@ -24,19 +24,19 @@ var registryDeleteCmd = &cli.Command{ func registryDelete(c *cli.Context) error { var ( - hostname = c.String("hostname") - reponame = c.String("repository") + hostname = c.String("hostname") + repoIDOrFullName = c.String("repository") ) - if reponame == "" { - reponame = c.Args().First() - } - owner, name, err := internal.ParseRepo(reponame) - if err != nil { - return err + if repoIDOrFullName == "" { + repoIDOrFullName = c.Args().First() } client, err := internal.NewClient(c) if err != nil { return err } - return client.RegistryDelete(owner, name, hostname) + repoID, err := internal.ParseRepo(client, repoIDOrFullName) + if err != nil { + return err + } + return client.RegistryDelete(repoID, hostname) } diff --git a/cli/registry/registry_set.go b/cli/registry/registry_set.go index 5f28eb23e..6d2dccc5b 100644 --- a/cli/registry/registry_set.go +++ b/cli/registry/registry_set.go @@ -14,7 +14,7 @@ import ( var registryUpdateCmd = &cli.Command{ Name: "update", Usage: "update a registry", - ArgsUsage: "[repo/name]", + ArgsUsage: "[repo-id|repo-full-name]", Action: registryUpdate, Flags: append(common.GlobalFlags, common.RepoFlag, @@ -36,19 +36,19 @@ var registryUpdateCmd = &cli.Command{ func registryUpdate(c *cli.Context) error { var ( - hostname = c.String("hostname") - username = c.String("username") - password = c.String("password") - reponame = c.String("repository") + hostname = c.String("hostname") + username = c.String("username") + password = c.String("password") + repoIDOrFullName = c.String("repository") ) - if reponame == "" { - reponame = c.Args().First() + if repoIDOrFullName == "" { + repoIDOrFullName = c.Args().First() } - owner, name, err := internal.ParseRepo(reponame) + client, err := internal.NewClient(c) if err != nil { return err } - client, err := internal.NewClient(c) + repoID, err := internal.ParseRepo(client, repoIDOrFullName) if err != nil { return err } @@ -65,9 +65,6 @@ func registryUpdate(c *cli.Context) error { } registry.Password = string(out) } - _, err = client.RegistryUpdate(owner, name, registry) - if err != nil { - return err - } - return nil + _, err = client.RegistryUpdate(repoID, registry) + return err } diff --git a/cli/repo/repo_add.go b/cli/repo/repo_add.go index 3423ab2b0..89b621e25 100644 --- a/cli/repo/repo_add.go +++ b/cli/repo/repo_add.go @@ -2,6 +2,7 @@ package repo import ( "fmt" + "strconv" "github.com/urfave/cli/v2" @@ -12,16 +13,16 @@ import ( var repoAddCmd = &cli.Command{ Name: "add", Usage: "add a repository", - ArgsUsage: "", + ArgsUsage: "", Action: repoAdd, Flags: common.GlobalFlags, } func repoAdd(c *cli.Context) error { - repo := c.Args().First() - owner, name, err := internal.ParseRepo(repo) + _forgeRemoteID := c.Args().First() + forgeRemoteID, err := strconv.Atoi(_forgeRemoteID) if err != nil { - return err + return fmt.Errorf("invalid forge remote id: %s", _forgeRemoteID) } client, err := internal.NewClient(c) @@ -29,9 +30,11 @@ func repoAdd(c *cli.Context) error { return err } - if _, err := client.RepoPost(owner, name); err != nil { + repo, err := client.RepoPost(int64(forgeRemoteID)) + if err != nil { return err } - fmt.Printf("Successfully activated repository %s/%s\n", owner, name) + + fmt.Printf("Successfully activated repository with forge remote %s\n", repo.FullName) return nil } diff --git a/cli/repo/repo_chown.go b/cli/repo/repo_chown.go index cfbf17c01..7720e0372 100644 --- a/cli/repo/repo_chown.go +++ b/cli/repo/repo_chown.go @@ -12,26 +12,27 @@ import ( var repoChownCmd = &cli.Command{ Name: "chown", Usage: "assume ownership of a repository", - ArgsUsage: "", + ArgsUsage: "", Action: repoChown, Flags: common.GlobalFlags, } func repoChown(c *cli.Context) error { - repo := c.Args().First() - owner, name, err := internal.ParseRepo(repo) - if err != nil { - return err - } - + repoIDOrFullName := c.Args().First() client, err := internal.NewClient(c) if err != nil { return err } - - if _, err := client.RepoChown(owner, name); err != nil { + repoID, err := internal.ParseRepo(client, repoIDOrFullName) + if err != nil { return err } - fmt.Printf("Successfully assumed ownership of repository %s/%s\n", owner, name) + + repo, err := client.RepoChown(repoID) + if err != nil { + return err + } + + fmt.Printf("Successfully assumed ownership of repository %s\n", repo.FullName) return nil } diff --git a/cli/repo/repo_info.go b/cli/repo/repo_info.go index a3fa7de5f..54c1e1350 100644 --- a/cli/repo/repo_info.go +++ b/cli/repo/repo_info.go @@ -13,7 +13,7 @@ import ( var repoInfoCmd = &cli.Command{ Name: "info", Usage: "show repository details", - ArgsUsage: "", + ArgsUsage: "", Action: repoInfo, Flags: append(common.GlobalFlags, common.FormatFlag(tmplRepoInfo), @@ -21,18 +21,17 @@ var repoInfoCmd = &cli.Command{ } func repoInfo(c *cli.Context) error { - arg := c.Args().First() - owner, name, err := internal.ParseRepo(arg) - if err != nil { - return err - } - + repoIDOrFullName := c.Args().First() client, err := internal.NewClient(c) if err != nil { return err } + repoID, err := internal.ParseRepo(client, repoIDOrFullName) + if err != nil { + return err + } - repo, err := client.Repo(owner, name) + repo, err := client.Repo(repoID) if err != nil { return err } @@ -47,11 +46,12 @@ func repoInfo(c *cli.Context) error { // template for repo information var tmplRepoInfo = `Owner: {{ .Owner }} Repo: {{ .Name }} -Type: {{ .Kind }} -Config: {{ .Config }} +Link: {{ .Link }} +Config path: {{ .Config }} Visibility: {{ .Visibility }} Private: {{ .IsSCMPrivate }} Trusted: {{ .IsTrusted }} Gated: {{ .IsGated }} -Forge: {{ .Clone }} +Clone url: {{ .Clone }} +Allow pull-requests: {{ .AllowPullRequests }} ` diff --git a/cli/repo/repo_list.go b/cli/repo/repo_list.go index c319e1075..005b0e85e 100644 --- a/cli/repo/repo_list.go +++ b/cli/repo/repo_list.go @@ -53,4 +53,4 @@ func repoList(c *cli.Context) error { } // template for repository list items -var tmplRepoList = `{{ .FullName }}` +var tmplRepoList = "\x1b[33m{{ .FullName }}\x1b[0m (id: {{ .ID }})" diff --git a/cli/repo/repo_repair.go b/cli/repo/repo_repair.go index 647503667..8da9350d2 100644 --- a/cli/repo/repo_repair.go +++ b/cli/repo/repo_repair.go @@ -1,6 +1,8 @@ package repo import ( + "fmt" + "github.com/urfave/cli/v2" "github.com/woodpecker-ci/woodpecker/cli/common" @@ -10,20 +12,26 @@ import ( var repoRepairCmd = &cli.Command{ Name: "repair", Usage: "repair repository webhooks", - ArgsUsage: "", + ArgsUsage: "", Action: repoRepair, Flags: common.GlobalFlags, } func repoRepair(c *cli.Context) error { - repo := c.Args().First() - owner, name, err := internal.ParseRepo(repo) - if err != nil { - return err - } + repoIDOrFullName := c.Args().First() client, err := internal.NewClient(c) if err != nil { return err } - return client.RepoRepair(owner, name) + repoID, err := internal.ParseRepo(client, repoIDOrFullName) + if err != nil { + return err + } + + if err := client.RepoRepair(repoID); err != nil { + return err + } + + fmt.Printf("Successfully removed repository %s\n", repoIDOrFullName) + return nil } diff --git a/cli/repo/repo_rm.go b/cli/repo/repo_rm.go index 07b17525d..6f8d21f85 100644 --- a/cli/repo/repo_rm.go +++ b/cli/repo/repo_rm.go @@ -12,26 +12,25 @@ import ( var repoRemoveCmd = &cli.Command{ Name: "rm", Usage: "remove a repository", - ArgsUsage: "", + ArgsUsage: "", Action: repoRemove, Flags: common.GlobalFlags, } func repoRemove(c *cli.Context) error { - repo := c.Args().First() - owner, name, err := internal.ParseRepo(repo) - if err != nil { - return err - } - + repoIDOrFullName := c.Args().First() client, err := internal.NewClient(c) if err != nil { return err } - - if err := client.RepoDel(owner, name); err != nil { + repoID, err := internal.ParseRepo(client, repoIDOrFullName) + if err != nil { return err } - fmt.Printf("Successfully removed repository %s/%s\n", owner, name) + + if err := client.RepoDel(repoID); err != nil { + return err + } + fmt.Printf("Successfully removed repository %s\n", repoIDOrFullName) return nil } diff --git a/cli/repo/repo_sync.go b/cli/repo/repo_sync.go index 22e4fa7c7..846230ce3 100644 --- a/cli/repo/repo_sync.go +++ b/cli/repo/repo_sync.go @@ -20,6 +20,7 @@ var repoSyncCmd = &cli.Command{ ), } +// TODO: remove this and add an option to the list cmd as we do not store the remote repo list anymore func repoSync(c *cli.Context) error { client, err := internal.NewClient(c) if err != nil { diff --git a/cli/repo/repo_update.go b/cli/repo/repo_update.go index 420a8fe4d..fa6fe9985 100644 --- a/cli/repo/repo_update.go +++ b/cli/repo/repo_update.go @@ -28,7 +28,7 @@ import ( var repoUpdateCmd = &cli.Command{ Name: "update", Usage: "update a repository", - ArgsUsage: "", + ArgsUsage: "", Action: repoUpdate, Flags: append(common.GlobalFlags, &cli.BoolFlag{ @@ -63,13 +63,12 @@ var repoUpdateCmd = &cli.Command{ } func repoUpdate(c *cli.Context) error { - repo := c.Args().First() - owner, name, err := internal.ParseRepo(repo) + repoIDOrFullName := c.Args().First() + client, err := internal.NewClient(c) if err != nil { return err } - - client, err := internal.NewClient(c) + repoID, err := internal.ParseRepo(client, repoIDOrFullName) if err != nil { return err } @@ -111,9 +110,11 @@ func repoUpdate(c *cli.Context) error { patch.PipelineCounter = &pipelineCounter } - if _, err := client.RepoPatch(owner, name, patch); err != nil { + repo, err := client.RepoPatch(repoID, patch) + if err != nil { return err } - fmt.Printf("Successfully updated repository %s/%s\n", owner, name) + + fmt.Printf("Successfully updated repository %s\n", repo.FullName) return nil } diff --git a/cli/secret/secret.go b/cli/secret/secret.go index 186a466b4..59788cf4a 100644 --- a/cli/secret/secret.go +++ b/cli/secret/secret.go @@ -7,6 +7,7 @@ import ( "github.com/woodpecker-ci/woodpecker/cli/common" "github.com/woodpecker-ci/woodpecker/cli/internal" + "github.com/woodpecker-ci/woodpecker/woodpecker-go/woodpecker" ) // Command exports the secret command. @@ -23,24 +24,29 @@ var Command = &cli.Command{ }, } -func parseTargetArgs(c *cli.Context) (global bool, owner, name string, err error) { +func parseTargetArgs(client woodpecker.Client, c *cli.Context) (global bool, owner string, repoID int64, err error) { if c.Bool("global") { - return true, "", "", nil + return true, "", -1, nil } + + repoIDOrFullName := c.String("repository") + if repoIDOrFullName == "" { + repoIDOrFullName = c.Args().First() + } + orgName := c.String("organization") - repoName := c.String("repository") - if orgName == "" && repoName == "" { - repoName = c.Args().First() + if orgName != "" && repoIDOrFullName == "" { + return false, orgName, -1, err } - if orgName == "" && !strings.Contains(repoName, "/") { - orgName = repoName + + if orgName != "" && !strings.Contains(repoIDOrFullName, "/") { + repoIDOrFullName = orgName + "/" + repoIDOrFullName } - if orgName != "" { - return false, orgName, "", err - } - owner, name, err = internal.ParseRepo(repoName) + + repoID, err = internal.ParseRepo(client, repoIDOrFullName) if err != nil { - return false, "", "", err + return false, "", -1, err } - return false, owner, name, nil + + return false, "", repoID, nil } diff --git a/cli/secret/secret_add.go b/cli/secret/secret_add.go index ae634f292..045df8979 100644 --- a/cli/secret/secret_add.go +++ b/cli/secret/secret_add.go @@ -14,7 +14,7 @@ import ( var secretCreateCmd = &cli.Command{ Name: "add", Usage: "adds a secret", - ArgsUsage: "[org/repo|org]", + ArgsUsage: "[repo-id|repo-full-name]", Action: secretCreate, Flags: append(common.GlobalFlags, &cli.BoolFlag{ @@ -74,7 +74,7 @@ func secretCreate(c *cli.Context) error { secret.Value = string(out) } - global, owner, repo, err := parseTargetArgs(c) + global, owner, repoID, err := parseTargetArgs(client, c) if err != nil { return err } @@ -83,11 +83,11 @@ func secretCreate(c *cli.Context) error { _, err = client.GlobalSecretCreate(secret) return err } - if repo == "" { + if owner != "" { _, err = client.OrgSecretCreate(owner, secret) return err } - _, err = client.SecretCreate(owner, repo, secret) + _, err = client.SecretCreate(repoID, secret) return err } diff --git a/cli/secret/secret_info.go b/cli/secret/secret_info.go index 26e9b4281..00399b924 100644 --- a/cli/secret/secret_info.go +++ b/cli/secret/secret_info.go @@ -14,7 +14,7 @@ import ( var secretInfoCmd = &cli.Command{ Name: "info", Usage: "display secret info", - ArgsUsage: "[org/repo|org]", + ArgsUsage: "[repo-id|repo-full-name]", Action: secretInfo, Flags: append(common.GlobalFlags, &cli.BoolFlag{ @@ -44,7 +44,7 @@ func secretInfo(c *cli.Context) error { return err } - global, owner, repo, err := parseTargetArgs(c) + global, owner, repoID, err := parseTargetArgs(client, c) if err != nil { return err } @@ -55,13 +55,13 @@ func secretInfo(c *cli.Context) error { if err != nil { return err } - } else if repo == "" { + } else if owner != "" { secret, err = client.OrgSecret(owner, secretName) if err != nil { return err } } else { - secret, err = client.Secret(owner, repo, secretName) + secret, err = client.Secret(repoID, secretName) if err != nil { return err } diff --git a/cli/secret/secret_list.go b/cli/secret/secret_list.go index e167ed99e..18d0f2cd3 100644 --- a/cli/secret/secret_list.go +++ b/cli/secret/secret_list.go @@ -15,7 +15,7 @@ import ( var secretListCmd = &cli.Command{ Name: "ls", Usage: "list secrets", - ArgsUsage: "[org/name|org]", + ArgsUsage: "[repo-id|repo-full-name]", Action: secretList, Flags: append(common.GlobalFlags, &cli.BoolFlag{ @@ -39,7 +39,7 @@ func secretList(c *cli.Context) error { return err } - global, owner, repo, err := parseTargetArgs(c) + global, owner, repoID, err := parseTargetArgs(client, c) if err != nil { return err } @@ -50,13 +50,13 @@ func secretList(c *cli.Context) error { if err != nil { return err } - } else if repo == "" { + } else if owner != "" { list, err = client.OrgSecretList(owner) if err != nil { return err } } else { - list, err = client.SecretList(owner, repo) + list, err = client.SecretList(repoID) if err != nil { return err } diff --git a/cli/secret/secret_rm.go b/cli/secret/secret_rm.go index a4fc25c67..5515ee52f 100644 --- a/cli/secret/secret_rm.go +++ b/cli/secret/secret_rm.go @@ -10,7 +10,7 @@ import ( var secretDeleteCmd = &cli.Command{ Name: "rm", Usage: "remove a secret", - ArgsUsage: "[org/repo|org]", + ArgsUsage: "[repo-id|repo-full-name]", Action: secretDelete, Flags: append(common.GlobalFlags, &cli.BoolFlag{ @@ -37,7 +37,7 @@ func secretDelete(c *cli.Context) error { return err } - global, owner, repo, err := parseTargetArgs(c) + global, owner, repoID, err := parseTargetArgs(client, c) if err != nil { return err } @@ -45,8 +45,8 @@ func secretDelete(c *cli.Context) error { if global { return client.GlobalSecretDelete(secretName) } - if repo == "" { + if owner != "" { return client.OrgSecretDelete(owner, secretName) } - return client.SecretDelete(owner, repo, secretName) + return client.SecretDelete(repoID, secretName) } diff --git a/cli/secret/secret_set.go b/cli/secret/secret_set.go index c725fdd7c..1b05b4ede 100644 --- a/cli/secret/secret_set.go +++ b/cli/secret/secret_set.go @@ -14,7 +14,7 @@ import ( var secretUpdateCmd = &cli.Command{ Name: "update", Usage: "update a secret", - ArgsUsage: "[org/repo|org]", + ArgsUsage: "[repo-id|repo-full-name]", Action: secretUpdate, Flags: append(common.GlobalFlags, &cli.BoolFlag{ @@ -71,7 +71,7 @@ func secretUpdate(c *cli.Context) error { secret.Value = string(out) } - global, owner, repo, err := parseTargetArgs(c) + global, owner, repoID, err := parseTargetArgs(client, c) if err != nil { return err } @@ -80,10 +80,10 @@ func secretUpdate(c *cli.Context) error { _, err = client.GlobalSecretUpdate(secret) return err } - if repo == "" { + if owner != "" { _, err = client.OrgSecretUpdate(owner, secret) return err } - _, err = client.SecretUpdate(owner, repo, secret) + _, err = client.SecretUpdate(repoID, secret) return err } diff --git a/cmd/server/docs/docs.go b/cmd/server/docs/docs.go index 81d42e083..876de38b9 100644 --- a/cmd/server/docs/docs.go +++ b/cmd/server/docs/docs.go @@ -250,7 +250,7 @@ const docTemplate = `{ } } }, - "/badges/{owner}/{name}/cc.xml": { + "/badges/{repo_id}/cc.xml": { "get": { "description": "CCMenu displays the pipeline status of projects on a CI server as an item in the Mac's menu bar.\nMore details on how to install, you can find at http://ccmenu.org/\nThe response format adheres to CCTray v1 Specification, https://cctray.org/v1/", "produces": [ @@ -283,7 +283,7 @@ const docTemplate = `{ } } }, - "/badges/{owner}/{name}/status.svg": { + "/badges/{repo_id}/status.svg": { "get": { "produces": [ "image/svg+xml" @@ -294,16 +294,9 @@ const docTemplate = `{ "summary": "Get status badge, SVG format", "parameters": [ { - "type": "string", - "description": "the repository owner's name", - "name": "owner", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "the repository name", - "name": "name", + "type": "integer", + "description": "the repository id", + "name": "repo_id", "in": "path", "required": true } @@ -751,7 +744,7 @@ const docTemplate = `{ } } }, - "/logs/{owner}/{name}/{pipeline}/{stepID}": { + "/logs/{repo_id}/{pipeline}/{stepID}": { "get": { "produces": [ "text/plain" @@ -762,16 +755,9 @@ const docTemplate = `{ "summary": "Log stream", "parameters": [ { - "type": "string", - "description": "the repository owner's name", - "name": "owner", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "the repository name", - "name": "name", + "type": "integer", + "description": "the repository id", + "name": "repo_id", "in": "path", "required": true }, @@ -1206,7 +1192,43 @@ const docTemplate = `{ } } }, - "/repos/{owner}/{name}": { + "/repos/lookup/{repo_full_name}": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "Repositories" + ], + "summary": "Get repository by full-name", + "parameters": [ + { + "type": "string", + "default": "Bearer \u003cpersonal access token\u003e", + "description": "Insert your personal access token", + "name": "Authorization", + "in": "header", + "required": true + }, + { + "type": "string", + "description": "the repository full-name / slug", + "name": "repo_full_name", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/Repo" + } + } + } + } + }, + "/repos/{repo_id}": { "get": { "produces": [ "application/json" @@ -1225,16 +1247,9 @@ const docTemplate = `{ "required": true }, { - "type": "string", - "description": "the repository owner's name", - "name": "owner", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "the repository name", - "name": "name", + "type": "integer", + "description": "the repository id", + "name": "repo_id", "in": "path", "required": true } @@ -1266,16 +1281,9 @@ const docTemplate = `{ "required": true }, { - "type": "string", - "description": "the repository owner's name", - "name": "owner", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "the repository name", - "name": "name", + "type": "integer", + "description": "the repository id", + "name": "repo_id", "in": "path", "required": true } @@ -1307,16 +1315,9 @@ const docTemplate = `{ "required": true }, { - "type": "string", - "description": "the repository owner's name", - "name": "owner", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "the repository name", - "name": "name", + "type": "integer", + "description": "the repository id", + "name": "repo_id", "in": "path", "required": true } @@ -1348,16 +1349,9 @@ const docTemplate = `{ "required": true }, { - "type": "string", - "description": "the repository owner's name", - "name": "owner", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "the repository name", - "name": "name", + "type": "integer", + "description": "the repository id", + "name": "repo_id", "in": "path", "required": true }, @@ -1381,7 +1375,7 @@ const docTemplate = `{ } } }, - "/repos/{owner}/{name}/branches": { + "/repos/{repo_id}/branches": { "get": { "produces": [ "application/json" @@ -1400,16 +1394,9 @@ const docTemplate = `{ "required": true }, { - "type": "string", - "description": "the repository owner's name", - "name": "owner", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "the repository name", - "name": "name", + "type": "integer", + "description": "the repository id", + "name": "repo_id", "in": "path", "required": true }, @@ -1441,7 +1428,7 @@ const docTemplate = `{ } } }, - "/repos/{owner}/{name}/chown": { + "/repos/{repo_id}/chown": { "post": { "produces": [ "application/json" @@ -1460,16 +1447,9 @@ const docTemplate = `{ "required": true }, { - "type": "string", - "description": "the repository owner's name", - "name": "owner", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "the repository name", - "name": "name", + "type": "integer", + "description": "the repository id", + "name": "repo_id", "in": "path", "required": true } @@ -1484,7 +1464,7 @@ const docTemplate = `{ } } }, - "/repos/{owner}/{name}/cron": { + "/repos/{repo_id}/cron": { "get": { "produces": [ "application/json" @@ -1503,16 +1483,9 @@ const docTemplate = `{ "required": true }, { - "type": "string", - "description": "the repository owner's name", - "name": "owner", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "the repository name", - "name": "name", + "type": "integer", + "description": "the repository id", + "name": "repo_id", "in": "path", "required": true }, @@ -1561,16 +1534,9 @@ const docTemplate = `{ "required": true }, { - "type": "string", - "description": "the repository owner's name", - "name": "owner", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "the repository name", - "name": "name", + "type": "integer", + "description": "the repository id", + "name": "repo_id", "in": "path", "required": true }, @@ -1594,7 +1560,7 @@ const docTemplate = `{ } } }, - "/repos/{owner}/{name}/cron/{cron}": { + "/repos/{repo_id}/cron/{cron}": { "get": { "produces": [ "application/json" @@ -1613,16 +1579,9 @@ const docTemplate = `{ "required": true }, { - "type": "string", - "description": "the repository owner's name", - "name": "owner", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "the repository name", - "name": "name", + "type": "integer", + "description": "the repository id", + "name": "repo_id", "in": "path", "required": true }, @@ -1661,16 +1620,9 @@ const docTemplate = `{ "required": true }, { - "type": "string", - "description": "the repository owner's name", - "name": "owner", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "the repository name", - "name": "name", + "type": "integer", + "description": "the repository id", + "name": "repo_id", "in": "path", "required": true }, @@ -1709,16 +1661,9 @@ const docTemplate = `{ "required": true }, { - "type": "string", - "description": "the repository owner's name", - "name": "owner", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "the repository name", - "name": "name", + "type": "integer", + "description": "the repository id", + "name": "repo_id", "in": "path", "required": true }, @@ -1754,16 +1699,9 @@ const docTemplate = `{ "required": true }, { - "type": "string", - "description": "the repository owner's name", - "name": "owner", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "the repository name", - "name": "name", + "type": "integer", + "description": "the repository id", + "name": "repo_id", "in": "path", "required": true }, @@ -1794,7 +1732,7 @@ const docTemplate = `{ } } }, - "/repos/{owner}/{name}/logs/{number}": { + "/repos/{repo_id}/logs/{number}": { "post": { "produces": [ "text/plain" @@ -1813,16 +1751,9 @@ const docTemplate = `{ "required": true }, { - "type": "string", - "description": "the repository owner's name", - "name": "owner", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "the repository name", - "name": "name", + "type": "integer", + "description": "the repository id", + "name": "repo_id", "in": "path", "required": true }, @@ -1841,7 +1772,7 @@ const docTemplate = `{ } } }, - "/repos/{owner}/{name}/logs/{number}/{stepID}": { + "/repos/{repo_id}/logs/{number}/{stepID}": { "get": { "produces": [ "application/json" @@ -1860,16 +1791,9 @@ const docTemplate = `{ "required": true }, { - "type": "string", - "description": "the repository owner's name", - "name": "owner", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "the repository name", - "name": "name", + "type": "integer", + "description": "the repository id", + "name": "repo_id", "in": "path", "required": true }, @@ -1901,7 +1825,7 @@ const docTemplate = `{ } } }, - "/repos/{owner}/{name}/move": { + "/repos/{repo_id}/move": { "post": { "produces": [ "text/plain" @@ -1920,16 +1844,9 @@ const docTemplate = `{ "required": true }, { - "type": "string", - "description": "the repository owner's name", - "name": "owner", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "the repository name", - "name": "name", + "type": "integer", + "description": "the repository id", + "name": "repo_id", "in": "path", "required": true }, @@ -1948,7 +1865,7 @@ const docTemplate = `{ } } }, - "/repos/{owner}/{name}/permissions": { + "/repos/{repo_id}/permissions": { "get": { "description": "The repository permission, according to the used access token.", "produces": [ @@ -1992,7 +1909,7 @@ const docTemplate = `{ } } }, - "/repos/{owner}/{name}/pipelines": { + "/repos/{repo_id}/pipelines": { "get": { "produces": [ "application/json" @@ -2011,16 +1928,9 @@ const docTemplate = `{ "required": true }, { - "type": "string", - "description": "the repository owner's name", - "name": "owner", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "the repository name", - "name": "name", + "type": "integer", + "description": "the repository id", + "name": "repo_id", "in": "path", "required": true }, @@ -2069,16 +1979,9 @@ const docTemplate = `{ "required": true }, { - "type": "string", - "description": "the repository owner's name", - "name": "owner", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "the repository name", - "name": "name", + "type": "integer", + "description": "the repository id", + "name": "repo_id", "in": "path", "required": true }, @@ -2102,7 +2005,7 @@ const docTemplate = `{ } } }, - "/repos/{owner}/{name}/pipelines/{number}": { + "/repos/{repo_id}/pipelines/{number}": { "get": { "produces": [ "application/json" @@ -2121,16 +2024,9 @@ const docTemplate = `{ "required": true }, { - "type": "string", - "description": "the repository owner's name", - "name": "owner", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "the repository name", - "name": "name", + "type": "integer", + "description": "the repository id", + "name": "repo_id", "in": "path", "required": true }, @@ -2213,7 +2109,7 @@ const docTemplate = `{ } } }, - "/repos/{owner}/{name}/pipelines/{number}/approve": { + "/repos/{repo_id}/pipelines/{number}/approve": { "post": { "produces": [ "application/json" @@ -2232,16 +2128,9 @@ const docTemplate = `{ "required": true }, { - "type": "string", - "description": "the repository owner's name", - "name": "owner", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "the repository name", - "name": "name", + "type": "integer", + "description": "the repository id", + "name": "repo_id", "in": "path", "required": true }, @@ -2263,7 +2152,7 @@ const docTemplate = `{ } } }, - "/repos/{owner}/{name}/pipelines/{number}/cancel": { + "/repos/{repo_id}/pipelines/{number}/cancel": { "post": { "produces": [ "text/plain" @@ -2282,16 +2171,9 @@ const docTemplate = `{ "required": true }, { - "type": "string", - "description": "the repository owner's name", - "name": "owner", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "the repository name", - "name": "name", + "type": "integer", + "description": "the repository id", + "name": "repo_id", "in": "path", "required": true }, @@ -2310,7 +2192,7 @@ const docTemplate = `{ } } }, - "/repos/{owner}/{name}/pipelines/{number}/config": { + "/repos/{repo_id}/pipelines/{number}/config": { "get": { "produces": [ "application/json" @@ -2329,16 +2211,9 @@ const docTemplate = `{ "required": true }, { - "type": "string", - "description": "the repository owner's name", - "name": "owner", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "the repository name", - "name": "name", + "type": "integer", + "description": "the repository id", + "name": "repo_id", "in": "path", "required": true }, @@ -2363,7 +2238,7 @@ const docTemplate = `{ } } }, - "/repos/{owner}/{name}/pipelines/{number}/decline": { + "/repos/{repo_id}/pipelines/{number}/decline": { "post": { "produces": [ "application/json" @@ -2382,16 +2257,9 @@ const docTemplate = `{ "required": true }, { - "type": "string", - "description": "the repository owner's name", - "name": "owner", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "the repository name", - "name": "name", + "type": "integer", + "description": "the repository id", + "name": "repo_id", "in": "path", "required": true }, @@ -2413,7 +2281,7 @@ const docTemplate = `{ } } }, - "/repos/{owner}/{name}/pull_requests": { + "/repos/{repo_id}/pull_requests": { "get": { "produces": [ "application/json" @@ -2432,16 +2300,9 @@ const docTemplate = `{ "required": true }, { - "type": "string", - "description": "the repository owner's name", - "name": "owner", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "the repository name", - "name": "name", + "type": "integer", + "description": "the repository id", + "name": "repo_id", "in": "path", "required": true }, @@ -2473,7 +2334,7 @@ const docTemplate = `{ } } }, - "/repos/{owner}/{name}/registry": { + "/repos/{repo_id}/registry": { "get": { "produces": [ "application/json" @@ -2492,16 +2353,9 @@ const docTemplate = `{ "required": true }, { - "type": "string", - "description": "the repository owner's name", - "name": "owner", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "the repository name", - "name": "name", + "type": "integer", + "description": "the repository id", + "name": "repo_id", "in": "path", "required": true }, @@ -2550,16 +2404,9 @@ const docTemplate = `{ "required": true }, { - "type": "string", - "description": "the repository owner's name", - "name": "owner", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "the repository name", - "name": "name", + "type": "integer", + "description": "the repository id", + "name": "repo_id", "in": "path", "required": true }, @@ -2583,7 +2430,7 @@ const docTemplate = `{ } } }, - "/repos/{owner}/{name}/registry/{registry}": { + "/repos/{repo_id}/registry/{registry}": { "get": { "produces": [ "application/json" @@ -2602,16 +2449,9 @@ const docTemplate = `{ "required": true }, { - "type": "string", - "description": "the repository owner's name", - "name": "owner", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "the repository name", - "name": "name", + "type": "integer", + "description": "the repository id", + "name": "repo_id", "in": "path", "required": true }, @@ -2650,16 +2490,9 @@ const docTemplate = `{ "required": true }, { - "type": "string", - "description": "the repository owner's name", - "name": "owner", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "the repository name", - "name": "name", + "type": "integer", + "description": "the repository id", + "name": "repo_id", "in": "path", "required": true }, @@ -2695,16 +2528,9 @@ const docTemplate = `{ "required": true }, { - "type": "string", - "description": "the repository owner's name", - "name": "owner", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "the repository name", - "name": "name", + "type": "integer", + "description": "the repository id", + "name": "repo_id", "in": "path", "required": true }, @@ -2735,7 +2561,7 @@ const docTemplate = `{ } } }, - "/repos/{owner}/{name}/repair": { + "/repos/{repo_id}/repair": { "post": { "produces": [ "text/plain" @@ -2754,16 +2580,9 @@ const docTemplate = `{ "required": true }, { - "type": "string", - "description": "the repository owner's name", - "name": "owner", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "the repository name", - "name": "name", + "type": "integer", + "description": "the repository id", + "name": "repo_id", "in": "path", "required": true } @@ -2775,7 +2594,7 @@ const docTemplate = `{ } } }, - "/repos/{owner}/{name}/secrets": { + "/repos/{repo_id}/secrets": { "get": { "produces": [ "application/json" @@ -2794,16 +2613,9 @@ const docTemplate = `{ "required": true }, { - "type": "string", - "description": "the repository owner's name", - "name": "owner", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "the repository name", - "name": "name", + "type": "integer", + "description": "the repository id", + "name": "repo_id", "in": "path", "required": true }, @@ -2852,16 +2664,9 @@ const docTemplate = `{ "required": true }, { - "type": "string", - "description": "the repository owner's name", - "name": "owner", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "the repository name", - "name": "name", + "type": "integer", + "description": "the repository id", + "name": "repo_id", "in": "path", "required": true }, @@ -2885,7 +2690,7 @@ const docTemplate = `{ } } }, - "/repos/{owner}/{name}/secrets/{secretName}": { + "/repos/{repo_id}/secrets/{secretName}": { "get": { "produces": [ "application/json" @@ -2904,16 +2709,9 @@ const docTemplate = `{ "required": true }, { - "type": "string", - "description": "the repository owner's name", - "name": "owner", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "the repository name", - "name": "name", + "type": "integer", + "description": "the repository id", + "name": "repo_id", "in": "path", "required": true }, @@ -2952,16 +2750,9 @@ const docTemplate = `{ "required": true }, { - "type": "string", - "description": "the repository owner's name", - "name": "owner", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "the repository name", - "name": "name", + "type": "integer", + "description": "the repository id", + "name": "repo_id", "in": "path", "required": true }, @@ -2997,16 +2788,9 @@ const docTemplate = `{ "required": true }, { - "type": "string", - "description": "the repository owner's name", - "name": "owner", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "the repository name", - "name": "name", + "type": "integer", + "description": "the repository id", + "name": "repo_id", "in": "path", "required": true }, @@ -3753,24 +3537,15 @@ const docTemplate = `{ "finished_at": { "type": "integer" }, - "full_name": { - "type": "string" - }, "id": { "type": "integer" }, "message": { "type": "string" }, - "name": { - "type": "string" - }, "number": { "type": "integer" }, - "owner": { - "type": "string" - }, "ref": { "type": "string" }, @@ -3780,6 +3555,9 @@ const docTemplate = `{ "remote": { "type": "string" }, + "repo_id": { + "type": "integer" + }, "started_at": { "type": "integer" }, @@ -4037,6 +3815,10 @@ const docTemplate = `{ "default_branch": { "type": "string" }, + "forge_remote_id": { + "description": "ForgeRemoteID is the unique identifier for the repository on the forge.", + "type": "string" + }, "full_name": { "type": "string" }, diff --git a/docs/docs/20-usage/80-badges.md b/docs/docs/20-usage/80-badges.md index 18a65def2..87ded5250 100644 --- a/docs/docs/20-usage/80-badges.md +++ b/docs/docs/20-usage/80-badges.md @@ -5,14 +5,14 @@ Woodpecker has integrated support for repository status badges. These badges can ## Badge endpoint ```text -:///api/badges///status.svg +:///api/badges//status.svg ``` The status badge displays the status for the latest build to your default branch (e.g. master). You can customize the branch by adding the `branch` query parameter. ```diff --:///api/badges///status.svg -+:///api/badges///status.svg?branch= +-:///api/badges//status.svg ++:///api/badges//status.svg?branch= ``` Please note status badges do not include pull request results, since the status of a pull request does not provide an accurate representation of your repository state. diff --git a/docs/docs/91-migrations.md b/docs/docs/91-migrations.md index 246660552..d4d388d6e 100644 --- a/docs/docs/91-migrations.md +++ b/docs/docs/91-migrations.md @@ -19,7 +19,13 @@ Some versions need some changes to the server configuration or the pipeline conf - Dropped support for [Coding](https://coding.net/) and [Gogs](https://gogs.io). - `/api/queue/resume` & `/api/queue/pause` endpoint methods were changed from `GET` to `POST` - rename `pipeline:` key in your workflow config to `steps:` -- If you want to migrate old logs to the new format, watch the error messages on start. If there are none we are good to go, else you have to plan a migration that can take hours. Set `WOODPECKER_ALLOW_LONG_MIGRATION` to true and let it run. +- If you want to migrate old logs to the new format, watch the error messages on start. If there are none we are good to go, else you have to plan a migration that can take hours. Set `WOODPECKER_MIGRATIONS_ALLOW_LONG` to true and let it run. +- Using `repo-id` in favor of `owner/repo` combination + - :warning: The api endpoints `/api/repos/{owner}/{repo}/...` were replaced by new endpoints using the repos id `/api/repos/{repo-id}` + - To find the id of a repo use the `/api/repos/lookup/{repo-full-name-with-slashes}` endpoint. + - The existing badge endpoint `/api/badges/{owner}/{repo}` will still work, but whenever possible try to use the new endpoint using the `repo-id`: `/api/badges/{repo-id}`. + - The UI urls for a repository changed from `/repos/{owner}/{repo}/...` to `/repos/{repo-id}/...`. You will be redirected automatically when using the old url. + - The woodpecker-go api-client is now using the `repo-id` instead of `owner/repo` for all functions ## 0.15.0 diff --git a/server/api/badge.go b/server/api/badge.go index 9b44b7813..c7443d9b6 100644 --- a/server/api/badge.go +++ b/server/api/badge.go @@ -22,6 +22,7 @@ import ( "errors" "fmt" "net/http" + "strconv" "github.com/gin-gonic/gin" "github.com/rs/zerolog/log" @@ -37,15 +38,29 @@ import ( // GetBadge // // @Summary Get status badge, SVG format -// @Router /badges/{owner}/{name}/status.svg [get] +// @Router /badges/{repo_id}/status.svg [get] // @Produce image/svg+xml // @Success 200 // @Tags Badges -// @Param owner path string true "the repository owner's name" -// @Param name path string true "the repository name" +// @Param repo_id path int true "the repository id" func GetBadge(c *gin.Context) { _store := store.FromContext(c) - repo, err := _store.GetRepoName(c.Param("owner") + "/" + c.Param("name")) + + var repo *model.Repo + var err error + + if c.Param("repo_name") != "" { + repo, err = _store.GetRepoName(c.Param("repo_id_or_owner") + "/" + c.Param("repo_name")) + } else { + var repoID int64 + repoID, err = strconv.ParseInt(c.Param("repo_id_or_owner"), 10, 64) + if err != nil { + c.AbortWithStatus(http.StatusBadRequest) + return + } + repo, err = _store.GetRepo(repoID) + } + if err != nil || !repo.IsActive { if err == nil || errors.Is(err, types.RecordNotExist) { c.AbortWithStatus(http.StatusNotFound) @@ -80,7 +95,7 @@ func GetBadge(c *gin.Context) { // @Description CCMenu displays the pipeline status of projects on a CI server as an item in the Mac's menu bar. // @Description More details on how to install, you can find at http://ccmenu.org/ // @Description The response format adheres to CCTray v1 Specification, https://cctray.org/v1/ -// @Router /badges/{owner}/{name}/cc.xml [get] +// @Router /badges/{repo_id}/cc.xml [get] // @Produce xml // @Success 200 // @Tags Badges diff --git a/server/api/cron.go b/server/api/cron.go index a98bb83d9..cedd14ff0 100644 --- a/server/api/cron.go +++ b/server/api/cron.go @@ -31,13 +31,12 @@ import ( // GetCron // // @Summary Get a cron job by id -// @Router /repos/{owner}/{name}/cron/{cron} [get] +// @Router /repos/{repo_id}/cron/{cron} [get] // @Produce json // @Success 200 {object} Cron // @Tags Repository cron jobs // @Param Authorization header string true "Insert your personal access token" default(Bearer ) -// @Param owner path string true "the repository owner's name" -// @Param name path string true "the repository name" +// @Param repo_id path int true "the repository id" // @Param cron path string true "the cron job id" func GetCron(c *gin.Context) { repo := session.Repo(c) @@ -58,13 +57,12 @@ func GetCron(c *gin.Context) { // RunCron // // @Summary Start a cron job now -// @Router /repos/{owner}/{name}/cron/{cron} [post] +// @Router /repos/{repo_id}/cron/{cron} [post] // @Produce json // @Success 200 {object} Pipeline // @Tags Repository cron jobs // @Param Authorization header string true "Insert your personal access token" default(Bearer ) -// @Param owner path string true "the repository owner's name" -// @Param name path string true "the repository name" +// @Param repo_id path int true "the repository id" // @Param cron path string true "the cron job id" func RunCron(c *gin.Context) { repo := session.Repo(c) @@ -99,13 +97,12 @@ func RunCron(c *gin.Context) { // PostCron // // @Summary Persist/creat a cron job -// @Router /repos/{owner}/{name}/cron [post] +// @Router /repos/{repo_id}/cron [post] // @Produce json // @Success 200 {object} Cron // @Tags Repository cron jobs // @Param Authorization header string true "Insert your personal access token" default(Bearer ) -// @Param owner path string true "the repository owner's name" -// @Param name path string true "the repository name" +// @Param repo_id path int true "the repository id" // @Param cronJob body Cron true "the new cron job" func PostCron(c *gin.Context) { repo := session.Repo(c) @@ -156,13 +153,12 @@ func PostCron(c *gin.Context) { // PatchCron // // @Summary Update a cron job -// @Router /repos/{owner}/{name}/cron/{cron} [patch] +// @Router /repos/{repo_id}/cron/{cron} [patch] // @Produce json // @Success 200 {object} Cron // @Tags Repository cron jobs // @Param Authorization header string true "Insert your personal access token" default(Bearer ) -// @Param owner path string true "the repository owner's name" -// @Param name path string true "the repository name" +// @Param repo_id path int true "the repository id" // @Param cron path string true "the cron job id" // @Param cronJob body Cron true "the cron job data" func PatchCron(c *gin.Context) { @@ -226,13 +222,12 @@ func PatchCron(c *gin.Context) { // GetCronList // // @Summary Get the cron job list -// @Router /repos/{owner}/{name}/cron [get] +// @Router /repos/{repo_id}/cron [get] // @Produce json // @Success 200 {array} Cron // @Tags Repository cron jobs // @Param Authorization header string true "Insert your personal access token" default(Bearer ) -// @Param owner path string true "the repository owner's name" -// @Param name path string true "the repository name" +// @Param repo_id path int true "the repository id" // @Param page query int false "for response pagination, page offset number" default(1) // @Param perPage query int false "for response pagination, max items per page" default(50) func GetCronList(c *gin.Context) { @@ -248,13 +243,12 @@ func GetCronList(c *gin.Context) { // DeleteCron // // @Summary Delete a cron job by id -// @Router /repos/{owner}/{name}/cron/{cron} [delete] +// @Router /repos/{repo_id}/cron/{cron} [delete] // @Produce plain // @Success 200 // @Tags Repository cron jobs // @Param Authorization header string true "Insert your personal access token" default(Bearer ) -// @Param owner path string true "the repository owner's name" -// @Param name path string true "the repository name" +// @Param repo_id path int true "the repository id" // @Param cron path string true "the cron job id" func DeleteCron(c *gin.Context) { repo := session.Repo(c) diff --git a/server/api/pipeline.go b/server/api/pipeline.go index f0520fb1f..c4dbcb0a3 100644 --- a/server/api/pipeline.go +++ b/server/api/pipeline.go @@ -39,13 +39,12 @@ import ( // CreatePipeline // // @Summary Run/trigger a pipelines -// @Router /repos/{owner}/{name}/pipelines [post] +// @Router /repos/{repo_id}/pipelines [post] // @Produce json // @Success 200 {object} Pipeline -// @Tags Pipelines +// @Tags Pipelines // @Param Authorization header string true "Insert your personal access token" default(Bearer ) -// @Param owner path string true "the repository owner's name" -// @Param name path string true "the repository name" +// @Param repo_id path int true "the repository id" // @Param options body PipelineOptions true "the options for the pipeline to run" func CreatePipeline(c *gin.Context) { _store := store.FromContext(c) @@ -97,15 +96,14 @@ func createTmpPipeline(event model.WebhookEvent, commitSHA string, repo *model.R // GetPipelines // // @Summary Get pipelines, current running and past ones -// @Router /repos/{owner}/{name}/pipelines [get] +// @Router /repos/{repo_id}/pipelines [get] // @Produce json // @Success 200 {array} Pipeline // @Tags Pipelines // @Param Authorization header string true "Insert your personal access token" default(Bearer ) -// @Param owner path string true "the repository owner's name" -// @Param name path string true "the repository name" +// @Param repo_id path int true "the repository id" // @Param page query int false "for response pagination, page offset number" default(1) -// @Param perPage query int false "for response pagination, max items per page" default(50) +// @Param perPage query int false "for response pagination, max items per page" default(50) func GetPipelines(c *gin.Context) { repo := session.Repo(c) @@ -124,14 +122,13 @@ func GetPipelines(c *gin.Context) { // GetPipeline // // @Summary Pipeline information by number -// @Router /repos/{owner}/{name}/pipelines/{number} [get] +// @Router /repos/{repo_id}/pipelines/{number} [get] // @Produce json // @Success 200 {object} Pipeline // @Tags Pipelines // @Param Authorization header string true "Insert your personal access token" default(Bearer ) -// @Param owner path string true "the repository owner's name" -// @Param name path string true "the repository name" -// @Param number path int true "the number of the pipeline, OR 'latest'" +// @Param repo_id path int true "the repository id" +// @Param number path int true "the number of the pipeline, OR 'latest'" func GetPipeline(c *gin.Context) { _store := store.FromContext(c) if c.Param("number") == "latest" { @@ -190,13 +187,12 @@ func GetPipelineLast(c *gin.Context) { // GetStepLogs // // @Summary Log information -// @Router /repos/{owner}/{name}/logs/{number}/{stepID} [get] +// @Router /repos/{repo_id}/logs/{number}/{stepID} [get] // @Produce json // @Success 200 {array} LogEntry // @Tags Pipeline logs // @Param Authorization header string true "Insert your personal access token" default(Bearer ) -// @Param owner path string true "the repository owner's name" -// @Param name path string true "the repository name" +// @Param repo_id path int true "the repository id" // @Param number path int true "the number of the pipeline" // @Param stepID path int true "the step id" func GetStepLogs(c *gin.Context) { @@ -247,13 +243,12 @@ func GetStepLogs(c *gin.Context) { // GetPipelineConfig // // @Summary Pipeline configuration -// @Router /repos/{owner}/{name}/pipelines/{number}/config [get] +// @Router /repos/{repo_id}/pipelines/{number}/config [get] // @Produce json // @Success 200 {array} Config -// @Tags Pipelines +// @Tags Pipelines // @Param Authorization header string true "Insert your personal access token" default(Bearer ) -// @Param owner path string true "the repository owner's name" -// @Param name path string true "the repository name" +// @Param repo_id path int true "the repository id" // @Param number path int true "the number of the pipeline" func GetPipelineConfig(c *gin.Context) { _store := store.FromContext(c) @@ -282,13 +277,12 @@ func GetPipelineConfig(c *gin.Context) { // CancelPipeline // // @Summary Cancels a pipeline -// @Router /repos/{owner}/{name}/pipelines/{number}/cancel [post] +// @Router /repos/{repo_id}/pipelines/{number}/cancel [post] // @Produce plain // @Success 200 // @Tags Pipelines // @Param Authorization header string true "Insert your personal access token" default(Bearer ) -// @Param owner path string true "the repository owner's name" -// @Param name path string true "the repository name" +// @Param repo_id path int true "the repository id" // @Param number path int true "the number of the pipeline" func CancelPipeline(c *gin.Context) { _store := store.FromContext(c) @@ -312,13 +306,12 @@ func CancelPipeline(c *gin.Context) { // PostApproval // // @Summary Start pipelines in gated repos -// @Router /repos/{owner}/{name}/pipelines/{number}/approve [post] +// @Router /repos/{repo_id}/pipelines/{number}/approve [post] // @Produce json // @Success 200 {object} Pipeline // @Tags Pipelines // @Param Authorization header string true "Insert your personal access token" default(Bearer ) -// @Param owner path string true "the repository owner's name" -// @Param name path string true "the repository name" +// @Param repo_id path int true "the repository id" // @Param number path int true "the number of the pipeline" func PostApproval(c *gin.Context) { var ( @@ -345,13 +338,12 @@ func PostApproval(c *gin.Context) { // PostDecline // // @Summary Decline pipelines in gated repos -// @Router /repos/{owner}/{name}/pipelines/{number}/decline [post] +// @Router /repos/{repo_id}/pipelines/{number}/decline [post] // @Produce json // @Success 200 {object} Pipeline // @Tags Pipelines // @Param Authorization header string true "Insert your personal access token" default(Bearer ) -// @Param owner path string true "the repository owner's name" -// @Param name path string true "the repository name" +// @Param repo_id path int true "the repository id" // @Param number path int true "the number of the pipeline" func PostDecline(c *gin.Context) { var ( @@ -396,7 +388,7 @@ func GetPipelineQueue(c *gin.Context) { // // @Summary Restart a pipeline // @Description Restarts a pipeline optional with altered event, deploy or environment -// @Router /repos/{owner}/{name}/pipelines/{number} [post] +// @Router /repos/{repo_id}/pipelines/{number} [post] // @Produce json // @Success 200 {object} Pipeline // @Tags Pipelines @@ -478,13 +470,12 @@ func PostPipeline(c *gin.Context) { // DeletePipelineLogs // // @Summary Deletes log -// @Router /repos/{owner}/{name}/logs/{number} [post] +// @Router /repos/{repo_id}/logs/{number} [post] // @Produce plain // @Success 200 // @Tags Pipeline logs // @Param Authorization header string true "Insert your personal access token" default(Bearer ) -// @Param owner path string true "the repository owner's name" -// @Param name path string true "the repository name" +// @Param repo_id path int true "the repository id" // @Param number path int true "the number of the pipeline" func DeletePipelineLogs(c *gin.Context) { _store := store.FromContext(c) diff --git a/server/api/registry.go b/server/api/registry.go index 0b86b3b45..055e161dc 100644 --- a/server/api/registry.go +++ b/server/api/registry.go @@ -27,13 +27,12 @@ import ( // GetRegistry // // @Summary Get a named registry -// @Router /repos/{owner}/{name}/registry/{registry} [get] +// @Router /repos/{repo_id}/registry/{registry} [get] // @Produce json // @Success 200 {object} Registry // @Tags Repository registries // @Param Authorization header string true "Insert your personal access token" default(Bearer ) -// @Param owner path string true "the repository owner's name" -// @Param name path string true "the repository name" +// @Param repo_id path int true "the repository id" // @Param registry path string true "the registry name" func GetRegistry(c *gin.Context) { var ( @@ -51,13 +50,12 @@ func GetRegistry(c *gin.Context) { // PostRegistry // // @Summary Persist/create a registry -// @Router /repos/{owner}/{name}/registry [post] +// @Router /repos/{repo_id}/registry [post] // @Produce json // @Success 200 {object} Registry // @Tags Repository registries // @Param Authorization header string true "Insert your personal access token" default(Bearer ) -// @Param owner path string true "the repository owner's name" -// @Param name path string true "the repository name" +// @Param repo_id path int true "the repository id" // @Param registry body Registry true "the new registry data" func PostRegistry(c *gin.Context) { repo := session.Repo(c) @@ -89,13 +87,12 @@ func PostRegistry(c *gin.Context) { // PatchRegistry // // @Summary Update a named registry -// @Router /repos/{owner}/{name}/registry/{registry} [patch] +// @Router /repos/{repo_id}/registry/{registry} [patch] // @Produce json // @Success 200 {object} Registry // @Tags Repository registries // @Param Authorization header string true "Insert your personal access token" default(Bearer ) -// @Param owner path string true "the repository owner's name" -// @Param name path string true "the repository name" +// @Param repo_id path int true "the repository id" // @Param registry path string true "the registry name" // @Param registryData body Registry true "the attributes for the registry" func PatchRegistry(c *gin.Context) { @@ -143,13 +140,12 @@ func PatchRegistry(c *gin.Context) { // GetRegistryList // // @Summary Get the registry list -// @Router /repos/{owner}/{name}/registry [get] +// @Router /repos/{repo_id}/registry [get] // @Produce json // @Success 200 {array} Registry // @Tags Repository registries // @Param Authorization header string true "Insert your personal access token" default(Bearer ) -// @Param owner path string true "the repository owner's name" -// @Param name path string true "the repository name" +// @Param repo_id path int true "the repository id" // @Param page query int false "for response pagination, page offset number" default(1) // @Param perPage query int false "for response pagination, max items per page" default(50) func GetRegistryList(c *gin.Context) { @@ -170,13 +166,12 @@ func GetRegistryList(c *gin.Context) { // DeleteRegistry // // @Summary Delete a named registry -// @Router /repos/{owner}/{name}/registry/{registry} [delete] +// @Router /repos/{repo_id}/registry/{registry} [delete] // @Produce plain // @Success 200 // @Tags Repository registries // @Param Authorization header string true "Insert your personal access token" default(Bearer ) -// @Param owner path string true "the repository owner's name" -// @Param name path string true "the repository name" +// @Param repo_id path int true "the repository id" // @Param registry path string true "the registry name" func DeleteRegistry(c *gin.Context) { var ( diff --git a/server/api/repo.go b/server/api/repo.go index 266b6aec4..264f679d3 100644 --- a/server/api/repo.go +++ b/server/api/repo.go @@ -21,6 +21,7 @@ import ( "fmt" "net/http" "strconv" + "strings" "time" "github.com/gin-gonic/gin" @@ -38,21 +39,19 @@ import ( // PostRepo // // @Summary Activate a repository -// @Router /repos/{owner}/{name} [post] +// @Router /repos/{repo_id} [post] // @Produce json // @Success 200 {object} Repo // @Tags Repositories // @Param Authorization header string true "Insert your personal access token" default(Bearer ) -// @Param owner path string true "the repository owner's name" -// @Param name path string true "the repository name" +// @Param repo_id path int true "the repository id" func PostRepo(c *gin.Context) { forge := server.Config.Services.Forge _store := store.FromContext(c) user := session.User(c) - owner := c.Param("owner") - name := c.Param("name") - repo, err := _store.GetRepoName(owner + "/" + name) + forgeRemoteID := model.ForgeRemoteID(c.Query("forge_remote_id")) + repo, err := _store.GetRepoForgeID(forgeRemoteID) enabledOnce := err == nil // if there's no error, the repo was found and enabled once already if enabledOnce && repo.IsActive { c.String(http.StatusConflict, "Repository is already active.") @@ -62,7 +61,7 @@ func PostRepo(c *gin.Context) { return } - from, err := forge.Repo(c, user, "0", owner, name) + from, err := forge.Repo(c, user, forgeRemoteID, "", "") if err != nil { c.String(http.StatusInternalServerError, "Could not fetch repository from forge.") return @@ -147,13 +146,12 @@ func PostRepo(c *gin.Context) { // PatchRepo // // @Summary Change a repository -// @Router /repos/{owner}/{name} [patch] +// @Router /repos/{repo_id} [patch] // @Produce json // @Success 200 {object} Repo // @Tags Repositories // @Param Authorization header string true "Insert your personal access token" default(Bearer ) -// @Param owner path string true "the repository owner's name" -// @Param name path string true "the repository name" +// @Param repo_id path int true "the repository id" // @Param repo body RepoPatch true "the repository's information" func PatchRepo(c *gin.Context) { _store := store.FromContext(c) @@ -218,13 +216,12 @@ func PatchRepo(c *gin.Context) { // ChownRepo // // @Summary Change a repository's owner, to the one holding the access token -// @Router /repos/{owner}/{name}/chown [post] +// @Router /repos/{repo_id}/chown [post] // @Produce json // @Success 200 {object} Repo // @Tags Repositories // @Param Authorization header string true "Insert your personal access token" default(Bearer ) -// @Param owner path string true "the repository owner's name" -// @Param name path string true "the repository name" +// @Param repo_id path int true "the repository id" func ChownRepo(c *gin.Context) { _store := store.FromContext(c) repo := session.Repo(c) @@ -239,16 +236,42 @@ func ChownRepo(c *gin.Context) { c.JSON(http.StatusOK, repo) } -// GetRepo +// LookupRepo // -// @Summary Get repository information -// @Router /repos/{owner}/{name} [get] +// @Summary Get repository by full-name +// @Router /repos/lookup/{repo_full_name} [get] // @Produce json // @Success 200 {object} Repo // @Tags Repositories // @Param Authorization header string true "Insert your personal access token" default(Bearer ) -// @Param owner path string true "the repository owner's name" -// @Param name path string true "the repository name" +// @Param repo_full_name path string true "the repository full-name / slug" +func LookupRepo(c *gin.Context) { + _store := store.FromContext(c) + repoFullName := strings.TrimLeft(c.Param("repo_full_name"), "/") + + repo, err := _store.GetRepoName(repoFullName) + if err != nil { + if errors.Is(err, types.RecordNotExist) { + c.AbortWithStatus(http.StatusNotFound) + return + } + + _ = c.AbortWithError(http.StatusInternalServerError, err) + return + } + + c.JSON(http.StatusOK, repo) +} + +// GetRepo +// +// @Summary Get repository information +// @Router /repos/{repo_id} [get] +// @Produce json +// @Success 200 {object} Repo +// @Tags Repositories +// @Param Authorization header string true "Insert your personal access token" default(Bearer ) +// @Param repo_id path int true "the repository id" func GetRepo(c *gin.Context) { c.JSON(http.StatusOK, session.Repo(c)) } @@ -257,7 +280,7 @@ func GetRepo(c *gin.Context) { // // @Summary Repository permission information // @Description The repository permission, according to the used access token. -// @Router /repos/{owner}/{name}/permissions [get] +// @Router /repos/{repo_id}/permissions [get] // @Produce json // @Success 200 {object} Perm // @Tags Repositories @@ -272,13 +295,12 @@ func GetRepoPermissions(c *gin.Context) { // GetRepoBranches // // @Summary Get repository branches -// @Router /repos/{owner}/{name}/branches [get] +// @Router /repos/{repo_id}/branches [get] // @Produce json // @Success 200 {array} string // @Tags Repositories // @Param Authorization header string true "Insert your personal access token" default(Bearer ) -// @Param owner path string true "the repository owner's name" -// @Param name path string true "the repository name" +// @Param repo_id path int true "the repository id" // @Param page query int false "for response pagination, page offset number" default(1) // @Param perPage query int false "for response pagination, max items per page" default(50) func GetRepoBranches(c *gin.Context) { @@ -298,13 +320,12 @@ func GetRepoBranches(c *gin.Context) { // GetRepoPullRequests // // @Summary List active pull requests -// @Router /repos/{owner}/{name}/pull_requests [get] +// @Router /repos/{repo_id}/pull_requests [get] // @Produce json // @Success 200 {array} PullRequest // @Tags Repositories // @Param Authorization header string true "Insert your personal access token" default(Bearer ) -// @Param owner path string true "the repository owner's name" -// @Param name path string true "the repository name" +// @Param repo_id path int true "the repository id" // @Param page query int false "for response pagination, page offset number" default(1) // @Param perPage query int false "for response pagination, max items per page" default(50) func GetRepoPullRequests(c *gin.Context) { @@ -324,13 +345,12 @@ func GetRepoPullRequests(c *gin.Context) { // DeleteRepo // // @Summary Delete a repository -// @Router /repos/{owner}/{name} [delete] +// @Router /repos/{repo_id} [delete] // @Produce json // @Success 200 {object} Repo // @Tags Repositories // @Param Authorization header string true "Insert your personal access token" default(Bearer ) -// @Param owner path string true "the repository owner's name" -// @Param name path string true "the repository name" +// @Param repo_id path int true "the repository id" func DeleteRepo(c *gin.Context) { remove, _ := strconv.ParseBool(c.Query("remove")) _store := store.FromContext(c) @@ -363,13 +383,12 @@ func DeleteRepo(c *gin.Context) { // RepairRepo // // @Summary Repair a repository -// @Router /repos/{owner}/{name}/repair [post] +// @Router /repos/{repo_id}/repair [post] // @Produce plain // @Success 200 // @Tags Repositories // @Param Authorization header string true "Insert your personal access token" default(Bearer ) -// @Param owner path string true "the repository owner's name" -// @Param name path string true "the repository name" +// @Param repo_id path int true "the repository id" func RepairRepo(c *gin.Context) { forge := server.Config.Services.Forge _store := store.FromContext(c) @@ -435,13 +454,12 @@ func RepairRepo(c *gin.Context) { // MoveRepo // // @Summary Move a repository to a new owner -// @Router /repos/{owner}/{name}/move [post] +// @Router /repos/{repo_id}/move [post] // @Produce plain // @Success 200 // @Tags Repositories // @Param Authorization header string true "Insert your personal access token" default(Bearer ) -// @Param owner path string true "the repository owner's name" -// @Param name path string true "the repository name" +// @Param repo_id path int true "the repository id" // @Param to query string true "the username to move the repository to" func MoveRepo(c *gin.Context) { forge := server.Config.Services.Forge diff --git a/server/api/repo_secret.go b/server/api/repo_secret.go index 382d0eabd..eead56e50 100644 --- a/server/api/repo_secret.go +++ b/server/api/repo_secret.go @@ -28,13 +28,12 @@ import ( // GetSecret // // @Summary Get a named secret -// @Router /repos/{owner}/{name}/secrets/{secretName} [get] +// @Router /repos/{repo_id}/secrets/{secretName} [get] // @Produce json // @Success 200 {object} Secret // @Tags Repository secrets // @Param Authorization header string true "Insert your personal access token" default(Bearer ) -// @Param owner path string true "the repository owner's name" -// @Param name path string true "the repository name" +// @Param repo_id path int true "the repository id" // @Param secretName path string true "the secret name" func GetSecret(c *gin.Context) { var ( @@ -52,13 +51,12 @@ func GetSecret(c *gin.Context) { // PostSecret // // @Summary Persist/create a secret -// @Router /repos/{owner}/{name}/secrets [post] +// @Router /repos/{repo_id}/secrets [post] // @Produce json // @Success 200 {object} Secret // @Tags Repository secrets // @Param Authorization header string true "Insert your personal access token" default(Bearer ) -// @Param owner path string true "the repository owner's name" -// @Param name path string true "the repository name" +// @Param repo_id path int true "the repository id" // @Param secret body Secret true "the new secret" func PostSecret(c *gin.Context) { repo := session.Repo(c) @@ -90,13 +88,12 @@ func PostSecret(c *gin.Context) { // PatchSecret // // @Summary Update a named secret -// @Router /repos/{owner}/{name}/secrets/{secretName} [patch] +// @Router /repos/{repo_id}/secrets/{secretName} [patch] // @Produce json // @Success 200 {object} Secret // @Tags Repository secrets // @Param Authorization header string true "Insert your personal access token" default(Bearer ) -// @Param owner path string true "the repository owner's name" -// @Param name path string true "the repository name" +// @Param repo_id path int true "the repository id" // @Param secretName path string true "the secret name" // @Param secret body Secret true "the secret itself" func PatchSecret(c *gin.Context) { @@ -142,13 +139,12 @@ func PatchSecret(c *gin.Context) { // GetSecretList // // @Summary Get the secret list -// @Router /repos/{owner}/{name}/secrets [get] +// @Router /repos/{repo_id}/secrets [get] // @Produce json // @Success 200 {array} Secret // @Tags Repository secrets // @Param Authorization header string true "Insert your personal access token" default(Bearer ) -// @Param owner path string true "the repository owner's name" -// @Param name path string true "the repository name" +// @Param repo_id path int true "the repository id" // @Param page query int false "for response pagination, page offset number" default(1) // @Param perPage query int false "for response pagination, max items per page" default(50) func GetSecretList(c *gin.Context) { @@ -169,13 +165,12 @@ func GetSecretList(c *gin.Context) { // DeleteSecret // // @Summary Delete a named secret -// @Router /repos/{owner}/{name}/secrets/{secretName} [delete] +// @Router /repos/{repo_id}/secrets/{secretName} [delete] // @Produce plain // @Success 200 // @Tags Repository secrets // @Param Authorization header string true "Insert your personal access token" default(Bearer ) -// @Param owner path string true "the repository owner's name" -// @Param name path string true "the repository name" +// @Param repo_id path int true "the repository id" // @Param secretName path string true "the secret name" func DeleteSecret(c *gin.Context) { var ( diff --git a/server/api/stream.go b/server/api/stream.go index 86f21e292..eedc9285e 100644 --- a/server/api/stream.go +++ b/server/api/stream.go @@ -124,12 +124,11 @@ func EventStreamSSE(c *gin.Context) { // LogStream // // @Summary Log stream -// @Router /logs/{owner}/{name}/{pipeline}/{stepID} [get] +// @Router /logs/{repo_id}/{pipeline}/{stepID} [get] // @Produce plain // @Success 200 // @Tags Pipeline logs -// @Param owner path string true "the repository owner's name" -// @Param name path string true "the repository name" +// @Param repo_id path int true "the repository id" // @Param pipeline path int true "the number of the pipeline" // @Param stepID path int true "the step id" func LogStreamSSE(c *gin.Context) { diff --git a/server/api/user.go b/server/api/user.go index e55740172..1c3e8c89c 100644 --- a/server/api/user.go +++ b/server/api/user.go @@ -96,9 +96,9 @@ func GetRepos(c *gin.Context) { } if all { - active := map[string]bool{} + active := map[model.ForgeRemoteID]*model.Repo{} for _, r := range activeRepos { - active[r.FullName] = r.IsActive + active[r.ForgeRemoteID] = r } _repos, err := _forge.Repos(c, user) @@ -106,13 +106,18 @@ func GetRepos(c *gin.Context) { c.String(http.StatusInternalServerError, "Error fetching repository list. %s", err) return } + var repos []*model.Repo for _, r := range _repos { if r.Perm.Push { - if active[r.FullName] { - r.IsActive = true + if active[r.ForgeRemoteID] != nil && active[r.ForgeRemoteID].IsActive { + existingRepo := active[r.ForgeRemoteID] + existingRepo.Update(r) + existingRepo.IsActive = true + repos = append(repos, existingRepo) + } else { + repos = append(repos, r) } - repos = append(repos, r) } } diff --git a/server/forge/common/status.go b/server/forge/common/status.go index 5743f32e4..952eaa864 100644 --- a/server/forge/common/status.go +++ b/server/forge/common/status.go @@ -74,8 +74,8 @@ func GetPipelineStatusDescription(status model.StatusValue) string { func GetPipelineStatusLink(repo *model.Repo, pipeline *model.Pipeline, step *model.Step) string { if step == nil { - return fmt.Sprintf("%s/%s/pipeline/%d", server.Config.Server.Host, repo.FullName, pipeline.Number) + return fmt.Sprintf("%s/repos/%d/pipeline/%d", server.Config.Server.Host, repo.ID, pipeline.Number) } - return fmt.Sprintf("%s/%s/pipeline/%d/%d", server.Config.Server.Host, repo.FullName, pipeline.Number, step.PID) + return fmt.Sprintf("%s/repos/%d/pipeline/%d/%d", server.Config.Server.Host, repo.ID, pipeline.Number, step.PID) } diff --git a/server/forge/gitlab/gitlab.go b/server/forge/gitlab/gitlab.go index f44b96f4e..cdfdf15e0 100644 --- a/server/forge/gitlab/gitlab.go +++ b/server/forge/gitlab/gitlab.go @@ -290,12 +290,6 @@ func (g *GitLab) Repos(ctx context.Context, user *model.User) ([]*model.Repo, er return nil, err } - // TODO(648) remove when woodpecker understands nested repos - if strings.Count(repo.FullName, "/") > 1 { - log.Debug().Msgf("Skipping nested repository %s for user %s, because they are not supported, yet (see #648).", repo.FullName, user.Login) - continue - } - repos = append(repos, repo) } diff --git a/server/model/feed.go b/server/model/feed.go index 12311efab..d330c2f85 100644 --- a/server/model/feed.go +++ b/server/model/feed.go @@ -17,10 +17,7 @@ package model // Feed represents an item in the user's feed or timeline. type Feed struct { - Owner string `json:"owner" xorm:"feed_repo_owner"` - Name string `json:"name" xorm:"feed_repo_name"` - FullName string `json:"full_name" xorm:"feed_repo_full_name"` - + RepoID int64 `json:"repo_id" xorm:"feed_repo_id"` ID int64 `json:"id,omitempty" xorm:"feed_pipeline_id"` Number int64 `json:"number,omitempty" xorm:"feed_pipeline_number"` Event string `json:"event,omitempty" xorm:"feed_pipeline_event"` diff --git a/server/model/repo.go b/server/model/repo.go index c8c230639..6845382d6 100644 --- a/server/model/repo.go +++ b/server/model/repo.go @@ -25,7 +25,7 @@ type Repo struct { ID int64 `json:"id,omitempty" xorm:"pk autoincr 'repo_id'"` UserID int64 `json:"-" xorm:"repo_user_id"` // ForgeRemoteID is the unique identifier for the repository on the forge. - ForgeRemoteID ForgeRemoteID `json:"-" xorm:"forge_remote_id"` + ForgeRemoteID ForgeRemoteID `json:"forge_remote_id" xorm:"forge_remote_id"` Owner string `json:"owner" xorm:"UNIQUE(name) 'repo_owner'"` Name string `json:"name" xorm:"UNIQUE(name) 'repo_name'"` FullName string `json:"full_name" xorm:"UNIQUE 'repo_full_name'"` diff --git a/server/router/api.go b/server/router/api.go index cff366b8d..3f44e175b 100644 --- a/server/router/api.go +++ b/server/router/api.go @@ -61,8 +61,9 @@ func apiRoutes(e *gin.Engine) { } } - apiBase.POST("/repos/:owner/:name", session.MustUser(), api.PostRepo) - repoBase := apiBase.Group("/repos/:owner/:name") + apiBase.GET("/repos/lookup/*repo_full_name", api.LookupRepo) // TODO: check if this public route is a security issue + apiBase.POST("/repos", session.MustUser(), api.PostRepo) + repoBase := apiBase.Group("/repos/:repo_id") { repoBase.Use(session.SetRepo()) repoBase.Use(session.SetPerm()) @@ -125,12 +126,18 @@ func apiRoutes(e *gin.Engine) { } } - badges := apiBase.Group("/badges/:owner/:name") + badges := apiBase.Group("/badges/:repo_id_or_owner") { badges.GET("/status.svg", api.GetBadge) badges.GET("/cc.xml", api.GetCC) } + _badges := apiBase.Group("/badges/:repo_id_or_owner/:repo_name") + { + _badges.GET("/status.svg", api.GetBadge) + _badges.GET("/cc.xml", api.GetCC) + } + pipelines := apiBase.Group("/pipelines") { pipelines.Use(session.MustAdmin()) diff --git a/server/router/middleware/session/repo.go b/server/router/middleware/session/repo.go index 16b0efe8c..3868ab859 100644 --- a/server/router/middleware/session/repo.go +++ b/server/router/middleware/session/repo.go @@ -17,6 +17,7 @@ package session import ( "errors" "net/http" + "strconv" "time" "github.com/gin-gonic/gin" @@ -44,14 +45,28 @@ func Repo(c *gin.Context) *model.Repo { func SetRepo() gin.HandlerFunc { return func(c *gin.Context) { var ( - _store = store.FromContext(c) - owner = c.Param("owner") - name = c.Param("name") - user = User(c) + _store = store.FromContext(c) + owner = c.Param("owner") + name = c.Param("name") + _repoID = c.Param("repo_id") + user = User(c) ) - repo, err := _store.GetRepoName(owner + "/" + name) - if err == nil { + var repo *model.Repo + var err error + if _repoID != "" { + var repoID int64 + repoID, err = strconv.ParseInt(_repoID, 10, 64) + if err != nil { + c.AbortWithStatus(http.StatusBadRequest) + return + } + repo, err = _store.GetRepo(repoID) + } else { + repo, err = _store.GetRepoName(owner + "/" + name) + } + + if repo != nil { c.Set("repo", repo) c.Next() return @@ -64,15 +79,17 @@ func SetRepo() gin.HandlerFunc { err.Error(), ) - if user != nil { - if errors.Is(err, types.RecordNotExist) { - c.AbortWithStatus(http.StatusNotFound) - return - } - _ = c.AbortWithError(http.StatusInternalServerError, err) - } else { + if user == nil { c.AbortWithStatus(http.StatusUnauthorized) + return } + + if errors.Is(err, types.RecordNotExist) { + c.AbortWithStatus(http.StatusNotFound) + return + } + + _ = c.AbortWithError(http.StatusInternalServerError, err) } } diff --git a/server/store/datastore/feed.go b/server/store/datastore/feed.go index bf894b132..01dcd611c 100644 --- a/server/store/datastore/feed.go +++ b/server/store/datastore/feed.go @@ -20,9 +20,7 @@ import ( "github.com/woodpecker-ci/woodpecker/server/model" ) -var feedItemSelect = `repos.repo_owner as feed_repo_owner, -repos.repo_name as feed_repo_name, -repos.repo_full_name as feed_repo_full_name, +var feedItemSelect = `repos.repo_id as feed_repo_id, pipelines.pipeline_id as feed_pipeline_id, pipelines.pipeline_number as feed_pipeline_number, pipelines.pipeline_event as feed_pipeline_event, diff --git a/server/store/datastore/feed_test.go b/server/store/datastore/feed_test.go index 0d2299013..f92c6bc43 100644 --- a/server/store/datastore/feed_test.go +++ b/server/store/datastore/feed_test.go @@ -126,6 +126,7 @@ func TestRepoListLatest(t *testing.T) { assert.NoError(t, store.CreateUser(user)) repo1 := &model.Repo{ + ID: 1, Owner: "bradrydzewski", Name: "test", FullName: "bradrydzewski/test", @@ -133,6 +134,7 @@ func TestRepoListLatest(t *testing.T) { IsActive: true, } repo2 := &model.Repo{ + ID: 2, Owner: "test", Name: "test", FullName: "test/test", @@ -140,6 +142,7 @@ func TestRepoListLatest(t *testing.T) { IsActive: true, } repo3 := &model.Repo{ + ID: 3, Owner: "octocat", Name: "hello-world", FullName: "octocat/hello-world", @@ -189,13 +192,13 @@ func TestRepoListLatest(t *testing.T) { if got, want := pipelines[0].Status, string(model.StatusRunning); want != got { t.Errorf("Want repository status %s, got %s", want, got) } - if got, want := pipelines[0].FullName, repo1.FullName; want != got { - t.Errorf("Want repository name %s, got %s", want, got) + if got, want := pipelines[0].RepoID, repo1.ID; want != got { + t.Errorf("Want repository id %d, got %d", want, got) } if got, want := pipelines[1].Status, string(model.StatusKilled); want != got { t.Errorf("Want repository status %s, got %s", want, got) } - if got, want := pipelines[1].FullName, repo2.FullName; want != got { - t.Errorf("Want repository name %s, got %s", want, got) + if got, want := pipelines[1].RepoID, repo2.ID; want != got { + t.Errorf("Want repository id %d, got %d", want, got) } } diff --git a/server/store/datastore/pipeline.go b/server/store/datastore/pipeline.go index 3403fb4fc..9751a865c 100644 --- a/server/store/datastore/pipeline.go +++ b/server/store/datastore/pipeline.go @@ -73,7 +73,7 @@ func (s storage) GetPipelineLastBefore(repo *model.Repo, branch string, num int6 } func (s storage) GetPipelineList(repo *model.Repo, p *model.ListOptions) ([]*model.Pipeline, error) { - var pipelines []*model.Pipeline + pipelines := make([]*model.Pipeline, 0, 16) return pipelines, s.paginate(p).Where("pipeline_repo_id = ?", repo.ID). Desc("pipeline_number"). Find(&pipelines) diff --git a/server/store/datastore/users_test.go b/server/store/datastore/users_test.go index 38cc4e0cc..a1cb1576e 100644 --- a/server/store/datastore/users_test.go +++ b/server/store/datastore/users_test.go @@ -245,9 +245,9 @@ func TestUsers(t *testing.T) { pipelines, err := store.UserFeed(user) g.Assert(err).IsNil() g.Assert(len(pipelines)).Equal(3) - g.Assert(pipelines[0].FullName).Equal(repo2.FullName) - g.Assert(pipelines[1].FullName).Equal(repo1.FullName) - g.Assert(pipelines[2].FullName).Equal(repo1.FullName) + g.Assert(pipelines[0].RepoID).Equal(repo2.ID) + g.Assert(pipelines[1].RepoID).Equal(repo1.ID) + g.Assert(pipelines[2].RepoID).Equal(repo1.ID) }) }) } diff --git a/web/src/components/layout/popups/DeployPipelinePopup.vue b/web/src/components/layout/popups/DeployPipelinePopup.vue index 8a52dea9c..20eacc104 100644 --- a/web/src/components/layout/popups/DeployPipelinePopup.vue +++ b/web/src/components/layout/popups/DeployPipelinePopup.vue @@ -99,12 +99,7 @@ function deleteVar(key: string) { const pipelineNumber = toRef(props, 'pipelineNumber'); async function triggerDeployPipeline() { loading.value = true; - const newPipeline = await apiClient.deployPipeline( - repo.value.owner, - repo.value.name, - pipelineNumber.value, - payload.value, - ); + const newPipeline = await apiClient.deployPipeline(repo.value.id, pipelineNumber.value, payload.value); emit('close'); diff --git a/web/src/components/layout/popups/ManualPipelinePopup.vue b/web/src/components/layout/popups/ManualPipelinePopup.vue index e74b1c8d1..164bfe27c 100644 --- a/web/src/components/layout/popups/ManualPipelinePopup.vue +++ b/web/src/components/layout/popups/ManualPipelinePopup.vue @@ -80,7 +80,7 @@ const newPipelineVariable = ref<{ name: string; value: string }>({ name: '', val const loading = ref(true); onMounted(async () => { - const data = await usePaginate((page) => apiClient.getRepoBranches(repo.value.owner, repo.value.name, page)); + const data = await usePaginate((page) => apiClient.getRepoBranches(repo.value.id, page)); branches.value = data.map((e) => ({ text: e, value: e, @@ -103,7 +103,7 @@ function deleteVar(key: string) { async function triggerManualPipeline() { loading.value = true; - const pipeline = await apiClient.createPipeline(repo.value.owner, repo.value.name, payload.value); + const pipeline = await apiClient.createPipeline(repo.value.id, payload.value); emit('close'); diff --git a/web/src/components/pipeline-feed/PipelineFeedItem.vue b/web/src/components/pipeline-feed/PipelineFeedItem.vue index 8aa756b1b..1a0f6a176 100644 --- a/web/src/components/pipeline-feed/PipelineFeedItem.vue +++ b/web/src/components/pipeline-feed/PipelineFeedItem.vue @@ -2,7 +2,7 @@
- {{ pipeline.owner }} / {{ pipeline.name }} + {{ repo?.owner }} / {{ repo?.name }} {{ message }}
@@ -23,32 +23,24 @@
- diff --git a/web/src/components/pipeline-feed/PipelineFeedSidebar.vue b/web/src/components/pipeline-feed/PipelineFeedSidebar.vue index 349438b4e..f73881e1c 100644 --- a/web/src/components/pipeline-feed/PipelineFeedSidebar.vue +++ b/web/src/components/pipeline-feed/PipelineFeedSidebar.vue @@ -1,43 +1,29 @@ - diff --git a/web/src/components/repo/pipeline/PipelineList.vue b/web/src/components/repo/pipeline/PipelineList.vue index 9d0351472..f82580bbb 100644 --- a/web/src/components/repo/pipeline/PipelineList.vue +++ b/web/src/components/repo/pipeline/PipelineList.vue @@ -5,7 +5,7 @@ :key="pipeline.id" :to="{ name: 'repo-pipeline', - params: { repoOwner: repo.owner, repoName: repo.name, pipelineId: pipeline.number }, + params: { pipelineId: pipeline.number }, }" :pipeline="pipeline" /> @@ -15,28 +15,12 @@
- diff --git a/web/src/components/repo/pipeline/PipelineLog.vue b/web/src/components/repo/pipeline/PipelineLog.vue index d8f061846..546c9955b 100644 --- a/web/src/components/repo/pipeline/PipelineLog.vue +++ b/web/src/components/repo/pipeline/PipelineLog.vue @@ -190,7 +190,7 @@ async function download() { let logs; try { downloadInProgress.value = true; - logs = await apiClient.getLogs(repo.value.owner, repo.value.name, pipeline.value.number, step.value.id); + logs = await apiClient.getLogs(repo.value.id, pipeline.value.number, step.value.id); } catch (e) { notifications.notifyError(e, i18n.t('repo.pipeline.log_download_error')); return; @@ -239,22 +239,16 @@ async function loadLogs() { } if (isStepFinished(step.value)) { - const logs = await apiClient.getLogs(repo.value.owner, repo.value.name, pipeline.value.number, step.value.id); + const logs = await apiClient.getLogs(repo.value.id, pipeline.value.number, step.value.id); logs?.forEach((line) => writeLog({ index: line.line, text: atob(line.data), time: line.time })); flushLogs(false); } if (isStepRunning(step.value)) { - stream.value = apiClient.streamLogs( - repo.value.owner, - repo.value.name, - pipeline.value.number, - step.value.id, - (line) => { - writeLog({ index: line.line, text: atob(line.data), time: line.time }); - flushLogs(true); - }, - ); + stream.value = apiClient.streamLogs(repo.value.id, pipeline.value.number, step.value.id, (line) => { + writeLog({ index: line.line, text: atob(line.data), time: line.time }); + flushLogs(true); + }); } } diff --git a/web/src/components/repo/settings/ActionsTab.vue b/web/src/components/repo/settings/ActionsTab.vue index a34aada8a..0cc570b03 100644 --- a/web/src/components/repo/settings/ActionsTab.vue +++ b/web/src/components/repo/settings/ActionsTab.vue @@ -45,8 +45,8 @@ - diff --git a/web/src/components/repo/settings/BadgeTab.vue b/web/src/components/repo/settings/BadgeTab.vue index d83cf6819..85b564cf5 100644 --- a/web/src/components/repo/settings/BadgeTab.vue +++ b/web/src/components/repo/settings/BadgeTab.vue @@ -75,7 +75,7 @@ export default defineComponent({ throw new Error('Unexpected: "repo" should be provided at this place'); } - branches.value = (await usePaginate((page) => apiClient.getRepoBranches(repo.value.owner, repo.value.name, page))) + branches.value = (await usePaginate((page) => apiClient.getRepoBranches(repo.value.id, page))) .map((b) => ({ value: b, text: b, @@ -91,14 +91,9 @@ export default defineComponent({ window.location.port ? `:${window.location.port}` : '' }`; const badgeUrl = computed( - () => - `/api/badges/${repo.value.owner}/${repo.value.name}/status.svg${ - branch.value !== '' ? `?branch=${branch.value}` : '' - }`, - ); - const repoUrl = computed( - () => `/${repo.value.owner}/${repo.value.name}${branch.value !== '' ? `/branches/${branch.value}` : ''}`, + () => `/api/badges/${repo.value.id}/status.svg${branch.value !== '' ? `?branch=${branch.value}` : ''}`, ); + const repoUrl = computed(() => `/${repo.value.id}${branch.value !== '' ? `/branches/${branch.value}` : ''}`); const badgeContent = computed(() => { if (!repo) { diff --git a/web/src/components/repo/settings/CronTab.vue b/web/src/components/repo/settings/CronTab.vue index 5b27a49ef..2f4dd708f 100644 --- a/web/src/components/repo/settings/CronTab.vue +++ b/web/src/components/repo/settings/CronTab.vue @@ -121,7 +121,7 @@ async function loadCrons(page: number): Promise { throw new Error("Unexpected: Can't load repo"); } - return apiClient.getCronList(repo.value.owner, repo.value.name, page); + return apiClient.getCronList(repo.value.id, page); } const { resetPage, data: crons } = usePagination(loadCrons, () => !selectedCron.value); @@ -136,9 +136,9 @@ const { doSubmit: createCron, isLoading: isSaving } = useAsyncAction(async () => } if (isEditingCron.value) { - await apiClient.updateCron(repo.value.owner, repo.value.name, selectedCron.value); + await apiClient.updateCron(repo.value.id, selectedCron.value); } else { - await apiClient.createCron(repo.value.owner, repo.value.name, selectedCron.value); + await apiClient.createCron(repo.value.id, selectedCron.value); } notifications.notify({ title: i18n.t(isEditingCron.value ? 'repo.settings.crons.saved' : i18n.t('repo.settings.crons.created')), @@ -153,7 +153,7 @@ const { doSubmit: deleteCron, isLoading: isDeleting } = useAsyncAction(async (_c throw new Error("Unexpected: Can't load repo"); } - await apiClient.deleteCron(repo.value.owner, repo.value.name, _cron.id); + await apiClient.deleteCron(repo.value.id, _cron.id); notifications.notify({ title: i18n.t('repo.settings.crons.deleted'), type: 'success' }); resetPage(); }); @@ -163,12 +163,10 @@ const { doSubmit: runCron } = useAsyncAction(async (_cron: Cron) => { throw new Error("Unexpected: Can't load repo"); } - const pipeline = await apiClient.runCron(repo.value.owner, repo.value.name, _cron.id); + const pipeline = await apiClient.runCron(repo.value.id, _cron.id); await router.push({ name: 'repo-pipeline', params: { - repoOwner: repo.value.owner, - repoName: repo.value.name, pipelineId: pipeline.number, }, }); diff --git a/web/src/components/repo/settings/GeneralTab.vue b/web/src/components/repo/settings/GeneralTab.vue index dc3573cc2..5053b72a0 100644 --- a/web/src/components/repo/settings/GeneralTab.vue +++ b/web/src/components/repo/settings/GeneralTab.vue @@ -148,7 +148,7 @@ export default defineComponent({ throw new Error('Unexpected: Repo should be set'); } - await repoStore.loadRepo(repo.value.owner, repo.value.name); + await repoStore.loadRepo(repo.value.id); loadRepoSettings(); } @@ -161,7 +161,7 @@ export default defineComponent({ throw new Error('Unexpected: Repo-Settings should be set'); } - await apiClient.updateRepo(repo.value.owner, repo.value.name, repoSettings.value); + await apiClient.updateRepo(repo.value.id, repoSettings.value); await loadRepo(); notifications.notify({ title: i18n.t('repo.settings.general.success'), type: 'success' }); }); diff --git a/web/src/components/repo/settings/RegistriesTab.vue b/web/src/components/repo/settings/RegistriesTab.vue index 4a82f3ff8..9ee7d36be 100644 --- a/web/src/components/repo/settings/RegistriesTab.vue +++ b/web/src/components/repo/settings/RegistriesTab.vue @@ -124,7 +124,7 @@ export default defineComponent({ throw new Error("Unexpected: Can't load repo"); } - return apiClient.getRegistryList(repo.value.owner, repo.value.name, page); + return apiClient.getRegistryList(repo.value.id, page); } const { resetPage, data: registries } = usePagination(loadRegistries, () => !selectedRegistry.value); @@ -139,9 +139,9 @@ export default defineComponent({ } if (isEditingRegistry.value) { - await apiClient.updateRegistry(repo.value.owner, repo.value.name, selectedRegistry.value); + await apiClient.updateRegistry(repo.value.id, selectedRegistry.value); } else { - await apiClient.createRegistry(repo.value.owner, repo.value.name, selectedRegistry.value); + await apiClient.createRegistry(repo.value.id, selectedRegistry.value); } notifications.notify({ title: i18n.t( @@ -159,7 +159,7 @@ export default defineComponent({ } const registryAddress = encodeURIComponent(_registry.address); - await apiClient.deleteRegistry(repo.value.owner, repo.value.name, registryAddress); + await apiClient.deleteRegistry(repo.value.id, registryAddress); notifications.notify({ title: i18n.t('repo.settings.registries.deleted'), type: 'success' }); resetPage(); }); diff --git a/web/src/components/repo/settings/SecretsTab.vue b/web/src/components/repo/settings/SecretsTab.vue index ccdb12904..a4ae6e8de 100644 --- a/web/src/components/repo/settings/SecretsTab.vue +++ b/web/src/components/repo/settings/SecretsTab.vue @@ -86,7 +86,7 @@ export default defineComponent({ throw new Error("Unexpected: Can't load repo"); } - return apiClient.getSecretList(repo.value.owner, repo.value.name, page); + return apiClient.getSecretList(repo.value.id, page); } const { resetPage, data: secrets } = usePagination(loadSecrets, () => !selectedSecret.value); @@ -101,9 +101,9 @@ export default defineComponent({ } if (isEditingSecret.value) { - await apiClient.updateSecret(repo.value.owner, repo.value.name, selectedSecret.value); + await apiClient.updateSecret(repo.value.id, selectedSecret.value); } else { - await apiClient.createSecret(repo.value.owner, repo.value.name, selectedSecret.value); + await apiClient.createSecret(repo.value.id, selectedSecret.value); } notifications.notify({ title: i18n.t(isEditingSecret.value ? 'repo.settings.secrets.saved' : 'repo.settings.secrets.created'), @@ -118,7 +118,7 @@ export default defineComponent({ throw new Error("Unexpected: Can't load repo"); } - await apiClient.deleteSecret(repo.value.owner, repo.value.name, _secret.name); + await apiClient.deleteSecret(repo.value.id, _secret.name); notifications.notify({ title: i18n.t('repo.settings.secrets.deleted'), type: 'success' }); resetPage(); }); diff --git a/web/src/compositions/useEvents.ts b/web/src/compositions/useEvents.ts index f4fc292b5..4cd276005 100644 --- a/web/src/compositions/useEvents.ts +++ b/web/src/compositions/useEvents.ts @@ -28,13 +28,13 @@ export default () => { return; } const { pipeline } = data; - pipelineStore.setPipeline(repo.owner, repo.name, pipeline); + pipelineStore.setPipeline(repo.id, pipeline); // contains step update if (!data.step) { return; } const { step } = data; - pipelineStore.setStep(repo.owner, repo.name, pipeline.number, step); + pipelineStore.setStep(repo.id, pipeline.number, step); }); }; diff --git a/web/src/compositions/useRouteBackOrDefault.ts b/web/src/compositions/useRouteBackOrDefault.ts index 476ad956a..6acafa4f3 100644 --- a/web/src/compositions/useRouteBackOrDefault.ts +++ b/web/src/compositions/useRouteBackOrDefault.ts @@ -3,12 +3,11 @@ import { RouteLocationRaw, useRouter } from 'vue-router'; export function useRouteBackOrDefault(to: RouteLocationRaw) { const router = useRouter(); - return () => { - // TODO: use history navigation once we have found a solution for filtering external history entries - // if (window.history.length > 2) { - // router.back(); - // } else { - router.replace(to); - // } + return async () => { + if ((window.history.state as { back: string }).back === null) { + await router.replace(to); + return; + } + router.back(); }; } diff --git a/web/src/lib/api/index.ts b/web/src/lib/api/index.ts index 8132af289..fc898b779 100644 --- a/web/src/lib/api/index.ts +++ b/web/src/lib/api/index.ts @@ -40,66 +40,70 @@ export default class WoodpeckerClient extends ApiClient { return this._get(`/api/user/repos?${query}`) as Promise; } - getRepo(owner: string, repo: string): Promise { - return this._get(`/api/repos/${owner}/${repo}`) as Promise; + lookupRepo(owner: string, name: string): Promise { + return this._get(`/api/repos/lookup/${owner}/${name}`) as Promise; } - getRepoPermissions(owner: string, repo: string): Promise { - return this._get(`/api/repos/${owner}/${repo}/permissions`) as Promise; + getRepo(repoId: number): Promise { + return this._get(`/api/repos/${repoId}`) as Promise; } - getRepoBranches(owner: string, repo: string, page: number): Promise { - return this._get(`/api/repos/${owner}/${repo}/branches?page=${page}`) as Promise; + getRepoPermissions(repoId: number): Promise { + return this._get(`/api/repos/${repoId}/permissions`) as Promise; } - getRepoPullRequests(owner: string, repo: string, page: number): Promise { - return this._get(`/api/repos/${owner}/${repo}/pull_requests?page=${page}`) as Promise; + getRepoBranches(repoId: number, page: number): Promise { + return this._get(`/api/repos/${repoId}/branches?page=${page}`) as Promise; } - activateRepo(owner: string, repo: string): Promise { - return this._post(`/api/repos/${owner}/${repo}`); + getRepoPullRequests(repoId: number, page: number): Promise { + return this._get(`/api/repos/${repoId}/pull_requests?page=${page}`) as Promise; } - updateRepo(owner: string, repo: string, repoSettings: RepoSettings): Promise { - return this._patch(`/api/repos/${owner}/${repo}`, repoSettings); + activateRepo(forgeRemoteId: string): Promise { + return this._post(`/api/repos?forge_remote_id=${forgeRemoteId}`) as Promise; } - deleteRepo(owner: string, repo: string, remove = true): Promise { + updateRepo(repoId: number, repoSettings: RepoSettings): Promise { + return this._patch(`/api/repos/${repoId}`, repoSettings); + } + + deleteRepo(repoId: number, remove = true): Promise { const query = encodeQueryString({ remove }); - return this._delete(`/api/repos/${owner}/${repo}?${query}`); + return this._delete(`/api/repos/${repoId}?${query}`); } - repairRepo(owner: string, repo: string): Promise { - return this._post(`/api/repos/${owner}/${repo}/repair`); + repairRepo(repoId: number): Promise { + return this._post(`/api/repos/${repoId}/repair`); } - createPipeline(owner: string, repo: string, options: PipelineOptions): Promise { - return this._post(`/api/repos/${owner}/${repo}/pipelines`, options) as Promise; + createPipeline(repoId: number, options: PipelineOptions): Promise { + return this._post(`/api/repos/${repoId}/pipelines`, options) as Promise; } // Deploy triggers a deployment for an existing pipeline using the // specified target environment. - deployPipeline(owner: string, repo: string, pipelineNumber: string, options: DeploymentOptions): Promise { + deployPipeline(repoId: number, pipelineNumber: string, options: DeploymentOptions): Promise { const vars = { ...options.variables, event: 'deployment', deploy_to: options.environment, }; const query = encodeQueryString(vars); - return this._post(`/api/repos/${owner}/${repo}/pipelines/${pipelineNumber}?${query}`) as Promise; + return this._post(`/api/repos/${repoId}/pipelines/${pipelineNumber}?${query}`) as Promise; } - getPipelineList(owner: string, repo: string, opts?: Record): Promise { + getPipelineList(repoId: number, opts?: Record): Promise { const query = encodeQueryString(opts); - return this._get(`/api/repos/${owner}/${repo}/pipelines?${query}`) as Promise; + return this._get(`/api/repos/${repoId}/pipelines?${query}`) as Promise; } - getPipeline(owner: string, repo: string, pipelineNumber: number | 'latest'): Promise { - return this._get(`/api/repos/${owner}/${repo}/pipelines/${pipelineNumber}`) as Promise; + getPipeline(repoId: number, pipelineNumber: number | 'latest'): Promise { + return this._get(`/api/repos/${repoId}/pipelines/${pipelineNumber}`) as Promise; } - getPipelineConfig(owner: string, repo: string, pipelineNumber: number): Promise { - return this._get(`/api/repos/${owner}/${repo}/pipelines/${pipelineNumber}/config`) as Promise; + getPipelineConfig(repoId: number, pipelineNumber: number): Promise { + return this._get(`/api/repos/${repoId}/pipelines/${pipelineNumber}/config`) as Promise; } getPipelineFeed(opts?: Record): Promise { @@ -107,82 +111,81 @@ export default class WoodpeckerClient extends ApiClient { return this._get(`/api/user/feed?${query}`) as Promise; } - cancelPipeline(owner: string, repo: string, pipelineNumber: number): Promise { - return this._post(`/api/repos/${owner}/${repo}/pipelines/${pipelineNumber}/cancel`); + cancelPipeline(repoId: number, pipelineNumber: number): Promise { + return this._post(`/api/repos/${repoId}/pipelines/${pipelineNumber}/cancel`); } - approvePipeline(owner: string, repo: string, pipelineNumber: string): Promise { - return this._post(`/api/repos/${owner}/${repo}/pipelines/${pipelineNumber}/approve`); + approvePipeline(repoId: number, pipelineNumber: string): Promise { + return this._post(`/api/repos/${repoId}/pipelines/${pipelineNumber}/approve`); } - declinePipeline(owner: string, repo: string, pipelineNumber: string): Promise { - return this._post(`/api/repos/${owner}/${repo}/pipelines/${pipelineNumber}/decline`); + declinePipeline(repoId: number, pipelineNumber: string): Promise { + return this._post(`/api/repos/${repoId}/pipelines/${pipelineNumber}/decline`); } restartPipeline( - owner: string, - repo: string, + repoId: number, pipeline: string, opts?: Record, ): Promise { const query = encodeQueryString(opts); - return this._post(`/api/repos/${owner}/${repo}/pipelines/${pipeline}?${query}`) as Promise; + return this._post(`/api/repos/${repoId}/pipelines/${pipeline}?${query}`) as Promise; } - getLogs(owner: string, repo: string, pipeline: number, stepId: number): Promise { - return this._get(`/api/repos/${owner}/${repo}/logs/${pipeline}/${stepId}`) as Promise; + getLogs(repoId: number, pipeline: number, step: number): Promise { + return this._get(`/api/repos/${repoId}/logs/${pipeline}/${step}`) as Promise; } - getSecretList(owner: string, repo: string, page: number): Promise { - return this._get(`/api/repos/${owner}/${repo}/secrets?page=${page}`) as Promise; + getSecretList(repoId: number, page: number): Promise { + return this._get(`/api/repos/${repoId}/secrets?page=${page}`) as Promise; } - createSecret(owner: string, repo: string, secret: Partial): Promise { - return this._post(`/api/repos/${owner}/${repo}/secrets`, secret); + createSecret(repoId: number, secret: Partial): Promise { + return this._post(`/api/repos/${repoId}/secrets`, secret); } - updateSecret(owner: string, repo: string, secret: Partial): Promise { - return this._patch(`/api/repos/${owner}/${repo}/secrets/${secret.name}`, secret); + updateSecret(repoId: number, secret: Partial): Promise { + return this._patch(`/api/repos/${repoId}/secrets/${secret.name}`, secret); } - deleteSecret(owner: string, repo: string, secretName: string): Promise { - return this._delete(`/api/repos/${owner}/${repo}/secrets/${secretName}`); + deleteSecret(repoId: number, secretName: string): Promise { + return this._delete(`/api/repos/${repoId}/secrets/${secretName}`); } - getRegistryList(owner: string, repo: string, page: number): Promise { - return this._get(`/api/repos/${owner}/${repo}/registry?page=${page}`) as Promise; + getRegistryList(repoId: number, page: number): Promise { + return this._get(`/api/repos/${repoId}/registry?page=${page}`) as Promise; } - createRegistry(owner: string, repo: string, registry: Partial): Promise { - return this._post(`/api/repos/${owner}/${repo}/registry`, registry); + createRegistry(repoId: number, registry: Partial): Promise { + return this._post(`/api/repos/${repoId}/registry`, registry); } - updateRegistry(owner: string, repo: string, registry: Partial): Promise { - return this._patch(`/api/repos/${owner}/${repo}/registry/${registry.address}`, registry); + updateRegistry(repoId: number, registry: Partial): Promise { + return this._patch(`/api/repos/${repoId}/registry/${registry.address}`, registry); } - deleteRegistry(owner: string, repo: string, registryAddress: string): Promise { - return this._delete(`/api/repos/${owner}/${repo}/registry/${registryAddress}`); + deleteRegistry(repoId: number, registryAddress: string): Promise { + return this._delete(`/api/repos/${repoId}/registry/${registryAddress}`); } - getCronList(owner: string, repo: string, page: number): Promise { - return this._get(`/api/repos/${owner}/${repo}/cron?page=${page}`) as Promise; + getCronList(repoId: number, page: number): Promise { + return this._get(`/api/repos/${repoId}/cron?page=${page}`) as Promise; } - createCron(owner: string, repo: string, cron: Partial): Promise { - return this._post(`/api/repos/${owner}/${repo}/cron`, cron); + createCron(repoId: number, cron: Partial): Promise { + return this._post(`/api/repos/${repoId}/cron`, cron); } - updateCron(owner: string, repo: string, cron: Partial): Promise { - return this._patch(`/api/repos/${owner}/${repo}/cron/${cron.id}`, cron); + updateCron(repoId: number, cron: Partial): Promise { + return this._patch(`/api/repos/${repoId}/cron/${cron.id}`, cron); } - deleteCron(owner: string, repo: string, cronId: number): Promise { - return this._delete(`/api/repos/${owner}/${repo}/cron/${cronId}`); + deleteCron(repoId: number, cronId: number): Promise { + return this._delete(`/api/repos/${repoId}/cron/${cronId}`); } - runCron(owner: string, repo: string, cronId: number): Promise { - return this._post(`/api/repos/${owner}/${repo}/cron/${cronId}`) as Promise; + runCron(repoId: number, cronId: number): Promise { + return this._post(`/api/repos/${repoId}/cron/${cronId}`) as Promise; } getOrgPermissions(owner: string): Promise { @@ -293,14 +296,13 @@ export default class WoodpeckerClient extends ApiClient { } streamLogs( - owner: string, - repo: string, + repoId: number, pipeline: number, step: number, // eslint-disable-next-line promise/prefer-await-to-callbacks callback: (data: PipelineLog) => void, ): EventSource { - return this._subscribe(`/api/stream/logs/${owner}/${repo}/${pipeline}/${step}`, callback, { + return this._subscribe(`/api/stream/logs/${repoId}/${pipeline}/${step}`, callback, { reconnect: true, }); } diff --git a/web/src/lib/api/types/pipeline.ts b/web/src/lib/api/types/pipeline.ts index f319fce78..fa3fb3cc2 100644 --- a/web/src/lib/api/types/pipeline.ts +++ b/web/src/lib/api/types/pipeline.ts @@ -127,7 +127,5 @@ export type PipelineLog = { }; export type PipelineFeed = Pipeline & { - owner: string; - name: string; - full_name: string; + repo_id: number; }; diff --git a/web/src/lib/api/types/repo.ts b/web/src/lib/api/types/repo.ts index 1e95795f9..4825f6a2a 100644 --- a/web/src/lib/api/types/repo.ts +++ b/web/src/lib/api/types/repo.ts @@ -6,6 +6,9 @@ export type Repo = { // The unique identifier for the repository. id: number; + // The id of the repository on the source control management system. + forge_remote_id: string; + // The source control management being used. // Currently this is either 'git' or 'hg' (Mercurial). scm: string; @@ -57,6 +60,7 @@ export type Repo = { // Events that will cancel running pipelines before starting a new one cancel_previous_pipeline_events: string[]; + netrc_only_trusted: boolean; }; diff --git a/web/src/router.ts b/web/src/router.ts index 055dc6410..6b79cfce6 100644 --- a/web/src/router.ts +++ b/web/src/router.ts @@ -12,31 +12,108 @@ const routes: RouteRecordRaw[] = [ }, { path: '/repos', - name: 'repos', - component: (): Component => import('~/views/Repos.vue'), - meta: { authentication: 'required' }, + component: (): Component => import('~/views/RouterView.vue'), + children: [ + { + path: '', + name: 'repos', + component: (): Component => import('~/views/Repos.vue'), + meta: { authentication: 'required' }, + }, + { + path: 'add', + name: 'repo-add', + component: (): Component => import('~/views/RepoAdd.vue'), + meta: { authentication: 'required' }, + }, + { + path: ':repoId', + name: 'repo-wrapper', + component: (): Component => import('~/views/repo/RepoWrapper.vue'), + props: true, + children: [ + { + path: '', + name: 'repo', + component: (): Component => import('~/views/repo/RepoPipelines.vue'), + meta: { repoHeader: true }, + }, + { + path: 'branches', + name: 'repo-branches', + component: (): Component => import('~/views/repo/RepoBranches.vue'), + meta: { repoHeader: true }, + }, + { + path: 'branches/:branch', + name: 'repo-branch', + component: (): Component => import('~/views/repo/RepoBranch.vue'), + meta: { repoHeader: true }, + props: (route) => ({ branch: route.params.branch }), + }, + { + path: 'pull-requests', + name: 'repo-pull-requests', + component: (): Component => import('~/views/repo/RepoPullRequests.vue'), + meta: { repoHeader: true }, + }, + { + path: 'pull-requests/:pullRequest', + name: 'repo-pull-request', + component: (): Component => import('~/views/repo/RepoPullRequest.vue'), + meta: { repoHeader: true }, + props: (route) => ({ pullRequest: route.params.pullRequest }), + }, + { + path: 'pipeline/:pipelineId', + component: (): Component => import('~/views/repo/pipeline/PipelineWrapper.vue'), + props: true, + children: [ + { + path: ':stepId?', + name: 'repo-pipeline', + component: (): Component => import('~/views/repo/pipeline/Pipeline.vue'), + props: true, + }, + { + path: 'changed-files', + name: 'repo-pipeline-changed-files', + component: (): Component => import('~/views/repo/pipeline/PipelineChangedFiles.vue'), + }, + { + path: 'config', + name: 'repo-pipeline-config', + component: (): Component => import('~/views/repo/pipeline/PipelineConfig.vue'), + props: true, + }, + ], + }, + { + path: 'settings', + name: 'repo-settings', + component: (): Component => import('~/views/repo/RepoSettings.vue'), + meta: { authentication: 'required' }, + props: true, + }, + ], + }, + { + path: ':repoOwner/:repoName/:pathMatch(.*)*', + component: () => import('~/views/repo/RepoDeprecatedRedirect.vue'), + props: true, + }, + ], }, { - path: '/repo/add', - name: 'repo-add', - component: (): Component => import('~/views/RepoAdd.vue'), - meta: { authentication: 'required' }, - }, - { - path: '/:repoOwner', - name: 'repos-owner', - component: (): Component => import('~/views/ReposOwner.vue'), - props: true, - }, - { - path: '/org/:repoOwner', + path: '/org/:orgName', component: (): Component => import('~/views/org/OrgWrapper.vue'), props: true, children: [ { path: '', name: 'org', - redirect: (route) => ({ name: 'repos-owner', params: route.params }), + component: (): Component => import('~/views/org/OrgRepos.vue'), + props: true, }, { path: 'settings', @@ -48,114 +125,25 @@ const routes: RouteRecordRaw[] = [ ], }, { - path: '/:repoOwner/:repoName', - name: 'repo-wrapper', - component: (): Component => import('~/views/repo/RepoWrapper.vue'), - props: true, + path: '/admin', + component: (): Component => import('~/views/RouterView.vue'), + meta: { authentication: 'required' }, children: [ { path: '', - name: 'repo', - component: (): Component => import('~/views/repo/RepoPipelines.vue'), - meta: { repoHeader: true }, - }, - { - path: 'branches', - name: 'repo-branches', - component: (): Component => import('~/views/repo/RepoBranches.vue'), - meta: { repoHeader: true }, - props: (route) => ({ branch: route.params.branch }), - }, - { - path: 'branches/:branch', - name: 'repo-branch', - component: (): Component => import('~/views/repo/RepoBranch.vue'), - meta: { repoHeader: true }, - props: (route) => ({ branch: route.params.branch }), - }, - { - path: 'pull-requests', - name: 'repo-pull-requests', - component: (): Component => import('~/views/repo/RepoPullRequests.vue'), - meta: { repoHeader: true }, - }, - { - path: 'pull-requests/:pullRequest', - name: 'repo-pull-request', - component: (): Component => import('~/views/repo/RepoPullRequest.vue'), - meta: { repoHeader: true }, - props: (route) => ({ pullRequest: route.params.pullRequest }), - }, - { - path: 'pipeline/:pipelineId', - component: (): Component => import('~/views/repo/pipeline/PipelineWrapper.vue'), + name: 'admin', + component: (): Component => import('~/views/admin/Admin.vue'), props: true, - children: [ - { - path: ':stepId?', - name: 'repo-pipeline', - component: (): Component => import('~/views/repo/pipeline/Pipeline.vue'), - props: true, - }, - { - path: 'changed-files', - name: 'repo-pipeline-changed-files', - component: (): Component => import('~/views/repo/pipeline/PipelineChangedFiles.vue'), - }, - { - path: 'config', - name: 'repo-pipeline-config', - component: (): Component => import('~/views/repo/pipeline/PipelineConfig.vue'), - props: true, - }, - ], }, { path: 'settings', - name: 'repo-settings', - component: (): Component => import('~/views/repo/RepoSettings.vue'), - meta: { authentication: 'required' }, + name: 'admin-settings', + component: (): Component => import('~/views/admin/AdminSettings.vue'), props: true, }, - // TODO: redirect to support backwards compatibility => remove after some time - { - path: ':pipelineId', - redirect: (route) => ({ name: 'repo-pipeline', params: route.params }), - }, - { - path: 'build/:pipelineId', - redirect: (route) => ({ name: 'repo-pipeline', params: route.params }), - children: [ - { - path: ':procId?', - redirect: (route) => ({ name: 'repo-pipeline', params: route.params }), - }, - { - path: 'changed-files', - redirect: (route) => ({ name: 'repo-pipeline-changed-files', params: route.params }), - }, - { - path: 'config', - redirect: (route) => ({ name: 'repo-pipeline-config', params: route.params }), - }, - ], - }, ], }, - { - path: '/admin', - name: 'admin', - component: (): Component => import('~/views/admin/Admin.vue'), - meta: { authentication: 'required' }, - props: true, - }, - { - path: '/admin/settings', - name: 'admin-settings', - component: (): Component => import('~/views/admin/AdminSettings.vue'), - meta: { authentication: 'required' }, - props: true, - }, + { path: '/user', name: 'user', @@ -177,6 +165,19 @@ const routes: RouteRecordRaw[] = [ meta: { blank: true }, props: true, }, + + // TODO: deprecated routes => remove after some time + { + path: '/:ownerOrOrgId', + redirect: (route) => ({ name: 'org', params: route.params }), + }, + { + path: '/:repoOwner/:repoName/:pathMatch(.*)*', + component: () => import('~/views/repo/RepoDeprecatedRedirect.vue'), + props: true, + }, + + // not found handler { path: '/:pathMatch(.*)*', name: 'not-found', @@ -198,7 +199,8 @@ router.beforeEach(async (to, _, next) => { } const authentication = useAuthentication(); - if (to.meta.authentication === 'required' && !authentication.isAuthenticated) { + const authenticationRequired = to.matched.some((record) => record.meta.authentication === 'required'); + if (authenticationRequired && !authentication.isAuthenticated) { next({ name: 'login', query: { url: to.fullPath } }); return; } diff --git a/web/src/store/pipelines.ts b/web/src/store/pipelines.ts index 7e9137f5f..a511d97df 100644 --- a/web/src/store/pipelines.ts +++ b/web/src/store/pipelines.ts @@ -4,41 +4,36 @@ import { computed, reactive, Ref, ref } from 'vue'; import useApiClient from '~/compositions/useApiClient'; import { Pipeline, PipelineFeed, PipelineStep } from '~/lib/api/types'; import { useRepoStore } from '~/store/repos'; -import { comparePipelines, isPipelineActive, repoSlug } from '~/utils/helpers'; +import { comparePipelines, isPipelineActive } from '~/utils/helpers'; export const usePipelineStore = defineStore('pipelines', () => { const apiClient = useApiClient(); const repoStore = useRepoStore(); - const pipelines: Map> = reactive(new Map()); + const pipelines: Map> = reactive(new Map()); - function setPipeline(owner: string, repo: string, pipeline: Pipeline) { - const _repoSlug = repoSlug(owner, repo); - const repoPipelines = pipelines.get(_repoSlug) || new Map(); + function setPipeline(repoId: number, pipeline: Pipeline) { + const repoPipelines = pipelines.get(repoId) || new Map(); repoPipelines.set(pipeline.number, { ...(repoPipelines.get(pipeline.number) || {}), ...pipeline, }); - pipelines.set(_repoSlug, repoPipelines); + pipelines.set(repoId, repoPipelines); } - function getRepoPipelines(owner: Ref, repo: Ref) { - return computed(() => { - const slug = repoSlug(owner.value, repo.value); - return Array.from(pipelines.get(slug)?.values() || []).sort(comparePipelines); - }); + function getRepoPipelines(repoId: Ref) { + return computed(() => Array.from(pipelines.get(repoId.value)?.values() || []).sort(comparePipelines)); } - function getPipeline(owner: Ref, repo: Ref, _pipelineNumber: Ref) { + function getPipeline(repoId: Ref, _pipelineNumber: Ref) { return computed(() => { - const slug = repoSlug(owner.value, repo.value); const pipelineNumber = parseInt(_pipelineNumber.value, 10); - return pipelines.get(slug)?.get(pipelineNumber); + return pipelines.get(repoId.value)?.get(pipelineNumber); }); } - function setStep(owner: string, repo: string, pipelineNumber: number, step: PipelineStep) { - const pipeline = getPipeline(ref(owner), ref(repo), ref(pipelineNumber.toString())).value; + function setStep(repoId: number, pipelineNumber: number, step: PipelineStep) { + const pipeline = getPipeline(ref(repoId), ref(pipelineNumber.toString())).value; if (!pipeline) { throw new Error("Can't find pipeline"); } @@ -48,38 +43,36 @@ export const usePipelineStore = defineStore('pipelines', () => { } pipeline.steps = [...pipeline.steps.filter((p) => p.pid !== step.pid), step]; - setPipeline(owner, repo, pipeline); + setPipeline(repoId, pipeline); } - async function loadRepoPipelines(owner: string, repo: string) { - const _pipelines = await apiClient.getPipelineList(owner, repo); + async function loadRepoPipelines(repoId: number) { + const _pipelines = await apiClient.getPipelineList(repoId); _pipelines.forEach((pipeline) => { - setPipeline(owner, repo, pipeline); + setPipeline(repoId, pipeline); }); } - async function loadPipeline(owner: string, repo: string, pipelinesNumber: number) { - const pipeline = await apiClient.getPipeline(owner, repo, pipelinesNumber); - setPipeline(owner, repo, pipeline); + async function loadPipeline(repoId: number, pipelinesNumber: number) { + const pipeline = await apiClient.getPipeline(repoId, pipelinesNumber); + setPipeline(repoId, pipeline); } const pipelineFeed = computed(() => Array.from(pipelines.entries()) - .reduce((acc, [_repoSlug, repoPipelines]) => { + .reduce((acc, [_repoId, repoPipelines]) => { const repoPipelinesArray = Array.from(repoPipelines.entries()).map( ([_pipelineNumber, pipeline]) => { ...pipeline, - full_name: _repoSlug, - owner: _repoSlug.split('/')[0], - name: _repoSlug.split('/')[1], + repo_id: _repoId, number: _pipelineNumber, }, ); return [...acc, ...repoPipelinesArray]; }, []) .sort(comparePipelines) - .filter((pipeline) => repoStore.ownedRepoSlugs.includes(pipeline.full_name)), + .filter((pipeline) => repoStore.ownedRepoIds.includes(pipeline.repo_id)), ); const activePipelines = computed(() => pipelineFeed.value.filter(isPipelineActive)); @@ -89,7 +82,7 @@ export const usePipelineStore = defineStore('pipelines', () => { const _pipelines = await apiClient.getPipelineFeed(); _pipelines.forEach((pipeline) => { - setPipeline(pipeline.owner, pipeline.name, pipeline); + setPipeline(pipeline.repo_id, pipeline); }); } diff --git a/web/src/store/repos.ts b/web/src/store/repos.ts index ff70de560..775615de7 100644 --- a/web/src/store/repos.ts +++ b/web/src/store/repos.ts @@ -3,49 +3,45 @@ import { computed, reactive, Ref, ref } from 'vue'; import useApiClient from '~/compositions/useApiClient'; import { Repo } from '~/lib/api/types'; -import { repoSlug } from '~/utils/helpers'; export const useRepoStore = defineStore('repos', () => { const apiClient = useApiClient(); - const repos: Map = reactive(new Map()); - const ownedRepoSlugs = ref([]); + const repos: Map = reactive(new Map()); + const ownedRepoIds = ref([]); const ownedRepos = computed(() => Array.from(repos.entries()) - .filter(([slug]) => ownedRepoSlugs.value.includes(slug)) + .filter(([repoId]) => ownedRepoIds.value.includes(repoId)) .map(([, repo]) => repo), ); - function getRepo(owner: Ref, name: Ref) { - return computed(() => { - const slug = repoSlug(owner.value, name.value); - return repos.get(slug); - }); + function getRepo(repoId: Ref) { + return computed(() => repos.get(repoId.value)); } function setRepo(repo: Repo) { - repos.set(repoSlug(repo), repo); + repos.set(repo.id, repo); } - async function loadRepo(owner: string, name: string) { - const repo = await apiClient.getRepo(owner, name); - repos.set(repoSlug(repo), repo); + async function loadRepo(repoId: number) { + const repo = await apiClient.getRepo(repoId); + repos.set(repo.id, repo); return repo; } async function loadRepos() { const _ownedRepos = await apiClient.getRepoList(); _ownedRepos.forEach((repo) => { - repos.set(repoSlug(repo), repo); + repos.set(repo.id, repo); }); - ownedRepoSlugs.value = _ownedRepos.map((repo) => repoSlug(repo)); + ownedRepoIds.value = _ownedRepos.map((repo) => repo.id); } return { repos, ownedRepos, - ownedRepoSlugs, + ownedRepoIds, getRepo, setRepo, loadRepo, diff --git a/web/src/views/RepoAdd.vue b/web/src/views/RepoAdd.vue index 6165eeb9f..6eb9a65f4 100644 --- a/web/src/views/RepoAdd.vue +++ b/web/src/views/RepoAdd.vue @@ -9,7 +9,7 @@ v-for="repo in searchedRepos" :key="repo.id" class="items-center" - :to="repo.active ? { name: 'repo', params: { repoOwner: repo.owner, repoName: repo.name } } : undefined" + :to="repo.active ? { name: 'repo', params: { repoId: repo.id } } : undefined" > {{ repo.full_name }} {{ $t('repo.enable.enabled') }} @@ -25,8 +25,8 @@ - diff --git a/web/src/views/Repos.vue b/web/src/views/Repos.vue index 2f5fb727e..af8990446 100644 --- a/web/src/views/Repos.vue +++ b/web/src/views/Repos.vue @@ -9,11 +9,7 @@
- + {{ `${repo.owner} / ${repo.name}` }}
diff --git a/web/src/views/ReposOwner.vue b/web/src/views/ReposOwner.vue deleted file mode 100644 index 98cd04940..000000000 --- a/web/src/views/ReposOwner.vue +++ /dev/null @@ -1,76 +0,0 @@ - - - diff --git a/web/src/views/RouterView.vue b/web/src/views/RouterView.vue new file mode 100644 index 000000000..98240aef8 --- /dev/null +++ b/web/src/views/RouterView.vue @@ -0,0 +1,3 @@ + diff --git a/web/src/views/org/OrgRepos.vue b/web/src/views/org/OrgRepos.vue new file mode 100644 index 000000000..12f326101 --- /dev/null +++ b/web/src/views/org/OrgRepos.vue @@ -0,0 +1,57 @@ + + + diff --git a/web/src/views/org/OrgSettings.vue b/web/src/views/org/OrgSettings.vue index 499f4ee03..f932120e2 100644 --- a/web/src/views/org/OrgSettings.vue +++ b/web/src/views/org/OrgSettings.vue @@ -2,7 +2,7 @@ - diff --git a/web/src/views/org/OrgWrapper.vue b/web/src/views/org/OrgWrapper.vue index 5592098bc..149d0f6c4 100644 --- a/web/src/views/org/OrgWrapper.vue +++ b/web/src/views/org/OrgWrapper.vue @@ -18,48 +18,35 @@ - diff --git a/web/src/views/repo/RepoBranches.vue b/web/src/views/repo/RepoBranches.vue index f44f6e7a7..a74d2d997 100644 --- a/web/src/views/repo/RepoBranches.vue +++ b/web/src/views/repo/RepoBranches.vue @@ -31,7 +31,7 @@ async function loadBranches(page: number): Promise { throw new Error('Unexpected: "repo" should be provided at this place'); } - return apiClient.getRepoBranches(repo.value.owner, repo.value.name, page); + return apiClient.getRepoBranches(repo.value.id, page); } const { resetPage, data: branches } = usePagination(loadBranches); diff --git a/web/src/views/repo/RepoDeprecatedRedirect.vue b/web/src/views/repo/RepoDeprecatedRedirect.vue new file mode 100644 index 000000000..6ada41a0b --- /dev/null +++ b/web/src/views/repo/RepoDeprecatedRedirect.vue @@ -0,0 +1,54 @@ + + + diff --git a/web/src/views/repo/RepoPullRequests.vue b/web/src/views/repo/RepoPullRequests.vue index 6bdea1f03..a384d1c63 100644 --- a/web/src/views/repo/RepoPullRequests.vue +++ b/web/src/views/repo/RepoPullRequests.vue @@ -35,7 +35,7 @@ async function loadPullRequests(page: number): Promise { throw new Error('Unexpected: "repo" should be provided at this place'); } - return apiClient.getRepoPullRequests(repo.value.owner, repo.value.name, page); + return apiClient.getRepoPullRequests(repo.value.id, page); } const { resetPage, data: pullRequests } = usePagination(loadPullRequests); diff --git a/web/src/views/repo/RepoSettings.vue b/web/src/views/repo/RepoSettings.vue index a09d3d6cf..4a2fad0ab 100644 --- a/web/src/views/repo/RepoSettings.vue +++ b/web/src/views/repo/RepoSettings.vue @@ -2,14 +2,11 @@