mirror of
https://github.com/woodpecker-ci/woodpecker.git
synced 2024-11-22 18:01:02 +00:00
Global and organization registries (#1672)
Co-authored-by: Anbraten <6918444+anbraten@users.noreply.github.com>
This commit is contained in:
parent
e5f3e67bf2
commit
28e982fffb
65 changed files with 3260 additions and 269 deletions
30
cli/admin/admin.go
Normal file
30
cli/admin/admin.go
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
// Copyright 2024 Woodpecker Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package admin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
|
||||||
|
"go.woodpecker-ci.org/woodpecker/v2/cli/admin/registry"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Command exports the admin command set.
|
||||||
|
var Command = &cli.Command{
|
||||||
|
Name: "admin",
|
||||||
|
Usage: "administer server settings",
|
||||||
|
Subcommands: []*cli.Command{
|
||||||
|
registry.Command,
|
||||||
|
},
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright 2023 Woodpecker Authors
|
// Copyright 2024 Woodpecker Authors
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
@ -21,7 +21,7 @@ import (
|
||||||
// Command exports the registry command set.
|
// Command exports the registry command set.
|
||||||
var Command = &cli.Command{
|
var Command = &cli.Command{
|
||||||
Name: "registry",
|
Name: "registry",
|
||||||
Usage: "manage registries",
|
Usage: "manage global registries",
|
||||||
Subcommands: []*cli.Command{
|
Subcommands: []*cli.Command{
|
||||||
registryCreateCmd,
|
registryCreateCmd,
|
||||||
registryDeleteCmd,
|
registryDeleteCmd,
|
75
cli/admin/registry/registry_add.go
Normal file
75
cli/admin/registry/registry_add.go
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
// Copyright 2024 Woodpecker Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package registry
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
|
||||||
|
"go.woodpecker-ci.org/woodpecker/v2/cli/internal"
|
||||||
|
"go.woodpecker-ci.org/woodpecker/v2/woodpecker-go/woodpecker"
|
||||||
|
)
|
||||||
|
|
||||||
|
var registryCreateCmd = &cli.Command{
|
||||||
|
Name: "add",
|
||||||
|
Usage: "adds a registry",
|
||||||
|
Action: registryCreate,
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "hostname",
|
||||||
|
Usage: "registry hostname",
|
||||||
|
Value: "docker.io",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "username",
|
||||||
|
Usage: "registry username",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "password",
|
||||||
|
Usage: "registry password",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func registryCreate(c *cli.Context) error {
|
||||||
|
var (
|
||||||
|
hostname = c.String("hostname")
|
||||||
|
username = c.String("username")
|
||||||
|
password = c.String("password")
|
||||||
|
)
|
||||||
|
|
||||||
|
client, err := internal.NewClient(c)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
registry := &woodpecker.Registry{
|
||||||
|
Address: hostname,
|
||||||
|
Username: username,
|
||||||
|
Password: password,
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(registry.Password, "@") {
|
||||||
|
path := strings.TrimPrefix(registry.Password, "@")
|
||||||
|
out, err := os.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
registry.Password = string(out)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = client.GlobalRegistryCreate(registry)
|
||||||
|
return err
|
||||||
|
}
|
62
cli/admin/registry/registry_info.go
Normal file
62
cli/admin/registry/registry_info.go
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
// Copyright 2024 Woodpecker Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package registry
|
||||||
|
|
||||||
|
import (
|
||||||
|
"html/template"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
|
||||||
|
"go.woodpecker-ci.org/woodpecker/v2/cli/common"
|
||||||
|
"go.woodpecker-ci.org/woodpecker/v2/cli/internal"
|
||||||
|
)
|
||||||
|
|
||||||
|
var registryInfoCmd = &cli.Command{
|
||||||
|
Name: "info",
|
||||||
|
Usage: "display registry info",
|
||||||
|
Action: registryInfo,
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "hostname",
|
||||||
|
Usage: "registry hostname",
|
||||||
|
Value: "docker.io",
|
||||||
|
},
|
||||||
|
common.FormatFlag(tmplRegistryList, true),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func registryInfo(c *cli.Context) error {
|
||||||
|
var (
|
||||||
|
hostname = c.String("hostname")
|
||||||
|
format = c.String("format") + "\n"
|
||||||
|
)
|
||||||
|
|
||||||
|
client, err := internal.NewClient(c)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
registry, err := client.GlobalRegistry(hostname)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
tmpl, err := template.New("_").Parse(format)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return tmpl.Execute(os.Stdout, registry)
|
||||||
|
}
|
65
cli/admin/registry/registry_list.go
Normal file
65
cli/admin/registry/registry_list.go
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
// Copyright 2024 Woodpecker Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package registry
|
||||||
|
|
||||||
|
import (
|
||||||
|
"html/template"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
|
||||||
|
"go.woodpecker-ci.org/woodpecker/v2/cli/common"
|
||||||
|
"go.woodpecker-ci.org/woodpecker/v2/cli/internal"
|
||||||
|
)
|
||||||
|
|
||||||
|
var registryListCmd = &cli.Command{
|
||||||
|
Name: "ls",
|
||||||
|
Usage: "list registries",
|
||||||
|
Action: registryList,
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
common.FormatFlag(tmplRegistryList, true),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func registryList(c *cli.Context) error {
|
||||||
|
format := c.String("format") + "\n"
|
||||||
|
|
||||||
|
client, err := internal.NewClient(c)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
list, err := client.GlobalRegistryList()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
tmpl, err := template.New("_").Parse(format)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, registry := range list {
|
||||||
|
if err := tmpl.Execute(os.Stdout, registry); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Template for registry list information.
|
||||||
|
var tmplRegistryList = "\x1b[33m{{ .Address }} \x1b[0m" + `
|
||||||
|
Username: {{ .Username }}
|
||||||
|
Email: {{ .Email }}
|
||||||
|
`
|
45
cli/admin/registry/registry_rm.go
Normal file
45
cli/admin/registry/registry_rm.go
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
// Copyright 2024 Woodpecker Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package registry
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
|
||||||
|
"go.woodpecker-ci.org/woodpecker/v2/cli/internal"
|
||||||
|
)
|
||||||
|
|
||||||
|
var registryDeleteCmd = &cli.Command{
|
||||||
|
Name: "rm",
|
||||||
|
Usage: "remove a registry",
|
||||||
|
Action: registryDelete,
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "hostname",
|
||||||
|
Usage: "registry hostname",
|
||||||
|
Value: "docker.io",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func registryDelete(c *cli.Context) error {
|
||||||
|
hostname := c.String("hostname")
|
||||||
|
|
||||||
|
client, err := internal.NewClient(c)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return client.GlobalRegistryDelete(hostname)
|
||||||
|
}
|
78
cli/admin/registry/registry_set.go
Normal file
78
cli/admin/registry/registry_set.go
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
// Copyright 2024 Woodpecker Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package registry
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
|
||||||
|
"go.woodpecker-ci.org/woodpecker/v2/cli/common"
|
||||||
|
"go.woodpecker-ci.org/woodpecker/v2/cli/internal"
|
||||||
|
"go.woodpecker-ci.org/woodpecker/v2/woodpecker-go/woodpecker"
|
||||||
|
)
|
||||||
|
|
||||||
|
var registryUpdateCmd = &cli.Command{
|
||||||
|
Name: "update",
|
||||||
|
Usage: "update a registry",
|
||||||
|
Action: registryUpdate,
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
common.OrgFlag,
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "hostname",
|
||||||
|
Usage: "registry hostname",
|
||||||
|
Value: "docker.io",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "username",
|
||||||
|
Usage: "registry username",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "password",
|
||||||
|
Usage: "registry password",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func registryUpdate(c *cli.Context) error {
|
||||||
|
var (
|
||||||
|
hostname = c.String("hostname")
|
||||||
|
username = c.String("username")
|
||||||
|
password = c.String("password")
|
||||||
|
)
|
||||||
|
|
||||||
|
client, err := internal.NewClient(c)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
registry := &woodpecker.Registry{
|
||||||
|
Address: hostname,
|
||||||
|
Username: username,
|
||||||
|
Password: password,
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(registry.Password, "@") {
|
||||||
|
path := strings.TrimPrefix(registry.Password, "@")
|
||||||
|
out, err := os.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
registry.Password = string(out)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = client.GlobalRegistryUpdate(registry)
|
||||||
|
return err
|
||||||
|
}
|
30
cli/org/org.go
Normal file
30
cli/org/org.go
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
// Copyright 2024 Woodpecker Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package org
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
|
||||||
|
"go.woodpecker-ci.org/woodpecker/v2/cli/org/registry"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Command exports the org command set.
|
||||||
|
var Command = &cli.Command{
|
||||||
|
Name: "org",
|
||||||
|
Usage: "manage organizations",
|
||||||
|
Subcommands: []*cli.Command{
|
||||||
|
registry.Command,
|
||||||
|
},
|
||||||
|
}
|
60
cli/org/registry/registry.go
Normal file
60
cli/org/registry/registry.go
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
// Copyright 2024 Woodpecker Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package registry
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
|
||||||
|
"go.woodpecker-ci.org/woodpecker/v2/woodpecker-go/woodpecker"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Command exports the registry command set.
|
||||||
|
var Command = &cli.Command{
|
||||||
|
Name: "registry",
|
||||||
|
Usage: "manage organization registries",
|
||||||
|
Subcommands: []*cli.Command{
|
||||||
|
registryCreateCmd,
|
||||||
|
registryDeleteCmd,
|
||||||
|
registryUpdateCmd,
|
||||||
|
registryInfoCmd,
|
||||||
|
registryListCmd,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseTargetArgs(client woodpecker.Client, c *cli.Context) (orgID int64, err error) {
|
||||||
|
orgIDOrName := c.String("organization")
|
||||||
|
if orgIDOrName == "" {
|
||||||
|
orgIDOrName = c.Args().First()
|
||||||
|
}
|
||||||
|
|
||||||
|
if orgIDOrName == "" {
|
||||||
|
if err := cli.ShowSubcommandHelp(c); err != nil {
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if orgID, err := strconv.ParseInt(orgIDOrName, 10, 64); err == nil {
|
||||||
|
return orgID, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
org, err := client.OrgLookup(orgIDOrName)
|
||||||
|
if err != nil {
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return org.ID, nil
|
||||||
|
}
|
83
cli/org/registry/registry_add.go
Normal file
83
cli/org/registry/registry_add.go
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
// Copyright 2024 Woodpecker Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package registry
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
|
||||||
|
"go.woodpecker-ci.org/woodpecker/v2/cli/common"
|
||||||
|
"go.woodpecker-ci.org/woodpecker/v2/cli/internal"
|
||||||
|
"go.woodpecker-ci.org/woodpecker/v2/woodpecker-go/woodpecker"
|
||||||
|
)
|
||||||
|
|
||||||
|
var registryCreateCmd = &cli.Command{
|
||||||
|
Name: "add",
|
||||||
|
Usage: "adds a registry",
|
||||||
|
ArgsUsage: "[org-id|org-full-name]",
|
||||||
|
Action: registryCreate,
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
common.OrgFlag,
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "hostname",
|
||||||
|
Usage: "registry hostname",
|
||||||
|
Value: "docker.io",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "username",
|
||||||
|
Usage: "registry username",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "password",
|
||||||
|
Usage: "registry password",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func registryCreate(c *cli.Context) error {
|
||||||
|
var (
|
||||||
|
hostname = c.String("hostname")
|
||||||
|
username = c.String("username")
|
||||||
|
password = c.String("password")
|
||||||
|
)
|
||||||
|
|
||||||
|
client, err := internal.NewClient(c)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
registry := &woodpecker.Registry{
|
||||||
|
Address: hostname,
|
||||||
|
Username: username,
|
||||||
|
Password: password,
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(registry.Password, "@") {
|
||||||
|
path := strings.TrimPrefix(registry.Password, "@")
|
||||||
|
out, err := os.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
registry.Password = string(out)
|
||||||
|
}
|
||||||
|
|
||||||
|
orgID, err := parseTargetArgs(client, c)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = client.OrgRegistryCreate(orgID, registry)
|
||||||
|
return err
|
||||||
|
}
|
69
cli/org/registry/registry_info.go
Normal file
69
cli/org/registry/registry_info.go
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
// Copyright 2024 Woodpecker Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package registry
|
||||||
|
|
||||||
|
import (
|
||||||
|
"html/template"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
|
||||||
|
"go.woodpecker-ci.org/woodpecker/v2/cli/common"
|
||||||
|
"go.woodpecker-ci.org/woodpecker/v2/cli/internal"
|
||||||
|
)
|
||||||
|
|
||||||
|
var registryInfoCmd = &cli.Command{
|
||||||
|
Name: "info",
|
||||||
|
Usage: "display registry info",
|
||||||
|
ArgsUsage: "[org-id|org-full-name]",
|
||||||
|
Action: registryInfo,
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
common.OrgFlag,
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "hostname",
|
||||||
|
Usage: "registry hostname",
|
||||||
|
Value: "docker.io",
|
||||||
|
},
|
||||||
|
common.FormatFlag(tmplRegistryList, true),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func registryInfo(c *cli.Context) error {
|
||||||
|
var (
|
||||||
|
hostname = c.String("hostname")
|
||||||
|
format = c.String("format") + "\n"
|
||||||
|
)
|
||||||
|
|
||||||
|
client, err := internal.NewClient(c)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
orgID, err := parseTargetArgs(client, c)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
registry, err := client.OrgRegistry(orgID, hostname)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
tmpl, err := template.New("_").Parse(format)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return tmpl.Execute(os.Stdout, registry)
|
||||||
|
}
|
72
cli/org/registry/registry_list.go
Normal file
72
cli/org/registry/registry_list.go
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
// Copyright 2024 Woodpecker Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package registry
|
||||||
|
|
||||||
|
import (
|
||||||
|
"html/template"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
|
||||||
|
"go.woodpecker-ci.org/woodpecker/v2/cli/common"
|
||||||
|
"go.woodpecker-ci.org/woodpecker/v2/cli/internal"
|
||||||
|
)
|
||||||
|
|
||||||
|
var registryListCmd = &cli.Command{
|
||||||
|
Name: "ls",
|
||||||
|
Usage: "list registries",
|
||||||
|
ArgsUsage: "[org-id|org-full-name]",
|
||||||
|
Action: registryList,
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
common.OrgFlag,
|
||||||
|
common.FormatFlag(tmplRegistryList, true),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func registryList(c *cli.Context) error {
|
||||||
|
format := c.String("format") + "\n"
|
||||||
|
|
||||||
|
client, err := internal.NewClient(c)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
orgID, err := parseTargetArgs(client, c)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
list, err := client.OrgRegistryList(orgID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
tmpl, err := template.New("_").Parse(format)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, registry := range list {
|
||||||
|
if err := tmpl.Execute(os.Stdout, registry); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Template for registry list information.
|
||||||
|
var tmplRegistryList = "\x1b[33m{{ .Address }} \x1b[0m" + `
|
||||||
|
Username: {{ .Username }}
|
||||||
|
Email: {{ .Email }}
|
||||||
|
`
|
53
cli/org/registry/registry_rm.go
Normal file
53
cli/org/registry/registry_rm.go
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
// Copyright 2024 Woodpecker Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package registry
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
|
||||||
|
"go.woodpecker-ci.org/woodpecker/v2/cli/common"
|
||||||
|
"go.woodpecker-ci.org/woodpecker/v2/cli/internal"
|
||||||
|
)
|
||||||
|
|
||||||
|
var registryDeleteCmd = &cli.Command{
|
||||||
|
Name: "rm",
|
||||||
|
Usage: "remove a registry",
|
||||||
|
ArgsUsage: "[org-id|org-full-name]",
|
||||||
|
Action: registryDelete,
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
common.OrgFlag,
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "hostname",
|
||||||
|
Usage: "registry hostname",
|
||||||
|
Value: "docker.io",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func registryDelete(c *cli.Context) error {
|
||||||
|
hostname := c.String("hostname")
|
||||||
|
|
||||||
|
client, err := internal.NewClient(c)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
orgID, err := parseTargetArgs(client, c)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return client.OrgRegistryDelete(orgID, hostname)
|
||||||
|
}
|
84
cli/org/registry/registry_set.go
Normal file
84
cli/org/registry/registry_set.go
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
// Copyright 2024 Woodpecker Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package registry
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
|
||||||
|
"go.woodpecker-ci.org/woodpecker/v2/cli/common"
|
||||||
|
"go.woodpecker-ci.org/woodpecker/v2/cli/internal"
|
||||||
|
"go.woodpecker-ci.org/woodpecker/v2/woodpecker-go/woodpecker"
|
||||||
|
)
|
||||||
|
|
||||||
|
var registryUpdateCmd = &cli.Command{
|
||||||
|
Name: "update",
|
||||||
|
Usage: "update a registry",
|
||||||
|
ArgsUsage: "[org-id|org-full-name]",
|
||||||
|
Action: registryUpdate,
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
common.OrgFlag,
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "hostname",
|
||||||
|
Usage: "registry hostname",
|
||||||
|
Value: "docker.io",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "username",
|
||||||
|
Usage: "registry username",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "password",
|
||||||
|
Usage: "registry password",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func registryUpdate(c *cli.Context) error {
|
||||||
|
var (
|
||||||
|
hostname = c.String("hostname")
|
||||||
|
username = c.String("username")
|
||||||
|
password = c.String("password")
|
||||||
|
)
|
||||||
|
|
||||||
|
client, err := internal.NewClient(c)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
registry := &woodpecker.Registry{
|
||||||
|
Address: hostname,
|
||||||
|
Username: username,
|
||||||
|
Password: password,
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(registry.Password, "@") {
|
||||||
|
path := strings.TrimPrefix(registry.Password, "@")
|
||||||
|
out, err := os.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
registry.Password = string(out)
|
||||||
|
}
|
||||||
|
|
||||||
|
orgID, err := parseTargetArgs(client, c)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = client.OrgRegistryUpdate(orgID, registry)
|
||||||
|
return err
|
||||||
|
}
|
44
cli/repo/registry/registry.go
Normal file
44
cli/repo/registry/registry.go
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
// Copyright 2023 Woodpecker Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package registry
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
|
||||||
|
"go.woodpecker-ci.org/woodpecker/v2/cli/internal"
|
||||||
|
"go.woodpecker-ci.org/woodpecker/v2/woodpecker-go/woodpecker"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Command exports the registry command set.
|
||||||
|
var Command = &cli.Command{
|
||||||
|
Name: "registry",
|
||||||
|
Usage: "manage registries",
|
||||||
|
Subcommands: []*cli.Command{
|
||||||
|
registryCreateCmd,
|
||||||
|
registryDeleteCmd,
|
||||||
|
registryUpdateCmd,
|
||||||
|
registryInfoCmd,
|
||||||
|
registryListCmd,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseTargetArgs(client woodpecker.Client, c *cli.Context) (repoID int64, err error) {
|
||||||
|
repoIDOrFullName := c.String("repository")
|
||||||
|
if repoIDOrFullName == "" {
|
||||||
|
repoIDOrFullName = c.Args().First()
|
||||||
|
}
|
||||||
|
|
||||||
|
return internal.ParseRepo(client, repoIDOrFullName)
|
||||||
|
}
|
|
@ -53,19 +53,12 @@ func registryCreate(c *cli.Context) error {
|
||||||
hostname = c.String("hostname")
|
hostname = c.String("hostname")
|
||||||
username = c.String("username")
|
username = c.String("username")
|
||||||
password = c.String("password")
|
password = c.String("password")
|
||||||
repoIDOrFullName = c.String("repository")
|
|
||||||
)
|
)
|
||||||
if repoIDOrFullName == "" {
|
|
||||||
repoIDOrFullName = c.Args().First()
|
|
||||||
}
|
|
||||||
client, err := internal.NewClient(c)
|
client, err := internal.NewClient(c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
repoID, err := internal.ParseRepo(client, repoIDOrFullName)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
registry := &woodpecker.Registry{
|
registry := &woodpecker.Registry{
|
||||||
Address: hostname,
|
Address: hostname,
|
||||||
Username: username,
|
Username: username,
|
||||||
|
@ -79,8 +72,12 @@ func registryCreate(c *cli.Context) error {
|
||||||
}
|
}
|
||||||
registry.Password = string(out)
|
registry.Password = string(out)
|
||||||
}
|
}
|
||||||
if _, err := client.RegistryCreate(repoID, registry); err != nil {
|
|
||||||
|
repoID, err := parseTargetArgs(client, c)
|
||||||
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
|
_, err = client.RegistryCreate(repoID, registry)
|
||||||
|
return err
|
||||||
}
|
}
|
|
@ -43,24 +43,24 @@ var registryInfoCmd = &cli.Command{
|
||||||
func registryInfo(c *cli.Context) error {
|
func registryInfo(c *cli.Context) error {
|
||||||
var (
|
var (
|
||||||
hostname = c.String("hostname")
|
hostname = c.String("hostname")
|
||||||
repoIDOrFullName = c.String("repository")
|
|
||||||
format = c.String("format") + "\n"
|
format = c.String("format") + "\n"
|
||||||
)
|
)
|
||||||
if repoIDOrFullName == "" {
|
|
||||||
repoIDOrFullName = c.Args().First()
|
|
||||||
}
|
|
||||||
client, err := internal.NewClient(c)
|
client, err := internal.NewClient(c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
repoID, err := internal.ParseRepo(client, repoIDOrFullName)
|
|
||||||
|
repoID, err := parseTargetArgs(client, c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
registry, err := client.Registry(repoID, hostname)
|
registry, err := client.Registry(repoID, hostname)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpl, err := template.New("_").Parse(format)
|
tmpl, err := template.New("_").Parse(format)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
|
@ -36,25 +36,23 @@ var registryListCmd = &cli.Command{
|
||||||
}
|
}
|
||||||
|
|
||||||
func registryList(c *cli.Context) error {
|
func registryList(c *cli.Context) error {
|
||||||
var (
|
format := c.String("format") + "\n"
|
||||||
format = c.String("format") + "\n"
|
|
||||||
repoIDOrFullName = c.String("repository")
|
|
||||||
)
|
|
||||||
if repoIDOrFullName == "" {
|
|
||||||
repoIDOrFullName = c.Args().First()
|
|
||||||
}
|
|
||||||
client, err := internal.NewClient(c)
|
client, err := internal.NewClient(c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
repoID, err := internal.ParseRepo(client, repoIDOrFullName)
|
|
||||||
|
repoID, err := parseTargetArgs(client, c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
list, err := client.RegistryList(repoID)
|
list, err := client.RegistryList(repoID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpl, err := template.New("_").Parse(format)
|
tmpl, err := template.New("_").Parse(format)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
|
@ -37,20 +37,17 @@ var registryDeleteCmd = &cli.Command{
|
||||||
}
|
}
|
||||||
|
|
||||||
func registryDelete(c *cli.Context) error {
|
func registryDelete(c *cli.Context) error {
|
||||||
var (
|
hostname := c.String("hostname")
|
||||||
hostname = c.String("hostname")
|
|
||||||
repoIDOrFullName = c.String("repository")
|
|
||||||
)
|
|
||||||
if repoIDOrFullName == "" {
|
|
||||||
repoIDOrFullName = c.Args().First()
|
|
||||||
}
|
|
||||||
client, err := internal.NewClient(c)
|
client, err := internal.NewClient(c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
repoID, err := internal.ParseRepo(client, repoIDOrFullName)
|
|
||||||
|
repoID, err := parseTargetArgs(client, c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return client.RegistryDelete(repoID, hostname)
|
return client.RegistryDelete(repoID, hostname)
|
||||||
}
|
}
|
|
@ -53,19 +53,13 @@ func registryUpdate(c *cli.Context) error {
|
||||||
hostname = c.String("hostname")
|
hostname = c.String("hostname")
|
||||||
username = c.String("username")
|
username = c.String("username")
|
||||||
password = c.String("password")
|
password = c.String("password")
|
||||||
repoIDOrFullName = c.String("repository")
|
|
||||||
)
|
)
|
||||||
if repoIDOrFullName == "" {
|
|
||||||
repoIDOrFullName = c.Args().First()
|
|
||||||
}
|
|
||||||
client, err := internal.NewClient(c)
|
client, err := internal.NewClient(c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
repoID, err := internal.ParseRepo(client, repoIDOrFullName)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
registry := &woodpecker.Registry{
|
registry := &woodpecker.Registry{
|
||||||
Address: hostname,
|
Address: hostname,
|
||||||
Username: username,
|
Username: username,
|
||||||
|
@ -79,6 +73,12 @@ func registryUpdate(c *cli.Context) error {
|
||||||
}
|
}
|
||||||
registry.Password = string(out)
|
registry.Password = string(out)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
repoID, err := parseTargetArgs(client, c)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
_, err = client.RegistryUpdate(repoID, registry)
|
_, err = client.RegistryUpdate(repoID, registry)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
|
@ -16,6 +16,8 @@ package repo
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
|
|
||||||
|
"go.woodpecker-ci.org/woodpecker/v2/cli/repo/registry"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Command exports the repository command.
|
// Command exports the repository command.
|
||||||
|
@ -31,5 +33,6 @@ var Command = &cli.Command{
|
||||||
repoRepairCmd,
|
repoRepairCmd,
|
||||||
repoChownCmd,
|
repoChownCmd,
|
||||||
repoSyncCmd,
|
repoSyncCmd,
|
||||||
|
registry.Command,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ package main
|
||||||
import (
|
import (
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
|
|
||||||
|
"go.woodpecker-ci.org/woodpecker/v2/cli/admin"
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/cli/common"
|
"go.woodpecker-ci.org/woodpecker/v2/cli/common"
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/cli/cron"
|
"go.woodpecker-ci.org/woodpecker/v2/cli/cron"
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/cli/deploy"
|
"go.woodpecker-ci.org/woodpecker/v2/cli/deploy"
|
||||||
|
@ -25,9 +26,10 @@ import (
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/cli/lint"
|
"go.woodpecker-ci.org/woodpecker/v2/cli/lint"
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/cli/log"
|
"go.woodpecker-ci.org/woodpecker/v2/cli/log"
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/cli/loglevel"
|
"go.woodpecker-ci.org/woodpecker/v2/cli/loglevel"
|
||||||
|
"go.woodpecker-ci.org/woodpecker/v2/cli/org"
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/cli/pipeline"
|
"go.woodpecker-ci.org/woodpecker/v2/cli/pipeline"
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/cli/registry"
|
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/cli/repo"
|
"go.woodpecker-ci.org/woodpecker/v2/cli/repo"
|
||||||
|
"go.woodpecker-ci.org/woodpecker/v2/cli/repo/registry"
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/cli/secret"
|
"go.woodpecker-ci.org/woodpecker/v2/cli/secret"
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/cli/setup"
|
"go.woodpecker-ci.org/woodpecker/v2/cli/setup"
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/cli/update"
|
"go.woodpecker-ci.org/woodpecker/v2/cli/update"
|
||||||
|
@ -47,14 +49,17 @@ func newApp() *cli.App {
|
||||||
app.After = common.After
|
app.After = common.After
|
||||||
app.Suggest = true
|
app.Suggest = true
|
||||||
app.Commands = []*cli.Command{
|
app.Commands = []*cli.Command{
|
||||||
|
admin.Command,
|
||||||
|
org.Command,
|
||||||
|
repo.Command,
|
||||||
pipeline.Command,
|
pipeline.Command,
|
||||||
log.Command,
|
log.Command,
|
||||||
deploy.Command,
|
deploy.Command,
|
||||||
exec.Command,
|
exec.Command,
|
||||||
info.Command,
|
info.Command,
|
||||||
|
// TODO: Remove in 3.x
|
||||||
registry.Command,
|
registry.Command,
|
||||||
secret.Command,
|
secret.Command,
|
||||||
repo.Command,
|
|
||||||
user.Command,
|
user.Command,
|
||||||
lint.Command,
|
lint.Command,
|
||||||
loglevel.Command,
|
loglevel.Command,
|
||||||
|
|
|
@ -1123,6 +1123,233 @@ const docTemplate = `{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/orgs/{org_id}/registries": {
|
||||||
|
"get": {
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Organization registries"
|
||||||
|
],
|
||||||
|
"summary": "List organization registries",
|
||||||
|
"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 org's id",
|
||||||
|
"name": "org_id",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"default": 1,
|
||||||
|
"description": "for response pagination, page offset number",
|
||||||
|
"name": "page",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"default": 50,
|
||||||
|
"description": "for response pagination, max items per page",
|
||||||
|
"name": "perPage",
|
||||||
|
"in": "query"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/Registry"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"post": {
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Organization registries"
|
||||||
|
],
|
||||||
|
"summary": "Create an organization registry",
|
||||||
|
"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 org's id",
|
||||||
|
"name": "org_id",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "the new registry",
|
||||||
|
"name": "registryData",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/Registry"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/Registry"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/orgs/{org_id}/registries/{registry}": {
|
||||||
|
"get": {
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Organization registries"
|
||||||
|
],
|
||||||
|
"summary": "Get a organization registry by address",
|
||||||
|
"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 org's id",
|
||||||
|
"name": "org_id",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "the registry's address",
|
||||||
|
"name": "registry",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/Registry"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"delete": {
|
||||||
|
"produces": [
|
||||||
|
"text/plain"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Organization registries"
|
||||||
|
],
|
||||||
|
"summary": "Delete an organization registry by 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 org's id",
|
||||||
|
"name": "org_id",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "the registry's name",
|
||||||
|
"name": "registry",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"204": {
|
||||||
|
"description": "No Content"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"patch": {
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Organization registries"
|
||||||
|
],
|
||||||
|
"summary": "Update an organization registry by 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 org's id",
|
||||||
|
"name": "org_id",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "the registry's name",
|
||||||
|
"name": "registry",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "the update registry data",
|
||||||
|
"name": "registryData",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/Registry"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/Registry"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/orgs/{org_id}/secrets": {
|
"/orgs/{org_id}/secrets": {
|
||||||
"get": {
|
"get": {
|
||||||
"produces": [
|
"produces": [
|
||||||
|
@ -1493,6 +1720,198 @@ const docTemplate = `{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/registries": {
|
||||||
|
"get": {
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Registries"
|
||||||
|
],
|
||||||
|
"summary": "List global registries",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"default": "Bearer \u003cpersonal access token\u003e",
|
||||||
|
"description": "Insert your personal access token",
|
||||||
|
"name": "Authorization",
|
||||||
|
"in": "header",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"default": 1,
|
||||||
|
"description": "for response pagination, page offset number",
|
||||||
|
"name": "page",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"default": 50,
|
||||||
|
"description": "for response pagination, max items per page",
|
||||||
|
"name": "perPage",
|
||||||
|
"in": "query"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/Registry"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"post": {
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Registries"
|
||||||
|
],
|
||||||
|
"summary": "Create a global registry",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"default": "Bearer \u003cpersonal access token\u003e",
|
||||||
|
"description": "Insert your personal access token",
|
||||||
|
"name": "Authorization",
|
||||||
|
"in": "header",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "the registry object data",
|
||||||
|
"name": "registry",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/Registry"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/Registry"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/registries/{registry}": {
|
||||||
|
"get": {
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Registries"
|
||||||
|
],
|
||||||
|
"summary": "Get a global registry by 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 registry's name",
|
||||||
|
"name": "registry",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/Registry"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"delete": {
|
||||||
|
"produces": [
|
||||||
|
"text/plain"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Registries"
|
||||||
|
],
|
||||||
|
"summary": "Delete a global registry by 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 registry's name",
|
||||||
|
"name": "registry",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"204": {
|
||||||
|
"description": "No Content"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"patch": {
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Registries"
|
||||||
|
],
|
||||||
|
"summary": "Update a global registry by 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 registry's name",
|
||||||
|
"name": "registry",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "the registry's data",
|
||||||
|
"name": "registryData",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/Registry"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/Registry"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/repos": {
|
"/repos": {
|
||||||
"get": {
|
"get": {
|
||||||
"description": "Returns a list of all repositories. Requires admin rights.",
|
"description": "Returns a list of all repositories. Requires admin rights.",
|
||||||
|
@ -2799,7 +3218,7 @@ const docTemplate = `{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/repos/{repo_id}/registry": {
|
"/repos/{repo_id}/registries": {
|
||||||
"get": {
|
"get": {
|
||||||
"produces": [
|
"produces": [
|
||||||
"application/json"
|
"application/json"
|
||||||
|
@ -2895,7 +3314,7 @@ const docTemplate = `{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/repos/{repo_id}/registry/{registry}": {
|
"/repos/{repo_id}/registries/{registry}": {
|
||||||
"get": {
|
"get": {
|
||||||
"produces": [
|
"produces": [
|
||||||
"application/json"
|
"application/json"
|
||||||
|
@ -4376,9 +4795,18 @@ const docTemplate = `{
|
||||||
"id": {
|
"id": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
|
"org_id": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
"password": {
|
"password": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"readonly": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"repo_id": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
"username": {
|
"username": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
|
|
||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.34.1
|
// protoc-gen-go v1.34.2
|
||||||
// protoc v4.25.1
|
// protoc v4.25.1
|
||||||
// source: woodpecker.proto
|
// source: woodpecker.proto
|
||||||
|
|
||||||
|
@ -1312,7 +1312,7 @@ func file_woodpecker_proto_rawDescGZIP() []byte {
|
||||||
}
|
}
|
||||||
|
|
||||||
var file_woodpecker_proto_msgTypes = make([]protoimpl.MessageInfo, 21)
|
var file_woodpecker_proto_msgTypes = make([]protoimpl.MessageInfo, 21)
|
||||||
var file_woodpecker_proto_goTypes = []interface{}{
|
var file_woodpecker_proto_goTypes = []any{
|
||||||
(*StepState)(nil), // 0: proto.StepState
|
(*StepState)(nil), // 0: proto.StepState
|
||||||
(*WorkflowState)(nil), // 1: proto.WorkflowState
|
(*WorkflowState)(nil), // 1: proto.WorkflowState
|
||||||
(*LogEntry)(nil), // 2: proto.LogEntry
|
(*LogEntry)(nil), // 2: proto.LogEntry
|
||||||
|
@ -1380,7 +1380,7 @@ func file_woodpecker_proto_init() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !protoimpl.UnsafeEnabled {
|
if !protoimpl.UnsafeEnabled {
|
||||||
file_woodpecker_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
file_woodpecker_proto_msgTypes[0].Exporter = func(v any, i int) any {
|
||||||
switch v := v.(*StepState); i {
|
switch v := v.(*StepState); i {
|
||||||
case 0:
|
case 0:
|
||||||
return &v.state
|
return &v.state
|
||||||
|
@ -1392,7 +1392,7 @@ func file_woodpecker_proto_init() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
file_woodpecker_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
|
file_woodpecker_proto_msgTypes[1].Exporter = func(v any, i int) any {
|
||||||
switch v := v.(*WorkflowState); i {
|
switch v := v.(*WorkflowState); i {
|
||||||
case 0:
|
case 0:
|
||||||
return &v.state
|
return &v.state
|
||||||
|
@ -1404,7 +1404,7 @@ func file_woodpecker_proto_init() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
file_woodpecker_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
|
file_woodpecker_proto_msgTypes[2].Exporter = func(v any, i int) any {
|
||||||
switch v := v.(*LogEntry); i {
|
switch v := v.(*LogEntry); i {
|
||||||
case 0:
|
case 0:
|
||||||
return &v.state
|
return &v.state
|
||||||
|
@ -1416,7 +1416,7 @@ func file_woodpecker_proto_init() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
file_woodpecker_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
|
file_woodpecker_proto_msgTypes[3].Exporter = func(v any, i int) any {
|
||||||
switch v := v.(*Filter); i {
|
switch v := v.(*Filter); i {
|
||||||
case 0:
|
case 0:
|
||||||
return &v.state
|
return &v.state
|
||||||
|
@ -1428,7 +1428,7 @@ func file_woodpecker_proto_init() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
file_woodpecker_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
|
file_woodpecker_proto_msgTypes[4].Exporter = func(v any, i int) any {
|
||||||
switch v := v.(*Workflow); i {
|
switch v := v.(*Workflow); i {
|
||||||
case 0:
|
case 0:
|
||||||
return &v.state
|
return &v.state
|
||||||
|
@ -1440,7 +1440,7 @@ func file_woodpecker_proto_init() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
file_woodpecker_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
|
file_woodpecker_proto_msgTypes[5].Exporter = func(v any, i int) any {
|
||||||
switch v := v.(*NextRequest); i {
|
switch v := v.(*NextRequest); i {
|
||||||
case 0:
|
case 0:
|
||||||
return &v.state
|
return &v.state
|
||||||
|
@ -1452,7 +1452,7 @@ func file_woodpecker_proto_init() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
file_woodpecker_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
|
file_woodpecker_proto_msgTypes[6].Exporter = func(v any, i int) any {
|
||||||
switch v := v.(*InitRequest); i {
|
switch v := v.(*InitRequest); i {
|
||||||
case 0:
|
case 0:
|
||||||
return &v.state
|
return &v.state
|
||||||
|
@ -1464,7 +1464,7 @@ func file_woodpecker_proto_init() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
file_woodpecker_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {
|
file_woodpecker_proto_msgTypes[7].Exporter = func(v any, i int) any {
|
||||||
switch v := v.(*WaitRequest); i {
|
switch v := v.(*WaitRequest); i {
|
||||||
case 0:
|
case 0:
|
||||||
return &v.state
|
return &v.state
|
||||||
|
@ -1476,7 +1476,7 @@ func file_woodpecker_proto_init() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
file_woodpecker_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} {
|
file_woodpecker_proto_msgTypes[8].Exporter = func(v any, i int) any {
|
||||||
switch v := v.(*DoneRequest); i {
|
switch v := v.(*DoneRequest); i {
|
||||||
case 0:
|
case 0:
|
||||||
return &v.state
|
return &v.state
|
||||||
|
@ -1488,7 +1488,7 @@ func file_woodpecker_proto_init() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
file_woodpecker_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} {
|
file_woodpecker_proto_msgTypes[9].Exporter = func(v any, i int) any {
|
||||||
switch v := v.(*ExtendRequest); i {
|
switch v := v.(*ExtendRequest); i {
|
||||||
case 0:
|
case 0:
|
||||||
return &v.state
|
return &v.state
|
||||||
|
@ -1500,7 +1500,7 @@ func file_woodpecker_proto_init() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
file_woodpecker_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} {
|
file_woodpecker_proto_msgTypes[10].Exporter = func(v any, i int) any {
|
||||||
switch v := v.(*UpdateRequest); i {
|
switch v := v.(*UpdateRequest); i {
|
||||||
case 0:
|
case 0:
|
||||||
return &v.state
|
return &v.state
|
||||||
|
@ -1512,7 +1512,7 @@ func file_woodpecker_proto_init() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
file_woodpecker_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} {
|
file_woodpecker_proto_msgTypes[11].Exporter = func(v any, i int) any {
|
||||||
switch v := v.(*LogRequest); i {
|
switch v := v.(*LogRequest); i {
|
||||||
case 0:
|
case 0:
|
||||||
return &v.state
|
return &v.state
|
||||||
|
@ -1524,7 +1524,7 @@ func file_woodpecker_proto_init() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
file_woodpecker_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} {
|
file_woodpecker_proto_msgTypes[12].Exporter = func(v any, i int) any {
|
||||||
switch v := v.(*Empty); i {
|
switch v := v.(*Empty); i {
|
||||||
case 0:
|
case 0:
|
||||||
return &v.state
|
return &v.state
|
||||||
|
@ -1536,7 +1536,7 @@ func file_woodpecker_proto_init() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
file_woodpecker_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} {
|
file_woodpecker_proto_msgTypes[13].Exporter = func(v any, i int) any {
|
||||||
switch v := v.(*ReportHealthRequest); i {
|
switch v := v.(*ReportHealthRequest); i {
|
||||||
case 0:
|
case 0:
|
||||||
return &v.state
|
return &v.state
|
||||||
|
@ -1548,7 +1548,7 @@ func file_woodpecker_proto_init() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
file_woodpecker_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} {
|
file_woodpecker_proto_msgTypes[14].Exporter = func(v any, i int) any {
|
||||||
switch v := v.(*RegisterAgentRequest); i {
|
switch v := v.(*RegisterAgentRequest); i {
|
||||||
case 0:
|
case 0:
|
||||||
return &v.state
|
return &v.state
|
||||||
|
@ -1560,7 +1560,7 @@ func file_woodpecker_proto_init() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
file_woodpecker_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} {
|
file_woodpecker_proto_msgTypes[15].Exporter = func(v any, i int) any {
|
||||||
switch v := v.(*VersionResponse); i {
|
switch v := v.(*VersionResponse); i {
|
||||||
case 0:
|
case 0:
|
||||||
return &v.state
|
return &v.state
|
||||||
|
@ -1572,7 +1572,7 @@ func file_woodpecker_proto_init() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
file_woodpecker_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} {
|
file_woodpecker_proto_msgTypes[16].Exporter = func(v any, i int) any {
|
||||||
switch v := v.(*NextResponse); i {
|
switch v := v.(*NextResponse); i {
|
||||||
case 0:
|
case 0:
|
||||||
return &v.state
|
return &v.state
|
||||||
|
@ -1584,7 +1584,7 @@ func file_woodpecker_proto_init() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
file_woodpecker_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} {
|
file_woodpecker_proto_msgTypes[17].Exporter = func(v any, i int) any {
|
||||||
switch v := v.(*RegisterAgentResponse); i {
|
switch v := v.(*RegisterAgentResponse); i {
|
||||||
case 0:
|
case 0:
|
||||||
return &v.state
|
return &v.state
|
||||||
|
@ -1596,7 +1596,7 @@ func file_woodpecker_proto_init() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
file_woodpecker_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} {
|
file_woodpecker_proto_msgTypes[18].Exporter = func(v any, i int) any {
|
||||||
switch v := v.(*AuthRequest); i {
|
switch v := v.(*AuthRequest); i {
|
||||||
case 0:
|
case 0:
|
||||||
return &v.state
|
return &v.state
|
||||||
|
@ -1608,7 +1608,7 @@ func file_woodpecker_proto_init() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
file_woodpecker_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} {
|
file_woodpecker_proto_msgTypes[19].Exporter = func(v any, i int) any {
|
||||||
switch v := v.(*AuthResponse); i {
|
switch v := v.(*AuthResponse); i {
|
||||||
case 0:
|
case 0:
|
||||||
return &v.state
|
return &v.state
|
||||||
|
|
170
server/api/global_registry.go
Normal file
170
server/api/global_registry.go
Normal file
|
@ -0,0 +1,170 @@
|
||||||
|
// Copyright 2024 Woodpecker Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
|
||||||
|
"go.woodpecker-ci.org/woodpecker/v2/server"
|
||||||
|
"go.woodpecker-ci.org/woodpecker/v2/server/model"
|
||||||
|
"go.woodpecker-ci.org/woodpecker/v2/server/router/middleware/session"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetGlobalRegistryList
|
||||||
|
//
|
||||||
|
// @Summary List global registries
|
||||||
|
// @Router /registries [get]
|
||||||
|
// @Produce json
|
||||||
|
// @Success 200 {array} Registry
|
||||||
|
// @Tags Registries
|
||||||
|
// @Param Authorization header string true "Insert your personal access token" default(Bearer <personal access token>)
|
||||||
|
// @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 GetGlobalRegistryList(c *gin.Context) {
|
||||||
|
registryService := server.Config.Services.Manager.RegistryService()
|
||||||
|
list, err := registryService.GlobalRegistryList(session.Pagination(c))
|
||||||
|
if err != nil {
|
||||||
|
c.String(http.StatusInternalServerError, "Error getting global registry list. %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// copy the registry detail to remove the sensitive
|
||||||
|
// password and token fields.
|
||||||
|
for i, registry := range list {
|
||||||
|
list[i] = registry.Copy()
|
||||||
|
}
|
||||||
|
c.JSON(http.StatusOK, list)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetGlobalRegistry
|
||||||
|
//
|
||||||
|
// @Summary Get a global registry by name
|
||||||
|
// @Router /registries/{registry} [get]
|
||||||
|
// @Produce json
|
||||||
|
// @Success 200 {object} Registry
|
||||||
|
// @Tags Registries
|
||||||
|
// @Param Authorization header string true "Insert your personal access token" default(Bearer <personal access token>)
|
||||||
|
// @Param registry path string true "the registry's name"
|
||||||
|
func GetGlobalRegistry(c *gin.Context) {
|
||||||
|
addr := c.Param("registry")
|
||||||
|
registryService := server.Config.Services.Manager.RegistryService()
|
||||||
|
registry, err := registryService.GlobalRegistryFind(addr)
|
||||||
|
if err != nil {
|
||||||
|
handleDBError(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.JSON(http.StatusOK, registry.Copy())
|
||||||
|
}
|
||||||
|
|
||||||
|
// PostGlobalRegistry
|
||||||
|
//
|
||||||
|
// @Summary Create a global registry
|
||||||
|
// @Router /registries [post]
|
||||||
|
// @Produce json
|
||||||
|
// @Success 200 {object} Registry
|
||||||
|
// @Tags Registries
|
||||||
|
// @Param Authorization header string true "Insert your personal access token" default(Bearer <personal access token>)
|
||||||
|
// @Param registry body Registry true "the registry object data"
|
||||||
|
func PostGlobalRegistry(c *gin.Context) {
|
||||||
|
in := new(model.Registry)
|
||||||
|
if err := c.Bind(in); err != nil {
|
||||||
|
c.String(http.StatusBadRequest, "Error parsing global registry. %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
registry := &model.Registry{
|
||||||
|
Address: in.Address,
|
||||||
|
Username: in.Username,
|
||||||
|
Password: in.Password,
|
||||||
|
}
|
||||||
|
if err := registry.Validate(); err != nil {
|
||||||
|
c.String(http.StatusBadRequest, "Error inserting global registry. %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
registryService := server.Config.Services.Manager.RegistryService()
|
||||||
|
if err := registryService.GlobalRegistryCreate(registry); err != nil {
|
||||||
|
c.String(http.StatusInternalServerError, "Error inserting global registry %q. %s", in.Address, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.JSON(http.StatusOK, registry.Copy())
|
||||||
|
}
|
||||||
|
|
||||||
|
// PatchGlobalRegistry
|
||||||
|
//
|
||||||
|
// @Summary Update a global registry by name
|
||||||
|
// @Router /registries/{registry} [patch]
|
||||||
|
// @Produce json
|
||||||
|
// @Success 200 {object} Registry
|
||||||
|
// @Tags Registries
|
||||||
|
// @Param Authorization header string true "Insert your personal access token" default(Bearer <personal access token>)
|
||||||
|
// @Param registry path string true "the registry's name"
|
||||||
|
// @Param registryData body Registry true "the registry's data"
|
||||||
|
func PatchGlobalRegistry(c *gin.Context) {
|
||||||
|
addr := c.Param("registry")
|
||||||
|
|
||||||
|
in := new(model.Registry)
|
||||||
|
err := c.Bind(in)
|
||||||
|
if err != nil {
|
||||||
|
c.String(http.StatusBadRequest, "Error parsing registry. %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
registryService := server.Config.Services.Manager.RegistryService()
|
||||||
|
registry, err := registryService.GlobalRegistryFind(addr)
|
||||||
|
if err != nil {
|
||||||
|
handleDBError(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if in.Address != "" {
|
||||||
|
registry.Address = in.Address
|
||||||
|
}
|
||||||
|
if in.Username != "" {
|
||||||
|
registry.Username = in.Username
|
||||||
|
}
|
||||||
|
if in.Password != "" {
|
||||||
|
registry.Password = in.Password
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := registry.Validate(); err != nil {
|
||||||
|
c.String(http.StatusBadRequest, "Error updating global registry. %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := registryService.GlobalRegistryUpdate(registry); err != nil {
|
||||||
|
c.String(http.StatusInternalServerError, "Error updating global registry %q. %s", in.Address, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.JSON(http.StatusOK, registry.Copy())
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteGlobalRegistry
|
||||||
|
//
|
||||||
|
// @Summary Delete a global registry by name
|
||||||
|
// @Router /registries/{registry} [delete]
|
||||||
|
// @Produce plain
|
||||||
|
// @Success 204
|
||||||
|
// @Tags Registries
|
||||||
|
// @Param Authorization header string true "Insert your personal access token" default(Bearer <personal access token>)
|
||||||
|
// @Param registry path string true "the registry's name"
|
||||||
|
func DeleteGlobalRegistry(c *gin.Context) {
|
||||||
|
addr := c.Param("registry")
|
||||||
|
registryService := server.Config.Services.Manager.RegistryService()
|
||||||
|
if err := registryService.GlobalRegistryDelete(addr); err != nil {
|
||||||
|
handleDBError(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.Status(http.StatusNoContent)
|
||||||
|
}
|
207
server/api/org_registry.go
Normal file
207
server/api/org_registry.go
Normal file
|
@ -0,0 +1,207 @@
|
||||||
|
// Copyright 2024 Woodpecker Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
|
||||||
|
"go.woodpecker-ci.org/woodpecker/v2/server"
|
||||||
|
"go.woodpecker-ci.org/woodpecker/v2/server/model"
|
||||||
|
"go.woodpecker-ci.org/woodpecker/v2/server/router/middleware/session"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetOrgRegistry
|
||||||
|
//
|
||||||
|
// @Summary Get a organization registry by address
|
||||||
|
// @Router /orgs/{org_id}/registries/{registry} [get]
|
||||||
|
// @Produce json
|
||||||
|
// @Success 200 {object} Registry
|
||||||
|
// @Tags Organization registries
|
||||||
|
// @Param Authorization header string true "Insert your personal access token" default(Bearer <personal access token>)
|
||||||
|
// @Param org_id path string true "the org's id"
|
||||||
|
// @Param registry path string true "the registry's address"
|
||||||
|
func GetOrgRegistry(c *gin.Context) {
|
||||||
|
addr := c.Param("registry")
|
||||||
|
|
||||||
|
orgID, err := strconv.ParseInt(c.Param("org_id"), 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
c.String(http.StatusBadRequest, "Error parsing org id. %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
registryService := server.Config.Services.Manager.RegistryService()
|
||||||
|
registry, err := registryService.OrgRegistryFind(orgID, addr)
|
||||||
|
if err != nil {
|
||||||
|
handleDBError(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.JSON(http.StatusOK, registry.Copy())
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetOrgRegistryList
|
||||||
|
//
|
||||||
|
// @Summary List organization registries
|
||||||
|
// @Router /orgs/{org_id}/registries [get]
|
||||||
|
// @Produce json
|
||||||
|
// @Success 200 {array} Registry
|
||||||
|
// @Tags Organization registries
|
||||||
|
// @Param Authorization header string true "Insert your personal access token" default(Bearer <personal access token>)
|
||||||
|
// @Param org_id path string true "the org's 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 GetOrgRegistryList(c *gin.Context) {
|
||||||
|
orgID, err := strconv.ParseInt(c.Param("org_id"), 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
c.String(http.StatusBadRequest, "Error parsing org id. %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
registryService := server.Config.Services.Manager.RegistryService()
|
||||||
|
list, err := registryService.OrgRegistryList(orgID, session.Pagination(c))
|
||||||
|
if err != nil {
|
||||||
|
c.String(http.StatusInternalServerError, "Error getting registry list for %q. %s", orgID, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// copy the registry detail to remove the sensitive
|
||||||
|
// password and token fields.
|
||||||
|
for i, registry := range list {
|
||||||
|
list[i] = registry.Copy()
|
||||||
|
}
|
||||||
|
c.JSON(http.StatusOK, list)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PostOrgRegistry
|
||||||
|
//
|
||||||
|
// @Summary Create an organization registry
|
||||||
|
// @Router /orgs/{org_id}/registries [post]
|
||||||
|
// @Produce json
|
||||||
|
// @Success 200 {object} Registry
|
||||||
|
// @Tags Organization registries
|
||||||
|
// @Param Authorization header string true "Insert your personal access token" default(Bearer <personal access token>)
|
||||||
|
// @Param org_id path string true "the org's id"
|
||||||
|
// @Param registryData body Registry true "the new registry"
|
||||||
|
func PostOrgRegistry(c *gin.Context) {
|
||||||
|
orgID, err := strconv.ParseInt(c.Param("org_id"), 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
c.String(http.StatusBadRequest, "Error parsing org id. %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
in := new(model.Registry)
|
||||||
|
if err := c.Bind(in); err != nil {
|
||||||
|
c.String(http.StatusBadRequest, "Error parsing org %q registry. %s", orgID, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
registry := &model.Registry{
|
||||||
|
OrgID: orgID,
|
||||||
|
Address: in.Address,
|
||||||
|
Username: in.Username,
|
||||||
|
Password: in.Password,
|
||||||
|
}
|
||||||
|
if err := registry.Validate(); err != nil {
|
||||||
|
c.String(http.StatusUnprocessableEntity, "Error inserting org %q registry. %s", orgID, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
registryService := server.Config.Services.Manager.RegistryService()
|
||||||
|
if err := registryService.OrgRegistryCreate(orgID, registry); err != nil {
|
||||||
|
c.String(http.StatusInternalServerError, "Error inserting org %q registry %q. %s", orgID, in.Address, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.JSON(http.StatusOK, registry.Copy())
|
||||||
|
}
|
||||||
|
|
||||||
|
// PatchOrgRegistry
|
||||||
|
//
|
||||||
|
// @Summary Update an organization registry by name
|
||||||
|
// @Router /orgs/{org_id}/registries/{registry} [patch]
|
||||||
|
// @Produce json
|
||||||
|
// @Success 200 {object} Registry
|
||||||
|
// @Tags Organization registries
|
||||||
|
// @Param Authorization header string true "Insert your personal access token" default(Bearer <personal access token>)
|
||||||
|
// @Param org_id path string true "the org's id"
|
||||||
|
// @Param registry path string true "the registry's name"
|
||||||
|
// @Param registryData body Registry true "the update registry data"
|
||||||
|
func PatchOrgRegistry(c *gin.Context) {
|
||||||
|
addr := c.Param("registry")
|
||||||
|
orgID, err := strconv.ParseInt(c.Param("org_id"), 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
c.String(http.StatusBadRequest, "Error parsing org id. %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
in := new(model.Registry)
|
||||||
|
err = c.Bind(in)
|
||||||
|
if err != nil {
|
||||||
|
c.String(http.StatusBadRequest, "Error parsing registry. %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
registryService := server.Config.Services.Manager.RegistryService()
|
||||||
|
registry, err := registryService.OrgRegistryFind(orgID, addr)
|
||||||
|
if err != nil {
|
||||||
|
handleDBError(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if in.Address != "" {
|
||||||
|
registry.Address = in.Address
|
||||||
|
}
|
||||||
|
if in.Username != "" {
|
||||||
|
registry.Username = in.Username
|
||||||
|
}
|
||||||
|
if in.Password != "" {
|
||||||
|
registry.Password = in.Password
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := registry.Validate(); err != nil {
|
||||||
|
c.String(http.StatusUnprocessableEntity, "Error updating org %q registry. %s", orgID, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := registryService.OrgRegistryUpdate(orgID, registry); err != nil {
|
||||||
|
c.String(http.StatusInternalServerError, "Error updating org %q registry %q. %s", orgID, in.Address, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.JSON(http.StatusOK, registry.Copy())
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteOrgRegistry
|
||||||
|
//
|
||||||
|
// @Summary Delete an organization registry by name
|
||||||
|
// @Router /orgs/{org_id}/registries/{registry} [delete]
|
||||||
|
// @Produce plain
|
||||||
|
// @Success 204
|
||||||
|
// @Tags Organization registries
|
||||||
|
// @Param Authorization header string true "Insert your personal access token" default(Bearer <personal access token>)
|
||||||
|
// @Param org_id path string true "the org's id"
|
||||||
|
// @Param registry path string true "the registry's name"
|
||||||
|
func DeleteOrgRegistry(c *gin.Context) {
|
||||||
|
addr := c.Param("registry")
|
||||||
|
orgID, err := strconv.ParseInt(c.Param("org_id"), 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
c.String(http.StatusBadRequest, "Error parsing org id. %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
registryService := server.Config.Services.Manager.RegistryService()
|
||||||
|
if err := registryService.OrgRegistryDelete(orgID, addr); err != nil {
|
||||||
|
handleDBError(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.Status(http.StatusNoContent)
|
||||||
|
}
|
|
@ -27,7 +27,7 @@ import (
|
||||||
// GetRegistry
|
// GetRegistry
|
||||||
//
|
//
|
||||||
// @Summary Get a registry by name
|
// @Summary Get a registry by name
|
||||||
// @Router /repos/{repo_id}/registry/{registry} [get]
|
// @Router /repos/{repo_id}/registries/{registry} [get]
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Success 200 {object} Registry
|
// @Success 200 {object} Registry
|
||||||
// @Tags Repository registries
|
// @Tags Repository registries
|
||||||
|
@ -36,10 +36,10 @@ import (
|
||||||
// @Param registry path string true "the registry name"
|
// @Param registry path string true "the registry name"
|
||||||
func GetRegistry(c *gin.Context) {
|
func GetRegistry(c *gin.Context) {
|
||||||
repo := session.Repo(c)
|
repo := session.Repo(c)
|
||||||
name := c.Param("registry")
|
addr := c.Param("registry")
|
||||||
|
|
||||||
registryService := server.Config.Services.Manager.RegistryServiceFromRepo(repo)
|
registryService := server.Config.Services.Manager.RegistryServiceFromRepo(repo)
|
||||||
registry, err := registryService.RegistryFind(repo, name)
|
registry, err := registryService.RegistryFind(repo, addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
handleDBError(c, err)
|
handleDBError(c, err)
|
||||||
return
|
return
|
||||||
|
@ -50,7 +50,7 @@ func GetRegistry(c *gin.Context) {
|
||||||
// PostRegistry
|
// PostRegistry
|
||||||
//
|
//
|
||||||
// @Summary Create a registry
|
// @Summary Create a registry
|
||||||
// @Router /repos/{repo_id}/registry [post]
|
// @Router /repos/{repo_id}/registries [post]
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Success 200 {object} Registry
|
// @Success 200 {object} Registry
|
||||||
// @Tags Repository registries
|
// @Tags Repository registries
|
||||||
|
@ -87,7 +87,7 @@ func PostRegistry(c *gin.Context) {
|
||||||
// PatchRegistry
|
// PatchRegistry
|
||||||
//
|
//
|
||||||
// @Summary Update a registry by name
|
// @Summary Update a registry by name
|
||||||
// @Router /repos/{repo_id}/registry/{registry} [patch]
|
// @Router /repos/{repo_id}/registries/{registry} [patch]
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Success 200 {object} Registry
|
// @Success 200 {object} Registry
|
||||||
// @Tags Repository registries
|
// @Tags Repository registries
|
||||||
|
@ -96,10 +96,8 @@ func PostRegistry(c *gin.Context) {
|
||||||
// @Param registry path string true "the registry name"
|
// @Param registry path string true "the registry name"
|
||||||
// @Param registryData body Registry true "the attributes for the registry"
|
// @Param registryData body Registry true "the attributes for the registry"
|
||||||
func PatchRegistry(c *gin.Context) {
|
func PatchRegistry(c *gin.Context) {
|
||||||
var (
|
repo := session.Repo(c)
|
||||||
repo = session.Repo(c)
|
addr := c.Param("registry")
|
||||||
name = c.Param("registry")
|
|
||||||
)
|
|
||||||
|
|
||||||
in := new(model.Registry)
|
in := new(model.Registry)
|
||||||
err := c.Bind(in)
|
err := c.Bind(in)
|
||||||
|
@ -109,7 +107,7 @@ func PatchRegistry(c *gin.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
registryService := server.Config.Services.Manager.RegistryServiceFromRepo(repo)
|
registryService := server.Config.Services.Manager.RegistryServiceFromRepo(repo)
|
||||||
registry, err := registryService.RegistryFind(repo, name)
|
registry, err := registryService.RegistryFind(repo, addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
handleDBError(c, err)
|
handleDBError(c, err)
|
||||||
return
|
return
|
||||||
|
@ -135,7 +133,7 @@ func PatchRegistry(c *gin.Context) {
|
||||||
// GetRegistryList
|
// GetRegistryList
|
||||||
//
|
//
|
||||||
// @Summary List registries
|
// @Summary List registries
|
||||||
// @Router /repos/{repo_id}/registry [get]
|
// @Router /repos/{repo_id}/registries [get]
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Success 200 {array} Registry
|
// @Success 200 {array} Registry
|
||||||
// @Tags Repository registries
|
// @Tags Repository registries
|
||||||
|
@ -162,7 +160,7 @@ func GetRegistryList(c *gin.Context) {
|
||||||
// DeleteRegistry
|
// DeleteRegistry
|
||||||
//
|
//
|
||||||
// @Summary Delete a registry by name
|
// @Summary Delete a registry by name
|
||||||
// @Router /repos/{repo_id}/registry/{registry} [delete]
|
// @Router /repos/{repo_id}/registries/{registry} [delete]
|
||||||
// @Produce plain
|
// @Produce plain
|
||||||
// @Success 204
|
// @Success 204
|
||||||
// @Tags Repository registries
|
// @Tags Repository registries
|
||||||
|
@ -171,10 +169,10 @@ func GetRegistryList(c *gin.Context) {
|
||||||
// @Param registry path string true "the registry name"
|
// @Param registry path string true "the registry name"
|
||||||
func DeleteRegistry(c *gin.Context) {
|
func DeleteRegistry(c *gin.Context) {
|
||||||
repo := session.Repo(c)
|
repo := session.Repo(c)
|
||||||
name := c.Param("registry")
|
addr := c.Param("registry")
|
||||||
|
|
||||||
registryService := server.Config.Services.Manager.RegistryServiceFromRepo(repo)
|
registryService := server.Config.Services.Manager.RegistryServiceFromRepo(repo)
|
||||||
err := registryService.RegistryDelete(repo, name)
|
err := registryService.RegistryDelete(repo, addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
handleDBError(c, err)
|
handleDBError(c, err)
|
||||||
return
|
return
|
||||||
|
|
|
@ -29,16 +29,33 @@ var (
|
||||||
// Registry represents a docker registry with credentials.
|
// Registry represents a docker registry with credentials.
|
||||||
type Registry struct {
|
type Registry struct {
|
||||||
ID int64 `json:"id" xorm:"pk autoincr 'id'"`
|
ID int64 `json:"id" xorm:"pk autoincr 'id'"`
|
||||||
RepoID int64 `json:"-" xorm:"UNIQUE(s) INDEX 'repo_id'"`
|
OrgID int64 `json:"org_id" xorm:"NOT NULL DEFAULT 0 UNIQUE(s) INDEX 'org_id'"`
|
||||||
Address string `json:"address" xorm:"UNIQUE(s) INDEX 'address'"`
|
RepoID int64 `json:"repo_id" xorm:"NOT NULL DEFAULT 0 UNIQUE(s) INDEX 'repo_id'"`
|
||||||
|
Address string `json:"address" xorm:"NOT NULL UNIQUE(s) INDEX 'address'"`
|
||||||
Username string `json:"username" xorm:"varchar(2000) 'username'"`
|
Username string `json:"username" xorm:"varchar(2000) 'username'"`
|
||||||
Password string `json:"password" xorm:"TEXT 'password'"`
|
Password string `json:"password" xorm:"TEXT 'password'"`
|
||||||
|
ReadOnly bool `json:"readonly" xorm:"-"`
|
||||||
} // @name Registry
|
} // @name Registry
|
||||||
|
|
||||||
func (r Registry) TableName() string {
|
func (r Registry) TableName() string {
|
||||||
return "registries"
|
return "registries"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Global registry.
|
||||||
|
func (r Registry) IsGlobal() bool {
|
||||||
|
return r.RepoID == 0 && r.OrgID == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Organization registry.
|
||||||
|
func (r Registry) IsOrganization() bool {
|
||||||
|
return r.RepoID == 0 && r.OrgID != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Repository registry.
|
||||||
|
func (r Registry) IsRepository() bool {
|
||||||
|
return r.RepoID != 0 && r.OrgID == 0
|
||||||
|
}
|
||||||
|
|
||||||
// Validate validates the registry information.
|
// Validate validates the registry information.
|
||||||
func (r *Registry) Validate() error {
|
func (r *Registry) Validate() error {
|
||||||
switch {
|
switch {
|
||||||
|
@ -58,8 +75,10 @@ func (r *Registry) Validate() error {
|
||||||
func (r *Registry) Copy() *Registry {
|
func (r *Registry) Copy() *Registry {
|
||||||
return &Registry{
|
return &Registry{
|
||||||
ID: r.ID,
|
ID: r.ID,
|
||||||
|
OrgID: r.OrgID,
|
||||||
RepoID: r.RepoID,
|
RepoID: r.RepoID,
|
||||||
Address: r.Address,
|
Address: r.Address,
|
||||||
Username: r.Username,
|
Username: r.Username,
|
||||||
|
ReadOnly: r.ReadOnly,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,13 +44,13 @@ func parsePipeline(forge forge.Forge, store store.Store, currentPipeline *model.
|
||||||
}
|
}
|
||||||
|
|
||||||
secretService := server.Config.Services.Manager.SecretServiceFromRepo(repo)
|
secretService := server.Config.Services.Manager.SecretServiceFromRepo(repo)
|
||||||
secs, err := secretService.SecretListPipeline(repo, currentPipeline, &model.ListOptions{All: true})
|
secs, err := secretService.SecretListPipeline(repo, currentPipeline)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().Err(err).Msgf("error getting secrets for %s#%d", repo.FullName, currentPipeline.Number)
|
log.Error().Err(err).Msgf("error getting secrets for %s#%d", repo.FullName, currentPipeline.Number)
|
||||||
}
|
}
|
||||||
|
|
||||||
registryService := server.Config.Services.Manager.RegistryServiceFromRepo(repo)
|
registryService := server.Config.Services.Manager.RegistryServiceFromRepo(repo)
|
||||||
regs, err := registryService.RegistryList(repo, &model.ListOptions{All: true})
|
regs, err := registryService.RegistryListPipeline(repo, currentPipeline)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().Err(err).Msgf("error getting registry credentials for %s#%d", repo.FullName, currentPipeline.Number)
|
log.Error().Err(err).Msgf("error getting registry credentials for %s#%d", repo.FullName, currentPipeline.Number)
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,11 +61,18 @@ func apiRoutes(e *gin.RouterGroup) {
|
||||||
org.Use(session.MustOrgMember(true))
|
org.Use(session.MustOrgMember(true))
|
||||||
org.DELETE("", session.MustAdmin(), api.DeleteOrg)
|
org.DELETE("", session.MustAdmin(), api.DeleteOrg)
|
||||||
org.GET("", api.GetOrg)
|
org.GET("", api.GetOrg)
|
||||||
|
|
||||||
org.GET("/secrets", api.GetOrgSecretList)
|
org.GET("/secrets", api.GetOrgSecretList)
|
||||||
org.POST("/secrets", api.PostOrgSecret)
|
org.POST("/secrets", api.PostOrgSecret)
|
||||||
org.GET("/secrets/:secret", api.GetOrgSecret)
|
org.GET("/secrets/:secret", api.GetOrgSecret)
|
||||||
org.PATCH("/secrets/:secret", api.PatchOrgSecret)
|
org.PATCH("/secrets/:secret", api.PatchOrgSecret)
|
||||||
org.DELETE("/secrets/:secret", api.DeleteOrgSecret)
|
org.DELETE("/secrets/:secret", api.DeleteOrgSecret)
|
||||||
|
|
||||||
|
org.GET("/registries", api.GetOrgRegistryList)
|
||||||
|
org.POST("/registries", api.PostOrgRegistry)
|
||||||
|
org.GET("/registries/:registry", api.GetOrgRegistry)
|
||||||
|
org.PATCH("/registries/:registry", api.PatchOrgRegistry)
|
||||||
|
org.DELETE("/registries/:registry", api.DeleteOrgRegistry)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -118,6 +125,13 @@ func apiRoutes(e *gin.RouterGroup) {
|
||||||
repo.DELETE("/secrets/:secret", session.MustPush, api.DeleteSecret)
|
repo.DELETE("/secrets/:secret", session.MustPush, api.DeleteSecret)
|
||||||
|
|
||||||
// requires push permissions
|
// requires push permissions
|
||||||
|
repo.GET("/registries", session.MustPush, api.GetRegistryList)
|
||||||
|
repo.POST("/registries", session.MustPush, api.PostRegistry)
|
||||||
|
repo.GET("/registries/:registry", session.MustPush, api.GetRegistry)
|
||||||
|
repo.PATCH("/registries/:registry", session.MustPush, api.PatchRegistry)
|
||||||
|
repo.DELETE("/registries/:registry", session.MustPush, api.DeleteRegistry)
|
||||||
|
|
||||||
|
// TODO: remove with 3.x
|
||||||
repo.GET("/registry", session.MustPush, api.GetRegistryList)
|
repo.GET("/registry", session.MustPush, api.GetRegistryList)
|
||||||
repo.POST("/registry", session.MustPush, api.PostRegistry)
|
repo.POST("/registry", session.MustPush, api.PostRegistry)
|
||||||
repo.GET("/registry/:registry", session.MustPush, api.GetRegistry)
|
repo.GET("/registry/:registry", session.MustPush, api.GetRegistry)
|
||||||
|
@ -184,6 +198,21 @@ func apiRoutes(e *gin.RouterGroup) {
|
||||||
secrets.DELETE("/:secret", api.DeleteGlobalSecret)
|
secrets.DELETE("/:secret", api.DeleteGlobalSecret)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// global registries can be read without actual values by any user
|
||||||
|
readGlobalRegistries := apiBase.Group("/registries")
|
||||||
|
{
|
||||||
|
readGlobalRegistries.Use(session.MustUser())
|
||||||
|
readGlobalRegistries.GET("", api.GetGlobalRegistryList)
|
||||||
|
readGlobalRegistries.GET("/:registry", api.GetGlobalRegistry)
|
||||||
|
}
|
||||||
|
registries := apiBase.Group("/registries")
|
||||||
|
{
|
||||||
|
registries.Use(session.MustAdmin())
|
||||||
|
registries.POST("", api.PostGlobalRegistry)
|
||||||
|
registries.PATCH("/:registry", api.PatchGlobalRegistry)
|
||||||
|
registries.DELETE("/:registry", api.DeleteGlobalRegistry)
|
||||||
|
}
|
||||||
|
|
||||||
logLevel := apiBase.Group("/log-level")
|
logLevel := apiBase.Group("/log-level")
|
||||||
{
|
{
|
||||||
logLevel.Use(session.MustAdmin())
|
logLevel.Use(session.MustAdmin())
|
||||||
|
|
|
@ -15,7 +15,10 @@
|
||||||
package registry
|
package registry
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
|
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/server/model"
|
"go.woodpecker-ci.org/woodpecker/v2/server/model"
|
||||||
|
"go.woodpecker-ci.org/woodpecker/v2/server/store/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
type combined struct {
|
type combined struct {
|
||||||
|
@ -31,29 +34,44 @@ func NewCombined(dbRegistry Service, registries ...ReadOnlyService) Service {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *combined) RegistryFind(repo *model.Repo, name string) (*model.Registry, error) {
|
func (c *combined) RegistryFind(repo *model.Repo, addr string) (*model.Registry, error) {
|
||||||
for _, registry := range c.registries {
|
return c.dbRegistry.RegistryFind(repo, addr)
|
||||||
res, err := registry.RegistryFind(repo, name)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if res != nil {
|
|
||||||
return res, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *combined) RegistryList(repo *model.Repo, p *model.ListOptions) ([]*model.Registry, error) {
|
func (c *combined) RegistryList(repo *model.Repo, p *model.ListOptions) ([]*model.Registry, error) {
|
||||||
var registries []*model.Registry
|
return c.dbRegistry.RegistryList(repo, p)
|
||||||
for _, registry := range c.registries {
|
}
|
||||||
list, err := registry.RegistryList(repo, &model.ListOptions{All: true})
|
|
||||||
|
func (c *combined) RegistryListPipeline(repo *model.Repo, pipeline *model.Pipeline) ([]*model.Registry, error) {
|
||||||
|
dbRegistries, err := c.dbRegistry.RegistryListPipeline(repo, pipeline)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
registries = append(registries, list...)
|
|
||||||
|
registries := make([]*model.Registry, 0, len(dbRegistries))
|
||||||
|
exists := make(map[string]struct{}, len(dbRegistries))
|
||||||
|
|
||||||
|
// Assign database stored registries to the map to avoid duplicates
|
||||||
|
// from the combined registries so to prioritize ones in database.
|
||||||
|
for _, reg := range dbRegistries {
|
||||||
|
exists[reg.Address] = struct{}{}
|
||||||
}
|
}
|
||||||
return model.ApplyPagination(p, registries), nil
|
|
||||||
|
for _, registry := range c.registries {
|
||||||
|
list, err := registry.GlobalRegistryList(&model.ListOptions{All: true})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, reg := range list {
|
||||||
|
if _, ok := exists[reg.Address]; ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
exists[reg.Address] = struct{}{}
|
||||||
|
registries = append(registries, reg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return append(registries, dbRegistries...), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *combined) RegistryCreate(repo *model.Repo, registry *model.Registry) error {
|
func (c *combined) RegistryCreate(repo *model.Repo, registry *model.Registry) error {
|
||||||
|
@ -64,6 +82,86 @@ func (c *combined) RegistryUpdate(repo *model.Repo, registry *model.Registry) er
|
||||||
return c.dbRegistry.RegistryUpdate(repo, registry)
|
return c.dbRegistry.RegistryUpdate(repo, registry)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *combined) RegistryDelete(repo *model.Repo, name string) error {
|
func (c *combined) RegistryDelete(repo *model.Repo, addr string) error {
|
||||||
return c.dbRegistry.RegistryDelete(repo, name)
|
return c.dbRegistry.RegistryDelete(repo, addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *combined) OrgRegistryFind(owner int64, addr string) (*model.Registry, error) {
|
||||||
|
return c.dbRegistry.OrgRegistryFind(owner, addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *combined) OrgRegistryList(owner int64, p *model.ListOptions) ([]*model.Registry, error) {
|
||||||
|
return c.dbRegistry.OrgRegistryList(owner, p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *combined) OrgRegistryCreate(owner int64, registry *model.Registry) error {
|
||||||
|
return c.dbRegistry.OrgRegistryCreate(owner, registry)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *combined) OrgRegistryUpdate(owner int64, registry *model.Registry) error {
|
||||||
|
return c.dbRegistry.OrgRegistryUpdate(owner, registry)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *combined) OrgRegistryDelete(owner int64, addr string) error {
|
||||||
|
return c.dbRegistry.OrgRegistryDelete(owner, addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *combined) GlobalRegistryFind(addr string) (*model.Registry, error) {
|
||||||
|
registry, err := c.dbRegistry.GlobalRegistryFind(addr)
|
||||||
|
if err != nil && !errors.Is(err, types.RecordNotExist) {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if registry != nil {
|
||||||
|
return registry, nil
|
||||||
|
}
|
||||||
|
for _, reg := range c.registries {
|
||||||
|
if registry, err := reg.GlobalRegistryFind(addr); err == nil {
|
||||||
|
return registry, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, types.RecordNotExist
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *combined) GlobalRegistryList(p *model.ListOptions) ([]*model.Registry, error) {
|
||||||
|
dbRegistries, err := c.dbRegistry.GlobalRegistryList(&model.ListOptions{All: true})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
registries := make([]*model.Registry, 0, len(dbRegistries))
|
||||||
|
exists := make(map[string]struct{}, len(dbRegistries))
|
||||||
|
|
||||||
|
// Assign database stored registries to the map to avoid duplicates
|
||||||
|
// from the combined registries so to prioritize ones in database.
|
||||||
|
for _, reg := range dbRegistries {
|
||||||
|
exists[reg.Address] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, registry := range c.registries {
|
||||||
|
list, err := registry.GlobalRegistryList(&model.ListOptions{All: true})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, reg := range list {
|
||||||
|
if _, ok := exists[reg.Address]; ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
exists[reg.Address] = struct{}{}
|
||||||
|
registries = append(registries, reg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return model.ApplyPagination(p, append(registries, dbRegistries...)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *combined) GlobalRegistryCreate(registry *model.Registry) error {
|
||||||
|
return c.dbRegistry.GlobalRegistryCreate(registry)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *combined) GlobalRegistryUpdate(registry *model.Registry) error {
|
||||||
|
return c.dbRegistry.GlobalRegistryUpdate(registry)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *combined) GlobalRegistryDelete(addr string) error {
|
||||||
|
return c.dbRegistry.GlobalRegistryDelete(addr)
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,12 +28,45 @@ func NewDB(store store.Store) Service {
|
||||||
return &db{store}
|
return &db{store}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *db) RegistryFind(repo *model.Repo, name string) (*model.Registry, error) {
|
func (d *db) RegistryFind(repo *model.Repo, addr string) (*model.Registry, error) {
|
||||||
return d.store.RegistryFind(repo, name)
|
return d.store.RegistryFind(repo, addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *db) RegistryList(repo *model.Repo, p *model.ListOptions) ([]*model.Registry, error) {
|
func (d *db) RegistryList(repo *model.Repo, p *model.ListOptions) ([]*model.Registry, error) {
|
||||||
return d.store.RegistryList(repo, p)
|
return d.store.RegistryList(repo, false, p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *db) RegistryListPipeline(repo *model.Repo, _ *model.Pipeline) ([]*model.Registry, error) {
|
||||||
|
r, err := d.store.RegistryList(repo, true, &model.ListOptions{All: true})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return only registries with unique address
|
||||||
|
// Priority order in case of duplicate addresses are repository, user/organization, global
|
||||||
|
registries := make([]*model.Registry, 0, len(r))
|
||||||
|
uniq := make(map[string]struct{})
|
||||||
|
for _, condition := range []struct {
|
||||||
|
IsRepository bool
|
||||||
|
IsOrganization bool
|
||||||
|
IsGlobal bool
|
||||||
|
}{
|
||||||
|
{IsRepository: true},
|
||||||
|
{IsOrganization: true},
|
||||||
|
{IsGlobal: true},
|
||||||
|
} {
|
||||||
|
for _, registry := range r {
|
||||||
|
if registry.IsRepository() != condition.IsRepository || registry.IsOrganization() != condition.IsOrganization || registry.IsGlobal() != condition.IsGlobal {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if _, ok := uniq[registry.Address]; ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
uniq[registry.Address] = struct{}{}
|
||||||
|
registries = append(registries, registry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return registries, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *db) RegistryCreate(_ *model.Repo, in *model.Registry) error {
|
func (d *db) RegistryCreate(_ *model.Repo, in *model.Registry) error {
|
||||||
|
@ -45,5 +78,57 @@ func (d *db) RegistryUpdate(_ *model.Repo, in *model.Registry) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *db) RegistryDelete(repo *model.Repo, addr string) error {
|
func (d *db) RegistryDelete(repo *model.Repo, addr string) error {
|
||||||
return d.store.RegistryDelete(repo, addr)
|
registry, err := d.store.RegistryFind(repo, addr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return d.store.RegistryDelete(registry)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *db) OrgRegistryFind(owner int64, name string) (*model.Registry, error) {
|
||||||
|
return d.store.OrgRegistryFind(owner, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *db) OrgRegistryList(owner int64, p *model.ListOptions) ([]*model.Registry, error) {
|
||||||
|
return d.store.OrgRegistryList(owner, p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *db) OrgRegistryCreate(_ int64, in *model.Registry) error {
|
||||||
|
return d.store.RegistryCreate(in)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *db) OrgRegistryUpdate(_ int64, in *model.Registry) error {
|
||||||
|
return d.store.RegistryUpdate(in)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *db) OrgRegistryDelete(owner int64, addr string) error {
|
||||||
|
registry, err := d.store.OrgRegistryFind(owner, addr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return d.store.RegistryDelete(registry)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *db) GlobalRegistryFind(addr string) (*model.Registry, error) {
|
||||||
|
return d.store.GlobalRegistryFind(addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *db) GlobalRegistryList(p *model.ListOptions) ([]*model.Registry, error) {
|
||||||
|
return d.store.GlobalRegistryList(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *db) GlobalRegistryCreate(in *model.Registry) error {
|
||||||
|
return d.store.RegistryCreate(in)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *db) GlobalRegistryUpdate(in *model.Registry) error {
|
||||||
|
return d.store.RegistryUpdate(in)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *db) GlobalRegistryDelete(addr string) error {
|
||||||
|
registry, err := d.store.GlobalRegistryFind(addr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return d.store.RegistryDelete(registry)
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,7 @@ import (
|
||||||
"github.com/docker/cli/cli/config/types"
|
"github.com/docker/cli/cli/config/types"
|
||||||
|
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/server/model"
|
"go.woodpecker-ci.org/woodpecker/v2/server/model"
|
||||||
|
model_types "go.woodpecker-ci.org/woodpecker/v2/server/store/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
type filesystem struct {
|
type filesystem struct {
|
||||||
|
@ -79,17 +80,29 @@ func parseDockerConfig(path string) ([]*model.Registry, error) {
|
||||||
Address: key,
|
Address: key,
|
||||||
Username: auth.Username,
|
Username: auth.Username,
|
||||||
Password: auth.Password,
|
Password: auth.Password,
|
||||||
|
ReadOnly: true,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return registries, nil
|
return registries, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *filesystem) RegistryFind(*model.Repo, string) (*model.Registry, error) {
|
func (f *filesystem) GlobalRegistryFind(addr string) (*model.Registry, error) {
|
||||||
return nil, nil
|
registries, err := f.GlobalRegistryList(&model.ListOptions{All: true})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *filesystem) RegistryList(_ *model.Repo, p *model.ListOptions) ([]*model.Registry, error) {
|
for _, reg := range registries {
|
||||||
|
if reg.Address == addr {
|
||||||
|
return reg, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, model_types.RecordNotExist
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *filesystem) GlobalRegistryList(p *model.ListOptions) ([]*model.Registry, error) {
|
||||||
regs, err := parseDockerConfig(f.path)
|
regs, err := parseDockerConfig(f.path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -18,15 +18,29 @@ import "go.woodpecker-ci.org/woodpecker/v2/server/model"
|
||||||
|
|
||||||
// Service defines a service for managing registries.
|
// Service defines a service for managing registries.
|
||||||
type Service interface {
|
type Service interface {
|
||||||
|
RegistryListPipeline(*model.Repo, *model.Pipeline) ([]*model.Registry, error)
|
||||||
|
// Repository registries
|
||||||
RegistryFind(*model.Repo, string) (*model.Registry, error)
|
RegistryFind(*model.Repo, string) (*model.Registry, error)
|
||||||
RegistryList(*model.Repo, *model.ListOptions) ([]*model.Registry, error)
|
RegistryList(*model.Repo, *model.ListOptions) ([]*model.Registry, error)
|
||||||
RegistryCreate(*model.Repo, *model.Registry) error
|
RegistryCreate(*model.Repo, *model.Registry) error
|
||||||
RegistryUpdate(*model.Repo, *model.Registry) error
|
RegistryUpdate(*model.Repo, *model.Registry) error
|
||||||
RegistryDelete(*model.Repo, string) error
|
RegistryDelete(*model.Repo, string) error
|
||||||
|
// Organization registries
|
||||||
|
OrgRegistryFind(int64, string) (*model.Registry, error)
|
||||||
|
OrgRegistryList(int64, *model.ListOptions) ([]*model.Registry, error)
|
||||||
|
OrgRegistryCreate(int64, *model.Registry) error
|
||||||
|
OrgRegistryUpdate(int64, *model.Registry) error
|
||||||
|
OrgRegistryDelete(int64, string) error
|
||||||
|
// Global registries
|
||||||
|
GlobalRegistryFind(string) (*model.Registry, error)
|
||||||
|
GlobalRegistryList(*model.ListOptions) ([]*model.Registry, error)
|
||||||
|
GlobalRegistryCreate(*model.Registry) error
|
||||||
|
GlobalRegistryUpdate(*model.Registry) error
|
||||||
|
GlobalRegistryDelete(string) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadOnlyService defines a service for managing registries.
|
// ReadOnlyService defines a service for managing registries.
|
||||||
type ReadOnlyService interface {
|
type ReadOnlyService interface {
|
||||||
RegistryFind(*model.Repo, string) (*model.Registry, error)
|
GlobalRegistryFind(string) (*model.Registry, error)
|
||||||
RegistryList(*model.Repo, *model.ListOptions) ([]*model.Registry, error)
|
GlobalRegistryList(*model.ListOptions) ([]*model.Registry, error)
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,8 +36,8 @@ func (d *db) SecretList(repo *model.Repo, p *model.ListOptions) ([]*model.Secret
|
||||||
return d.store.SecretList(repo, false, p)
|
return d.store.SecretList(repo, false, p)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *db) SecretListPipeline(repo *model.Repo, _ *model.Pipeline, p *model.ListOptions) ([]*model.Secret, error) {
|
func (d *db) SecretListPipeline(repo *model.Repo, _ *model.Pipeline) ([]*model.Secret, error) {
|
||||||
s, err := d.store.SecretList(repo, true, p)
|
s, err := d.store.SecretList(repo, true, &model.ListOptions{All: true})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,7 +64,7 @@ func TestSecretListPipeline(t *testing.T) {
|
||||||
repoSecret,
|
repoSecret,
|
||||||
}, nil)
|
}, nil)
|
||||||
|
|
||||||
s, err := secret.NewDB(mockStore).SecretListPipeline(&model.Repo{}, &model.Pipeline{}, &model.ListOptions{})
|
s, err := secret.NewDB(mockStore).SecretListPipeline(&model.Repo{}, &model.Pipeline{})
|
||||||
g.Assert(err).IsNil()
|
g.Assert(err).IsNil()
|
||||||
|
|
||||||
g.Assert(len(s)).Equal(1)
|
g.Assert(len(s)).Equal(1)
|
||||||
|
@ -77,7 +77,7 @@ func TestSecretListPipeline(t *testing.T) {
|
||||||
orgSecret,
|
orgSecret,
|
||||||
}, nil)
|
}, nil)
|
||||||
|
|
||||||
s, err := secret.NewDB(mockStore).SecretListPipeline(&model.Repo{}, &model.Pipeline{}, &model.ListOptions{})
|
s, err := secret.NewDB(mockStore).SecretListPipeline(&model.Repo{}, &model.Pipeline{})
|
||||||
g.Assert(err).IsNil()
|
g.Assert(err).IsNil()
|
||||||
|
|
||||||
g.Assert(len(s)).Equal(1)
|
g.Assert(len(s)).Equal(1)
|
||||||
|
@ -89,7 +89,7 @@ func TestSecretListPipeline(t *testing.T) {
|
||||||
globalSecret,
|
globalSecret,
|
||||||
}, nil)
|
}, nil)
|
||||||
|
|
||||||
s, err := secret.NewDB(mockStore).SecretListPipeline(&model.Repo{}, &model.Pipeline{}, &model.ListOptions{})
|
s, err := secret.NewDB(mockStore).SecretListPipeline(&model.Repo{}, &model.Pipeline{})
|
||||||
g.Assert(err).IsNil()
|
g.Assert(err).IsNil()
|
||||||
|
|
||||||
g.Assert(len(s)).Equal(1)
|
g.Assert(len(s)).Equal(1)
|
||||||
|
|
|
@ -18,7 +18,7 @@ import "go.woodpecker-ci.org/woodpecker/v2/server/model"
|
||||||
|
|
||||||
// Service defines a service for managing secrets.
|
// Service defines a service for managing secrets.
|
||||||
type Service interface {
|
type Service interface {
|
||||||
SecretListPipeline(*model.Repo, *model.Pipeline, *model.ListOptions) ([]*model.Secret, error)
|
SecretListPipeline(*model.Repo, *model.Pipeline) ([]*model.Secret, error)
|
||||||
// Repository secrets
|
// Repository secrets
|
||||||
SecretFind(*model.Repo, string) (*model.Secret, error)
|
SecretFind(*model.Repo, string) (*model.Secret, error)
|
||||||
SecretList(*model.Repo, *model.ListOptions) ([]*model.Secret, error)
|
SecretList(*model.Repo, *model.ListOptions) ([]*model.Secret, error)
|
||||||
|
|
33
server/store/datastore/migration/032_registries_add_user.go
Normal file
33
server/store/datastore/migration/032_registries_add_user.go
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
// Copyright 2024 Woodpecker Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package migration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"src.techknowlogick.com/xormigrate"
|
||||||
|
"xorm.io/xorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
var alterTableRegistriesFixRequiredFields = xormigrate.Migration{
|
||||||
|
ID: "alter-table-registries-fix-required-fields",
|
||||||
|
MigrateSession: func(sess *xorm.Session) error {
|
||||||
|
if err := alterColumnDefault(sess, "registries", "repo_id", "0"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := alterColumnNull(sess, "registries", "repo_id", false); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return alterColumnNull(sess, "registries", "address", false)
|
||||||
|
},
|
||||||
|
}
|
|
@ -195,6 +195,7 @@ func alterColumnDefault(sess *xorm.Session, table, column, defValue string) erro
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//nolint:unparam
|
||||||
func alterColumnNull(sess *xorm.Session, table, column string, null bool) error {
|
func alterColumnNull(sess *xorm.Session, table, column string, null bool) error {
|
||||||
val := "NULL"
|
val := "NULL"
|
||||||
if !null {
|
if !null {
|
||||||
|
|
|
@ -61,6 +61,7 @@ var migrationTasks = []*xormigrate.Migration{
|
||||||
&cleanRegistryPipeline,
|
&cleanRegistryPipeline,
|
||||||
&setForgeID,
|
&setForgeID,
|
||||||
&unifyColumnsTables,
|
&unifyColumnsTables,
|
||||||
|
&alterTableRegistriesFixRequiredFields,
|
||||||
}
|
}
|
||||||
|
|
||||||
var allBeans = []any{
|
var allBeans = []any{
|
||||||
|
|
|
@ -20,6 +20,8 @@ import (
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/server/model"
|
"go.woodpecker-ci.org/woodpecker/v2/server/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const orderRegistriesBy = "id"
|
||||||
|
|
||||||
func (s storage) RegistryFind(repo *model.Repo, addr string) (*model.Registry, error) {
|
func (s storage) RegistryFind(repo *model.Repo, addr string) (*model.Registry, error) {
|
||||||
reg := new(model.Registry)
|
reg := new(model.Registry)
|
||||||
return reg, wrapGet(s.engine.Where(
|
return reg, wrapGet(s.engine.Where(
|
||||||
|
@ -27,9 +29,19 @@ func (s storage) RegistryFind(repo *model.Repo, addr string) (*model.Registry, e
|
||||||
).Get(reg))
|
).Get(reg))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s storage) RegistryList(repo *model.Repo, p *model.ListOptions) ([]*model.Registry, error) {
|
func (s storage) RegistryList(repo *model.Repo, includeGlobalAndOrg bool, p *model.ListOptions) ([]*model.Registry, error) {
|
||||||
var regs []*model.Registry
|
var regs []*model.Registry
|
||||||
return regs, s.paginate(p).OrderBy("id").Where("repo_id = ?", repo.ID).Find(®s)
|
var cond builder.Cond = builder.Eq{"repo_id": repo.ID}
|
||||||
|
if includeGlobalAndOrg {
|
||||||
|
cond = cond.Or(builder.Eq{"org_id": repo.OrgID}).
|
||||||
|
Or(builder.And(builder.Eq{"org_id": 0}, builder.Eq{"repo_id": 0}))
|
||||||
|
}
|
||||||
|
return regs, s.paginate(p).Where(cond).OrderBy(orderRegistriesBy).Find(®s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s storage) RegistryListAll() ([]*model.Registry, error) {
|
||||||
|
var registries []*model.Registry
|
||||||
|
return registries, s.engine.Find(®istries)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s storage) RegistryCreate(registry *model.Registry) error {
|
func (s storage) RegistryCreate(registry *model.Registry) error {
|
||||||
|
@ -43,10 +55,32 @@ func (s storage) RegistryUpdate(registry *model.Registry) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s storage) RegistryDelete(repo *model.Repo, addr string) error {
|
func (s storage) RegistryDelete(registry *model.Registry) error {
|
||||||
registry, err := s.RegistryFind(repo, addr)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return wrapDelete(s.engine.ID(registry.ID).Delete(new(model.Registry)))
|
return wrapDelete(s.engine.ID(registry.ID).Delete(new(model.Registry)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s storage) OrgRegistryFind(orgID int64, name string) (*model.Registry, error) {
|
||||||
|
registry := new(model.Registry)
|
||||||
|
return registry, wrapGet(s.engine.Where(
|
||||||
|
builder.Eq{"org_id": orgID, "address": name},
|
||||||
|
).Get(registry))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s storage) OrgRegistryList(orgID int64, p *model.ListOptions) ([]*model.Registry, error) {
|
||||||
|
registries := make([]*model.Registry, 0)
|
||||||
|
return registries, s.paginate(p).Where("org_id = ?", orgID).OrderBy(orderRegistriesBy).Find(®istries)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s storage) GlobalRegistryFind(name string) (*model.Registry, error) {
|
||||||
|
registry := new(model.Registry)
|
||||||
|
return registry, wrapGet(s.engine.Where(
|
||||||
|
builder.Eq{"org_id": 0, "repo_id": 0, "address": name},
|
||||||
|
).Get(registry))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s storage) GlobalRegistryList(p *model.ListOptions) ([]*model.Registry, error) {
|
||||||
|
registries := make([]*model.Registry, 0)
|
||||||
|
return registries, s.paginate(p).Where(
|
||||||
|
builder.Eq{"org_id": 0, "repo_id": 0},
|
||||||
|
).OrderBy(orderRegistriesBy).Find(®istries)
|
||||||
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/server/model"
|
"go.woodpecker-ci.org/woodpecker/v2/server/model"
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/server/store/types"
|
"go.woodpecker-ci.org/woodpecker/v2/server/store/types"
|
||||||
|
@ -60,7 +61,7 @@ func TestRegistryList(t *testing.T) {
|
||||||
Password: "bar",
|
Password: "bar",
|
||||||
}))
|
}))
|
||||||
|
|
||||||
list, err := store.RegistryList(&model.Repo{ID: 1}, &model.ListOptions{Page: 1, PerPage: 50})
|
list, err := store.RegistryList(&model.Repo{ID: 1}, false, &model.ListOptions{Page: 1, PerPage: 50})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Len(t, list, 2)
|
assert.Len(t, list, 2)
|
||||||
}
|
}
|
||||||
|
@ -117,6 +118,88 @@ func TestRegistryDelete(t *testing.T) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.NoError(t, store.RegistryDelete(&model.Repo{ID: 1}, "index.docker.io"))
|
assert.NoError(t, store.RegistryDelete(reg1))
|
||||||
assert.ErrorIs(t, store.RegistryDelete(&model.Repo{ID: 1}, "index.docker.io"), types.RecordNotExist)
|
assert.ErrorIs(t, store.RegistryDelete(reg1), types.RecordNotExist)
|
||||||
|
}
|
||||||
|
|
||||||
|
func createTestRegistries(t *testing.T, store *storage) {
|
||||||
|
assert.NoError(t, store.RegistryCreate(&model.Registry{
|
||||||
|
OrgID: 12,
|
||||||
|
Address: "my.regsitry.local",
|
||||||
|
}))
|
||||||
|
assert.NoError(t, store.RegistryCreate(&model.Registry{
|
||||||
|
RepoID: 1,
|
||||||
|
Address: "private.registry.local",
|
||||||
|
}))
|
||||||
|
assert.NoError(t, store.RegistryCreate(&model.Registry{
|
||||||
|
RepoID: 1,
|
||||||
|
Address: "very-private.registry.local",
|
||||||
|
}))
|
||||||
|
assert.NoError(t, store.RegistryCreate(&model.Registry{
|
||||||
|
Address: "index.docker.io",
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOrgRegistryFind(t *testing.T) {
|
||||||
|
store, closer := newTestStore(t, new(model.Registry))
|
||||||
|
defer closer()
|
||||||
|
|
||||||
|
err := store.RegistryCreate(&model.Registry{
|
||||||
|
OrgID: 12,
|
||||||
|
Address: "my.regsitry.local",
|
||||||
|
Username: "username",
|
||||||
|
Password: "password",
|
||||||
|
})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
registry, err := store.OrgRegistryFind(12, "my.regsitry.local")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.EqualValues(t, 12, registry.OrgID)
|
||||||
|
assert.Equal(t, "my.regsitry.local", registry.Address)
|
||||||
|
assert.Equal(t, "username", registry.Username)
|
||||||
|
assert.Equal(t, "password", registry.Password)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOrgRegistryList(t *testing.T) {
|
||||||
|
store, closer := newTestStore(t, new(model.Registry))
|
||||||
|
defer closer()
|
||||||
|
|
||||||
|
createTestRegistries(t, store)
|
||||||
|
|
||||||
|
list, err := store.OrgRegistryList(12, &model.ListOptions{All: true})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
require.Len(t, list, 1)
|
||||||
|
|
||||||
|
assert.True(t, list[0].IsOrganization())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGlobalRegistryFind(t *testing.T) {
|
||||||
|
store, closer := newTestStore(t, new(model.Registry))
|
||||||
|
defer closer()
|
||||||
|
|
||||||
|
err := store.RegistryCreate(&model.Registry{
|
||||||
|
Address: "my.regsitry.local",
|
||||||
|
Username: "username",
|
||||||
|
Password: "password",
|
||||||
|
})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
registry, err := store.GlobalRegistryFind("my.regsitry.local")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, "my.regsitry.local", registry.Address)
|
||||||
|
assert.Equal(t, "username", registry.Username)
|
||||||
|
assert.Equal(t, "password", registry.Password)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGlobalRegistryList(t *testing.T) {
|
||||||
|
store, closer := newTestStore(t, new(model.Registry))
|
||||||
|
defer closer()
|
||||||
|
|
||||||
|
createTestRegistries(t, store)
|
||||||
|
|
||||||
|
list, err := store.GlobalRegistryList(&model.ListOptions{All: true})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Len(t, list, 1)
|
||||||
|
|
||||||
|
assert.True(t, list[0].IsGlobal())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1190,6 +1190,66 @@ func (_m *Store) GetUserRemoteID(_a0 model.ForgeRemoteID, _a1 string) (*model.Us
|
||||||
return r0, r1
|
return r0, r1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GlobalRegistryFind provides a mock function with given fields: _a0
|
||||||
|
func (_m *Store) GlobalRegistryFind(_a0 string) (*model.Registry, error) {
|
||||||
|
ret := _m.Called(_a0)
|
||||||
|
|
||||||
|
if len(ret) == 0 {
|
||||||
|
panic("no return value specified for GlobalRegistryFind")
|
||||||
|
}
|
||||||
|
|
||||||
|
var r0 *model.Registry
|
||||||
|
var r1 error
|
||||||
|
if rf, ok := ret.Get(0).(func(string) (*model.Registry, error)); ok {
|
||||||
|
return rf(_a0)
|
||||||
|
}
|
||||||
|
if rf, ok := ret.Get(0).(func(string) *model.Registry); ok {
|
||||||
|
r0 = rf(_a0)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(*model.Registry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if rf, ok := ret.Get(1).(func(string) error); ok {
|
||||||
|
r1 = rf(_a0)
|
||||||
|
} else {
|
||||||
|
r1 = ret.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0, r1
|
||||||
|
}
|
||||||
|
|
||||||
|
// GlobalRegistryList provides a mock function with given fields: _a0
|
||||||
|
func (_m *Store) GlobalRegistryList(_a0 *model.ListOptions) ([]*model.Registry, error) {
|
||||||
|
ret := _m.Called(_a0)
|
||||||
|
|
||||||
|
if len(ret) == 0 {
|
||||||
|
panic("no return value specified for GlobalRegistryList")
|
||||||
|
}
|
||||||
|
|
||||||
|
var r0 []*model.Registry
|
||||||
|
var r1 error
|
||||||
|
if rf, ok := ret.Get(0).(func(*model.ListOptions) ([]*model.Registry, error)); ok {
|
||||||
|
return rf(_a0)
|
||||||
|
}
|
||||||
|
if rf, ok := ret.Get(0).(func(*model.ListOptions) []*model.Registry); ok {
|
||||||
|
r0 = rf(_a0)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).([]*model.Registry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if rf, ok := ret.Get(1).(func(*model.ListOptions) error); ok {
|
||||||
|
r1 = rf(_a0)
|
||||||
|
} else {
|
||||||
|
r1 = ret.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0, r1
|
||||||
|
}
|
||||||
|
|
||||||
// GlobalSecretFind provides a mock function with given fields: _a0
|
// GlobalSecretFind provides a mock function with given fields: _a0
|
||||||
func (_m *Store) GlobalSecretFind(_a0 string) (*model.Secret, error) {
|
func (_m *Store) GlobalSecretFind(_a0 string) (*model.Secret, error) {
|
||||||
ret := _m.Called(_a0)
|
ret := _m.Called(_a0)
|
||||||
|
@ -1488,6 +1548,66 @@ func (_m *Store) OrgList(_a0 *model.ListOptions) ([]*model.Org, error) {
|
||||||
return r0, r1
|
return r0, r1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// OrgRegistryFind provides a mock function with given fields: _a0, _a1
|
||||||
|
func (_m *Store) OrgRegistryFind(_a0 int64, _a1 string) (*model.Registry, error) {
|
||||||
|
ret := _m.Called(_a0, _a1)
|
||||||
|
|
||||||
|
if len(ret) == 0 {
|
||||||
|
panic("no return value specified for OrgRegistryFind")
|
||||||
|
}
|
||||||
|
|
||||||
|
var r0 *model.Registry
|
||||||
|
var r1 error
|
||||||
|
if rf, ok := ret.Get(0).(func(int64, string) (*model.Registry, error)); ok {
|
||||||
|
return rf(_a0, _a1)
|
||||||
|
}
|
||||||
|
if rf, ok := ret.Get(0).(func(int64, string) *model.Registry); ok {
|
||||||
|
r0 = rf(_a0, _a1)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(*model.Registry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if rf, ok := ret.Get(1).(func(int64, string) error); ok {
|
||||||
|
r1 = rf(_a0, _a1)
|
||||||
|
} else {
|
||||||
|
r1 = ret.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0, r1
|
||||||
|
}
|
||||||
|
|
||||||
|
// OrgRegistryList provides a mock function with given fields: _a0, _a1
|
||||||
|
func (_m *Store) OrgRegistryList(_a0 int64, _a1 *model.ListOptions) ([]*model.Registry, error) {
|
||||||
|
ret := _m.Called(_a0, _a1)
|
||||||
|
|
||||||
|
if len(ret) == 0 {
|
||||||
|
panic("no return value specified for OrgRegistryList")
|
||||||
|
}
|
||||||
|
|
||||||
|
var r0 []*model.Registry
|
||||||
|
var r1 error
|
||||||
|
if rf, ok := ret.Get(0).(func(int64, *model.ListOptions) ([]*model.Registry, error)); ok {
|
||||||
|
return rf(_a0, _a1)
|
||||||
|
}
|
||||||
|
if rf, ok := ret.Get(0).(func(int64, *model.ListOptions) []*model.Registry); ok {
|
||||||
|
r0 = rf(_a0, _a1)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).([]*model.Registry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if rf, ok := ret.Get(1).(func(int64, *model.ListOptions) error); ok {
|
||||||
|
r1 = rf(_a0, _a1)
|
||||||
|
} else {
|
||||||
|
r1 = ret.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0, r1
|
||||||
|
}
|
||||||
|
|
||||||
// OrgRepoList provides a mock function with given fields: _a0, _a1
|
// OrgRepoList provides a mock function with given fields: _a0, _a1
|
||||||
func (_m *Store) OrgRepoList(_a0 *model.Org, _a1 *model.ListOptions) ([]*model.Repo, error) {
|
func (_m *Store) OrgRepoList(_a0 *model.Org, _a1 *model.ListOptions) ([]*model.Repo, error) {
|
||||||
ret := _m.Called(_a0, _a1)
|
ret := _m.Called(_a0, _a1)
|
||||||
|
@ -1698,17 +1818,17 @@ func (_m *Store) RegistryCreate(_a0 *model.Registry) error {
|
||||||
return r0
|
return r0
|
||||||
}
|
}
|
||||||
|
|
||||||
// RegistryDelete provides a mock function with given fields: repo, addr
|
// RegistryDelete provides a mock function with given fields: _a0
|
||||||
func (_m *Store) RegistryDelete(repo *model.Repo, addr string) error {
|
func (_m *Store) RegistryDelete(_a0 *model.Registry) error {
|
||||||
ret := _m.Called(repo, addr)
|
ret := _m.Called(_a0)
|
||||||
|
|
||||||
if len(ret) == 0 {
|
if len(ret) == 0 {
|
||||||
panic("no return value specified for RegistryDelete")
|
panic("no return value specified for RegistryDelete")
|
||||||
}
|
}
|
||||||
|
|
||||||
var r0 error
|
var r0 error
|
||||||
if rf, ok := ret.Get(0).(func(*model.Repo, string) error); ok {
|
if rf, ok := ret.Get(0).(func(*model.Registry) error); ok {
|
||||||
r0 = rf(repo, addr)
|
r0 = rf(_a0)
|
||||||
} else {
|
} else {
|
||||||
r0 = ret.Error(0)
|
r0 = ret.Error(0)
|
||||||
}
|
}
|
||||||
|
@ -1746,9 +1866,9 @@ func (_m *Store) RegistryFind(_a0 *model.Repo, _a1 string) (*model.Registry, err
|
||||||
return r0, r1
|
return r0, r1
|
||||||
}
|
}
|
||||||
|
|
||||||
// RegistryList provides a mock function with given fields: _a0, _a1
|
// RegistryList provides a mock function with given fields: _a0, _a1, _a2
|
||||||
func (_m *Store) RegistryList(_a0 *model.Repo, _a1 *model.ListOptions) ([]*model.Registry, error) {
|
func (_m *Store) RegistryList(_a0 *model.Repo, _a1 bool, _a2 *model.ListOptions) ([]*model.Registry, error) {
|
||||||
ret := _m.Called(_a0, _a1)
|
ret := _m.Called(_a0, _a1, _a2)
|
||||||
|
|
||||||
if len(ret) == 0 {
|
if len(ret) == 0 {
|
||||||
panic("no return value specified for RegistryList")
|
panic("no return value specified for RegistryList")
|
||||||
|
@ -1756,19 +1876,49 @@ func (_m *Store) RegistryList(_a0 *model.Repo, _a1 *model.ListOptions) ([]*model
|
||||||
|
|
||||||
var r0 []*model.Registry
|
var r0 []*model.Registry
|
||||||
var r1 error
|
var r1 error
|
||||||
if rf, ok := ret.Get(0).(func(*model.Repo, *model.ListOptions) ([]*model.Registry, error)); ok {
|
if rf, ok := ret.Get(0).(func(*model.Repo, bool, *model.ListOptions) ([]*model.Registry, error)); ok {
|
||||||
return rf(_a0, _a1)
|
return rf(_a0, _a1, _a2)
|
||||||
}
|
}
|
||||||
if rf, ok := ret.Get(0).(func(*model.Repo, *model.ListOptions) []*model.Registry); ok {
|
if rf, ok := ret.Get(0).(func(*model.Repo, bool, *model.ListOptions) []*model.Registry); ok {
|
||||||
r0 = rf(_a0, _a1)
|
r0 = rf(_a0, _a1, _a2)
|
||||||
} else {
|
} else {
|
||||||
if ret.Get(0) != nil {
|
if ret.Get(0) != nil {
|
||||||
r0 = ret.Get(0).([]*model.Registry)
|
r0 = ret.Get(0).([]*model.Registry)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if rf, ok := ret.Get(1).(func(*model.Repo, *model.ListOptions) error); ok {
|
if rf, ok := ret.Get(1).(func(*model.Repo, bool, *model.ListOptions) error); ok {
|
||||||
r1 = rf(_a0, _a1)
|
r1 = rf(_a0, _a1, _a2)
|
||||||
|
} else {
|
||||||
|
r1 = ret.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0, r1
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegistryListAll provides a mock function with given fields:
|
||||||
|
func (_m *Store) RegistryListAll() ([]*model.Registry, error) {
|
||||||
|
ret := _m.Called()
|
||||||
|
|
||||||
|
if len(ret) == 0 {
|
||||||
|
panic("no return value specified for RegistryListAll")
|
||||||
|
}
|
||||||
|
|
||||||
|
var r0 []*model.Registry
|
||||||
|
var r1 error
|
||||||
|
if rf, ok := ret.Get(0).(func() ([]*model.Registry, error)); ok {
|
||||||
|
return rf()
|
||||||
|
}
|
||||||
|
if rf, ok := ret.Get(0).(func() []*model.Registry); ok {
|
||||||
|
r0 = rf()
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).([]*model.Registry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if rf, ok := ret.Get(1).(func() error); ok {
|
||||||
|
r1 = rf()
|
||||||
} else {
|
} else {
|
||||||
r1 = ret.Error(1)
|
r1 = ret.Error(1)
|
||||||
}
|
}
|
||||||
|
|
|
@ -120,10 +120,15 @@ type Store interface {
|
||||||
|
|
||||||
// Registries
|
// Registries
|
||||||
RegistryFind(*model.Repo, string) (*model.Registry, error)
|
RegistryFind(*model.Repo, string) (*model.Registry, error)
|
||||||
RegistryList(*model.Repo, *model.ListOptions) ([]*model.Registry, error)
|
RegistryList(*model.Repo, bool, *model.ListOptions) ([]*model.Registry, error)
|
||||||
|
RegistryListAll() ([]*model.Registry, error)
|
||||||
RegistryCreate(*model.Registry) error
|
RegistryCreate(*model.Registry) error
|
||||||
RegistryUpdate(*model.Registry) error
|
RegistryUpdate(*model.Registry) error
|
||||||
RegistryDelete(repo *model.Repo, addr string) error
|
RegistryDelete(*model.Registry) error
|
||||||
|
OrgRegistryFind(int64, string) (*model.Registry, error)
|
||||||
|
OrgRegistryList(int64, *model.ListOptions) ([]*model.Registry, error)
|
||||||
|
GlobalRegistryFind(string) (*model.Registry, error)
|
||||||
|
GlobalRegistryList(*model.ListOptions) ([]*model.Registry, error)
|
||||||
|
|
||||||
// Steps
|
// Steps
|
||||||
StepLoad(int64) (*model.Step, error)
|
StepLoad(int64) (*model.Step, error)
|
||||||
|
|
6
web/components.d.ts
vendored
6
web/components.d.ts
vendored
|
@ -14,6 +14,7 @@ declare module 'vue' {
|
||||||
AdminOrgsTab: typeof import('./src/components/admin/settings/AdminOrgsTab.vue')['default']
|
AdminOrgsTab: typeof import('./src/components/admin/settings/AdminOrgsTab.vue')['default']
|
||||||
AdminQueueStats: typeof import('./src/components/admin/settings/queue/AdminQueueStats.vue')['default']
|
AdminQueueStats: typeof import('./src/components/admin/settings/queue/AdminQueueStats.vue')['default']
|
||||||
AdminQueueTab: typeof import('./src/components/admin/settings/AdminQueueTab.vue')['default']
|
AdminQueueTab: typeof import('./src/components/admin/settings/AdminQueueTab.vue')['default']
|
||||||
|
AdminRegistriesTab: typeof import('./src/components/admin/settings/AdminRegistriesTab.vue')['default']
|
||||||
AdminReposTab: typeof import('./src/components/admin/settings/AdminReposTab.vue')['default']
|
AdminReposTab: typeof import('./src/components/admin/settings/AdminReposTab.vue')['default']
|
||||||
AdminSecretsTab: typeof import('./src/components/admin/settings/AdminSecretsTab.vue')['default']
|
AdminSecretsTab: typeof import('./src/components/admin/settings/AdminSecretsTab.vue')['default']
|
||||||
AdminUsersTab: typeof import('./src/components/admin/settings/AdminUsersTab.vue')['default']
|
AdminUsersTab: typeof import('./src/components/admin/settings/AdminUsersTab.vue')['default']
|
||||||
|
@ -66,12 +67,12 @@ declare module 'vue' {
|
||||||
IMdiPlay: typeof import('~icons/mdi/play')['default']
|
IMdiPlay: typeof import('~icons/mdi/play')['default']
|
||||||
IMdiRadioboxBlank: typeof import('~icons/mdi/radiobox-blank')['default']
|
IMdiRadioboxBlank: typeof import('~icons/mdi/radiobox-blank')['default']
|
||||||
IMdiRadioboxIndeterminateVariant: typeof import('~icons/mdi/radiobox-indeterminate-variant')['default']
|
IMdiRadioboxIndeterminateVariant: typeof import('~icons/mdi/radiobox-indeterminate-variant')['default']
|
||||||
|
IMdiSync: typeof import('~icons/mdi/sync')['default']
|
||||||
IMdiSourceBranch: typeof import('~icons/mdi/source-branch')['default']
|
IMdiSourceBranch: typeof import('~icons/mdi/source-branch')['default']
|
||||||
IMdiSourceCommit: typeof import('~icons/mdi/source-commit')['default']
|
IMdiSourceCommit: typeof import('~icons/mdi/source-commit')['default']
|
||||||
IMdiSourceMerge: typeof import('~icons/mdi/source-merge')['default']
|
IMdiSourceMerge: typeof import('~icons/mdi/source-merge')['default']
|
||||||
IMdiSourcePull: typeof import('~icons/mdi/source-pull')['default']
|
IMdiSourcePull: typeof import('~icons/mdi/source-pull')['default']
|
||||||
IMdiStop: typeof import('~icons/mdi/stop')['default']
|
IMdiStop: typeof import('~icons/mdi/stop')['default']
|
||||||
IMdiSync: typeof import('~icons/mdi/sync')['default']
|
|
||||||
IMdiTagOutline: typeof import('~icons/mdi/tag-outline')['default']
|
IMdiTagOutline: typeof import('~icons/mdi/tag-outline')['default']
|
||||||
InputField: typeof import('./src/components/form/InputField.vue')['default']
|
InputField: typeof import('./src/components/form/InputField.vue')['default']
|
||||||
IPhGitlabLogoSimpleFill: typeof import('~icons/ph/gitlab-logo-simple-fill')['default']
|
IPhGitlabLogoSimpleFill: typeof import('~icons/ph/gitlab-logo-simple-fill')['default']
|
||||||
|
@ -85,6 +86,7 @@ declare module 'vue' {
|
||||||
ManualPipelinePopup: typeof import('./src/components/layout/popups/ManualPipelinePopup.vue')['default']
|
ManualPipelinePopup: typeof import('./src/components/layout/popups/ManualPipelinePopup.vue')['default']
|
||||||
Navbar: typeof import('./src/components/layout/header/Navbar.vue')['default']
|
Navbar: typeof import('./src/components/layout/header/Navbar.vue')['default']
|
||||||
NumberField: typeof import('./src/components/form/NumberField.vue')['default']
|
NumberField: typeof import('./src/components/form/NumberField.vue')['default']
|
||||||
|
OrgRegistriesTab: typeof import('./src/components/org/settings/OrgRegistriesTab.vue')['default']
|
||||||
OrgSecretsTab: typeof import('./src/components/org/settings/OrgSecretsTab.vue')['default']
|
OrgSecretsTab: typeof import('./src/components/org/settings/OrgSecretsTab.vue')['default']
|
||||||
Panel: typeof import('./src/components/layout/Panel.vue')['default']
|
Panel: typeof import('./src/components/layout/Panel.vue')['default']
|
||||||
PipelineFeedItem: typeof import('./src/components/pipeline-feed/PipelineFeedItem.vue')['default']
|
PipelineFeedItem: typeof import('./src/components/pipeline-feed/PipelineFeedItem.vue')['default']
|
||||||
|
@ -98,7 +100,9 @@ declare module 'vue' {
|
||||||
PipelineStepList: typeof import('./src/components/repo/pipeline/PipelineStepList.vue')['default']
|
PipelineStepList: typeof import('./src/components/repo/pipeline/PipelineStepList.vue')['default']
|
||||||
Popup: typeof import('./src/components/layout/Popup.vue')['default']
|
Popup: typeof import('./src/components/layout/Popup.vue')['default']
|
||||||
RadioField: typeof import('./src/components/form/RadioField.vue')['default']
|
RadioField: typeof import('./src/components/form/RadioField.vue')['default']
|
||||||
|
RegistryEdit: typeof import('./src/components/registry/RegistryEdit.vue')['default']
|
||||||
RegistriesTab: typeof import('./src/components/repo/settings/RegistriesTab.vue')['default']
|
RegistriesTab: typeof import('./src/components/repo/settings/RegistriesTab.vue')['default']
|
||||||
|
RegistryList: typeof import('./src/components/registry/RegistryList.vue')['default']
|
||||||
RouterLink: typeof import('vue-router')['RouterLink']
|
RouterLink: typeof import('vue-router')['RouterLink']
|
||||||
RouterView: typeof import('vue-router')['RouterView']
|
RouterView: typeof import('vue-router')['RouterView']
|
||||||
Scaffold: typeof import('./src/components/layout/scaffold/Scaffold.vue')['default']
|
Scaffold: typeof import('./src/components/layout/scaffold/Scaffold.vue')['default']
|
||||||
|
|
|
@ -122,24 +122,6 @@
|
||||||
"desc": "Enable to cancel pending and running pipelines of the same event and context before starting the newly triggered one."
|
"desc": "Enable to cancel pending and running pipelines of the same event and context before starting the newly triggered one."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"registries": {
|
|
||||||
"registries": "Registries",
|
|
||||||
"credentials": "Registry credentials",
|
|
||||||
"desc": "Registries credentials can be added to use private images for your pipeline.",
|
|
||||||
"show": "Show registries",
|
|
||||||
"add": "Add registry",
|
|
||||||
"none": "There are no registry credentials yet.",
|
|
||||||
"save": "Save registry",
|
|
||||||
"created": "Registry credentials created",
|
|
||||||
"saved": "Registry credentials saved",
|
|
||||||
"deleted": "Registry credentials deleted",
|
|
||||||
"address": {
|
|
||||||
"address": "Address",
|
|
||||||
"placeholder": "Registry Address (e.g. docker.io)"
|
|
||||||
},
|
|
||||||
"edit": "Edit registry",
|
|
||||||
"delete": "Delete registry"
|
|
||||||
},
|
|
||||||
"crons": {
|
"crons": {
|
||||||
"crons": "Crons",
|
"crons": "Crons",
|
||||||
"desc": "Cron jobs can be used to trigger pipelines on a regular basis.",
|
"desc": "Cron jobs can be used to trigger pipelines on a regular basis.",
|
||||||
|
@ -266,6 +248,9 @@
|
||||||
"not_allowed": "You are not allowed to access this organization's settings",
|
"not_allowed": "You are not allowed to access this organization's settings",
|
||||||
"secrets": {
|
"secrets": {
|
||||||
"desc": "Organization secrets can be passed to all organization's repository individual pipeline steps at runtime as environmental variables."
|
"desc": "Organization secrets can be passed to all organization's repository individual pipeline steps at runtime as environmental variables."
|
||||||
|
},
|
||||||
|
"registries": {
|
||||||
|
"desc": "Organization registry credentials can be added to use private images for all organization's pipelines."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -276,6 +261,10 @@
|
||||||
"desc": "Global secrets can be passed to all repositories individual pipeline steps at runtime as environmental variables.",
|
"desc": "Global secrets can be passed to all repositories individual pipeline steps at runtime as environmental variables.",
|
||||||
"warning": "These secrets will be available for all server users."
|
"warning": "These secrets will be available for all server users."
|
||||||
},
|
},
|
||||||
|
"registries": {
|
||||||
|
"desc": "Global registry credentials can be added to use private images for all server's pipelines.",
|
||||||
|
"warning": "These registry creditentials will be available for all server users."
|
||||||
|
},
|
||||||
"agents": {
|
"agents": {
|
||||||
"agents": "Agents",
|
"agents": "Agents",
|
||||||
"desc": "Agents registered for this server",
|
"desc": "Agents registered for this server",
|
||||||
|
@ -435,6 +424,26 @@
|
||||||
"edit": "Edit secret",
|
"edit": "Edit secret",
|
||||||
"delete": "Delete secret"
|
"delete": "Delete secret"
|
||||||
},
|
},
|
||||||
|
"registries": {
|
||||||
|
"registries": "Registries",
|
||||||
|
"credentials": "Registry credentials",
|
||||||
|
"desc": "Registries credentials can be added to use private images for your pipeline.",
|
||||||
|
"none": "There are no registry credentials yet.",
|
||||||
|
"address": {
|
||||||
|
"address": "Address",
|
||||||
|
"desc": "Registry Address (e.g. docker.io)"
|
||||||
|
},
|
||||||
|
"show": "Show registries",
|
||||||
|
"save": "Save registry",
|
||||||
|
"add": "Add registry",
|
||||||
|
"view": "View registry",
|
||||||
|
"edit": "Edit registry",
|
||||||
|
"delete": "Delete registry",
|
||||||
|
"delete_confirm": "Do you really want to delete this registry?",
|
||||||
|
"created": "Registry credentials created",
|
||||||
|
"saved": "Registry credentials saved",
|
||||||
|
"deleted": "Registry credentials deleted"
|
||||||
|
},
|
||||||
"default": "default",
|
"default": "default",
|
||||||
"info": "Info",
|
"info": "Info",
|
||||||
"running_version": "You are running Woodpecker {0}",
|
"running_version": "You are running Woodpecker {0}",
|
||||||
|
|
101
web/src/components/admin/settings/AdminRegistriesTab.vue
Normal file
101
web/src/components/admin/settings/AdminRegistriesTab.vue
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
<template>
|
||||||
|
<Settings
|
||||||
|
:title="$t('registries.registries')"
|
||||||
|
:desc="$t('admin.settings.registries.desc')"
|
||||||
|
docs-url="docs/usage/registries"
|
||||||
|
:warning="$t('admin.settings.registries.warning')"
|
||||||
|
>
|
||||||
|
<template #titleActions>
|
||||||
|
<Button
|
||||||
|
v-if="selectedRegistry"
|
||||||
|
:text="$t('registries.show')"
|
||||||
|
start-icon="back"
|
||||||
|
@click="selectedRegistry = undefined"
|
||||||
|
/>
|
||||||
|
<Button v-else :text="$t('registries.add')" start-icon="plus" @click="showAddRegistry" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<RegistryList
|
||||||
|
v-if="!selectedRegistry"
|
||||||
|
v-model="registries"
|
||||||
|
:is-deleting="isDeleting"
|
||||||
|
@edit="editRegistry"
|
||||||
|
@delete="deleteRegistry"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<RegistryEdit
|
||||||
|
v-else
|
||||||
|
v-model="selectedRegistry"
|
||||||
|
:is-saving="isSaving"
|
||||||
|
@save="createRegistry"
|
||||||
|
@cancel="selectedRegistry = undefined"
|
||||||
|
/>
|
||||||
|
</Settings>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { cloneDeep } from 'lodash';
|
||||||
|
import { computed, ref } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
|
import Button from '~/components/atomic/Button.vue';
|
||||||
|
import Settings from '~/components/layout/Settings.vue';
|
||||||
|
import RegistryEdit from '~/components/registry/RegistryEdit.vue';
|
||||||
|
import RegistryList from '~/components/registry/RegistryList.vue';
|
||||||
|
import useApiClient from '~/compositions/useApiClient';
|
||||||
|
import { useAsyncAction } from '~/compositions/useAsyncAction';
|
||||||
|
import useNotifications from '~/compositions/useNotifications';
|
||||||
|
import { usePagination } from '~/compositions/usePaginate';
|
||||||
|
import type { Registry } from '~/lib/api/types';
|
||||||
|
|
||||||
|
const emptyRegistry: Partial<Registry> = {
|
||||||
|
address: '',
|
||||||
|
username: '',
|
||||||
|
password: '',
|
||||||
|
};
|
||||||
|
|
||||||
|
const apiClient = useApiClient();
|
||||||
|
const notifications = useNotifications();
|
||||||
|
const i18n = useI18n();
|
||||||
|
|
||||||
|
const selectedRegistry = ref<Partial<Registry>>();
|
||||||
|
const isEditingRegistry = computed(() => !!selectedRegistry.value?.id);
|
||||||
|
|
||||||
|
async function loadRegistries(page: number): Promise<Registry[] | null> {
|
||||||
|
return apiClient.getGlobalRegistryList({ page });
|
||||||
|
}
|
||||||
|
|
||||||
|
const { resetPage, data: registries } = usePagination(loadRegistries, () => !selectedRegistry.value);
|
||||||
|
|
||||||
|
const { doSubmit: createRegistry, isLoading: isSaving } = useAsyncAction(async () => {
|
||||||
|
if (!selectedRegistry.value) {
|
||||||
|
throw new Error("Unexpected: Can't get registry");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isEditingRegistry.value) {
|
||||||
|
await apiClient.updateGlobalRegistry(selectedRegistry.value);
|
||||||
|
} else {
|
||||||
|
await apiClient.createGlobalRegistry(selectedRegistry.value);
|
||||||
|
}
|
||||||
|
notifications.notify({
|
||||||
|
title: isEditingRegistry.value ? i18n.t('registries.saved') : i18n.t('registries.created'),
|
||||||
|
type: 'success',
|
||||||
|
});
|
||||||
|
selectedRegistry.value = undefined;
|
||||||
|
resetPage();
|
||||||
|
});
|
||||||
|
|
||||||
|
const { doSubmit: deleteRegistry, isLoading: isDeleting } = useAsyncAction(async (_registry: Registry) => {
|
||||||
|
await apiClient.deleteGlobalRegistry(_registry.address);
|
||||||
|
notifications.notify({ title: i18n.t('registries.deleted'), type: 'success' });
|
||||||
|
resetPage();
|
||||||
|
});
|
||||||
|
|
||||||
|
function editRegistry(registry: Registry) {
|
||||||
|
selectedRegistry.value = cloneDeep(registry);
|
||||||
|
}
|
||||||
|
|
||||||
|
function showAddRegistry() {
|
||||||
|
selectedRegistry.value = cloneDeep(emptyRegistry);
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -3,7 +3,7 @@
|
||||||
<button
|
<button
|
||||||
v-for="tab in tabs"
|
v-for="tab in tabs"
|
||||||
:key="tab.id"
|
:key="tab.id"
|
||||||
class="w-full py-1 md:py-2 md:w-auto md:px-8 flex cursor-pointer md:border-b-2 text-wp-text-100 hover:text-wp-text-200 items-center"
|
class="w-full py-1 md:py-2 md:w-auto md:px-6 flex cursor-pointer md:border-b-2 text-wp-text-100 hover:text-wp-text-200 items-center"
|
||||||
:class="{
|
:class="{
|
||||||
'border-wp-text-100': activeTab === tab.id,
|
'border-wp-text-100': activeTab === tab.id,
|
||||||
'border-transparent': activeTab !== tab.id,
|
'border-transparent': activeTab !== tab.id,
|
||||||
|
|
113
web/src/components/org/settings/OrgRegistriesTab.vue
Normal file
113
web/src/components/org/settings/OrgRegistriesTab.vue
Normal file
|
@ -0,0 +1,113 @@
|
||||||
|
<template>
|
||||||
|
<Settings
|
||||||
|
:title="$t('registries.registries')"
|
||||||
|
:desc="$t('org.settings.registries.desc')"
|
||||||
|
docs-url="docs/usage/registries"
|
||||||
|
>
|
||||||
|
<template #titleActions>
|
||||||
|
<Button
|
||||||
|
v-if="selectedRegistry"
|
||||||
|
:text="$t('registries.show')"
|
||||||
|
start-icon="back"
|
||||||
|
@click="selectedRegistry = undefined"
|
||||||
|
/>
|
||||||
|
<Button v-else :text="$t('registries.add')" start-icon="plus" @click="showAddRegistry" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<RegistryList
|
||||||
|
v-if="!selectedRegistry"
|
||||||
|
v-model="registries"
|
||||||
|
:is-deleting="isDeleting"
|
||||||
|
@edit="editRegistry"
|
||||||
|
@delete="deleteRegistry"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<RegistryEdit
|
||||||
|
v-else
|
||||||
|
v-model="selectedRegistry"
|
||||||
|
:is-saving="isSaving"
|
||||||
|
@save="createRegistry"
|
||||||
|
@cancel="selectedRegistry = undefined"
|
||||||
|
/>
|
||||||
|
</Settings>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { cloneDeep } from 'lodash';
|
||||||
|
import { computed, inject, ref, type Ref } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
|
import Button from '~/components/atomic/Button.vue';
|
||||||
|
import Settings from '~/components/layout/Settings.vue';
|
||||||
|
import RegistryEdit from '~/components/registry/RegistryEdit.vue';
|
||||||
|
import RegistryList from '~/components/registry/RegistryList.vue';
|
||||||
|
import useApiClient from '~/compositions/useApiClient';
|
||||||
|
import { useAsyncAction } from '~/compositions/useAsyncAction';
|
||||||
|
import useNotifications from '~/compositions/useNotifications';
|
||||||
|
import { usePagination } from '~/compositions/usePaginate';
|
||||||
|
import type { Org, Registry } from '~/lib/api/types';
|
||||||
|
|
||||||
|
const emptyRegistry: Partial<Registry> = {
|
||||||
|
address: '',
|
||||||
|
username: '',
|
||||||
|
password: '',
|
||||||
|
};
|
||||||
|
|
||||||
|
const apiClient = useApiClient();
|
||||||
|
const notifications = useNotifications();
|
||||||
|
const i18n = useI18n();
|
||||||
|
|
||||||
|
const org = inject<Ref<Org>>('org');
|
||||||
|
const selectedRegistry = ref<Partial<Registry>>();
|
||||||
|
const isEditing = computed(() => !!selectedRegistry.value?.id);
|
||||||
|
|
||||||
|
async function loadRegistries(page: number): Promise<Registry[] | null> {
|
||||||
|
if (!org?.value) {
|
||||||
|
throw new Error("Unexpected: Can't load org");
|
||||||
|
}
|
||||||
|
|
||||||
|
return apiClient.getOrgRegistryList(org.value.id, { page });
|
||||||
|
}
|
||||||
|
|
||||||
|
const { resetPage, data: registries } = usePagination(loadRegistries, () => !selectedRegistry.value);
|
||||||
|
|
||||||
|
const { doSubmit: createRegistry, isLoading: isSaving } = useAsyncAction(async () => {
|
||||||
|
if (!org?.value) {
|
||||||
|
throw new Error("Unexpected: Can't load org");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!selectedRegistry.value) {
|
||||||
|
throw new Error("Unexpected: Can't get registry");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isEditing.value) {
|
||||||
|
await apiClient.updateOrgRegistry(org.value.id, selectedRegistry.value);
|
||||||
|
} else {
|
||||||
|
await apiClient.createOrgRegistry(org.value.id, selectedRegistry.value);
|
||||||
|
}
|
||||||
|
notifications.notify({
|
||||||
|
title: isEditing.value ? i18n.t('registries.saved') : i18n.t('registries.created'),
|
||||||
|
type: 'success',
|
||||||
|
});
|
||||||
|
selectedRegistry.value = undefined;
|
||||||
|
resetPage();
|
||||||
|
});
|
||||||
|
|
||||||
|
const { doSubmit: deleteRegistry, isLoading: isDeleting } = useAsyncAction(async (_registry: Registry) => {
|
||||||
|
if (!org?.value) {
|
||||||
|
throw new Error("Unexpected: Can't load org");
|
||||||
|
}
|
||||||
|
|
||||||
|
await apiClient.deleteOrgRegistry(org.value.id, _registry.address);
|
||||||
|
notifications.notify({ title: i18n.t('registries.deleted'), type: 'success' });
|
||||||
|
resetPage();
|
||||||
|
});
|
||||||
|
|
||||||
|
function editRegistry(registry: Registry) {
|
||||||
|
selectedRegistry.value = cloneDeep(registry);
|
||||||
|
}
|
||||||
|
|
||||||
|
function showAddRegistry() {
|
||||||
|
selectedRegistry.value = cloneDeep(emptyRegistry);
|
||||||
|
}
|
||||||
|
</script>
|
78
web/src/components/registry/RegistryEdit.vue
Normal file
78
web/src/components/registry/RegistryEdit.vue
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
<template>
|
||||||
|
<div v-if="innerValue" class="space-y-4">
|
||||||
|
<form @submit.prevent="save">
|
||||||
|
<InputField v-slot="{ id }" :label="$t('registries.address.address')">
|
||||||
|
<!-- TODO: check input field Address is a valid address -->
|
||||||
|
<TextField
|
||||||
|
:id="id"
|
||||||
|
v-model="innerValue.address"
|
||||||
|
:placeholder="$t('registries.address.desc')"
|
||||||
|
required
|
||||||
|
:disabled="isEditing || isReadOnly"
|
||||||
|
/>
|
||||||
|
</InputField>
|
||||||
|
|
||||||
|
<InputField v-slot="{ id }" :label="$t('username')">
|
||||||
|
<TextField
|
||||||
|
:id="id"
|
||||||
|
v-model="innerValue.username"
|
||||||
|
:placeholder="$t('username')"
|
||||||
|
required
|
||||||
|
:disabled="isReadOnly"
|
||||||
|
/>
|
||||||
|
</InputField>
|
||||||
|
|
||||||
|
<InputField v-if="!isReadOnly" v-slot="{ id }" :label="$t('password')">
|
||||||
|
<TextField :id="id" v-model="innerValue.password" :placeholder="$t('password')" :required="!isEditing" />
|
||||||
|
</InputField>
|
||||||
|
|
||||||
|
<div v-if="!isReadOnly" class="flex gap-2">
|
||||||
|
<Button type="button" color="gray" :text="$t('cancel')" @click="$emit('cancel')" />
|
||||||
|
<Button
|
||||||
|
type="submit"
|
||||||
|
color="green"
|
||||||
|
:is-loading="isSaving"
|
||||||
|
:text="isEditing ? $t('registries.save') : $t('registries.add')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { computed, toRef } from 'vue';
|
||||||
|
|
||||||
|
import Button from '~/components/atomic/Button.vue';
|
||||||
|
import InputField from '~/components/form/InputField.vue';
|
||||||
|
import TextField from '~/components/form/TextField.vue';
|
||||||
|
import type { Registry } from '~/lib/api/types';
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
modelValue: Partial<Registry>;
|
||||||
|
isSaving: boolean;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(event: 'update:modelValue', value: Partial<Registry> | undefined): void;
|
||||||
|
(event: 'save', value: Partial<Registry>): void;
|
||||||
|
(event: 'cancel'): void;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const modelValue = toRef(props, 'modelValue');
|
||||||
|
const innerValue = computed({
|
||||||
|
get: () => modelValue.value,
|
||||||
|
set: (value) => {
|
||||||
|
emit('update:modelValue', value);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const isEditing = computed(() => !!innerValue.value?.id);
|
||||||
|
const isReadOnly = computed(() => !!innerValue.value?.readonly);
|
||||||
|
|
||||||
|
function save() {
|
||||||
|
if (!innerValue.value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
emit('save', innerValue.value);
|
||||||
|
}
|
||||||
|
</script>
|
63
web/src/components/registry/RegistryList.vue
Normal file
63
web/src/components/registry/RegistryList.vue
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
<template>
|
||||||
|
<div class="space-y-4 text-wp-text-100">
|
||||||
|
<ListItem
|
||||||
|
v-for="registry in registries"
|
||||||
|
:key="registry.id"
|
||||||
|
class="items-center !bg-wp-background-200 !dark:bg-wp-background-100"
|
||||||
|
>
|
||||||
|
<span>{{ registry.address }}</span>
|
||||||
|
<IconButton
|
||||||
|
:icon="registry.readonly ? 'chevron-right' : 'edit'"
|
||||||
|
class="ml-auto w-8 h-8"
|
||||||
|
:title="registry.readonly ? $t('registries.view') : $t('registries.edit')"
|
||||||
|
@click="editRegistry(registry)"
|
||||||
|
/>
|
||||||
|
<IconButton
|
||||||
|
v-if="!registry.readonly"
|
||||||
|
icon="trash"
|
||||||
|
class="w-8 h-8 hover:text-wp-control-error-100"
|
||||||
|
:is-loading="isDeleting"
|
||||||
|
:title="$t('registries.delete')"
|
||||||
|
@click="deleteRegistry(registry)"
|
||||||
|
/>
|
||||||
|
</ListItem>
|
||||||
|
|
||||||
|
<div v-if="registries?.length === 0" class="ml-2">{{ $t('registries.none') }}</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { toRef } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
|
import IconButton from '~/components/atomic/IconButton.vue';
|
||||||
|
import ListItem from '~/components/atomic/ListItem.vue';
|
||||||
|
import type { Registry } from '~/lib/api/types';
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
modelValue: (Registry & { edit?: boolean })[];
|
||||||
|
isDeleting: boolean;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(event: 'edit', registry: Registry): void;
|
||||||
|
(event: 'delete', registry: Registry): void;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const i18n = useI18n();
|
||||||
|
|
||||||
|
const registries = toRef(props, 'modelValue');
|
||||||
|
|
||||||
|
function editRegistry(registry: Registry) {
|
||||||
|
emit('edit', registry);
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteRegistry(registry: Registry) {
|
||||||
|
// TODO: use proper dialog
|
||||||
|
// eslint-disable-next-line no-alert
|
||||||
|
if (!confirm(i18n.t('registries.delete_confirm'))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
emit('delete', registry);
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -1,95 +1,54 @@
|
||||||
<template>
|
<template>
|
||||||
<Settings
|
<Settings :title="$t('registries.credentials')" :desc="$t('registries.desc')" docs-url="docs/usage/registries">
|
||||||
:title="$t('repo.settings.registries.credentials')"
|
|
||||||
:desc="$t('repo.settings.registries.desc')"
|
|
||||||
docs-url="docs/usage/registries"
|
|
||||||
>
|
|
||||||
<template #titleActions>
|
<template #titleActions>
|
||||||
<Button
|
<Button
|
||||||
v-if="selectedRegistry"
|
v-if="selectedRegistry"
|
||||||
|
:text="$t('registries.show')"
|
||||||
start-icon="back"
|
start-icon="back"
|
||||||
:text="$t('repo.settings.registries.show')"
|
|
||||||
@click="selectedRegistry = undefined"
|
@click="selectedRegistry = undefined"
|
||||||
/>
|
/>
|
||||||
<Button v-else start-icon="plus" :text="$t('repo.settings.registries.add')" @click="selectedRegistry = {}" />
|
<Button v-else :text="$t('registries.add')" start-icon="plus" @click="showAddRegistry" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<div v-if="!selectedRegistry" class="space-y-4 text-wp-text-100">
|
<RegistryList
|
||||||
<ListItem
|
v-if="!selectedRegistry"
|
||||||
v-for="registry in registries"
|
v-model="registries"
|
||||||
:key="registry.id"
|
:is-deleting="isDeleting"
|
||||||
class="items-center !bg-wp-background-200 !dark:bg-wp-background-100"
|
@edit="editRegistry"
|
||||||
>
|
@delete="deleteRegistry"
|
||||||
<span>{{ registry.address }}</span>
|
|
||||||
<IconButton
|
|
||||||
icon="edit"
|
|
||||||
class="ml-auto w-8 h-8"
|
|
||||||
:title="$t('repo.settings.registries.edit')"
|
|
||||||
@click="selectedRegistry = registry"
|
|
||||||
/>
|
/>
|
||||||
<IconButton
|
|
||||||
icon="trash"
|
<RegistryEdit
|
||||||
class="w-8 h-8 hover:text-wp-control-error-100"
|
v-else
|
||||||
:is-loading="isDeleting"
|
v-model="selectedRegistry"
|
||||||
:title="$t('repo.settings.registries.delete')"
|
:is-saving="isSaving"
|
||||||
@click="deleteRegistry(registry)"
|
@save="createRegistry"
|
||||||
|
@cancel="selectedRegistry = undefined"
|
||||||
/>
|
/>
|
||||||
</ListItem>
|
|
||||||
|
|
||||||
<div v-if="registries?.length === 0" class="ml-2">{{ $t('repo.settings.registries.none') }}</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div v-else class="space-y-4">
|
|
||||||
<form @submit.prevent="createRegistry">
|
|
||||||
<InputField v-slot="{ id }" :label="$t('repo.settings.registries.address.address')">
|
|
||||||
<!-- TODO: check input field Address is a valid address -->
|
|
||||||
<TextField
|
|
||||||
:id="id"
|
|
||||||
v-model="selectedRegistry.address"
|
|
||||||
:placeholder="$t('repo.settings.registries.address.placeholder')"
|
|
||||||
required
|
|
||||||
:disabled="isEditingRegistry"
|
|
||||||
/>
|
|
||||||
</InputField>
|
|
||||||
|
|
||||||
<InputField v-slot="{ id }" :label="$t('username')">
|
|
||||||
<TextField :id="id" v-model="selectedRegistry.username" :placeholder="$t('username')" required />
|
|
||||||
</InputField>
|
|
||||||
|
|
||||||
<InputField v-slot="{ id }" :label="$t('password')">
|
|
||||||
<TextField :id="id" v-model="selectedRegistry.password" :placeholder="$t('password')" required />
|
|
||||||
</InputField>
|
|
||||||
|
|
||||||
<div class="flex gap-2">
|
|
||||||
<Button type="button" color="gray" :text="$t('cancel')" @click="selectedRegistry = undefined" />
|
|
||||||
<Button
|
|
||||||
type="submit"
|
|
||||||
color="green"
|
|
||||||
:is-loading="isSaving"
|
|
||||||
:text="isEditingRegistry ? $t('repo.settings.registries.save') : $t('repo.settings.registries.add')"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</Settings>
|
</Settings>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { cloneDeep } from 'lodash';
|
||||||
import { computed, inject, ref, type Ref } from 'vue';
|
import { computed, inject, ref, type Ref } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
import Button from '~/components/atomic/Button.vue';
|
import Button from '~/components/atomic/Button.vue';
|
||||||
import IconButton from '~/components/atomic/IconButton.vue';
|
|
||||||
import ListItem from '~/components/atomic/ListItem.vue';
|
|
||||||
import InputField from '~/components/form/InputField.vue';
|
|
||||||
import TextField from '~/components/form/TextField.vue';
|
|
||||||
import Settings from '~/components/layout/Settings.vue';
|
import Settings from '~/components/layout/Settings.vue';
|
||||||
|
import RegistryEdit from '~/components/registry/RegistryEdit.vue';
|
||||||
|
import RegistryList from '~/components/registry/RegistryList.vue';
|
||||||
import useApiClient from '~/compositions/useApiClient';
|
import useApiClient from '~/compositions/useApiClient';
|
||||||
import { useAsyncAction } from '~/compositions/useAsyncAction';
|
import { useAsyncAction } from '~/compositions/useAsyncAction';
|
||||||
import useNotifications from '~/compositions/useNotifications';
|
import useNotifications from '~/compositions/useNotifications';
|
||||||
import { usePagination } from '~/compositions/usePaginate';
|
import { usePagination } from '~/compositions/usePaginate';
|
||||||
import type { Registry, Repo } from '~/lib/api/types';
|
import type { Registry, Repo } from '~/lib/api/types';
|
||||||
|
|
||||||
|
const emptyRegistry: Partial<Registry> = {
|
||||||
|
address: '',
|
||||||
|
username: '',
|
||||||
|
password: '',
|
||||||
|
};
|
||||||
|
|
||||||
const apiClient = useApiClient();
|
const apiClient = useApiClient();
|
||||||
const notifications = useNotifications();
|
const notifications = useNotifications();
|
||||||
const i18n = useI18n();
|
const i18n = useI18n();
|
||||||
|
@ -123,9 +82,7 @@ const { doSubmit: createRegistry, isLoading: isSaving } = useAsyncAction(async (
|
||||||
await apiClient.createRegistry(repo.value.id, selectedRegistry.value);
|
await apiClient.createRegistry(repo.value.id, selectedRegistry.value);
|
||||||
}
|
}
|
||||||
notifications.notify({
|
notifications.notify({
|
||||||
title: isEditingRegistry.value
|
title: isEditingRegistry.value ? i18n.t('registries.saved') : i18n.t('registries.created'),
|
||||||
? i18n.t('repo.settings.registries.saved')
|
|
||||||
: i18n.t('repo.settings.registries.created'),
|
|
||||||
type: 'success',
|
type: 'success',
|
||||||
});
|
});
|
||||||
selectedRegistry.value = undefined;
|
selectedRegistry.value = undefined;
|
||||||
|
@ -139,7 +96,15 @@ const { doSubmit: deleteRegistry, isLoading: isDeleting } = useAsyncAction(async
|
||||||
|
|
||||||
const registryAddress = encodeURIComponent(_registry.address);
|
const registryAddress = encodeURIComponent(_registry.address);
|
||||||
await apiClient.deleteRegistry(repo.value.id, registryAddress);
|
await apiClient.deleteRegistry(repo.value.id, registryAddress);
|
||||||
notifications.notify({ title: i18n.t('repo.settings.registries.deleted'), type: 'success' });
|
notifications.notify({ title: i18n.t('registries.deleted'), type: 'success' });
|
||||||
resetPage();
|
resetPage();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function editRegistry(registry: Registry) {
|
||||||
|
selectedRegistry.value = cloneDeep(registry);
|
||||||
|
}
|
||||||
|
|
||||||
|
function showAddRegistry() {
|
||||||
|
selectedRegistry.value = cloneDeep(emptyRegistry);
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -170,19 +170,53 @@ export default class WoodpeckerClient extends ApiClient {
|
||||||
|
|
||||||
getRegistryList(repoId: number, opts?: PaginationOptions): Promise<Registry[] | null> {
|
getRegistryList(repoId: number, opts?: PaginationOptions): Promise<Registry[] | null> {
|
||||||
const query = encodeQueryString(opts);
|
const query = encodeQueryString(opts);
|
||||||
return this._get(`/api/repos/${repoId}/registry?${query}`) as Promise<Registry[] | null>;
|
return this._get(`/api/repos/${repoId}/registries?${query}`) as Promise<Registry[] | null>;
|
||||||
}
|
}
|
||||||
|
|
||||||
createRegistry(repoId: number, registry: Partial<Registry>): Promise<unknown> {
|
createRegistry(repoId: number, registry: Partial<Registry>): Promise<unknown> {
|
||||||
return this._post(`/api/repos/${repoId}/registry`, registry);
|
return this._post(`/api/repos/${repoId}/registries`, registry);
|
||||||
}
|
}
|
||||||
|
|
||||||
updateRegistry(repoId: number, registry: Partial<Registry>): Promise<unknown> {
|
updateRegistry(repoId: number, registry: Partial<Registry>): Promise<unknown> {
|
||||||
return this._patch(`/api/repos/${repoId}/registry/${registry.address}`, registry);
|
return this._patch(`/api/repos/${repoId}/registries/${registry.address}`, registry);
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteRegistry(repoId: number, registryAddress: string): Promise<unknown> {
|
deleteRegistry(repoId: number, registryAddress: string): Promise<unknown> {
|
||||||
return this._delete(`/api/repos/${repoId}/registry/${registryAddress}`);
|
return this._delete(`/api/repos/${repoId}/registries/${registryAddress}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
getOrgRegistryList(orgId: number, opts?: PaginationOptions): Promise<Registry[] | null> {
|
||||||
|
const query = encodeQueryString(opts);
|
||||||
|
return this._get(`/api/orgs/${orgId}/registries?${query}`) as Promise<Registry[] | null>;
|
||||||
|
}
|
||||||
|
|
||||||
|
createOrgRegistry(orgId: number, registry: Partial<Registry>): Promise<unknown> {
|
||||||
|
return this._post(`/api/orgs/${orgId}/registries`, registry);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateOrgRegistry(orgId: number, registry: Partial<Registry>): Promise<unknown> {
|
||||||
|
return this._patch(`/api/orgs/${orgId}/registries/${registry.address}`, registry);
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteOrgRegistry(orgId: number, registryAddress: string): Promise<unknown> {
|
||||||
|
return this._delete(`/api/orgs/${orgId}/registries/${registryAddress}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
getGlobalRegistryList(opts?: PaginationOptions): Promise<Registry[] | null> {
|
||||||
|
const query = encodeQueryString(opts);
|
||||||
|
return this._get(`/api/registries?${query}`) as Promise<Registry[] | null>;
|
||||||
|
}
|
||||||
|
|
||||||
|
createGlobalRegistry(registry: Partial<Registry>): Promise<unknown> {
|
||||||
|
return this._post(`/api/registries`, registry);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateGlobalRegistry(registry: Partial<Registry>): Promise<unknown> {
|
||||||
|
return this._patch(`/api/registries/${registry.address}`, registry);
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteGlobalRegistry(registryAddress: string): Promise<unknown> {
|
||||||
|
return this._delete(`/api/registries/${registryAddress}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
getCronList(repoId: number, opts?: PaginationOptions): Promise<Cron[] | null> {
|
getCronList(repoId: number, opts?: PaginationOptions): Promise<Cron[] | null> {
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
export interface Registry {
|
export interface Registry {
|
||||||
id: string;
|
id: string;
|
||||||
|
repo_id: number;
|
||||||
|
org_id: number;
|
||||||
address: string;
|
address: string;
|
||||||
username: string;
|
username: string;
|
||||||
password: string;
|
password: string;
|
||||||
|
readonly: boolean;
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,9 @@
|
||||||
<Tab id="secrets" :title="$t('secrets.secrets')">
|
<Tab id="secrets" :title="$t('secrets.secrets')">
|
||||||
<AdminSecretsTab />
|
<AdminSecretsTab />
|
||||||
</Tab>
|
</Tab>
|
||||||
|
<Tab id="registries" :title="$t('registries.registries')">
|
||||||
|
<AdminRegistriesTab />
|
||||||
|
</Tab>
|
||||||
<Tab id="repos" :title="$t('admin.settings.repos.repos')">
|
<Tab id="repos" :title="$t('admin.settings.repos.repos')">
|
||||||
<AdminReposTab />
|
<AdminReposTab />
|
||||||
</Tab>
|
</Tab>
|
||||||
|
@ -36,6 +39,7 @@ import AdminAgentsTab from '~/components/admin/settings/AdminAgentsTab.vue';
|
||||||
import AdminInfoTab from '~/components/admin/settings/AdminInfoTab.vue';
|
import AdminInfoTab from '~/components/admin/settings/AdminInfoTab.vue';
|
||||||
import AdminOrgsTab from '~/components/admin/settings/AdminOrgsTab.vue';
|
import AdminOrgsTab from '~/components/admin/settings/AdminOrgsTab.vue';
|
||||||
import AdminQueueTab from '~/components/admin/settings/AdminQueueTab.vue';
|
import AdminQueueTab from '~/components/admin/settings/AdminQueueTab.vue';
|
||||||
|
import AdminRegistriesTab from '~/components/admin/settings/AdminRegistriesTab.vue';
|
||||||
import AdminReposTab from '~/components/admin/settings/AdminReposTab.vue';
|
import AdminReposTab from '~/components/admin/settings/AdminReposTab.vue';
|
||||||
import AdminSecretsTab from '~/components/admin/settings/AdminSecretsTab.vue';
|
import AdminSecretsTab from '~/components/admin/settings/AdminSecretsTab.vue';
|
||||||
import AdminUsersTab from '~/components/admin/settings/AdminUsersTab.vue';
|
import AdminUsersTab from '~/components/admin/settings/AdminUsersTab.vue';
|
||||||
|
|
|
@ -14,6 +14,10 @@
|
||||||
<Tab id="secrets" :title="$t('secrets.secrets')">
|
<Tab id="secrets" :title="$t('secrets.secrets')">
|
||||||
<OrgSecretsTab />
|
<OrgSecretsTab />
|
||||||
</Tab>
|
</Tab>
|
||||||
|
|
||||||
|
<Tab id="registries" :title="$t('registries.registries')">
|
||||||
|
<OrgRegistriesTab />
|
||||||
|
</Tab>
|
||||||
</Scaffold>
|
</Scaffold>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -23,6 +27,7 @@ import { useI18n } from 'vue-i18n';
|
||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router';
|
||||||
|
|
||||||
import Tab from '~/components/layout/scaffold/Tab.vue';
|
import Tab from '~/components/layout/scaffold/Tab.vue';
|
||||||
|
import OrgRegistriesTab from '~/components/org/settings/OrgRegistriesTab.vue';
|
||||||
import OrgSecretsTab from '~/components/org/settings/OrgSecretsTab.vue';
|
import OrgSecretsTab from '~/components/org/settings/OrgSecretsTab.vue';
|
||||||
import { inject } from '~/compositions/useInjectProvide';
|
import { inject } from '~/compositions/useInjectProvide';
|
||||||
import useNotifications from '~/compositions/useNotifications';
|
import useNotifications from '~/compositions/useNotifications';
|
||||||
|
|
|
@ -2,15 +2,15 @@
|
||||||
<Scaffold enable-tabs :go-back="goBack">
|
<Scaffold enable-tabs :go-back="goBack">
|
||||||
<template #title>
|
<template #title>
|
||||||
<span>
|
<span>
|
||||||
<router-link :to="{ name: 'org', params: { orgId: repo!.org_id } }" class="hover:underline">
|
<router-link :to="{ name: 'org', params: { orgId: repo!.org_id } }" class="hover:underline">{{
|
||||||
{{ repo!.owner }}
|
repo!.owner
|
||||||
<!-- eslint-disable-next-line @intlify/vue-i18n/no-raw-text -->
|
/* eslint-disable-next-line @intlify/vue-i18n/no-raw-text */
|
||||||
</router-link>
|
}}</router-link>
|
||||||
/
|
/
|
||||||
<router-link :to="{ name: 'repo' }" class="hover:underline">
|
<router-link :to="{ name: 'repo' }" class="hover:underline">{{
|
||||||
{{ repo!.name }}
|
repo!.name
|
||||||
<!-- eslint-disable-next-line @intlify/vue-i18n/no-raw-text -->
|
/* eslint-disable-next-line @intlify/vue-i18n/no-raw-text */
|
||||||
</router-link>
|
}}</router-link>
|
||||||
/
|
/
|
||||||
{{ $t('settings') }}
|
{{ $t('settings') }}
|
||||||
</span>
|
</span>
|
||||||
|
@ -22,7 +22,7 @@
|
||||||
<Tab id="secrets" :title="$t('secrets.secrets')">
|
<Tab id="secrets" :title="$t('secrets.secrets')">
|
||||||
<SecretsTab />
|
<SecretsTab />
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab id="registries" :title="$t('repo.settings.registries.registries')">
|
<Tab id="registries" :title="$t('registries.registries')">
|
||||||
<RegistriesTab />
|
<RegistriesTab />
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab id="crons" :title="$t('repo.settings.crons.crons')">
|
<Tab id="crons" :title="$t('repo.settings.crons.crons')">
|
||||||
|
|
|
@ -9,8 +9,10 @@
|
||||||
<span class="flex">
|
<span class="flex">
|
||||||
<router-link :to="{ name: 'org', params: { orgId: repo.org_id } }" class="hover:underline">{{
|
<router-link :to="{ name: 'org', params: { orgId: repo.org_id } }" class="hover:underline">{{
|
||||||
repo.owner
|
repo.owner
|
||||||
|
/* eslint-disable-next-line @intlify/vue-i18n/no-raw-text */
|
||||||
}}</router-link>
|
}}</router-link>
|
||||||
{{ ` / ${repo.name}` }}
|
/
|
||||||
|
{{ repo.name }}
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
<template #titleActions>
|
<template #titleActions>
|
||||||
|
|
|
@ -10,14 +10,12 @@
|
||||||
>
|
>
|
||||||
<template #title>
|
<template #title>
|
||||||
<span>
|
<span>
|
||||||
<router-link :to="{ name: 'org', params: { orgId: repo.org_id } }" class="hover:underline">
|
<router-link :to="{ name: 'org', params: { orgId: repo.org_id } }" class="hover:underline">{{
|
||||||
{{ repo.owner }}
|
repo.owner
|
||||||
<!-- eslint-disable-next-line @intlify/vue-i18n/no-raw-text -->
|
/* eslint-disable-next-line @intlify/vue-i18n/no-raw-text */
|
||||||
</router-link>
|
}}</router-link>
|
||||||
/
|
/
|
||||||
<router-link :to="{ name: 'repo' }" class="hover:underline">
|
<router-link :to="{ name: 'repo' }" class="hover:underline">{{ repo.name }}</router-link>
|
||||||
{{ repo.name }}
|
|
||||||
</router-link>
|
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
46
woodpecker-go/woodpecker/global_registry.go
Normal file
46
woodpecker-go/woodpecker/global_registry.go
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
package woodpecker
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
const (
|
||||||
|
pathGlobalRegistries = "%s/api/registries"
|
||||||
|
pathGlobalRegistry = "%s/api/registries/%s"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GlobalRegistry returns an global registry by name.
|
||||||
|
func (c *client) GlobalRegistry(registry string) (*Registry, error) {
|
||||||
|
out := new(Registry)
|
||||||
|
uri := fmt.Sprintf(pathGlobalRegistry, c.addr, registry)
|
||||||
|
err := c.get(uri, out)
|
||||||
|
return out, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GlobalRegistryList returns a list of all global registries.
|
||||||
|
func (c *client) GlobalRegistryList() ([]*Registry, error) {
|
||||||
|
var out []*Registry
|
||||||
|
uri := fmt.Sprintf(pathGlobalRegistries, c.addr)
|
||||||
|
err := c.get(uri, &out)
|
||||||
|
return out, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GlobalRegistryCreate creates a global registry.
|
||||||
|
func (c *client) GlobalRegistryCreate(in *Registry) (*Registry, error) {
|
||||||
|
out := new(Registry)
|
||||||
|
uri := fmt.Sprintf(pathGlobalRegistries, c.addr)
|
||||||
|
err := c.post(uri, in, out)
|
||||||
|
return out, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GlobalRegistryUpdate updates a global registry.
|
||||||
|
func (c *client) GlobalRegistryUpdate(in *Registry) (*Registry, error) {
|
||||||
|
out := new(Registry)
|
||||||
|
uri := fmt.Sprintf(pathGlobalRegistry, c.addr, in.Address)
|
||||||
|
err := c.patch(uri, in, out)
|
||||||
|
return out, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GlobalRegistryDelete deletes a global registry.
|
||||||
|
func (c *client) GlobalRegistryDelete(registry string) error {
|
||||||
|
uri := fmt.Sprintf(pathGlobalRegistry, c.addr, registry)
|
||||||
|
return c.delete(uri)
|
||||||
|
}
|
|
@ -138,6 +138,36 @@ type Client interface {
|
||||||
// RegistryDelete deletes a registry.
|
// RegistryDelete deletes a registry.
|
||||||
RegistryDelete(repoID int64, hostname string) error
|
RegistryDelete(repoID int64, hostname string) error
|
||||||
|
|
||||||
|
// OrgRegistry returns an organization registry by address.
|
||||||
|
OrgRegistry(orgID int64, registry string) (*Registry, error)
|
||||||
|
|
||||||
|
// OrgRegistryList returns a list of all organization registries.
|
||||||
|
OrgRegistryList(orgID int64) ([]*Registry, error)
|
||||||
|
|
||||||
|
// OrgRegistryCreate creates an organization registry.
|
||||||
|
OrgRegistryCreate(orgID int64, registry *Registry) (*Registry, error)
|
||||||
|
|
||||||
|
// OrgRegistryUpdate updates an organization registry.
|
||||||
|
OrgRegistryUpdate(orgID int64, registry *Registry) (*Registry, error)
|
||||||
|
|
||||||
|
// OrgRegistryDelete deletes an organization registry.
|
||||||
|
OrgRegistryDelete(orgID int64, registry string) error
|
||||||
|
|
||||||
|
// GlobalRegistry returns an global registry by address.
|
||||||
|
GlobalRegistry(registry string) (*Registry, error)
|
||||||
|
|
||||||
|
// GlobalRegistryList returns a list of all global registries.
|
||||||
|
GlobalRegistryList() ([]*Registry, error)
|
||||||
|
|
||||||
|
// GlobalRegistryCreate creates a global registry.
|
||||||
|
GlobalRegistryCreate(registry *Registry) (*Registry, error)
|
||||||
|
|
||||||
|
// GlobalRegistryUpdate updates a global registry.
|
||||||
|
GlobalRegistryUpdate(registry *Registry) (*Registry, error)
|
||||||
|
|
||||||
|
// GlobalRegistryDelete deletes a global registry.
|
||||||
|
GlobalRegistryDelete(registry string) error
|
||||||
|
|
||||||
// Secret returns a secret by name.
|
// Secret returns a secret by name.
|
||||||
Secret(repoID int64, secret string) (*Secret, error)
|
Secret(repoID int64, secret string) (*Secret, error)
|
||||||
|
|
||||||
|
|
|
@ -353,6 +353,144 @@ func (_m *Client) Deploy(repoID int64, pipeline int64, env string, params map[st
|
||||||
return r0, r1
|
return r0, r1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GlobalRegistry provides a mock function with given fields: registry
|
||||||
|
func (_m *Client) GlobalRegistry(registry string) (*woodpecker.Registry, error) {
|
||||||
|
ret := _m.Called(registry)
|
||||||
|
|
||||||
|
if len(ret) == 0 {
|
||||||
|
panic("no return value specified for GlobalRegistry")
|
||||||
|
}
|
||||||
|
|
||||||
|
var r0 *woodpecker.Registry
|
||||||
|
var r1 error
|
||||||
|
if rf, ok := ret.Get(0).(func(string) (*woodpecker.Registry, error)); ok {
|
||||||
|
return rf(registry)
|
||||||
|
}
|
||||||
|
if rf, ok := ret.Get(0).(func(string) *woodpecker.Registry); ok {
|
||||||
|
r0 = rf(registry)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(*woodpecker.Registry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if rf, ok := ret.Get(1).(func(string) error); ok {
|
||||||
|
r1 = rf(registry)
|
||||||
|
} else {
|
||||||
|
r1 = ret.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0, r1
|
||||||
|
}
|
||||||
|
|
||||||
|
// GlobalRegistryCreate provides a mock function with given fields: registry
|
||||||
|
func (_m *Client) GlobalRegistryCreate(registry *woodpecker.Registry) (*woodpecker.Registry, error) {
|
||||||
|
ret := _m.Called(registry)
|
||||||
|
|
||||||
|
if len(ret) == 0 {
|
||||||
|
panic("no return value specified for GlobalRegistryCreate")
|
||||||
|
}
|
||||||
|
|
||||||
|
var r0 *woodpecker.Registry
|
||||||
|
var r1 error
|
||||||
|
if rf, ok := ret.Get(0).(func(*woodpecker.Registry) (*woodpecker.Registry, error)); ok {
|
||||||
|
return rf(registry)
|
||||||
|
}
|
||||||
|
if rf, ok := ret.Get(0).(func(*woodpecker.Registry) *woodpecker.Registry); ok {
|
||||||
|
r0 = rf(registry)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(*woodpecker.Registry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if rf, ok := ret.Get(1).(func(*woodpecker.Registry) error); ok {
|
||||||
|
r1 = rf(registry)
|
||||||
|
} else {
|
||||||
|
r1 = ret.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0, r1
|
||||||
|
}
|
||||||
|
|
||||||
|
// GlobalRegistryDelete provides a mock function with given fields: registry
|
||||||
|
func (_m *Client) GlobalRegistryDelete(registry string) error {
|
||||||
|
ret := _m.Called(registry)
|
||||||
|
|
||||||
|
if len(ret) == 0 {
|
||||||
|
panic("no return value specified for GlobalRegistryDelete")
|
||||||
|
}
|
||||||
|
|
||||||
|
var r0 error
|
||||||
|
if rf, ok := ret.Get(0).(func(string) error); ok {
|
||||||
|
r0 = rf(registry)
|
||||||
|
} else {
|
||||||
|
r0 = ret.Error(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
|
// GlobalRegistryList provides a mock function with given fields:
|
||||||
|
func (_m *Client) GlobalRegistryList() ([]*woodpecker.Registry, error) {
|
||||||
|
ret := _m.Called()
|
||||||
|
|
||||||
|
if len(ret) == 0 {
|
||||||
|
panic("no return value specified for GlobalRegistryList")
|
||||||
|
}
|
||||||
|
|
||||||
|
var r0 []*woodpecker.Registry
|
||||||
|
var r1 error
|
||||||
|
if rf, ok := ret.Get(0).(func() ([]*woodpecker.Registry, error)); ok {
|
||||||
|
return rf()
|
||||||
|
}
|
||||||
|
if rf, ok := ret.Get(0).(func() []*woodpecker.Registry); ok {
|
||||||
|
r0 = rf()
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).([]*woodpecker.Registry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if rf, ok := ret.Get(1).(func() error); ok {
|
||||||
|
r1 = rf()
|
||||||
|
} else {
|
||||||
|
r1 = ret.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0, r1
|
||||||
|
}
|
||||||
|
|
||||||
|
// GlobalRegistryUpdate provides a mock function with given fields: registry
|
||||||
|
func (_m *Client) GlobalRegistryUpdate(registry *woodpecker.Registry) (*woodpecker.Registry, error) {
|
||||||
|
ret := _m.Called(registry)
|
||||||
|
|
||||||
|
if len(ret) == 0 {
|
||||||
|
panic("no return value specified for GlobalRegistryUpdate")
|
||||||
|
}
|
||||||
|
|
||||||
|
var r0 *woodpecker.Registry
|
||||||
|
var r1 error
|
||||||
|
if rf, ok := ret.Get(0).(func(*woodpecker.Registry) (*woodpecker.Registry, error)); ok {
|
||||||
|
return rf(registry)
|
||||||
|
}
|
||||||
|
if rf, ok := ret.Get(0).(func(*woodpecker.Registry) *woodpecker.Registry); ok {
|
||||||
|
r0 = rf(registry)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(*woodpecker.Registry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if rf, ok := ret.Get(1).(func(*woodpecker.Registry) error); ok {
|
||||||
|
r1 = rf(registry)
|
||||||
|
} else {
|
||||||
|
r1 = ret.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0, r1
|
||||||
|
}
|
||||||
|
|
||||||
// GlobalSecret provides a mock function with given fields: secret
|
// GlobalSecret provides a mock function with given fields: secret
|
||||||
func (_m *Client) GlobalSecret(secret string) (*woodpecker.Secret, error) {
|
func (_m *Client) GlobalSecret(secret string) (*woodpecker.Secret, error) {
|
||||||
ret := _m.Called(secret)
|
ret := _m.Called(secret)
|
||||||
|
@ -599,6 +737,144 @@ func (_m *Client) OrgLookup(orgName string) (*woodpecker.Org, error) {
|
||||||
return r0, r1
|
return r0, r1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// OrgRegistry provides a mock function with given fields: orgID, registry
|
||||||
|
func (_m *Client) OrgRegistry(orgID int64, registry string) (*woodpecker.Registry, error) {
|
||||||
|
ret := _m.Called(orgID, registry)
|
||||||
|
|
||||||
|
if len(ret) == 0 {
|
||||||
|
panic("no return value specified for OrgRegistry")
|
||||||
|
}
|
||||||
|
|
||||||
|
var r0 *woodpecker.Registry
|
||||||
|
var r1 error
|
||||||
|
if rf, ok := ret.Get(0).(func(int64, string) (*woodpecker.Registry, error)); ok {
|
||||||
|
return rf(orgID, registry)
|
||||||
|
}
|
||||||
|
if rf, ok := ret.Get(0).(func(int64, string) *woodpecker.Registry); ok {
|
||||||
|
r0 = rf(orgID, registry)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(*woodpecker.Registry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if rf, ok := ret.Get(1).(func(int64, string) error); ok {
|
||||||
|
r1 = rf(orgID, registry)
|
||||||
|
} else {
|
||||||
|
r1 = ret.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0, r1
|
||||||
|
}
|
||||||
|
|
||||||
|
// OrgRegistryCreate provides a mock function with given fields: orgID, registry
|
||||||
|
func (_m *Client) OrgRegistryCreate(orgID int64, registry *woodpecker.Registry) (*woodpecker.Registry, error) {
|
||||||
|
ret := _m.Called(orgID, registry)
|
||||||
|
|
||||||
|
if len(ret) == 0 {
|
||||||
|
panic("no return value specified for OrgRegistryCreate")
|
||||||
|
}
|
||||||
|
|
||||||
|
var r0 *woodpecker.Registry
|
||||||
|
var r1 error
|
||||||
|
if rf, ok := ret.Get(0).(func(int64, *woodpecker.Registry) (*woodpecker.Registry, error)); ok {
|
||||||
|
return rf(orgID, registry)
|
||||||
|
}
|
||||||
|
if rf, ok := ret.Get(0).(func(int64, *woodpecker.Registry) *woodpecker.Registry); ok {
|
||||||
|
r0 = rf(orgID, registry)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(*woodpecker.Registry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if rf, ok := ret.Get(1).(func(int64, *woodpecker.Registry) error); ok {
|
||||||
|
r1 = rf(orgID, registry)
|
||||||
|
} else {
|
||||||
|
r1 = ret.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0, r1
|
||||||
|
}
|
||||||
|
|
||||||
|
// OrgRegistryDelete provides a mock function with given fields: orgID, registry
|
||||||
|
func (_m *Client) OrgRegistryDelete(orgID int64, registry string) error {
|
||||||
|
ret := _m.Called(orgID, registry)
|
||||||
|
|
||||||
|
if len(ret) == 0 {
|
||||||
|
panic("no return value specified for OrgRegistryDelete")
|
||||||
|
}
|
||||||
|
|
||||||
|
var r0 error
|
||||||
|
if rf, ok := ret.Get(0).(func(int64, string) error); ok {
|
||||||
|
r0 = rf(orgID, registry)
|
||||||
|
} else {
|
||||||
|
r0 = ret.Error(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
|
// OrgRegistryList provides a mock function with given fields: orgID
|
||||||
|
func (_m *Client) OrgRegistryList(orgID int64) ([]*woodpecker.Registry, error) {
|
||||||
|
ret := _m.Called(orgID)
|
||||||
|
|
||||||
|
if len(ret) == 0 {
|
||||||
|
panic("no return value specified for OrgRegistryList")
|
||||||
|
}
|
||||||
|
|
||||||
|
var r0 []*woodpecker.Registry
|
||||||
|
var r1 error
|
||||||
|
if rf, ok := ret.Get(0).(func(int64) ([]*woodpecker.Registry, error)); ok {
|
||||||
|
return rf(orgID)
|
||||||
|
}
|
||||||
|
if rf, ok := ret.Get(0).(func(int64) []*woodpecker.Registry); ok {
|
||||||
|
r0 = rf(orgID)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).([]*woodpecker.Registry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if rf, ok := ret.Get(1).(func(int64) error); ok {
|
||||||
|
r1 = rf(orgID)
|
||||||
|
} else {
|
||||||
|
r1 = ret.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0, r1
|
||||||
|
}
|
||||||
|
|
||||||
|
// OrgRegistryUpdate provides a mock function with given fields: orgID, registry
|
||||||
|
func (_m *Client) OrgRegistryUpdate(orgID int64, registry *woodpecker.Registry) (*woodpecker.Registry, error) {
|
||||||
|
ret := _m.Called(orgID, registry)
|
||||||
|
|
||||||
|
if len(ret) == 0 {
|
||||||
|
panic("no return value specified for OrgRegistryUpdate")
|
||||||
|
}
|
||||||
|
|
||||||
|
var r0 *woodpecker.Registry
|
||||||
|
var r1 error
|
||||||
|
if rf, ok := ret.Get(0).(func(int64, *woodpecker.Registry) (*woodpecker.Registry, error)); ok {
|
||||||
|
return rf(orgID, registry)
|
||||||
|
}
|
||||||
|
if rf, ok := ret.Get(0).(func(int64, *woodpecker.Registry) *woodpecker.Registry); ok {
|
||||||
|
r0 = rf(orgID, registry)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(*woodpecker.Registry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if rf, ok := ret.Get(1).(func(int64, *woodpecker.Registry) error); ok {
|
||||||
|
r1 = rf(orgID, registry)
|
||||||
|
} else {
|
||||||
|
r1 = ret.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0, r1
|
||||||
|
}
|
||||||
|
|
||||||
// OrgSecret provides a mock function with given fields: orgID, secret
|
// OrgSecret provides a mock function with given fields: orgID, secret
|
||||||
func (_m *Client) OrgSecret(orgID int64, secret string) (*woodpecker.Secret, error) {
|
func (_m *Client) OrgSecret(orgID int64, secret string) (*woodpecker.Secret, error) {
|
||||||
ret := _m.Called(orgID, secret)
|
ret := _m.Called(orgID, secret)
|
||||||
|
|
|
@ -7,6 +7,8 @@ const (
|
||||||
pathOrgLookup = "%s/api/orgs/lookup/%s"
|
pathOrgLookup = "%s/api/orgs/lookup/%s"
|
||||||
pathOrgSecrets = "%s/api/orgs/%d/secrets"
|
pathOrgSecrets = "%s/api/orgs/%d/secrets"
|
||||||
pathOrgSecret = "%s/api/orgs/%d/secrets/%s"
|
pathOrgSecret = "%s/api/orgs/%d/secrets/%s"
|
||||||
|
pathOrgRegistries = "%s/api/orgs/%d/registries"
|
||||||
|
pathOrgRegistry = "%s/api/orgs/%d/registries/%s"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Org returns an organization by id.
|
// Org returns an organization by id.
|
||||||
|
@ -62,3 +64,41 @@ func (c *client) OrgSecretDelete(orgID int64, secret string) error {
|
||||||
uri := fmt.Sprintf(pathOrgSecret, c.addr, orgID, secret)
|
uri := fmt.Sprintf(pathOrgSecret, c.addr, orgID, secret)
|
||||||
return c.delete(uri)
|
return c.delete(uri)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// OrgRegistry returns an organization registry by address.
|
||||||
|
func (c *client) OrgRegistry(orgID int64, registry string) (*Registry, error) {
|
||||||
|
out := new(Registry)
|
||||||
|
uri := fmt.Sprintf(pathOrgRegistry, c.addr, orgID, registry)
|
||||||
|
err := c.get(uri, out)
|
||||||
|
return out, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// OrgRegistryList returns a list of all organization registries.
|
||||||
|
func (c *client) OrgRegistryList(orgID int64) ([]*Registry, error) {
|
||||||
|
var out []*Registry
|
||||||
|
uri := fmt.Sprintf(pathOrgRegistries, c.addr, orgID)
|
||||||
|
err := c.get(uri, &out)
|
||||||
|
return out, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// OrgRegistryCreate creates an organization registry.
|
||||||
|
func (c *client) OrgRegistryCreate(orgID int64, in *Registry) (*Registry, error) {
|
||||||
|
out := new(Registry)
|
||||||
|
uri := fmt.Sprintf(pathOrgRegistries, c.addr, orgID)
|
||||||
|
err := c.post(uri, in, out)
|
||||||
|
return out, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// OrgRegistryUpdate updates an organization registry.
|
||||||
|
func (c *client) OrgRegistryUpdate(orgID int64, in *Registry) (*Registry, error) {
|
||||||
|
out := new(Registry)
|
||||||
|
uri := fmt.Sprintf(pathOrgRegistry, c.addr, orgID, in.Address)
|
||||||
|
err := c.patch(uri, in, out)
|
||||||
|
return out, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// OrgRegistryDelete deletes an organization registry.
|
||||||
|
func (c *client) OrgRegistryDelete(orgID int64, registry string) error {
|
||||||
|
uri := fmt.Sprintf(pathOrgRegistry, c.addr, orgID, registry)
|
||||||
|
return c.delete(uri)
|
||||||
|
}
|
||||||
|
|
|
@ -18,8 +18,8 @@ const (
|
||||||
pathStop = "%s/api/repos/%d/pipelines/%d/cancel"
|
pathStop = "%s/api/repos/%d/pipelines/%d/cancel"
|
||||||
pathRepoSecrets = "%s/api/repos/%d/secrets"
|
pathRepoSecrets = "%s/api/repos/%d/secrets"
|
||||||
pathRepoSecret = "%s/api/repos/%d/secrets/%s"
|
pathRepoSecret = "%s/api/repos/%d/secrets/%s"
|
||||||
pathRepoRegistries = "%s/api/repos/%d/registry"
|
pathRepoRegistries = "%s/api/repos/%d/registries"
|
||||||
pathRepoRegistry = "%s/api/repos/%d/registry/%s"
|
pathRepoRegistry = "%s/api/repos/%d/registries/%s"
|
||||||
pathRepoCrons = "%s/api/repos/%d/cron"
|
pathRepoCrons = "%s/api/repos/%d/cron"
|
||||||
pathRepoCron = "%s/api/repos/%d/cron/%d"
|
pathRepoCron = "%s/api/repos/%d/cron/%d"
|
||||||
)
|
)
|
||||||
|
|
|
@ -132,6 +132,8 @@ type (
|
||||||
// Registry represents a docker registry with credentials.
|
// Registry represents a docker registry with credentials.
|
||||||
Registry struct {
|
Registry struct {
|
||||||
ID int64 `json:"id"`
|
ID int64 `json:"id"`
|
||||||
|
OrgID int64 `json:"org_id"`
|
||||||
|
RepoID int64 `json:"repo_id"`
|
||||||
Address string `json:"address"`
|
Address string `json:"address"`
|
||||||
Username string `json:"username"`
|
Username string `json:"username"`
|
||||||
Password string `json:"password,omitempty"`
|
Password string `json:"password,omitempty"`
|
||||||
|
|
Loading…
Reference in a new issue