mirror of
https://github.com/woodpecker-ci/woodpecker.git
synced 2024-09-28 22:41:59 +00:00
Merge remote-tracking branch 'upstream/main' into pr/6543/3539
This commit is contained in:
commit
5dc778e80d
145 changed files with 3396 additions and 1502 deletions
19
.cspell.json
19
.cspell.json
|
@ -79,7 +79,22 @@
|
||||||
"HEALTHCHECK",
|
"HEALTHCHECK",
|
||||||
"devx",
|
"devx",
|
||||||
"gomod",
|
"gomod",
|
||||||
"laszlocph"
|
"laszlocph",
|
||||||
|
"charmbracelet",
|
||||||
|
"GODEBUG",
|
||||||
|
"netdns",
|
||||||
|
"BUILDPLATFORM",
|
||||||
|
"repology",
|
||||||
|
"WORKDIR",
|
||||||
|
"corepack",
|
||||||
|
"binutils",
|
||||||
|
"nocolor",
|
||||||
|
"logfile",
|
||||||
|
"Keyfunc",
|
||||||
|
"protoc",
|
||||||
|
"PROTOC",
|
||||||
|
"GOBIN",
|
||||||
|
"GOPATH"
|
||||||
],
|
],
|
||||||
"ignorePaths": [
|
"ignorePaths": [
|
||||||
"**/node_modules/**/*",
|
"**/node_modules/**/*",
|
||||||
|
@ -103,10 +118,8 @@
|
||||||
"agent/",
|
"agent/",
|
||||||
"cli/",
|
"cli/",
|
||||||
"cmd/",
|
"cmd/",
|
||||||
"docker/",
|
|
||||||
"docs/",
|
"docs/",
|
||||||
"pipeline/",
|
"pipeline/",
|
||||||
"shared/",
|
|
||||||
"server/"
|
"server/"
|
||||||
],
|
],
|
||||||
"enableFiletypes": ["dockercompose"]
|
"enableFiletypes": ["dockercompose"]
|
||||||
|
|
16
.github/renovate.json
vendored
16
.github/renovate.json
vendored
|
@ -2,18 +2,28 @@
|
||||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||||
"extends": ["github>woodpecker-ci/renovate-config"],
|
"extends": ["github>woodpecker-ci/renovate-config"],
|
||||||
"packageRules": [
|
"packageRules": [
|
||||||
|
{
|
||||||
|
"matchCurrentVersion": "<1.0.0",
|
||||||
|
"matchPackageNames": ["github.com/distribution/reference"],
|
||||||
|
"matchUpdateTypes": ["major", "minor"],
|
||||||
|
"dependencyDashboardApproval": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"matchPackageNames": ["github.com/charmbracelet/huh/spinner"],
|
||||||
|
"enabled": false
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"matchManagers": ["docker-compose"],
|
"matchManagers": ["docker-compose"],
|
||||||
"matchFileNames": ["docker-compose.gitpod.yml"],
|
"matchFileNames": ["docker-compose.gitpod.yaml"],
|
||||||
"addLabels": ["devx"]
|
"addLabels": ["devx"]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"groupName": "golang (lang)",
|
"groupName": "golang-lang",
|
||||||
"matchPackagePatterns": ["^golang$", "xgo"],
|
"matchPackagePatterns": ["^golang$", "xgo"],
|
||||||
"matchUpdateTypes": ["minor", "patch"]
|
"matchUpdateTypes": ["minor", "patch"]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"groupName": "golang (packages)",
|
"groupName": "golang-packages",
|
||||||
"matchManagers": ["gomod"],
|
"matchManagers": ["gomod"],
|
||||||
"matchUpdateTypes": ["minor", "patch"]
|
"matchUpdateTypes": ["minor", "patch"]
|
||||||
},
|
},
|
||||||
|
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -41,6 +41,7 @@ extras/
|
||||||
/build/
|
/build/
|
||||||
/dist/
|
/dist/
|
||||||
/data/
|
/data/
|
||||||
|
datastore/migration/testfiles/
|
||||||
|
|
||||||
docs/venv
|
docs/venv
|
||||||
|
|
||||||
|
|
2
.mockery.yaml
Normal file
2
.mockery.yaml
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
---
|
||||||
|
disable-version-string: true
|
|
@ -5,12 +5,12 @@ repos:
|
||||||
- id: check-hooks-apply
|
- id: check-hooks-apply
|
||||||
- id: check-useless-excludes
|
- id: check-useless-excludes
|
||||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||||
rev: v4.5.0
|
rev: v4.6.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: end-of-file-fixer
|
- id: end-of-file-fixer
|
||||||
- id: trailing-whitespace
|
- id: trailing-whitespace
|
||||||
- repo: https://github.com/golangci/golangci-lint
|
- repo: https://github.com/golangci/golangci-lint
|
||||||
rev: v1.57.1
|
rev: v1.57.2
|
||||||
hooks:
|
hooks:
|
||||||
- id: golangci-lint
|
- id: golangci-lint
|
||||||
- repo: https://github.com/igorshubovych/markdownlint-cli
|
- repo: https://github.com/igorshubovych/markdownlint-cli
|
||||||
|
@ -24,7 +24,7 @@ repos:
|
||||||
- id: checkmake
|
- id: checkmake
|
||||||
exclude: '^docker/Dockerfile.make$' # actually a Dockerfile and not a makefile
|
exclude: '^docker/Dockerfile.make$' # actually a Dockerfile and not a makefile
|
||||||
- repo: https://github.com/hadolint/hadolint
|
- repo: https://github.com/hadolint/hadolint
|
||||||
rev: v2.12.1-beta
|
rev: v2.12.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: hadolint
|
- id: hadolint
|
||||||
- repo: https://github.com/pre-commit/mirrors-prettier
|
- repo: https://github.com/pre-commit/mirrors-prettier
|
||||||
|
|
|
@ -2,7 +2,7 @@ when:
|
||||||
event: tag
|
event: tag
|
||||||
|
|
||||||
variables:
|
variables:
|
||||||
- &golang_image 'docker.io/golang:1.22.1'
|
- &golang_image 'docker.io/golang:1.22.2'
|
||||||
- &node_image 'docker.io/node:21-alpine'
|
- &node_image 'docker.io/node:21-alpine'
|
||||||
- &xgo_image 'docker.io/techknowlogick/xgo:go-1.22.1'
|
- &xgo_image 'docker.io/techknowlogick/xgo:go-1.22.1'
|
||||||
- &xgo_version 'go-1.21.2'
|
- &xgo_version 'go-1.21.2'
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
variables:
|
variables:
|
||||||
- &golang_image 'docker.io/golang:1.22.1'
|
- &golang_image 'docker.io/golang:1.22.2'
|
||||||
- &node_image 'docker.io/node:21-alpine'
|
- &node_image 'docker.io/node:21-alpine'
|
||||||
- &xgo_image 'docker.io/techknowlogick/xgo:go-1.22.1'
|
- &xgo_image 'docker.io/techknowlogick/xgo:go-1.22.1'
|
||||||
- &xgo_version 'go-1.21.2'
|
- &xgo_version 'go-1.21.2'
|
||||||
- &buildx_plugin 'docker.io/woodpeckerci/plugin-docker-buildx:3.2.0'
|
- &buildx_plugin 'docker.io/woodpeckerci/plugin-docker-buildx:3.2.1'
|
||||||
- &platforms_release 'linux/arm/v6,linux/arm/v7,linux/arm64/v8,linux/386,linux/amd64,linux/ppc64le,linux/riscv64,linux/s390x,freebsd/arm64,freebsd/amd64,openbsd/arm64,openbsd/amd64'
|
- &platforms_release 'linux/arm/v6,linux/arm/v7,linux/arm64/v8,linux/386,linux/amd64,linux/ppc64le,linux/riscv64,linux/s390x,freebsd/arm64,freebsd/amd64,openbsd/arm64,openbsd/amd64'
|
||||||
- &platforms_server 'linux/arm/v7,linux/arm64/v8,linux/amd64,linux/ppc64le,linux/riscv64'
|
- &platforms_server 'linux/arm/v7,linux/arm64/v8,linux/amd64,linux/ppc64le,linux/riscv64'
|
||||||
- &platforms_preview 'linux/amd64'
|
- &platforms_preview 'linux/amd64'
|
||||||
|
|
13
Makefile
13
Makefile
|
@ -58,8 +58,6 @@ ifeq (in_docker,$(firstword $(MAKECMDGOALS)))
|
||||||
-e TARGETOS="$(TARGETOS)" \
|
-e TARGETOS="$(TARGETOS)" \
|
||||||
-e TARGETARCH="$(TARGETARCH)" \
|
-e TARGETARCH="$(TARGETARCH)" \
|
||||||
-e CGO_ENABLED="$(CGO_ENABLED)" \
|
-e CGO_ENABLED="$(CGO_ENABLED)" \
|
||||||
-e GOPATH=/tmp/go \
|
|
||||||
-e HOME=/tmp/home \
|
|
||||||
-v $(PWD):/build --rm woodpecker/make:local make $(MAKE_ARGS)
|
-v $(PWD):/build --rm woodpecker/make:local make $(MAKE_ARGS)
|
||||||
else
|
else
|
||||||
|
|
||||||
|
@ -110,7 +108,7 @@ clean-all: clean ## Clean all artifacts
|
||||||
rm -rf docs/docs/40-cli.md docs/swagger.json
|
rm -rf docs/docs/40-cli.md docs/swagger.json
|
||||||
|
|
||||||
.PHONY: generate
|
.PHONY: generate
|
||||||
generate: generate-swagger ## Run all code generations
|
generate: install-tools generate-swagger ## Run all code generations
|
||||||
go generate ./...
|
go generate ./...
|
||||||
|
|
||||||
generate-swagger: install-tools ## Run swagger code generation
|
generate-swagger: install-tools ## Run swagger code generation
|
||||||
|
@ -137,6 +135,15 @@ install-tools: ## Install development tools
|
||||||
fi ; \
|
fi ; \
|
||||||
hash addlicense > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
hash addlicense > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||||
go install github.com/google/addlicense@latest; \
|
go install github.com/google/addlicense@latest; \
|
||||||
|
fi ; \
|
||||||
|
hash mockery > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||||
|
go install github.com/vektra/mockery/v2@latest; \
|
||||||
|
fi ; \
|
||||||
|
hash protoc-gen-go > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||||
|
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest; \
|
||||||
|
fi ; \
|
||||||
|
hash protoc-gen-go-grpc > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||||
|
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest; \
|
||||||
fi
|
fi
|
||||||
|
|
||||||
ui-dependencies: ## Install UI dependencies
|
ui-dependencies: ## Install UI dependencies
|
||||||
|
|
|
@ -53,6 +53,7 @@ func (c *client) Close() error {
|
||||||
|
|
||||||
func (c *client) newBackOff() backoff.BackOff {
|
func (c *client) newBackOff() backoff.BackOff {
|
||||||
b := backoff.NewExponentialBackOff()
|
b := backoff.NewExponentialBackOff()
|
||||||
|
b.MaxElapsedTime = 0
|
||||||
b.MaxInterval = 10 * time.Second //nolint: gomnd
|
b.MaxInterval = 10 * time.Second //nolint: gomnd
|
||||||
b.InitialInterval = 10 * time.Millisecond //nolint: gomnd
|
b.InitialInterval = 10 * time.Millisecond //nolint: gomnd
|
||||||
return b
|
return b
|
||||||
|
|
|
@ -50,7 +50,7 @@ func NewRunner(workEngine rpc.Peer, f rpc.Filter, h string, state *State, backen
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Runner) Run(runnerCtx context.Context) error {
|
func (r *Runner) Run(runnerCtx context.Context) error { //nolint:contextcheck
|
||||||
log.Debug().Msg("request next execution")
|
log.Debug().Msg("request next execution")
|
||||||
|
|
||||||
meta, _ := metadata.FromOutgoingContext(runnerCtx)
|
meta, _ := metadata.FromOutgoingContext(runnerCtx)
|
||||||
|
|
|
@ -37,7 +37,7 @@ func Before(c *cli.Context) error {
|
||||||
|
|
||||||
log.Debug().Msg("Checking for updates ...")
|
log.Debug().Msg("Checking for updates ...")
|
||||||
|
|
||||||
newVersion, err := update.CheckForUpdate(waitForUpdateCheck, true)
|
newVersion, err := update.CheckForUpdate(waitForUpdateCheck, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().Err(err).Msgf("Failed to check for updates")
|
log.Error().Err(err).Msgf("Failed to check for updates")
|
||||||
return
|
return
|
||||||
|
@ -57,8 +57,8 @@ func After(_ *cli.Context) error {
|
||||||
if waitForUpdateCheck != nil {
|
if waitForUpdateCheck != nil {
|
||||||
select {
|
select {
|
||||||
case <-waitForUpdateCheck.Done():
|
case <-waitForUpdateCheck.Done():
|
||||||
// When the actual command already finished, we still wait 250ms for the update check to finish
|
// When the actual command already finished, we still wait 500ms for the update check to finish
|
||||||
case <-time.After(time.Millisecond * 250):
|
case <-time.After(time.Millisecond * 500):
|
||||||
log.Debug().Msg("Update check stopped due to timeout")
|
log.Debug().Msg("Update check stopped due to timeout")
|
||||||
cancelWaitForUpdate(errors.New("update check timeout"))
|
cancelWaitForUpdate(errors.New("update check timeout"))
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,9 +30,12 @@ func Load(c *cli.Context) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if config == nil && !c.IsSet("server-url") && !c.IsSet("token") {
|
if config == nil {
|
||||||
log.Info().Msg("The woodpecker-cli is not yet set up. Please run `woodpecker-cli setup`")
|
config = &Config{
|
||||||
return errors.New("woodpecker-cli is not setup")
|
LogLevel: "info",
|
||||||
|
ServerURL: c.String("server-url"),
|
||||||
|
Token: c.String("token"),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !c.IsSet("server") {
|
if !c.IsSet("server") {
|
||||||
|
@ -56,6 +59,11 @@ func Load(c *cli.Context) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if config.ServerURL == "" || config.Token == "" {
|
||||||
|
log.Info().Msg("The woodpecker-cli is not yet set up. Please run `woodpecker-cli setup` or provide the required environment variables / flags.")
|
||||||
|
return errors.New("woodpecker-cli is not configured")
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -114,7 +114,7 @@ func lintFile(_ *cli.Context, file string) error {
|
||||||
hasErrors = true
|
hasErrors = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if data := err.GetLinterData(); data != nil {
|
if data := pipeline_errors.GetLinterData(err); data != nil {
|
||||||
line = fmt.Sprintf("%s %s\t%s", line, output.String(data.Field).Bold(), err.Message)
|
line = fmt.Sprintf("%s %s\t%s", line, output.String(data.Field).Bold(), err.Message)
|
||||||
} else {
|
} else {
|
||||||
line = fmt.Sprintf("%s %s", line, err.Message)
|
line = fmt.Sprintf("%s %s", line, err.Message)
|
||||||
|
|
|
@ -15,6 +15,8 @@ import (
|
||||||
var Command = &cli.Command{
|
var Command = &cli.Command{
|
||||||
Name: "setup",
|
Name: "setup",
|
||||||
Usage: "setup the woodpecker-cli for the first time",
|
Usage: "setup the woodpecker-cli for the first time",
|
||||||
|
Args: true,
|
||||||
|
ArgsUsage: "[server-url]",
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
Name: "server-url",
|
Name: "server-url",
|
||||||
|
@ -45,6 +47,9 @@ func setup(c *cli.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
serverURL := c.String("server-url")
|
serverURL := c.String("server-url")
|
||||||
|
if serverURL == "" {
|
||||||
|
serverURL = c.Args().First()
|
||||||
|
}
|
||||||
|
|
||||||
if serverURL == "" {
|
if serverURL == "" {
|
||||||
serverURL, err = ui.Ask("Enter the URL of the woodpecker server", "https://ci.woodpecker-ci.org", true)
|
serverURL, err = ui.Ask("Enter the URL of the woodpecker server", "https://ci.woodpecker-ci.org", true)
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"runtime"
|
"runtime"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/charmbracelet/huh/spinner"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
)
|
)
|
||||||
|
@ -37,13 +38,27 @@ func receiveTokenFromUI(c context.Context, serverURL string) (string, error) {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
spinnerCtx, spinnerDone := context.WithCancelCause(c)
|
||||||
|
go func() {
|
||||||
|
err = spinner.New().
|
||||||
|
Title("Waiting for token ...").
|
||||||
|
Context(spinnerCtx).
|
||||||
|
Run()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
// wait for token to be received or timeout
|
// wait for token to be received or timeout
|
||||||
select {
|
select {
|
||||||
case token := <-tokenReceived:
|
case token := <-tokenReceived:
|
||||||
|
spinnerDone(nil)
|
||||||
return token, nil
|
return token, nil
|
||||||
case <-c.Done():
|
case <-c.Done():
|
||||||
|
spinnerDone(nil)
|
||||||
return "", c.Err()
|
return "", c.Err()
|
||||||
case <-time.After(5 * time.Minute):
|
case <-time.After(5 * time.Minute):
|
||||||
|
spinnerDone(nil)
|
||||||
return "", errors.New("timed out waiting for token")
|
return "", errors.New("timed out waiting for token")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,79 +1,26 @@
|
||||||
package ui
|
package ui
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"errors"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/charmbracelet/bubbles/textinput"
|
"github.com/charmbracelet/huh"
|
||||||
tea "github.com/charmbracelet/bubbletea"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type askModel struct {
|
|
||||||
prompt string
|
|
||||||
required bool
|
|
||||||
textInput textinput.Model
|
|
||||||
err error
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m askModel) Init() tea.Cmd {
|
|
||||||
return textinput.Blink
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m askModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|
||||||
var cmd tea.Cmd
|
|
||||||
|
|
||||||
switch msg := msg.(type) {
|
|
||||||
case tea.KeyMsg:
|
|
||||||
switch msg.Type {
|
|
||||||
case tea.KeyEnter:
|
|
||||||
if !m.required || (m.required && strings.TrimSpace(m.textInput.Value()) != "") {
|
|
||||||
return m, tea.Quit
|
|
||||||
}
|
|
||||||
case tea.KeyCtrlC, tea.KeyEsc:
|
|
||||||
return m, tea.Quit
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return m, cmd
|
|
||||||
}
|
|
||||||
|
|
||||||
m.textInput, cmd = m.textInput.Update(msg)
|
|
||||||
return m, cmd
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m askModel) View() string {
|
|
||||||
return fmt.Sprintf(
|
|
||||||
"%s\n\n%s\n\n%s",
|
|
||||||
m.prompt,
|
|
||||||
m.textInput.View(),
|
|
||||||
"(esc to quit)",
|
|
||||||
) + "\n"
|
|
||||||
}
|
|
||||||
|
|
||||||
func Ask(prompt, placeholder string, required bool) (string, error) {
|
func Ask(prompt, placeholder string, required bool) (string, error) {
|
||||||
ti := textinput.New()
|
var input string
|
||||||
ti.Placeholder = placeholder
|
err := huh.NewInput().
|
||||||
ti.Focus()
|
Title(prompt).
|
||||||
ti.CharLimit = 156
|
Value(&input).
|
||||||
ti.Width = 40
|
Placeholder(placeholder).Validate(func(s string) error {
|
||||||
|
if required && strings.TrimSpace(s) == "" {
|
||||||
p := tea.NewProgram(askModel{
|
return errors.New("required")
|
||||||
prompt: prompt,
|
}
|
||||||
textInput: ti,
|
return nil
|
||||||
required: required,
|
}).Run()
|
||||||
err: nil,
|
|
||||||
})
|
|
||||||
|
|
||||||
_m, err := p.Run()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
m, ok := _m.(askModel)
|
return strings.TrimSpace(input), nil
|
||||||
if !ok {
|
|
||||||
return "", fmt.Errorf("unexpected model: %T", _m)
|
|
||||||
}
|
|
||||||
|
|
||||||
text := strings.TrimSpace(m.textInput.Value())
|
|
||||||
|
|
||||||
return text, nil
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,71 +1,19 @@
|
||||||
package ui
|
package ui
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"github.com/charmbracelet/huh"
|
||||||
|
|
||||||
"github.com/charmbracelet/bubbles/textinput"
|
|
||||||
tea "github.com/charmbracelet/bubbletea"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type confirmModel struct {
|
|
||||||
confirmed bool
|
|
||||||
prompt string
|
|
||||||
err error
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m confirmModel) Init() tea.Cmd {
|
|
||||||
return textinput.Blink
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m confirmModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|
||||||
var cmd tea.Cmd
|
|
||||||
|
|
||||||
switch msg := msg.(type) {
|
|
||||||
case tea.KeyMsg:
|
|
||||||
if msg.Runes != nil {
|
|
||||||
switch msg.Runes[0] {
|
|
||||||
case 'y':
|
|
||||||
m.confirmed = true
|
|
||||||
return m, tea.Quit
|
|
||||||
case 'n':
|
|
||||||
m.confirmed = false
|
|
||||||
return m, tea.Quit
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch msg.Type {
|
|
||||||
case tea.KeyCtrlC, tea.KeyEsc:
|
|
||||||
return m, tea.Quit
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return m, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return m, cmd
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m confirmModel) View() string {
|
|
||||||
return fmt.Sprintf(
|
|
||||||
"%s y / n (esc to quit)",
|
|
||||||
m.prompt,
|
|
||||||
) + "\n"
|
|
||||||
}
|
|
||||||
|
|
||||||
func Confirm(prompt string) (bool, error) {
|
func Confirm(prompt string) (bool, error) {
|
||||||
p := tea.NewProgram(confirmModel{
|
var confirm bool
|
||||||
prompt: prompt,
|
err := huh.NewConfirm().
|
||||||
err: nil,
|
Title(prompt).
|
||||||
})
|
Affirmative("Yes!").
|
||||||
|
Negative("No.").
|
||||||
_m, err := p.Run()
|
Value(&confirm).Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
m, ok := _m.(confirmModel)
|
return confirm, err
|
||||||
if !ok {
|
|
||||||
return false, fmt.Errorf("unexpected model: %T", _m)
|
|
||||||
}
|
|
||||||
|
|
||||||
return m.confirmed, nil
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
package update
|
package update
|
||||||
|
|
||||||
type GithubRelease struct {
|
type VersionData struct {
|
||||||
TagName string `json:"tag_name"`
|
Latest string `json:"latest"`
|
||||||
Assets []struct {
|
Next string `json:"next"`
|
||||||
Name string `json:"name"`
|
RC string `json:"rc"`
|
||||||
BrowserDownloadURL string `json:"browser_download_url"`
|
|
||||||
} `json:"assets"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type NewVersion struct {
|
type NewVersion struct {
|
||||||
|
@ -13,4 +11,7 @@ type NewVersion struct {
|
||||||
AssetURL string
|
AssetURL string
|
||||||
}
|
}
|
||||||
|
|
||||||
const githubReleaseURL = "https://api.github.com/repos/woodpecker-ci/woodpecker/releases/latest"
|
const (
|
||||||
|
woodpeckerVersionURL = "https://woodpecker-ci.org/version.json"
|
||||||
|
githubBinaryURL = "https://github.com/woodpecker-ci/woodpecker/releases/download/v%s/woodpecker-cli_%s_%s.tar.gz"
|
||||||
|
)
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
|
|
||||||
|
@ -17,14 +18,18 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func CheckForUpdate(ctx context.Context, force bool) (*NewVersion, error) {
|
func CheckForUpdate(ctx context.Context, force bool) (*NewVersion, error) {
|
||||||
|
return checkForUpdate(ctx, woodpeckerVersionURL, force)
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkForUpdate(ctx context.Context, versionURL string, force bool) (*NewVersion, error) {
|
||||||
log.Debug().Msgf("Current version: %s", version.String())
|
log.Debug().Msgf("Current version: %s", version.String())
|
||||||
|
|
||||||
if version.String() == "dev" && !force {
|
if (version.String() == "dev" || strings.HasPrefix(version.String(), "next-")) && !force {
|
||||||
log.Debug().Msgf("Skipping update check for development version")
|
log.Debug().Msgf("Skipping update check for development & next versions")
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
req, err := http.NewRequestWithContext(ctx, "GET", githubReleaseURL, nil)
|
req, err := http.NewRequestWithContext(ctx, "GET", versionURL, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -39,34 +44,32 @@ func CheckForUpdate(ctx context.Context, force bool) (*NewVersion, error) {
|
||||||
return nil, errors.New("failed to fetch the latest release")
|
return nil, errors.New("failed to fetch the latest release")
|
||||||
}
|
}
|
||||||
|
|
||||||
var release GithubRelease
|
var versionData VersionData
|
||||||
if err := json.NewDecoder(resp.Body).Decode(&release); err != nil {
|
if err := json.NewDecoder(resp.Body).Decode(&versionData); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
upstreamVersion := versionData.Latest
|
||||||
|
if strings.HasPrefix(version.String(), "next-") {
|
||||||
|
upstreamVersion = versionData.Next
|
||||||
|
} else if strings.HasSuffix(version.String(), "rc-") {
|
||||||
|
upstreamVersion = versionData.RC
|
||||||
|
}
|
||||||
|
|
||||||
|
installedVersion := strings.TrimPrefix(version.Version, "v")
|
||||||
|
upstreamVersion = strings.TrimPrefix(upstreamVersion, "v")
|
||||||
|
|
||||||
// using the latest release
|
// using the latest release
|
||||||
if release.TagName == version.String() && !force {
|
if installedVersion == upstreamVersion && !force {
|
||||||
|
log.Debug().Msgf("No new version available")
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debug().Msgf("Latest version: %s", release.TagName)
|
log.Debug().Msgf("New version available: %s", upstreamVersion)
|
||||||
|
|
||||||
assetURL := ""
|
|
||||||
fileName := fmt.Sprintf("woodpecker-cli_%s_%s.tar.gz", runtime.GOOS, runtime.GOARCH)
|
|
||||||
for _, asset := range release.Assets {
|
|
||||||
if fileName == asset.Name {
|
|
||||||
assetURL = asset.BrowserDownloadURL
|
|
||||||
log.Debug().Msgf("Found asset for the current OS and arch: %s", assetURL)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if assetURL == "" {
|
|
||||||
return nil, errors.New("no asset found for the current OS")
|
|
||||||
}
|
|
||||||
|
|
||||||
|
assetURL := fmt.Sprintf(githubBinaryURL, upstreamVersion, runtime.GOOS, runtime.GOARCH)
|
||||||
return &NewVersion{
|
return &NewVersion{
|
||||||
Version: release.TagName,
|
Version: upstreamVersion,
|
||||||
AssetURL: assetURL,
|
AssetURL: assetURL,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
61
cli/update/updater_test.go
Normal file
61
cli/update/updater_test.go
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
package update
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"go.woodpecker-ci.org/woodpecker/v2/version"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCheckForUpdate(t *testing.T) {
|
||||||
|
version.Version = "1.0.0"
|
||||||
|
fixtureHandler := func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if r.URL.Path != "/version.json" {
|
||||||
|
http.NotFound(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_, _ = io.WriteString(w, `{"latest": "1.0.1", "next": "1.0.2", "rc": "1.0.3"}`)
|
||||||
|
}
|
||||||
|
ts := httptest.NewServer(http.HandlerFunc(fixtureHandler))
|
||||||
|
defer ts.Close()
|
||||||
|
|
||||||
|
newVersion, err := checkForUpdate(context.Background(), ts.URL+"/version.json", false)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to check for updates: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if newVersion == nil || newVersion.Version != "1.0.1" {
|
||||||
|
t.Fatalf("Expected a new version 1.0.1, got: %s", newVersion)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDownloadNewVersion(t *testing.T) {
|
||||||
|
downloadFilePath := "/woodpecker-cli_linux_amd64.tar.gz"
|
||||||
|
|
||||||
|
fixtureHandler := func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if r.URL.Path != downloadFilePath {
|
||||||
|
http.NotFound(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_, _ = io.WriteString(w, `blob`)
|
||||||
|
}
|
||||||
|
ts := httptest.NewServer(http.HandlerFunc(fixtureHandler))
|
||||||
|
defer ts.Close()
|
||||||
|
|
||||||
|
file, err := downloadNewVersion(context.Background(), ts.URL+downloadFilePath)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to download new version: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if file == "" {
|
||||||
|
t.Fatalf("Expected a file path, got: %s", file)
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = os.Remove(file)
|
||||||
|
}
|
|
@ -2012,6 +2012,53 @@ const docTemplate = `{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/repos/{repo_id}/logs/{number}/{stepId}": {
|
||||||
|
"delete": {
|
||||||
|
"produces": [
|
||||||
|
"text/plain"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Pipeline logs"
|
||||||
|
],
|
||||||
|
"summary": "Deletes step log",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"default": "Bearer \u003cpersonal access token\u003e",
|
||||||
|
"description": "Insert your personal access token",
|
||||||
|
"name": "Authorization",
|
||||||
|
"in": "header",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "the repository id",
|
||||||
|
"name": "repo_id",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "the number of the pipeline",
|
||||||
|
"name": "number",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "the step id",
|
||||||
|
"name": "stepId",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"204": {
|
||||||
|
"description": "No Content"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/repos/{repo_id}/move": {
|
"/repos/{repo_id}/move": {
|
||||||
"post": {
|
"post": {
|
||||||
"produces": [
|
"produces": [
|
||||||
|
@ -3844,6 +3891,9 @@ const docTemplate = `{
|
||||||
"Org": {
|
"Org": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"forge_id": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
"id": {
|
"id": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
|
@ -3922,7 +3972,7 @@ const docTemplate = `{
|
||||||
"errors": {
|
"errors": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": {
|
"items": {
|
||||||
"$ref": "#/definitions/errors.PipelineError"
|
"$ref": "#/definitions/types.PipelineError"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"event": {
|
"event": {
|
||||||
|
@ -4048,6 +4098,9 @@ const docTemplate = `{
|
||||||
"active": {
|
"active": {
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
},
|
},
|
||||||
|
"allow_deploy": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
"allow_pr": {
|
"allow_pr": {
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
},
|
},
|
||||||
|
@ -4072,6 +4125,9 @@ const docTemplate = `{
|
||||||
"default_branch": {
|
"default_branch": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"forge_id": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
"forge_remote_id": {
|
"forge_remote_id": {
|
||||||
"description": "ForgeRemoteID is the unique identifier for the repository on the forge.",
|
"description": "ForgeRemoteID is the unique identifier for the repository on the forge.",
|
||||||
"type": "string"
|
"type": "string"
|
||||||
|
@ -4123,6 +4179,9 @@ const docTemplate = `{
|
||||||
"RepoPatch": {
|
"RepoPatch": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"allow_deploy": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
"allow_pr": {
|
"allow_pr": {
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
},
|
},
|
||||||
|
@ -4365,6 +4424,9 @@ const docTemplate = `{
|
||||||
"description": "Email is the email address for this user.\n\nrequired: true",
|
"description": "Email is the email address for this user.\n\nrequired: true",
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"forge_id": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
"id": {
|
"id": {
|
||||||
"description": "the id for this user.\n\nrequired: true",
|
"description": "the id for this user.\n\nrequired: true",
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
|
@ -4402,45 +4464,6 @@ const docTemplate = `{
|
||||||
"EventManual"
|
"EventManual"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"errors.PipelineError": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"data": {},
|
|
||||||
"is_warning": {
|
|
||||||
"type": "boolean"
|
|
||||||
},
|
|
||||||
"message": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"type": {
|
|
||||||
"$ref": "#/definitions/errors.PipelineErrorType"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"errors.PipelineErrorType": {
|
|
||||||
"type": "string",
|
|
||||||
"enum": [
|
|
||||||
"linter",
|
|
||||||
"deprecation",
|
|
||||||
"compiler",
|
|
||||||
"generic",
|
|
||||||
"bad_habit"
|
|
||||||
],
|
|
||||||
"x-enum-comments": {
|
|
||||||
"PipelineErrorTypeBadHabit": "some bad-habit error",
|
|
||||||
"PipelineErrorTypeCompiler": "some error with the config semantics",
|
|
||||||
"PipelineErrorTypeDeprecation": "using some deprecated feature",
|
|
||||||
"PipelineErrorTypeGeneric": "some generic error",
|
|
||||||
"PipelineErrorTypeLinter": "some error with the config syntax"
|
|
||||||
},
|
|
||||||
"x-enum-varnames": [
|
|
||||||
"PipelineErrorTypeLinter",
|
|
||||||
"PipelineErrorTypeDeprecation",
|
|
||||||
"PipelineErrorTypeCompiler",
|
|
||||||
"PipelineErrorTypeGeneric",
|
|
||||||
"PipelineErrorTypeBadHabit"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"model.Workflow": {
|
"model.Workflow": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
@ -4487,6 +4510,45 @@ const docTemplate = `{
|
||||||
"$ref": "#/definitions/StatusValue"
|
"$ref": "#/definitions/StatusValue"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"types.PipelineError": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"data": {},
|
||||||
|
"is_warning": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"message": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"$ref": "#/definitions/types.PipelineErrorType"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"types.PipelineErrorType": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"linter",
|
||||||
|
"deprecation",
|
||||||
|
"compiler",
|
||||||
|
"generic",
|
||||||
|
"bad_habit"
|
||||||
|
],
|
||||||
|
"x-enum-comments": {
|
||||||
|
"PipelineErrorTypeBadHabit": "some bad-habit error",
|
||||||
|
"PipelineErrorTypeCompiler": "some error with the config semantics",
|
||||||
|
"PipelineErrorTypeDeprecation": "using some deprecated feature",
|
||||||
|
"PipelineErrorTypeGeneric": "some generic error",
|
||||||
|
"PipelineErrorTypeLinter": "some error with the config syntax"
|
||||||
|
},
|
||||||
|
"x-enum-varnames": [
|
||||||
|
"PipelineErrorTypeLinter",
|
||||||
|
"PipelineErrorTypeDeprecation",
|
||||||
|
"PipelineErrorTypeCompiler",
|
||||||
|
"PipelineErrorTypeGeneric",
|
||||||
|
"PipelineErrorTypeBadHabit"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}`
|
}`
|
||||||
|
|
|
@ -246,11 +246,6 @@ var flags = append([]cli.Flag{
|
||||||
Usage: "Disable version check in admin web ui.",
|
Usage: "Disable version check in admin web ui.",
|
||||||
Name: "skip-version-check",
|
Name: "skip-version-check",
|
||||||
},
|
},
|
||||||
&cli.StringSliceFlag{
|
|
||||||
EnvVars: []string{"WOODPECKER_ADDONS"},
|
|
||||||
Name: "addons",
|
|
||||||
Usage: "list of addon files",
|
|
||||||
},
|
|
||||||
//
|
//
|
||||||
// backend options for pipeline compiler
|
// backend options for pipeline compiler
|
||||||
//
|
//
|
||||||
|
@ -309,6 +304,35 @@ var flags = append([]cli.Flag{
|
||||||
Usage: "set the cpus allowed to execute containers",
|
Usage: "set the cpus allowed to execute containers",
|
||||||
},
|
},
|
||||||
//
|
//
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "forge-url",
|
||||||
|
Usage: "url of the forge",
|
||||||
|
EnvVars: []string{"WOODPECKER_FORGE_URL", "WOODPECKER_GITHUB_URL", "WOODPECKER_GITLAB_URL", "WOODPECKER_GITEA_URL", "WOODPECKER_BITBUCKET_URL"},
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "forge-oauth-client",
|
||||||
|
Usage: "oauth2 client id",
|
||||||
|
EnvVars: []string{"WOODPECKER_FORGE_CLIENT", "WOODPECKER_GITHUB_CLIENT", "WOODPECKER_GITLAB_CLIENT", "WOODPECKER_GITEA_CLIENT", "WOODPECKER_BITBUCKET_CLIENT", "WOODPECKER_BITBUCKET_DC_CLIENT_ID"},
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "forge-oauth-secret",
|
||||||
|
Usage: "oauth2 client secret",
|
||||||
|
EnvVars: []string{"WOODPECKER_FORGE_SECRET", "WOODPECKER_GITHUB_SECRET", "WOODPECKER_GITLAB_SECRET", "WOODPECKER_GITEA_SECRET", "WOODPECKER_BITBUCKET_SECRET", "WOODPECKER_BITBUCKET_DC_CLIENT_SECRET"},
|
||||||
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "forge-skip-verify",
|
||||||
|
Usage: "skip ssl verification",
|
||||||
|
EnvVars: []string{"WOODPECKER_FORGE_SKIP_VERIFY", "WOODPECKER_GITHUB_SKIP_VERIFY", "WOODPECKER_GITLAB_SKIP_VERIFY", "WOODPECKER_GITEA_SKIP_VERIFY", "WOODPECKER_BITBUCKET_SKIP_VERIFY"},
|
||||||
|
},
|
||||||
|
//
|
||||||
|
// Addon
|
||||||
|
//
|
||||||
|
&cli.StringFlag{
|
||||||
|
EnvVars: []string{"WOODPECKER_ADDON_FORGE"},
|
||||||
|
Name: "addon-forge",
|
||||||
|
Usage: "path to forge addon executable",
|
||||||
|
},
|
||||||
|
//
|
||||||
// GitHub
|
// GitHub
|
||||||
//
|
//
|
||||||
&cli.BoolFlag{
|
&cli.BoolFlag{
|
||||||
|
@ -316,24 +340,6 @@ var flags = append([]cli.Flag{
|
||||||
Name: "github",
|
Name: "github",
|
||||||
Usage: "github driver is enabled",
|
Usage: "github driver is enabled",
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
|
||||||
EnvVars: []string{"WOODPECKER_GITHUB_URL"},
|
|
||||||
Name: "github-server",
|
|
||||||
Usage: "github server address",
|
|
||||||
Value: "https://github.com",
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
EnvVars: []string{"WOODPECKER_GITHUB_CLIENT"},
|
|
||||||
Name: "github-client",
|
|
||||||
Usage: "github oauth2 client id",
|
|
||||||
FilePath: os.Getenv("WOODPECKER_GITHUB_CLIENT_FILE"),
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
EnvVars: []string{"WOODPECKER_GITHUB_SECRET"},
|
|
||||||
Name: "github-secret",
|
|
||||||
Usage: "github oauth2 client secret",
|
|
||||||
FilePath: os.Getenv("WOODPECKER_GITHUB_SECRET_FILE"),
|
|
||||||
},
|
|
||||||
&cli.BoolFlag{
|
&cli.BoolFlag{
|
||||||
EnvVars: []string{"WOODPECKER_GITHUB_MERGE_REF"},
|
EnvVars: []string{"WOODPECKER_GITHUB_MERGE_REF"},
|
||||||
Name: "github-merge-ref",
|
Name: "github-merge-ref",
|
||||||
|
@ -341,9 +347,10 @@ var flags = append([]cli.Flag{
|
||||||
Value: true,
|
Value: true,
|
||||||
},
|
},
|
||||||
&cli.BoolFlag{
|
&cli.BoolFlag{
|
||||||
EnvVars: []string{"WOODPECKER_GITHUB_SKIP_VERIFY"},
|
EnvVars: []string{"WOODPECKER_GITHUB_PUBLIC_ONLY"},
|
||||||
Name: "github-skip-verify",
|
Name: "github-public-only",
|
||||||
Usage: "github skip ssl verification",
|
Usage: "github tokens should only get access to public repos",
|
||||||
|
Value: false,
|
||||||
},
|
},
|
||||||
//
|
//
|
||||||
// Gitea
|
// Gitea
|
||||||
|
@ -353,29 +360,6 @@ var flags = append([]cli.Flag{
|
||||||
Name: "gitea",
|
Name: "gitea",
|
||||||
Usage: "gitea driver is enabled",
|
Usage: "gitea driver is enabled",
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
|
||||||
EnvVars: []string{"WOODPECKER_GITEA_URL"},
|
|
||||||
Name: "gitea-server",
|
|
||||||
Usage: "gitea server address",
|
|
||||||
Value: "https://try.gitea.io",
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
EnvVars: []string{"WOODPECKER_GITEA_CLIENT"},
|
|
||||||
Name: "gitea-client",
|
|
||||||
Usage: "gitea oauth2 client id",
|
|
||||||
FilePath: os.Getenv("WOODPECKER_GITEA_CLIENT_FILE"),
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
EnvVars: []string{"WOODPECKER_GITEA_SECRET"},
|
|
||||||
Name: "gitea-secret",
|
|
||||||
Usage: "gitea oauth2 client secret",
|
|
||||||
FilePath: os.Getenv("WOODPECKER_GITEA_SECRET_FILE"),
|
|
||||||
},
|
|
||||||
&cli.BoolFlag{
|
|
||||||
EnvVars: []string{"WOODPECKER_GITEA_SKIP_VERIFY"},
|
|
||||||
Name: "gitea-skip-verify",
|
|
||||||
Usage: "gitea skip ssl verification",
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
EnvVars: []string{"WOODPECKER_DEV_GITEA_OAUTH_URL"},
|
EnvVars: []string{"WOODPECKER_DEV_GITEA_OAUTH_URL"},
|
||||||
Name: "gitea-oauth-server",
|
Name: "gitea-oauth-server",
|
||||||
|
@ -389,18 +373,6 @@ var flags = append([]cli.Flag{
|
||||||
Name: "bitbucket",
|
Name: "bitbucket",
|
||||||
Usage: "bitbucket driver is enabled",
|
Usage: "bitbucket driver is enabled",
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
|
||||||
EnvVars: []string{"WOODPECKER_BITBUCKET_CLIENT"},
|
|
||||||
Name: "bitbucket-client",
|
|
||||||
Usage: "bitbucket oauth2 client id",
|
|
||||||
FilePath: os.Getenv("WOODPECKER_BITBUCKET_CLIENT_FILE"),
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
EnvVars: []string{"WOODPECKER_BITBUCKET_SECRET"},
|
|
||||||
Name: "bitbucket-secret",
|
|
||||||
Usage: "bitbucket oauth2 client secret",
|
|
||||||
FilePath: os.Getenv("WOODPECKER_BITBUCKET_SECRET_FILE"),
|
|
||||||
},
|
|
||||||
//
|
//
|
||||||
// Gitlab
|
// Gitlab
|
||||||
//
|
//
|
||||||
|
@ -409,29 +381,6 @@ var flags = append([]cli.Flag{
|
||||||
Name: "gitlab",
|
Name: "gitlab",
|
||||||
Usage: "gitlab driver is enabled",
|
Usage: "gitlab driver is enabled",
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
|
||||||
EnvVars: []string{"WOODPECKER_GITLAB_URL"},
|
|
||||||
Name: "gitlab-server",
|
|
||||||
Usage: "gitlab server address",
|
|
||||||
Value: "https://gitlab.com",
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
EnvVars: []string{"WOODPECKER_GITLAB_CLIENT"},
|
|
||||||
Name: "gitlab-client",
|
|
||||||
Usage: "gitlab oauth2 client id",
|
|
||||||
FilePath: os.Getenv("WOODPECKER_GITLAB_CLIENT_FILE"),
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
EnvVars: []string{"WOODPECKER_GITLAB_SECRET"},
|
|
||||||
Name: "gitlab-secret",
|
|
||||||
Usage: "gitlab oauth2 client secret",
|
|
||||||
FilePath: os.Getenv("WOODPECKER_GITLAB_SECRET_FILE"),
|
|
||||||
},
|
|
||||||
&cli.BoolFlag{
|
|
||||||
EnvVars: []string{"WOODPECKER_GITLAB_SKIP_VERIFY"},
|
|
||||||
Name: "gitlab-skip-verify",
|
|
||||||
Usage: "gitlab skip ssl verification",
|
|
||||||
},
|
|
||||||
//
|
//
|
||||||
// Bitbucket DataCenter/Server (previously Stash)
|
// Bitbucket DataCenter/Server (previously Stash)
|
||||||
//
|
//
|
||||||
|
@ -440,23 +389,6 @@ var flags = append([]cli.Flag{
|
||||||
Name: "bitbucket-dc",
|
Name: "bitbucket-dc",
|
||||||
Usage: "Bitbucket DataCenter/Server driver is enabled",
|
Usage: "Bitbucket DataCenter/Server driver is enabled",
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
|
||||||
EnvVars: []string{"WOODPECKER_BITBUCKET_DC_URL"},
|
|
||||||
Name: "bitbucket-dc-server",
|
|
||||||
Usage: "Bitbucket DataCenter/Server server address",
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
EnvVars: []string{"WOODPECKER_BITBUCKET_DC_CLIENT_ID"},
|
|
||||||
Name: "bitbucket-dc-client-id",
|
|
||||||
Usage: "Bitbucket DataCenter/Server OAuth 2.0 client id",
|
|
||||||
FilePath: os.Getenv("WOODPECKER_BITBUCKET_DC_CLIENT_ID_FILE"),
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
EnvVars: []string{"WOODPECKER_BITBUCKET_DC_CLIENT_SECRET"},
|
|
||||||
Name: "bitbucket-dc-client-secret",
|
|
||||||
Usage: "Bitbucket DataCenter/Server OAuth 2.0 client secret",
|
|
||||||
FilePath: os.Getenv("WOODPECKER_BITBUCKET_DC_CLIENT_SECRET_FILE"),
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
EnvVars: []string{"WOODPECKER_BITBUCKET_DC_GIT_USERNAME"},
|
EnvVars: []string{"WOODPECKER_BITBUCKET_DC_GIT_USERNAME"},
|
||||||
Name: "bitbucket-dc-git-username",
|
Name: "bitbucket-dc-git-username",
|
||||||
|
|
|
@ -38,7 +38,7 @@ import (
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/pipeline/rpc/proto"
|
"go.woodpecker-ci.org/woodpecker/v2/pipeline/rpc/proto"
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/server"
|
"go.woodpecker-ci.org/woodpecker/v2/server"
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/server/cron"
|
"go.woodpecker-ci.org/woodpecker/v2/server/cron"
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/server/forge"
|
"go.woodpecker-ci.org/woodpecker/v2/server/forge/setup"
|
||||||
woodpeckerGrpcServer "go.woodpecker-ci.org/woodpecker/v2/server/grpc"
|
woodpeckerGrpcServer "go.woodpecker-ci.org/woodpecker/v2/server/grpc"
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/server/logging"
|
"go.woodpecker-ci.org/woodpecker/v2/server/logging"
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/server/model"
|
"go.woodpecker-ci.org/woodpecker/v2/server/model"
|
||||||
|
@ -82,11 +82,6 @@ func run(c *cli.Context) error {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
_forge, err := setupForge(c)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("can't setup forge: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_store, err := setupStore(c)
|
_store, err := setupStore(c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("can't setup store: %w", err)
|
return fmt.Errorf("can't setup store: %w", err)
|
||||||
|
@ -97,7 +92,7 @@ func run(c *cli.Context) error {
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
err = setupEvilGlobals(c, _store, _forge)
|
err = setupEvilGlobals(c, _store)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("can't setup globals: %w", err)
|
return fmt.Errorf("can't setup globals: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -107,7 +102,7 @@ func run(c *cli.Context) error {
|
||||||
setupMetrics(&g, _store)
|
setupMetrics(&g, _store)
|
||||||
|
|
||||||
g.Go(func() error {
|
g.Go(func() error {
|
||||||
return cron.Start(c.Context, _store, _forge)
|
return cron.Start(c.Context, _store)
|
||||||
})
|
})
|
||||||
|
|
||||||
// start the grpc server
|
// start the grpc server
|
||||||
|
@ -130,7 +125,6 @@ func run(c *cli.Context) error {
|
||||||
)
|
)
|
||||||
|
|
||||||
woodpeckerServer := woodpeckerGrpcServer.NewWoodpeckerServer(
|
woodpeckerServer := woodpeckerGrpcServer.NewWoodpeckerServer(
|
||||||
_forge,
|
|
||||||
server.Config.Services.Queue,
|
server.Config.Services.Queue,
|
||||||
server.Config.Services.Logs,
|
server.Config.Services.Logs,
|
||||||
server.Config.Services.Pubsub,
|
server.Config.Services.Pubsub,
|
||||||
|
@ -270,17 +264,13 @@ func run(c *cli.Context) error {
|
||||||
return g.Wait()
|
return g.Wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
func setupEvilGlobals(c *cli.Context, s store.Store, f forge.Forge) error {
|
func setupEvilGlobals(c *cli.Context, s store.Store) error {
|
||||||
// forge
|
|
||||||
server.Config.Services.Forge = f
|
|
||||||
|
|
||||||
// services
|
// services
|
||||||
server.Config.Services.Queue = setupQueue(c, s)
|
server.Config.Services.Queue = setupQueue(c, s)
|
||||||
server.Config.Services.Logs = logging.New()
|
server.Config.Services.Logs = logging.New()
|
||||||
server.Config.Services.Pubsub = pubsub.New()
|
server.Config.Services.Pubsub = pubsub.New()
|
||||||
server.Config.Services.Membership = setupMembershipService(c, f)
|
server.Config.Services.Membership = setupMembershipService(c, s)
|
||||||
|
serviceMangager, err := services.NewManager(c, s, setup.Forge)
|
||||||
serviceMangager, err := services.NewManager(c, s)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("could not setup service manager: %w", err)
|
return fmt.Errorf("could not setup service manager: %w", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,9 +18,7 @@ package main
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/url"
|
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
|
@ -31,17 +29,9 @@ import (
|
||||||
|
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/server"
|
"go.woodpecker-ci.org/woodpecker/v2/server"
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/server/cache"
|
"go.woodpecker-ci.org/woodpecker/v2/server/cache"
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/server/forge"
|
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/server/forge/bitbucket"
|
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/server/forge/bitbucketdatacenter"
|
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/server/forge/gitea"
|
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/server/forge/github"
|
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/server/forge/gitlab"
|
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/server/queue"
|
"go.woodpecker-ci.org/woodpecker/v2/server/queue"
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/server/store"
|
"go.woodpecker-ci.org/woodpecker/v2/server/store"
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/server/store/datastore"
|
"go.woodpecker-ci.org/woodpecker/v2/server/store/datastore"
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/shared/addon"
|
|
||||||
addonTypes "go.woodpecker-ci.org/woodpecker/v2/shared/addon/types"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func setupStore(c *cli.Context) (store.Store, error) {
|
func setupStore(c *cli.Context) (store.Store, error) {
|
||||||
|
@ -101,108 +91,8 @@ func setupQueue(c *cli.Context, s store.Store) queue.Queue {
|
||||||
return queue.WithTaskStore(queue.New(c.Context), s)
|
return queue.WithTaskStore(queue.New(c.Context), s)
|
||||||
}
|
}
|
||||||
|
|
||||||
func setupMembershipService(_ *cli.Context, r forge.Forge) cache.MembershipService {
|
func setupMembershipService(_ *cli.Context, _store store.Store) cache.MembershipService {
|
||||||
return cache.NewMembershipService(r)
|
return cache.NewMembershipService(_store)
|
||||||
}
|
|
||||||
|
|
||||||
// setupForge helper function to set up the forge from the CLI arguments.
|
|
||||||
func setupForge(c *cli.Context) (forge.Forge, error) {
|
|
||||||
addonForge, err := addon.Load[forge.Forge](c.StringSlice("addons"), addonTypes.TypeForge)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if addonForge != nil {
|
|
||||||
return addonForge.Value, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
switch {
|
|
||||||
case c.Bool("github"):
|
|
||||||
return setupGitHub(c)
|
|
||||||
case c.Bool("gitlab"):
|
|
||||||
return setupGitLab(c)
|
|
||||||
case c.Bool("bitbucket"):
|
|
||||||
return setupBitbucket(c)
|
|
||||||
case c.Bool("bitbucket-dc"):
|
|
||||||
return setupBitbucketDatacenter(c)
|
|
||||||
case c.Bool("gitea"):
|
|
||||||
return setupGitea(c)
|
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf("version control system not configured")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// setupBitbucket helper function to setup the Bitbucket forge from the CLI arguments.
|
|
||||||
func setupBitbucket(c *cli.Context) (forge.Forge, error) {
|
|
||||||
opts := &bitbucket.Opts{
|
|
||||||
Client: c.String("bitbucket-client"),
|
|
||||||
Secret: c.String("bitbucket-secret"),
|
|
||||||
}
|
|
||||||
log.Trace().Msgf("forge (bitbucket) opts: %#v", opts)
|
|
||||||
return bitbucket.New(opts)
|
|
||||||
}
|
|
||||||
|
|
||||||
// setupGitea helper function to set up the Gitea forge from the CLI arguments.
|
|
||||||
func setupGitea(c *cli.Context) (forge.Forge, error) {
|
|
||||||
server, err := url.Parse(c.String("gitea-server"))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
oauth2Server := c.String("gitea-oauth-server")
|
|
||||||
if oauth2Server != "" {
|
|
||||||
oauth2URL, err := url.Parse(oauth2Server)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
oauth2Server = strings.TrimRight(oauth2URL.String(), "/")
|
|
||||||
}
|
|
||||||
opts := gitea.Opts{
|
|
||||||
URL: strings.TrimRight(server.String(), "/"),
|
|
||||||
OAuth2URL: oauth2Server,
|
|
||||||
Client: c.String("gitea-client"),
|
|
||||||
Secret: c.String("gitea-secret"),
|
|
||||||
SkipVerify: c.Bool("gitea-skip-verify"),
|
|
||||||
}
|
|
||||||
if len(opts.URL) == 0 {
|
|
||||||
return nil, fmt.Errorf("WOODPECKER_GITEA_URL must be set")
|
|
||||||
}
|
|
||||||
log.Trace().Msgf("forge (gitea) opts: %#v", opts)
|
|
||||||
return gitea.New(opts)
|
|
||||||
}
|
|
||||||
|
|
||||||
// setupBitbucketDatacenter helper function to setup the Bitbucket DataCenter/Server forge from the CLI arguments.
|
|
||||||
func setupBitbucketDatacenter(c *cli.Context) (forge.Forge, error) {
|
|
||||||
opts := bitbucketdatacenter.Opts{
|
|
||||||
URL: c.String("bitbucket-dc-server"),
|
|
||||||
Username: c.String("bitbucket-dc-git-username"),
|
|
||||||
Password: c.String("bitbucket-dc-git-password"),
|
|
||||||
ClientID: c.String("bitbucket-dc-client-id"),
|
|
||||||
ClientSecret: c.String("bitbucket-dc-client-secret"),
|
|
||||||
}
|
|
||||||
log.Trace().Msgf("Forge (bitbucketdatacenter) opts: %#v", opts)
|
|
||||||
return bitbucketdatacenter.New(opts)
|
|
||||||
}
|
|
||||||
|
|
||||||
// setupGitLab helper function to setup the GitLab forge from the CLI arguments.
|
|
||||||
func setupGitLab(c *cli.Context) (forge.Forge, error) {
|
|
||||||
return gitlab.New(gitlab.Opts{
|
|
||||||
URL: c.String("gitlab-server"),
|
|
||||||
ClientID: c.String("gitlab-client"),
|
|
||||||
ClientSecret: c.String("gitlab-secret"),
|
|
||||||
SkipVerify: c.Bool("gitlab-skip-verify"),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// setupGitHub helper function to setup the GitHub forge from the CLI arguments.
|
|
||||||
func setupGitHub(c *cli.Context) (forge.Forge, error) {
|
|
||||||
opts := github.Opts{
|
|
||||||
URL: c.String("github-server"),
|
|
||||||
Client: c.String("github-client"),
|
|
||||||
Secret: c.String("github-secret"),
|
|
||||||
SkipVerify: c.Bool("github-skip-verify"),
|
|
||||||
MergeRef: c.Bool("github-merge-ref"),
|
|
||||||
}
|
|
||||||
log.Trace().Msgf("forge (github) opts: %#v", opts)
|
|
||||||
return github.New(opts)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func setupMetrics(g *errgroup.Group, _store store.Store) {
|
func setupMetrics(g *errgroup.Group, _store store.Store) {
|
||||||
|
|
|
@ -1,17 +1,19 @@
|
||||||
# docker build --rm -f docker/Dockerfile.make -t woodpecker/make:local .
|
# docker build --rm -f docker/Dockerfile.make -t woodpecker/make:local .
|
||||||
FROM docker.io/golang:1.22-alpine3.18 as golang_image
|
FROM docker.io/golang:1.22-alpine3.19 as golang_image
|
||||||
FROM docker.io/node:21-alpine3.18
|
FROM docker.io/node:21-alpine3.19
|
||||||
|
|
||||||
# renovate: datasource=repology depName=alpine_3_18/make versioning=loose
|
# renovate: datasource=repology depName=alpine_3_19/make versioning=loose
|
||||||
ENV MAKE_VERSION="4.4.1-r1"
|
ENV MAKE_VERSION="4.4.1-r2"
|
||||||
# renovate: datasource=repology depName=alpine_3_18/gcc versioning=loose
|
# renovate: datasource=repology depName=alpine_3_19/gcc versioning=loose
|
||||||
ENV GCC_VERSION="12.2.1_git20220924-r10"
|
ENV GCC_VERSION="13.2.1_git20231014-r0"
|
||||||
# renovate: datasource=repology depName=alpine_3_18/binutils-gold versioning=loose
|
# renovate: datasource=repology depName=alpine_3_19/binutils-gold versioning=loose
|
||||||
ENV BINUTILS_GOLD_VERSION="2.40-r7"
|
ENV BINUTILS_GOLD_VERSION="2.41-r0"
|
||||||
# renovate: datasource=repology depName=alpine_3_18/musl-dev versioning=loose
|
# renovate: datasource=repology depName=alpine_3_19/musl-dev versioning=loose
|
||||||
ENV MUSL_DEV_VERSION="1.2.4-r2"
|
ENV MUSL_DEV_VERSION="1.2.4_git20230717-r4"
|
||||||
|
# renovate: datasource=repology depName=alpine_3_19/protoc versioning=loose
|
||||||
|
ENV PROTOC_VERSION="24.4-r0"
|
||||||
|
|
||||||
RUN apk add --no-cache --update make=${MAKE_VERSION} gcc=${GCC_VERSION} binutils-gold=${BINUTILS_GOLD_VERSION} musl-dev=${MUSL_DEV_VERSION} && \
|
RUN apk add --no-cache --update make=${MAKE_VERSION} gcc=${GCC_VERSION} binutils-gold=${BINUTILS_GOLD_VERSION} musl-dev=${MUSL_DEV_VERSION} protoc=${PROTOC_VERSION} && \
|
||||||
corepack enable
|
corepack enable
|
||||||
|
|
||||||
# Build packages.
|
# Build packages.
|
||||||
|
@ -20,9 +22,12 @@ COPY Makefile /
|
||||||
ENV PATH=$PATH:/usr/local/go/bin
|
ENV PATH=$PATH:/usr/local/go/bin
|
||||||
|
|
||||||
# Cache tools
|
# Cache tools
|
||||||
RUN make install-tools && \
|
RUN GOBIN=/usr/local/go/bin make install-tools && \
|
||||||
mv /root/go/bin/* /usr/local/go/bin/ && \
|
rm -rf /Makefile
|
||||||
chmod 755 /usr/local/go/bin/*
|
|
||||||
|
ENV GOPATH=/tmp/go
|
||||||
|
ENV HOME=/tmp/home
|
||||||
|
ENV PATH=$PATH:/usr/local/go/bin:/tmp/go/bin
|
||||||
|
|
||||||
WORKDIR /build
|
WORKDIR /build
|
||||||
RUN chmod -R 777 /root
|
RUN chmod -R 777 /root
|
||||||
|
|
|
@ -75,7 +75,7 @@ kubectl apply -f $PLUGIN_TEMPLATE
|
||||||
|
|
||||||
```yaml title=".woodpecker.yaml"
|
```yaml title=".woodpecker.yaml"
|
||||||
steps:
|
steps:
|
||||||
deploy-to-k8s:
|
- name: deploy-to-k8s
|
||||||
image: laszlocloud/my-k8s-plugin
|
image: laszlocloud/my-k8s-plugin
|
||||||
settings:
|
settings:
|
||||||
template: config/k8s/service.yaml
|
template: config/k8s/service.yaml
|
||||||
|
|
|
@ -50,7 +50,8 @@ git commit -m "updated README [CI SKIP]"
|
||||||
|
|
||||||
## Steps
|
## Steps
|
||||||
|
|
||||||
Every step of your workflow executes commands inside a specified container. The defined commands are executed serially.
|
Every step of your workflow executes commands inside a specified container.<br>
|
||||||
|
The defined steps are executed in sequence by default, if they should run in parallel you can use [`depends_on`](./20-workflow-syntax.md#depends_on).<br>
|
||||||
The associated commit is checked out with git to a workspace which is mounted to every step of the workflow as the working directory.
|
The associated commit is checked out with git to a workspace which is mounted to every step of the workflow as the working directory.
|
||||||
|
|
||||||
```diff
|
```diff
|
||||||
|
@ -484,6 +485,19 @@ Normally steps of a workflow are executed serially in the order in which they ar
|
||||||
- go test
|
- go test
|
||||||
```
|
```
|
||||||
|
|
||||||
|
:::note
|
||||||
|
You can define a step to start immediately without dependencies by adding an empty `depends_on: []`. By setting `depends_on` on a single step all other steps will be immediately executed as well if no further dependencies are specified.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
steps:
|
||||||
|
- name: check code format
|
||||||
|
image: mstruebing/editorconfig-checker
|
||||||
|
depends_on: [] # enable parallel steps
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
### `volumes`
|
### `volumes`
|
||||||
|
|
||||||
Woodpecker gives the ability to define Docker volumes in the YAML. You can use this parameter to mount files or folders on the host machine into your containers.
|
Woodpecker gives the ability to define Docker volumes in the YAML. You can use this parameter to mount files or folders on the host machine into your containers.
|
||||||
|
|
|
@ -135,5 +135,5 @@ docker run --rm \
|
||||||
These should also be built for different OS/architectures.
|
These should also be built for different OS/architectures.
|
||||||
- Use [built-in env vars](../50-environment.md#built-in-environment-variables) where possible.
|
- Use [built-in env vars](../50-environment.md#built-in-environment-variables) where possible.
|
||||||
- Do not use any configuration except settings (and internal env vars). This means: Don't require using [`environment`](../50-environment.md) and don't require specific secret names.
|
- Do not use any configuration except settings (and internal env vars). This means: Don't require using [`environment`](../50-environment.md) and don't require specific secret names.
|
||||||
- Add a `docs.md` file, listing all your settings and plugin metadata ([example](https://codeberg.org/woodpecker-plugins/plugin-docker-buildx/src/branch/main/docs.md)).
|
- Add a `docs.md` file, listing all your settings and plugin metadata ([example](https://github.com/woodpecker-ci/plugin-git/blob/main/docs.md)).
|
||||||
- Add your plugin to the [plugin index](/plugins) using your `docs.md` ([the example above in the index](https://woodpecker-ci.org/plugins/Docker%20Buildx)).
|
- Add your plugin to the [plugin index](/plugins) using your `docs.md` ([the example above in the index](https://woodpecker-ci.org/plugins/Git%20Clone)).
|
||||||
|
|
|
@ -16,6 +16,15 @@ Your Version-Control-System will notify Woodpecker about events via webhooks. If
|
||||||
|
|
||||||
Enables handling webhook's pull request event. If disabled, then pipeline won't run for pull requests.
|
Enables handling webhook's pull request event. If disabled, then pipeline won't run for pull requests.
|
||||||
|
|
||||||
|
## Allow deployments
|
||||||
|
|
||||||
|
Enables a pipeline to be started with the `deploy` event from a successful pipeline.
|
||||||
|
|
||||||
|
:::danger
|
||||||
|
Only activate this option if you trust all users who have push access to your repository.
|
||||||
|
Otherwise, these users will be able to steal secrets that are only available for `deploy` events.
|
||||||
|
:::
|
||||||
|
|
||||||
## Protected
|
## Protected
|
||||||
|
|
||||||
Every pipeline initiated by an webhook event needs to be approved by a project members with push permissions before being executed.
|
Every pipeline initiated by an webhook event needs to be approved by a project members with push permissions before being executed.
|
||||||
|
|
|
@ -473,12 +473,6 @@ Supported variables:
|
||||||
- `owner`: the repo's owner
|
- `owner`: the repo's owner
|
||||||
- `repo`: the repo's name
|
- `repo`: the repo's name
|
||||||
|
|
||||||
### `WOODPECKER_ADDONS`
|
|
||||||
|
|
||||||
> Default: empty
|
|
||||||
|
|
||||||
List of addon files. See [addons](./75-addons/75-overview.md).
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### `WOODPECKER_LIMIT_MEM_SWAP`
|
### `WOODPECKER_LIMIT_MEM_SWAP`
|
||||||
|
@ -559,4 +553,8 @@ See [Bitbucket configuration](./11-forges/50-bitbucket.md#configuration)
|
||||||
|
|
||||||
### `WOODPECKER_GITLAB_...`
|
### `WOODPECKER_GITLAB_...`
|
||||||
|
|
||||||
See [Gitlab configuration](./11-forges/40-gitlab.md#configuration)
|
See [GitLab configuration](./11-forges/40-gitlab.md#configuration)
|
||||||
|
|
||||||
|
### `WOODPECKER_ADDON_FORGE`
|
||||||
|
|
||||||
|
See [addon forges](./11-forges/100-addon.md).
|
||||||
|
|
68
docs/docs/30-administration/11-forges/100-addon.md
Normal file
68
docs/docs/30-administration/11-forges/100-addon.md
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
# Addon forges
|
||||||
|
|
||||||
|
If the forge you're using does not comply with [Woodpecker's requirements](../../92-development/02-core-ideas.md#forge) or your setup is too specific to be added to Woodpecker's core, you can write your own forge using an addon forge.
|
||||||
|
|
||||||
|
:::warning
|
||||||
|
Addon forges are still experimental. Their implementation can change and break at any time.
|
||||||
|
:::
|
||||||
|
|
||||||
|
:::danger
|
||||||
|
You need to trust the author of the addon forge you use. It can access authentication codes and other possibly sensitive information.
|
||||||
|
:::
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
To use an addon forge, download the correct addon version. Then, you can add the following to your configuration:
|
||||||
|
|
||||||
|
```ini
|
||||||
|
WOODPECKER_ADDON_FORGE=/path/to/your/addon/forge/file
|
||||||
|
```
|
||||||
|
|
||||||
|
In case you run Woodpecker as container, you probably want to mount the addon binary to `/opt/addons/`.
|
||||||
|
|
||||||
|
### Bug reports
|
||||||
|
|
||||||
|
If you experience bugs, please check which component has the issue. If it's the addon, **do not raise an issue in the main repository**, but rather use the separate addon repositories. To check which component is responsible for the bug, look at the logs. Logs from addons are marked with a special field `addon` containing their addon file name.
|
||||||
|
|
||||||
|
## List of addon forges
|
||||||
|
|
||||||
|
If you wrote or found an addon forge, please add it here so others can find it!
|
||||||
|
|
||||||
|
_Be the first one to add your addon forge!_
|
||||||
|
|
||||||
|
## Creating addon forges
|
||||||
|
|
||||||
|
Addons use RPC to communicate to the server and are implemented using the [`go-plugin` library](https://github.com/hashicorp/go-plugin).
|
||||||
|
|
||||||
|
### Writing your code
|
||||||
|
|
||||||
|
This example will use the Go language.
|
||||||
|
|
||||||
|
Directly import Woodpecker's Go packages (`go.woodpecker-ci.org/woodpecker/woodpecker/v2`) and use the interfaces and types defined there.
|
||||||
|
|
||||||
|
In the `main` function, just call `"go.woodpecker-ci.org/woodpecker/v2/server/forge/addon".Serve` with a `"go.woodpecker-ci.org/woodpecker/v2/server/forge".Forge` as argument.
|
||||||
|
This will take care of connecting the addon forge to the server.
|
||||||
|
|
||||||
|
### Example structure
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"go.woodpecker-ci.org/woodpecker/v2/server/forge/addon"
|
||||||
|
forgeTypes "go.woodpecker-ci.org/woodpecker/v2/server/forge/types"
|
||||||
|
"go.woodpecker-ci.org/woodpecker/v2/server/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
addon.Serve(config{})
|
||||||
|
}
|
||||||
|
|
||||||
|
type config struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
// `config` must implement `"go.woodpecker-ci.org/woodpecker/v2/server/forge".Forge`. You must directly use Woodpecker's packages - see imports above.
|
||||||
|
```
|
|
@ -81,3 +81,9 @@ Read the value for `WOODPECKER_GITHUB_SECRET` from the specified filepath.
|
||||||
> Default: `false`
|
> Default: `false`
|
||||||
|
|
||||||
Configure if SSL verification should be skipped.
|
Configure if SSL verification should be skipped.
|
||||||
|
|
||||||
|
### `WOODPECKER_GITHUB_PUBLIC_ONLY`
|
||||||
|
|
||||||
|
> Default: `false`
|
||||||
|
|
||||||
|
Configures the GitHub OAuth client to only obtain a token that can manage public repositories.
|
||||||
|
|
|
@ -10,11 +10,10 @@ Woodpecker comes with experimental support for Bitbucket Datacenter / Server, fo
|
||||||
|
|
||||||
To enable Bitbucket Server you should configure the Woodpecker container using the following environment variables:
|
To enable Bitbucket Server you should configure the Woodpecker container using the following environment variables:
|
||||||
|
|
||||||
```diff
|
```diff title="docker-compose.yaml"
|
||||||
# docker-compose.yml
|
version: '3'
|
||||||
version: '3'
|
|
||||||
|
|
||||||
services:
|
services:
|
||||||
woodpecker-server:
|
woodpecker-server:
|
||||||
[...]
|
[...]
|
||||||
environment:
|
environment:
|
||||||
|
|
|
@ -40,6 +40,11 @@ steps:
|
||||||
|
|
||||||
You can use [Limit Ranges](https://kubernetes.io/docs/concepts/policy/limit-range/) if you want to set the limits by per-namespace basis.
|
You can use [Limit Ranges](https://kubernetes.io/docs/concepts/policy/limit-range/) if you want to set the limits by per-namespace basis.
|
||||||
|
|
||||||
|
### Runtime class
|
||||||
|
|
||||||
|
`runtimeClassName` specifies the name of the RuntimeClass which will be used to run this pod. If no `runtimeClassName` is specified, the default RuntimeHandler will be used.
|
||||||
|
See the [kubernetes documentation](https://kubernetes.io/docs/concepts/containers/runtime-class/) for more information on specifying runtime classes.
|
||||||
|
|
||||||
### Service account
|
### Service account
|
||||||
|
|
||||||
`serviceAccountName` specifies the name of the ServiceAccount which the pod will mount. This service account must be created externally.
|
`serviceAccountName` specifies the name of the ServiceAccount which the pod will mount. This service account must be created externally.
|
||||||
|
|
|
@ -1,97 +0,0 @@
|
||||||
# Creating addons
|
|
||||||
|
|
||||||
Addons are written in Go.
|
|
||||||
|
|
||||||
## Writing your code
|
|
||||||
|
|
||||||
An addon consists of two variables/functions in Go.
|
|
||||||
|
|
||||||
1. The `Type` variable. Specifies the type of the addon and must be directly accessed from `shared/addons/types/types.go`.
|
|
||||||
2. The `Addon` function which is the main point of your addon.
|
|
||||||
This function takes the `zerolog` logger you should use to log errors, warnings, etc. as argument.
|
|
||||||
|
|
||||||
It returns two values:
|
|
||||||
|
|
||||||
1. The actual addon. For type reference see [table below](#return-types).
|
|
||||||
2. An error. If this error is not `nil`, Woodpecker exits.
|
|
||||||
|
|
||||||
Directly import Woodpecker's Go package (`go.woodpecker-ci.org/woodpecker/woodpecker/v2`) and use the interfaces and types defined there.
|
|
||||||
|
|
||||||
### Return types
|
|
||||||
|
|
||||||
| Addon type | Return type |
|
|
||||||
| ---------- | -------------------------------------------------------------------- |
|
|
||||||
| `Forge` | `"go.woodpecker-ci.org/woodpecker/woodpecker/v2/server/forge".Forge` |
|
|
||||||
|
|
||||||
### Using configurations
|
|
||||||
|
|
||||||
If you write a plugin for the server (`Forge` and the services), you can access the server config.
|
|
||||||
|
|
||||||
Therefore, use the `"go.woodpecker-ci.org/woodpecker/v2/server".Config` variable.
|
|
||||||
|
|
||||||
:::warning
|
|
||||||
The config is not available when your addon is initialized, i.e., the `Addon` function is called.
|
|
||||||
Only use the config in the interface methods.
|
|
||||||
:::
|
|
||||||
|
|
||||||
## Compiling
|
|
||||||
|
|
||||||
After you write your addon code, compile your addon:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
go build -buildmode plugin
|
|
||||||
```
|
|
||||||
|
|
||||||
The output file is your addon that is now ready to be used.
|
|
||||||
|
|
||||||
## Restrictions
|
|
||||||
|
|
||||||
Addons must directly depend on Woodpecker's core (`go.woodpecker-ci.org/woodpecker/woodpecker/v2`).
|
|
||||||
The addon must have been built with **exactly the same code** as the Woodpecker instance you'd like to use it on. This means: If you build your addon with a specific commit from Woodpecker `next`, you can likely only use it with the Woodpecker version compiled from this commit.
|
|
||||||
Also, if you change something inside Woodpecker without committing, it might fail because you need to recompile your addon with this code first.
|
|
||||||
|
|
||||||
In addition to this, addons are only supported on Linux, FreeBSD, and macOS.
|
|
||||||
|
|
||||||
:::info
|
|
||||||
It is recommended to at least support the latest version of Woodpecker.
|
|
||||||
:::
|
|
||||||
|
|
||||||
### Compile for different versions
|
|
||||||
|
|
||||||
As long as there are no changes to Woodpecker's interfaces,
|
|
||||||
or they are backwards-compatible, you can compile the addon for multiple versions
|
|
||||||
by changing the version of `go.woodpecker-ci.org/woodpecker/woodpecker/v2` using `go get` before compiling.
|
|
||||||
|
|
||||||
## Logging
|
|
||||||
|
|
||||||
The entrypoint receives a `zerolog.Logger` as input. **Do not use any other logging solution.** This logger follows the configuration of the Woodpecker instance and adds a special field `addon` to the log entries which allows users to find out which component is writing the log messages.
|
|
||||||
|
|
||||||
## Example structure
|
|
||||||
|
|
||||||
```go
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"github.com/rs/zerolog"
|
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/server/forge"
|
|
||||||
forge_types "go.woodpecker-ci.org/woodpecker/v2/server/forge/types"
|
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/server/model"
|
|
||||||
addon_types "go.woodpecker-ci.org/woodpecker/v2/shared/addon/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
var Type = addon_types.TypeForge
|
|
||||||
|
|
||||||
func Addon(logger zerolog.Logger) (forge.Forge, error) {
|
|
||||||
logger.Info().Msg("hello world from addon")
|
|
||||||
return &config{l: logger}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type config struct {
|
|
||||||
l zerolog.Logger
|
|
||||||
}
|
|
||||||
|
|
||||||
// In this case, `config` must implement `forge.Forge`. You must directly use Woodpecker's packages - see imports above.
|
|
||||||
```
|
|
|
@ -1,40 +0,0 @@
|
||||||
# Addons
|
|
||||||
|
|
||||||
:::warning
|
|
||||||
Addons are still experimental. Their implementation can change and break at any time.
|
|
||||||
:::
|
|
||||||
|
|
||||||
:::danger
|
|
||||||
You need to trust the author of the addons you use. Depending on their type, addons can access forge authentication codes, your secrets or other sensitive information.
|
|
||||||
:::
|
|
||||||
|
|
||||||
To adapt Woodpecker to your needs beyond the [configuration](../10-server-config.md), Woodpecker has its own **addon** system, built ontop of [Go's internal plugin system](https://go.dev/pkg/plugin).
|
|
||||||
|
|
||||||
Addons can be used for:
|
|
||||||
|
|
||||||
- Forges
|
|
||||||
|
|
||||||
## Restrictions
|
|
||||||
|
|
||||||
Addons are restricted by how Go plugins work. This includes the following restrictions:
|
|
||||||
|
|
||||||
- only supported on Linux, FreeBSD, and macOS
|
|
||||||
- addons must have been built for the correct Woodpecker version. If an addon is not provided specifically for this version, you likely won't be able to use it.
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
To use an addon, download the addon version built for your Woodpecker version. Then, you can add the following to your configuration:
|
|
||||||
|
|
||||||
```ini
|
|
||||||
WOODPECKER_ADDONS=/path/to/your/addon/file.so
|
|
||||||
```
|
|
||||||
|
|
||||||
In case you run Woodpecker as container, you probably want to mount the addon binaries to `/opt/addons/`.
|
|
||||||
|
|
||||||
You can list multiple addons, Woodpecker will automatically determine their type. If you specify multiple addons with the same type, only the first one will be used.
|
|
||||||
|
|
||||||
Using an addon always overwrites Woodpecker's internal setup. This means, that a forge addon will be used if specified, no matter what's configured for the forges natively supported by Woodpecker.
|
|
||||||
|
|
||||||
### Bug reports
|
|
||||||
|
|
||||||
If you experience bugs, please check which component has the issue. If it's the addon, **do not raise an issue in the main repository**, but rather use the separate addon repositories. To check which component is responsible for the bug, look at the logs. Logs from addons are marked with a special field `addon` containing their addon file name.
|
|
|
@ -1,6 +0,0 @@
|
||||||
label: 'Addons'
|
|
||||||
collapsible: true
|
|
||||||
collapsed: true
|
|
||||||
link:
|
|
||||||
type: 'doc'
|
|
||||||
id: 'overview'
|
|
|
@ -8,7 +8,7 @@
|
||||||
## Addons and extensions
|
## Addons and extensions
|
||||||
|
|
||||||
If you are wondering whether your contribution will be accepted to be merged in the Woodpecker core, or whether it's better to write an
|
If you are wondering whether your contribution will be accepted to be merged in the Woodpecker core, or whether it's better to write an
|
||||||
[addon](../30-administration/75-addons/75-overview.md), [extension](../30-administration/100-external-configuration-api.md) or an
|
[addon forge](../30-administration/11-forges/100-addon.md), [extension](../30-administration/100-external-configuration-api.md) or an
|
||||||
[external custom backend](../30-administration/22-backends/50-custom-backends.md), please check these points:
|
[external custom backend](../30-administration/22-backends/50-custom-backends.md), please check these points:
|
||||||
|
|
||||||
- Is your change very specific to your setup and unlikely to be used by anyone else?
|
- Is your change very specific to your setup and unlikely to be used by anyone else?
|
||||||
|
|
|
@ -194,6 +194,11 @@
|
||||||
"name": "Forge deployments",
|
"name": "Forge deployments",
|
||||||
"docs": "https://raw.githubusercontent.com/woodpecker-ci/plugin-deployments/main/docs.md",
|
"docs": "https://raw.githubusercontent.com/woodpecker-ci/plugin-deployments/main/docs.md",
|
||||||
"verified": true
|
"verified": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Twine",
|
||||||
|
"docs": "https://gitea.elara.ws/music-kraken/plugin-twine/raw/branch/master/docs.md",
|
||||||
|
"verified": false
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,13 +20,13 @@ importers:
|
||||||
version: 3.1.1(react-dom@18.2.0)(react@18.2.0)(typescript@5.4.3)
|
version: 3.1.1(react-dom@18.2.0)(react@18.2.0)(typescript@5.4.3)
|
||||||
'@docusaurus/preset-classic':
|
'@docusaurus/preset-classic':
|
||||||
specifier: ^3.1.0
|
specifier: ^3.1.0
|
||||||
version: 3.1.1(@algolia/client-search@4.22.1)(@types/react@18.2.69)(react-dom@18.2.0)(react@18.2.0)(search-insights@2.13.0)(typescript@5.4.3)
|
version: 3.1.1(@algolia/client-search@4.22.1)(@types/react@18.2.70)(react-dom@18.2.0)(react@18.2.0)(search-insights@2.13.0)(typescript@5.4.3)
|
||||||
'@easyops-cn/docusaurus-search-local':
|
'@easyops-cn/docusaurus-search-local':
|
||||||
specifier: ^0.40.1
|
specifier: ^0.40.1
|
||||||
version: 0.40.1(@docusaurus/theme-common@3.1.1)(@docusaurus/types@3.1.1)(react-dom@18.2.0)(react@18.2.0)(typescript@5.4.3)
|
version: 0.40.1(@docusaurus/theme-common@3.1.1)(@docusaurus/types@3.1.1)(react-dom@18.2.0)(react@18.2.0)(typescript@5.4.3)
|
||||||
'@mdx-js/react':
|
'@mdx-js/react':
|
||||||
specifier: ^3.0.0
|
specifier: ^3.0.0
|
||||||
version: 3.0.1(@types/react@18.2.69)(react@18.2.0)
|
version: 3.0.1(@types/react@18.2.70)(react@18.2.0)
|
||||||
'@svgr/webpack':
|
'@svgr/webpack':
|
||||||
specifier: ^8.1.0
|
specifier: ^8.1.0
|
||||||
version: 8.1.0(typescript@5.4.3)
|
version: 8.1.0(typescript@5.4.3)
|
||||||
|
@ -69,7 +69,7 @@ importers:
|
||||||
version: 20.11.30
|
version: 20.11.30
|
||||||
'@types/react':
|
'@types/react':
|
||||||
specifier: ^18.2.67
|
specifier: ^18.2.67
|
||||||
version: 18.2.69
|
version: 18.2.70
|
||||||
'@types/react-helmet':
|
'@types/react-helmet':
|
||||||
specifier: ^6.1.11
|
specifier: ^6.1.11
|
||||||
version: 6.1.11
|
version: 6.1.11
|
||||||
|
@ -100,7 +100,7 @@ importers:
|
||||||
version: 3.1.1(react-dom@18.2.0)(react@18.2.0)
|
version: 3.1.1(react-dom@18.2.0)(react@18.2.0)
|
||||||
'@docusaurus/theme-classic':
|
'@docusaurus/theme-classic':
|
||||||
specifier: ^3.0.0
|
specifier: ^3.0.0
|
||||||
version: 3.1.1(@types/react@18.2.69)(react-dom@18.2.0)(react@18.2.0)(typescript@5.4.3)
|
version: 3.1.1(@types/react@18.2.70)(react-dom@18.2.0)(react@18.2.0)(typescript@5.4.3)
|
||||||
'@docusaurus/types':
|
'@docusaurus/types':
|
||||||
specifier: ^3.0.0
|
specifier: ^3.0.0
|
||||||
version: 3.1.1(react-dom@18.2.0)(react@18.2.0)
|
version: 3.1.1(react-dom@18.2.0)(react@18.2.0)
|
||||||
|
@ -1481,7 +1481,7 @@ packages:
|
||||||
resolution: {integrity: sha512-+sbxb71sWre+PwDK7X2T8+bhS6clcVMLwBPznX45Qu6opJcgRjAp7gYSDzVFp187J+feSj5dNBN1mJoi6ckkUQ==}
|
resolution: {integrity: sha512-+sbxb71sWre+PwDK7X2T8+bhS6clcVMLwBPznX45Qu6opJcgRjAp7gYSDzVFp187J+feSj5dNBN1mJoi6ckkUQ==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@docsearch/react@3.6.0(@algolia/client-search@4.22.1)(@types/react@18.2.69)(react-dom@18.2.0)(react@18.2.0)(search-insights@2.13.0):
|
/@docsearch/react@3.6.0(@algolia/client-search@4.22.1)(@types/react@18.2.70)(react-dom@18.2.0)(react@18.2.0)(search-insights@2.13.0):
|
||||||
resolution: {integrity: sha512-HUFut4ztcVNmqy9gp/wxNbC7pTOHhgVVkHVGCACTuLhUKUhKAF9KYHJtMiLUJxEqiFLQiuri1fWF8zqwM/cu1w==}
|
resolution: {integrity: sha512-HUFut4ztcVNmqy9gp/wxNbC7pTOHhgVVkHVGCACTuLhUKUhKAF9KYHJtMiLUJxEqiFLQiuri1fWF8zqwM/cu1w==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
'@types/react': '>= 16.8.0 < 19.0.0'
|
'@types/react': '>= 16.8.0 < 19.0.0'
|
||||||
|
@ -1501,7 +1501,7 @@ packages:
|
||||||
'@algolia/autocomplete-core': 1.9.3(@algolia/client-search@4.22.1)(algoliasearch@4.22.1)(search-insights@2.13.0)
|
'@algolia/autocomplete-core': 1.9.3(@algolia/client-search@4.22.1)(algoliasearch@4.22.1)(search-insights@2.13.0)
|
||||||
'@algolia/autocomplete-preset-algolia': 1.9.3(@algolia/client-search@4.22.1)(algoliasearch@4.22.1)
|
'@algolia/autocomplete-preset-algolia': 1.9.3(@algolia/client-search@4.22.1)(algoliasearch@4.22.1)
|
||||||
'@docsearch/css': 3.6.0
|
'@docsearch/css': 3.6.0
|
||||||
'@types/react': 18.2.69
|
'@types/react': 18.2.70
|
||||||
algoliasearch: 4.22.1
|
algoliasearch: 4.22.1
|
||||||
react: 18.2.0
|
react: 18.2.0
|
||||||
react-dom: 18.2.0(react@18.2.0)
|
react-dom: 18.2.0(react@18.2.0)
|
||||||
|
@ -1676,7 +1676,7 @@ packages:
|
||||||
'@docusaurus/react-loadable': 5.5.2(react@18.2.0)
|
'@docusaurus/react-loadable': 5.5.2(react@18.2.0)
|
||||||
'@docusaurus/types': 3.1.1(react-dom@18.2.0)(react@18.2.0)
|
'@docusaurus/types': 3.1.1(react-dom@18.2.0)(react@18.2.0)
|
||||||
'@types/history': 4.7.11
|
'@types/history': 4.7.11
|
||||||
'@types/react': 18.2.69
|
'@types/react': 18.2.70
|
||||||
'@types/react-router-config': 5.0.11
|
'@types/react-router-config': 5.0.11
|
||||||
'@types/react-router-dom': 5.3.3
|
'@types/react-router-dom': 5.3.3
|
||||||
react: 18.2.0
|
react: 18.2.0
|
||||||
|
@ -1979,7 +1979,7 @@ packages:
|
||||||
- webpack-cli
|
- webpack-cli
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@docusaurus/preset-classic@3.1.1(@algolia/client-search@4.22.1)(@types/react@18.2.69)(react-dom@18.2.0)(react@18.2.0)(search-insights@2.13.0)(typescript@5.4.3):
|
/@docusaurus/preset-classic@3.1.1(@algolia/client-search@4.22.1)(@types/react@18.2.70)(react-dom@18.2.0)(react@18.2.0)(search-insights@2.13.0)(typescript@5.4.3):
|
||||||
resolution: {integrity: sha512-jG4ys/hWYf69iaN/xOmF+3kjs4Nnz1Ay3CjFLDtYa8KdxbmUhArA9HmP26ru5N0wbVWhY+6kmpYhTJpez5wTyg==}
|
resolution: {integrity: sha512-jG4ys/hWYf69iaN/xOmF+3kjs4Nnz1Ay3CjFLDtYa8KdxbmUhArA9HmP26ru5N0wbVWhY+6kmpYhTJpez5wTyg==}
|
||||||
engines: {node: '>=18.0'}
|
engines: {node: '>=18.0'}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
|
@ -1995,9 +1995,9 @@ packages:
|
||||||
'@docusaurus/plugin-google-gtag': 3.1.1(react-dom@18.2.0)(react@18.2.0)(typescript@5.4.3)
|
'@docusaurus/plugin-google-gtag': 3.1.1(react-dom@18.2.0)(react@18.2.0)(typescript@5.4.3)
|
||||||
'@docusaurus/plugin-google-tag-manager': 3.1.1(react-dom@18.2.0)(react@18.2.0)(typescript@5.4.3)
|
'@docusaurus/plugin-google-tag-manager': 3.1.1(react-dom@18.2.0)(react@18.2.0)(typescript@5.4.3)
|
||||||
'@docusaurus/plugin-sitemap': 3.1.1(react-dom@18.2.0)(react@18.2.0)(typescript@5.4.3)
|
'@docusaurus/plugin-sitemap': 3.1.1(react-dom@18.2.0)(react@18.2.0)(typescript@5.4.3)
|
||||||
'@docusaurus/theme-classic': 3.1.1(@types/react@18.2.69)(react-dom@18.2.0)(react@18.2.0)(typescript@5.4.3)
|
'@docusaurus/theme-classic': 3.1.1(@types/react@18.2.70)(react-dom@18.2.0)(react@18.2.0)(typescript@5.4.3)
|
||||||
'@docusaurus/theme-common': 3.1.1(@docusaurus/types@3.1.1)(react-dom@18.2.0)(react@18.2.0)(typescript@5.4.3)
|
'@docusaurus/theme-common': 3.1.1(@docusaurus/types@3.1.1)(react-dom@18.2.0)(react@18.2.0)(typescript@5.4.3)
|
||||||
'@docusaurus/theme-search-algolia': 3.1.1(@algolia/client-search@4.22.1)(@docusaurus/types@3.1.1)(@types/react@18.2.69)(react-dom@18.2.0)(react@18.2.0)(search-insights@2.13.0)(typescript@5.4.3)
|
'@docusaurus/theme-search-algolia': 3.1.1(@algolia/client-search@4.22.1)(@docusaurus/types@3.1.1)(@types/react@18.2.70)(react-dom@18.2.0)(react@18.2.0)(search-insights@2.13.0)(typescript@5.4.3)
|
||||||
'@docusaurus/types': 3.1.1(react-dom@18.2.0)(react@18.2.0)
|
'@docusaurus/types': 3.1.1(react-dom@18.2.0)(react@18.2.0)
|
||||||
react: 18.2.0
|
react: 18.2.0
|
||||||
react-dom: 18.2.0(react@18.2.0)
|
react-dom: 18.2.0(react@18.2.0)
|
||||||
|
@ -2028,11 +2028,11 @@ packages:
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
react: '*'
|
react: '*'
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/react': 18.2.69
|
'@types/react': 18.2.70
|
||||||
prop-types: 15.8.1
|
prop-types: 15.8.1
|
||||||
react: 18.2.0
|
react: 18.2.0
|
||||||
|
|
||||||
/@docusaurus/theme-classic@3.1.1(@types/react@18.2.69)(react-dom@18.2.0)(react@18.2.0)(typescript@5.4.3):
|
/@docusaurus/theme-classic@3.1.1(@types/react@18.2.70)(react-dom@18.2.0)(react@18.2.0)(typescript@5.4.3):
|
||||||
resolution: {integrity: sha512-GiPE/jbWM8Qv1A14lk6s9fhc0LhPEQ00eIczRO4QL2nAQJZXkjPG6zaVx+1cZxPFWbAsqSjKe2lqkwF3fGkQ7Q==}
|
resolution: {integrity: sha512-GiPE/jbWM8Qv1A14lk6s9fhc0LhPEQ00eIczRO4QL2nAQJZXkjPG6zaVx+1cZxPFWbAsqSjKe2lqkwF3fGkQ7Q==}
|
||||||
engines: {node: '>=18.0'}
|
engines: {node: '>=18.0'}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
|
@ -2051,7 +2051,7 @@ packages:
|
||||||
'@docusaurus/utils': 3.1.1(@docusaurus/types@3.1.1)
|
'@docusaurus/utils': 3.1.1(@docusaurus/types@3.1.1)
|
||||||
'@docusaurus/utils-common': 3.1.1(@docusaurus/types@3.1.1)
|
'@docusaurus/utils-common': 3.1.1(@docusaurus/types@3.1.1)
|
||||||
'@docusaurus/utils-validation': 3.1.1(@docusaurus/types@3.1.1)
|
'@docusaurus/utils-validation': 3.1.1(@docusaurus/types@3.1.1)
|
||||||
'@mdx-js/react': 3.0.1(@types/react@18.2.69)(react@18.2.0)
|
'@mdx-js/react': 3.0.1(@types/react@18.2.70)(react@18.2.0)
|
||||||
clsx: 2.1.0
|
clsx: 2.1.0
|
||||||
copy-text-to-clipboard: 3.2.0
|
copy-text-to-clipboard: 3.2.0
|
||||||
infima: 0.2.0-alpha.43
|
infima: 0.2.0-alpha.43
|
||||||
|
@ -2100,7 +2100,7 @@ packages:
|
||||||
'@docusaurus/utils': 3.1.1(@docusaurus/types@3.1.1)
|
'@docusaurus/utils': 3.1.1(@docusaurus/types@3.1.1)
|
||||||
'@docusaurus/utils-common': 3.1.1(@docusaurus/types@3.1.1)
|
'@docusaurus/utils-common': 3.1.1(@docusaurus/types@3.1.1)
|
||||||
'@types/history': 4.7.11
|
'@types/history': 4.7.11
|
||||||
'@types/react': 18.2.69
|
'@types/react': 18.2.70
|
||||||
'@types/react-router-config': 5.0.11
|
'@types/react-router-config': 5.0.11
|
||||||
clsx: 2.1.0
|
clsx: 2.1.0
|
||||||
parse-numeric-range: 1.3.0
|
parse-numeric-range: 1.3.0
|
||||||
|
@ -2128,14 +2128,14 @@ packages:
|
||||||
- vue-template-compiler
|
- vue-template-compiler
|
||||||
- webpack-cli
|
- webpack-cli
|
||||||
|
|
||||||
/@docusaurus/theme-search-algolia@3.1.1(@algolia/client-search@4.22.1)(@docusaurus/types@3.1.1)(@types/react@18.2.69)(react-dom@18.2.0)(react@18.2.0)(search-insights@2.13.0)(typescript@5.4.3):
|
/@docusaurus/theme-search-algolia@3.1.1(@algolia/client-search@4.22.1)(@docusaurus/types@3.1.1)(@types/react@18.2.70)(react-dom@18.2.0)(react@18.2.0)(search-insights@2.13.0)(typescript@5.4.3):
|
||||||
resolution: {integrity: sha512-tBH9VY5EpRctVdaAhT+b1BY8y5dyHVZGFXyCHgTrvcXQy5CV4q7serEX7U3SveNT9zksmchPyct6i1sFDC4Z5g==}
|
resolution: {integrity: sha512-tBH9VY5EpRctVdaAhT+b1BY8y5dyHVZGFXyCHgTrvcXQy5CV4q7serEX7U3SveNT9zksmchPyct6i1sFDC4Z5g==}
|
||||||
engines: {node: '>=18.0'}
|
engines: {node: '>=18.0'}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
react: ^18.0.0
|
react: ^18.0.0
|
||||||
react-dom: ^18.0.0
|
react-dom: ^18.0.0
|
||||||
dependencies:
|
dependencies:
|
||||||
'@docsearch/react': 3.6.0(@algolia/client-search@4.22.1)(@types/react@18.2.69)(react-dom@18.2.0)(react@18.2.0)(search-insights@2.13.0)
|
'@docsearch/react': 3.6.0(@algolia/client-search@4.22.1)(@types/react@18.2.70)(react-dom@18.2.0)(react@18.2.0)(search-insights@2.13.0)
|
||||||
'@docusaurus/core': 3.1.1(@docusaurus/types@3.1.1)(debug@4.3.4)(react-dom@18.2.0)(react@18.2.0)(typescript@5.4.3)
|
'@docusaurus/core': 3.1.1(@docusaurus/types@3.1.1)(debug@4.3.4)(react-dom@18.2.0)(react@18.2.0)(typescript@5.4.3)
|
||||||
'@docusaurus/logger': 3.1.1
|
'@docusaurus/logger': 3.1.1
|
||||||
'@docusaurus/plugin-content-docs': 3.1.1(debug@4.3.4)(react-dom@18.2.0)(react@18.2.0)(typescript@5.4.3)
|
'@docusaurus/plugin-content-docs': 3.1.1(debug@4.3.4)(react-dom@18.2.0)(react@18.2.0)(typescript@5.4.3)
|
||||||
|
@ -2195,7 +2195,7 @@ packages:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@mdx-js/mdx': 3.0.1
|
'@mdx-js/mdx': 3.0.1
|
||||||
'@types/history': 4.7.11
|
'@types/history': 4.7.11
|
||||||
'@types/react': 18.2.69
|
'@types/react': 18.2.70
|
||||||
commander: 5.1.0
|
commander: 5.1.0
|
||||||
joi: 17.12.2
|
joi: 17.12.2
|
||||||
react: 18.2.0
|
react: 18.2.0
|
||||||
|
@ -2656,14 +2656,14 @@ packages:
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
/@mdx-js/react@3.0.1(@types/react@18.2.69)(react@18.2.0):
|
/@mdx-js/react@3.0.1(@types/react@18.2.70)(react@18.2.0):
|
||||||
resolution: {integrity: sha512-9ZrPIU4MGf6et1m1ov3zKf+q9+deetI51zprKB1D/z3NOb+rUxxtEl3mCjW5wTGh6VhRdwPueh1oRzi6ezkA8A==}
|
resolution: {integrity: sha512-9ZrPIU4MGf6et1m1ov3zKf+q9+deetI51zprKB1D/z3NOb+rUxxtEl3mCjW5wTGh6VhRdwPueh1oRzi6ezkA8A==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
'@types/react': '>=16'
|
'@types/react': '>=16'
|
||||||
react: '>=16'
|
react: '>=16'
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/mdx': 2.0.12
|
'@types/mdx': 2.0.12
|
||||||
'@types/react': 18.2.69
|
'@types/react': 18.2.70
|
||||||
react: 18.2.0
|
react: 18.2.0
|
||||||
|
|
||||||
/@napi-rs/wasm-runtime@0.1.2:
|
/@napi-rs/wasm-runtime@0.1.2:
|
||||||
|
@ -3406,31 +3406,31 @@ packages:
|
||||||
/@types/react-helmet@6.1.11:
|
/@types/react-helmet@6.1.11:
|
||||||
resolution: {integrity: sha512-0QcdGLddTERotCXo3VFlUSWO3ztraw8nZ6e3zJSgG7apwV5xt+pJUS8ewPBqT4NYB1optGLprNQzFleIY84u/g==}
|
resolution: {integrity: sha512-0QcdGLddTERotCXo3VFlUSWO3ztraw8nZ6e3zJSgG7apwV5xt+pJUS8ewPBqT4NYB1optGLprNQzFleIY84u/g==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/react': 18.2.69
|
'@types/react': 18.2.70
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@types/react-router-config@5.0.11:
|
/@types/react-router-config@5.0.11:
|
||||||
resolution: {integrity: sha512-WmSAg7WgqW7m4x8Mt4N6ZyKz0BubSj/2tVUMsAHp+Yd2AMwcSbeFq9WympT19p5heCFmF97R9eD5uUR/t4HEqw==}
|
resolution: {integrity: sha512-WmSAg7WgqW7m4x8Mt4N6ZyKz0BubSj/2tVUMsAHp+Yd2AMwcSbeFq9WympT19p5heCFmF97R9eD5uUR/t4HEqw==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/history': 4.7.11
|
'@types/history': 4.7.11
|
||||||
'@types/react': 18.2.69
|
'@types/react': 18.2.70
|
||||||
'@types/react-router': 5.1.20
|
'@types/react-router': 5.1.20
|
||||||
|
|
||||||
/@types/react-router-dom@5.3.3:
|
/@types/react-router-dom@5.3.3:
|
||||||
resolution: {integrity: sha512-kpqnYK4wcdm5UaWI3fLcELopqLrHgLqNsdpHauzlQktfkHL3npOSwtj1Uz9oKBAzs7lFtVkV8j83voAz2D8fhw==}
|
resolution: {integrity: sha512-kpqnYK4wcdm5UaWI3fLcELopqLrHgLqNsdpHauzlQktfkHL3npOSwtj1Uz9oKBAzs7lFtVkV8j83voAz2D8fhw==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/history': 4.7.11
|
'@types/history': 4.7.11
|
||||||
'@types/react': 18.2.69
|
'@types/react': 18.2.70
|
||||||
'@types/react-router': 5.1.20
|
'@types/react-router': 5.1.20
|
||||||
|
|
||||||
/@types/react-router@5.1.20:
|
/@types/react-router@5.1.20:
|
||||||
resolution: {integrity: sha512-jGjmu/ZqS7FjSH6owMcD5qpq19+1RS9DeVRqfl1FeBMxTDQAGwlMWOcs52NDoXaNKyG3d1cYQFMs9rCrb88o9Q==}
|
resolution: {integrity: sha512-jGjmu/ZqS7FjSH6owMcD5qpq19+1RS9DeVRqfl1FeBMxTDQAGwlMWOcs52NDoXaNKyG3d1cYQFMs9rCrb88o9Q==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/history': 4.7.11
|
'@types/history': 4.7.11
|
||||||
'@types/react': 18.2.69
|
'@types/react': 18.2.70
|
||||||
|
|
||||||
/@types/react@18.2.69:
|
/@types/react@18.2.70:
|
||||||
resolution: {integrity: sha512-W1HOMUWY/1Yyw0ba5TkCV+oqynRjG7BnteBB+B7JmAK7iw3l2SW+VGOxL+akPweix6jk2NNJtyJKpn4TkpfK3Q==}
|
resolution: {integrity: sha512-hjlM2hho2vqklPhopNkXkdkeq6Lv8WSZTpr7956zY+3WS5cfYUewtCzsJLsbW5dEv3lfSeQ4W14ZFeKC437JRQ==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/prop-types': 15.7.12
|
'@types/prop-types': 15.7.12
|
||||||
'@types/scheduler': 0.16.8
|
'@types/scheduler': 0.16.8
|
||||||
|
@ -3963,7 +3963,7 @@ packages:
|
||||||
hasBin: true
|
hasBin: true
|
||||||
dependencies:
|
dependencies:
|
||||||
caniuse-lite: 1.0.30001600
|
caniuse-lite: 1.0.30001600
|
||||||
electron-to-chromium: 1.4.715
|
electron-to-chromium: 1.4.717
|
||||||
node-releases: 2.0.14
|
node-releases: 2.0.14
|
||||||
update-browserslist-db: 1.0.13(browserslist@4.23.0)
|
update-browserslist-db: 1.0.13(browserslist@4.23.0)
|
||||||
|
|
||||||
|
@ -4926,8 +4926,8 @@ packages:
|
||||||
/ee-first@1.1.1:
|
/ee-first@1.1.1:
|
||||||
resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==}
|
resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==}
|
||||||
|
|
||||||
/electron-to-chromium@1.4.715:
|
/electron-to-chromium@1.4.717:
|
||||||
resolution: {integrity: sha512-XzWNH4ZSa9BwVUQSDorPWAUQ5WGuYz7zJUNpNif40zFCiCl20t8zgylmreNmn26h5kiyw2lg7RfTmeMBsDklqg==}
|
resolution: {integrity: sha512-6Fmg8QkkumNOwuZ/5mIbMU9WI3H2fmn5ajcVya64I5Yr5CcNmO7vcLt0Y7c96DCiMO5/9G+4sI2r6eEvdg1F7A==}
|
||||||
|
|
||||||
/emoji-regex@8.0.0:
|
/emoji-regex@8.0.0:
|
||||||
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
|
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
|
||||||
|
@ -4983,8 +4983,8 @@ packages:
|
||||||
resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==}
|
resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
/es-module-lexer@1.4.2:
|
/es-module-lexer@1.5.0:
|
||||||
resolution: {integrity: sha512-7nOqkomXZEaxUDJw21XZNtRk739QvrPSoZoRtbsEfcii00vdzZUh6zh1CQwHhrib8MdEtJfv5rJiGeb4KuV/vw==}
|
resolution: {integrity: sha512-pqrTKmwEIgafsYZAGw9kszYzmagcE/n4dbgwGWLEXg7J4QFJVQRBld8j3Q3GNez79jzxZshq0bcT962QHOghjw==}
|
||||||
|
|
||||||
/es6-promise@3.3.1:
|
/es6-promise@3.3.1:
|
||||||
resolution: {integrity: sha512-SOp9Phqvqn7jtEUxPWdWfWoLmyt2VaJ6MpvP9Comy1MceMXqE6bxvaTu4iaxpYYPzhny28Lc+M87/c2cPK6lDg==}
|
resolution: {integrity: sha512-SOp9Phqvqn7jtEUxPWdWfWoLmyt2VaJ6MpvP9Comy1MceMXqE6bxvaTu4iaxpYYPzhny28Lc+M87/c2cPK6lDg==}
|
||||||
|
@ -4996,7 +4996,7 @@ packages:
|
||||||
webpack: ^4.40.0 || ^5.0.0
|
webpack: ^4.40.0 || ^5.0.0
|
||||||
dependencies:
|
dependencies:
|
||||||
esbuild: 0.20.2
|
esbuild: 0.20.2
|
||||||
get-tsconfig: 4.7.2
|
get-tsconfig: 4.7.3
|
||||||
loader-utils: 2.0.4
|
loader-utils: 2.0.4
|
||||||
webpack: 5.91.0
|
webpack: 5.91.0
|
||||||
webpack-sources: 1.4.3
|
webpack-sources: 1.4.3
|
||||||
|
@ -5163,8 +5163,8 @@ packages:
|
||||||
signal-exit: 3.0.7
|
signal-exit: 3.0.7
|
||||||
strip-final-newline: 2.0.0
|
strip-final-newline: 2.0.0
|
||||||
|
|
||||||
/express@4.19.1:
|
/express@4.19.2:
|
||||||
resolution: {integrity: sha512-K4w1/Bp7y8iSiVObmCrtq8Cs79XjJc/RU2YYkZQ7wpUu5ZyZ7MtPHkqoMz4pf+mgXfNvo2qft8D9OnrH2ABk9w==}
|
resolution: {integrity: sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==}
|
||||||
engines: {node: '>= 0.10.0'}
|
engines: {node: '>= 0.10.0'}
|
||||||
dependencies:
|
dependencies:
|
||||||
accepts: 1.3.8
|
accepts: 1.3.8
|
||||||
|
@ -5469,8 +5469,8 @@ packages:
|
||||||
resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==}
|
resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
/get-tsconfig@4.7.2:
|
/get-tsconfig@4.7.3:
|
||||||
resolution: {integrity: sha512-wuMsz4leaj5hbGgg4IvDU0bqJagpftG5l5cXIAvo8uZrqn0NJqwtfupTN00VnkQJPcIRrxYrm1Ue24btpCha2A==}
|
resolution: {integrity: sha512-ZvkrzoUA0PQZM6fy6+/Hce561s+faD1rsNwhnO5FelNjyy7EMGJ3Rz1AQ8GYDWjhRs/7dBLOEJvhK8MiEJOAFg==}
|
||||||
dependencies:
|
dependencies:
|
||||||
resolve-pkg-maps: 1.0.0
|
resolve-pkg-maps: 1.0.0
|
||||||
dev: false
|
dev: false
|
||||||
|
@ -9605,7 +9605,7 @@ packages:
|
||||||
compression: 1.7.4
|
compression: 1.7.4
|
||||||
connect-history-api-fallback: 2.0.0
|
connect-history-api-fallback: 2.0.0
|
||||||
default-gateway: 6.0.3
|
default-gateway: 6.0.3
|
||||||
express: 4.19.1
|
express: 4.19.2
|
||||||
graceful-fs: 4.2.11
|
graceful-fs: 4.2.11
|
||||||
html-entities: 2.5.2
|
html-entities: 2.5.2
|
||||||
http-proxy-middleware: 2.0.6(@types/express@4.17.21)(debug@4.3.4)
|
http-proxy-middleware: 2.0.6(@types/express@4.17.21)(debug@4.3.4)
|
||||||
|
@ -9667,7 +9667,7 @@ packages:
|
||||||
browserslist: 4.23.0
|
browserslist: 4.23.0
|
||||||
chrome-trace-event: 1.0.3
|
chrome-trace-event: 1.0.3
|
||||||
enhanced-resolve: 5.16.0
|
enhanced-resolve: 5.16.0
|
||||||
es-module-lexer: 1.4.2
|
es-module-lexer: 1.5.0
|
||||||
eslint-scope: 5.1.1
|
eslint-scope: 5.1.1
|
||||||
events: 3.3.0
|
events: 3.3.0
|
||||||
glob-to-regexp: 0.4.1
|
glob-to-regexp: 0.4.1
|
||||||
|
|
|
@ -50,7 +50,8 @@ git commit -m "updated README [CI SKIP]"
|
||||||
|
|
||||||
## Steps
|
## Steps
|
||||||
|
|
||||||
Every step of your workflow executes commands inside a specified container. The defined commands are executed serially.
|
Every step of your workflow executes commands inside a specified container.<br>
|
||||||
|
The defined steps are executed in sequence by default, if they should run in parallel you can use [`depends_on`](./20-workflow-syntax.md#depends_on).<br>
|
||||||
The associated commit is checked out with git to a workspace which is mounted to every step of the workflow as the working directory.
|
The associated commit is checked out with git to a workspace which is mounted to every step of the workflow as the working directory.
|
||||||
|
|
||||||
```diff
|
```diff
|
||||||
|
@ -478,6 +479,19 @@ Normally steps of a workflow are executed serially in the order in which they ar
|
||||||
- go test
|
- go test
|
||||||
```
|
```
|
||||||
|
|
||||||
|
:::note
|
||||||
|
You can define a step to start immediately without dependencies by adding an empty `depends_on: []`. By setting `depends_on` on a single step all other steps will be immediately executed as well if no further dependencies are specified.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
steps:
|
||||||
|
- name: check code format
|
||||||
|
image: mstruebing/editorconfig-checker
|
||||||
|
depends_on: [] # enable parallel steps
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
### `volumes`
|
### `volumes`
|
||||||
|
|
||||||
Woodpecker gives the ability to define Docker volumes in the YAML. You can use this parameter to mount files or folders on the host machine into your containers.
|
Woodpecker gives the ability to define Docker volumes in the YAML. You can use this parameter to mount files or folders on the host machine into your containers.
|
||||||
|
|
23
go.mod
23
go.mod
|
@ -11,25 +11,27 @@ require (
|
||||||
github.com/alessio/shellescape v1.4.2
|
github.com/alessio/shellescape v1.4.2
|
||||||
github.com/bmatcuk/doublestar/v4 v4.6.1
|
github.com/bmatcuk/doublestar/v4 v4.6.1
|
||||||
github.com/caddyserver/certmagic v0.20.0
|
github.com/caddyserver/certmagic v0.20.0
|
||||||
github.com/cenkalti/backoff/v4 v4.2.1
|
github.com/cenkalti/backoff/v4 v4.3.0
|
||||||
github.com/charmbracelet/bubbles v0.18.0
|
github.com/charmbracelet/huh v0.3.0
|
||||||
github.com/charmbracelet/bubbletea v0.25.0
|
github.com/charmbracelet/huh/spinner v0.0.0-20240327025511-ec643317aa10
|
||||||
github.com/distribution/reference v0.5.0
|
github.com/distribution/reference v0.5.0
|
||||||
github.com/docker/cli v24.0.9+incompatible
|
github.com/docker/cli v24.0.9+incompatible
|
||||||
github.com/docker/docker v24.0.9+incompatible
|
github.com/docker/docker v24.0.9+incompatible
|
||||||
github.com/docker/go-connections v0.5.0
|
github.com/docker/go-connections v0.5.0
|
||||||
github.com/docker/go-units v0.5.0
|
github.com/docker/go-units v0.5.0
|
||||||
github.com/drone/envsubst v1.0.3
|
github.com/drone/envsubst v1.0.3
|
||||||
github.com/expr-lang/expr v1.16.2
|
github.com/expr-lang/expr v1.16.3
|
||||||
github.com/franela/goblin v0.0.0-20211003143422-0a4f594942bf
|
github.com/franela/goblin v0.0.0-20211003143422-0a4f594942bf
|
||||||
github.com/fsnotify/fsnotify v1.7.0
|
github.com/fsnotify/fsnotify v1.7.0
|
||||||
github.com/gin-gonic/gin v1.9.1
|
github.com/gin-gonic/gin v1.9.1
|
||||||
github.com/go-ap/httpsig v0.0.0-20221203064646-3647b4d88fdf
|
github.com/go-ap/httpsig v0.0.0-20221203064646-3647b4d88fdf
|
||||||
github.com/go-sql-driver/mysql v1.8.0
|
github.com/go-sql-driver/mysql v1.8.1
|
||||||
github.com/golang-jwt/jwt/v5 v5.2.1
|
github.com/golang-jwt/jwt/v5 v5.2.1
|
||||||
github.com/google/go-github/v60 v60.0.0
|
github.com/google/go-github/v61 v61.0.0
|
||||||
github.com/google/tink/go v1.7.0
|
github.com/google/tink/go v1.7.0
|
||||||
github.com/gorilla/securecookie v1.1.2
|
github.com/gorilla/securecookie v1.1.2
|
||||||
|
github.com/hashicorp/go-hclog v1.2.0
|
||||||
|
github.com/hashicorp/go-plugin v1.4.3
|
||||||
github.com/jellydator/ttlcache/v3 v3.2.0
|
github.com/jellydator/ttlcache/v3 v3.2.0
|
||||||
github.com/joho/godotenv v1.5.1
|
github.com/joho/godotenv v1.5.1
|
||||||
github.com/kinbiko/jsonassert v1.1.1
|
github.com/kinbiko/jsonassert v1.1.1
|
||||||
|
@ -81,7 +83,10 @@ require (
|
||||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
|
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
|
||||||
github.com/beorn7/perks v1.0.1 // indirect
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
github.com/bytedance/sonic v1.10.2 // indirect
|
github.com/bytedance/sonic v1.10.2 // indirect
|
||||||
|
github.com/catppuccin/go v0.2.0 // indirect
|
||||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||||
|
github.com/charmbracelet/bubbles v0.18.0 // indirect
|
||||||
|
github.com/charmbracelet/bubbletea v0.25.0 // indirect
|
||||||
github.com/charmbracelet/lipgloss v0.9.1 // indirect
|
github.com/charmbracelet/lipgloss v0.9.1 // indirect
|
||||||
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect
|
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect
|
||||||
github.com/chenzhuoyu/iasm v0.9.1 // indirect
|
github.com/chenzhuoyu/iasm v0.9.1 // indirect
|
||||||
|
@ -116,9 +121,9 @@ require (
|
||||||
github.com/google/gofuzz v1.2.0 // indirect
|
github.com/google/gofuzz v1.2.0 // indirect
|
||||||
github.com/google/uuid v1.6.0 // indirect
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||||
github.com/hashicorp/go-hclog v1.2.0 // indirect
|
|
||||||
github.com/hashicorp/go-retryablehttp v0.7.5 // indirect
|
github.com/hashicorp/go-retryablehttp v0.7.5 // indirect
|
||||||
github.com/hashicorp/go-version v1.6.0 // indirect
|
github.com/hashicorp/go-version v1.6.0 // indirect
|
||||||
|
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb // indirect
|
||||||
github.com/imdario/mergo v0.3.16 // indirect
|
github.com/imdario/mergo v0.3.16 // indirect
|
||||||
github.com/josharian/intern v1.0.0 // indirect
|
github.com/josharian/intern v1.0.0 // indirect
|
||||||
github.com/json-iterator/go v1.1.12 // indirect
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
|
@ -134,13 +139,15 @@ require (
|
||||||
github.com/mattn/go-runewidth v0.0.15 // indirect
|
github.com/mattn/go-runewidth v0.0.15 // indirect
|
||||||
github.com/mholt/acmez v1.2.0 // indirect
|
github.com/mholt/acmez v1.2.0 // indirect
|
||||||
github.com/miekg/dns v1.1.57 // indirect
|
github.com/miekg/dns v1.1.57 // indirect
|
||||||
|
github.com/mitchellh/go-testing-interface v1.0.0 // indirect
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
github.com/morikuni/aec v1.0.0 // indirect
|
github.com/morikuni/aec v1.0.0 // indirect
|
||||||
github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b // indirect
|
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
|
||||||
github.com/muesli/cancelreader v0.2.2 // indirect
|
github.com/muesli/cancelreader v0.2.2 // indirect
|
||||||
github.com/muesli/reflow v0.3.0 // indirect
|
github.com/muesli/reflow v0.3.0 // indirect
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||||
|
github.com/oklog/run v1.0.0 // indirect
|
||||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||||
github.com/opencontainers/image-spec v1.0.2 // indirect
|
github.com/opencontainers/image-spec v1.0.2 // indirect
|
||||||
github.com/pelletier/go-toml/v2 v2.1.1 // indirect
|
github.com/pelletier/go-toml/v2 v2.1.1 // indirect
|
||||||
|
|
74
go.sum
74
go.sum
|
@ -1,3 +1,4 @@
|
||||||
|
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
code.gitea.io/sdk/gitea v0.17.1 h1:3jCPOG2ojbl8AcfaUCRYLT5MUcBMFwS0OSK2mA5Zok8=
|
code.gitea.io/sdk/gitea v0.17.1 h1:3jCPOG2ojbl8AcfaUCRYLT5MUcBMFwS0OSK2mA5Zok8=
|
||||||
code.gitea.io/sdk/gitea v0.17.1/go.mod h1:aCnBqhHpoEWA180gMbaCtdX9Pl6BWBAuuP2miadoTNM=
|
code.gitea.io/sdk/gitea v0.17.1/go.mod h1:aCnBqhHpoEWA180gMbaCtdX9Pl6BWBAuuP2miadoTNM=
|
||||||
codeberg.org/6543/go-yaml2json v1.0.0 h1:heGqo9VEi7gY2yNqjj7X4ADs5nzlFIbGsJtgYDLrnig=
|
codeberg.org/6543/go-yaml2json v1.0.0 h1:heGqo9VEi7gY2yNqjj7X4ADs5nzlFIbGsJtgYDLrnig=
|
||||||
|
@ -40,14 +41,21 @@ github.com/bytedance/sonic v1.10.2 h1:GQebETVBxYB7JGWJtLBi07OVzWwt+8dWA00gEVW2ZF
|
||||||
github.com/bytedance/sonic v1.10.2/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4=
|
github.com/bytedance/sonic v1.10.2/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4=
|
||||||
github.com/caddyserver/certmagic v0.20.0 h1:bTw7LcEZAh9ucYCRXyCpIrSAGplplI0vGYJ4BpCQ/Fc=
|
github.com/caddyserver/certmagic v0.20.0 h1:bTw7LcEZAh9ucYCRXyCpIrSAGplplI0vGYJ4BpCQ/Fc=
|
||||||
github.com/caddyserver/certmagic v0.20.0/go.mod h1:N4sXgpICQUskEWpj7zVzvWD41p3NYacrNoZYiRM2jTg=
|
github.com/caddyserver/certmagic v0.20.0/go.mod h1:N4sXgpICQUskEWpj7zVzvWD41p3NYacrNoZYiRM2jTg=
|
||||||
github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
|
github.com/catppuccin/go v0.2.0 h1:ktBeIrIP42b/8FGiScP9sgrWOss3lw0Z5SktRoithGA=
|
||||||
github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
github.com/catppuccin/go v0.2.0/go.mod h1:8IHJuMGaUUjQM82qBrGNBv7LFq6JI3NnQCF6MOlZjpc=
|
||||||
|
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
|
||||||
|
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||||
|
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||||
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
||||||
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
github.com/charmbracelet/bubbles v0.18.0 h1:PYv1A036luoBGroX6VWjQIE9Syf2Wby2oOl/39KLfy0=
|
github.com/charmbracelet/bubbles v0.18.0 h1:PYv1A036luoBGroX6VWjQIE9Syf2Wby2oOl/39KLfy0=
|
||||||
github.com/charmbracelet/bubbles v0.18.0/go.mod h1:08qhZhtIwzgrtBjAcJnij1t1H0ZRjwHyGsy6AL11PSw=
|
github.com/charmbracelet/bubbles v0.18.0/go.mod h1:08qhZhtIwzgrtBjAcJnij1t1H0ZRjwHyGsy6AL11PSw=
|
||||||
github.com/charmbracelet/bubbletea v0.25.0 h1:bAfwk7jRz7FKFl9RzlIULPkStffg5k6pNt5dywy4TcM=
|
github.com/charmbracelet/bubbletea v0.25.0 h1:bAfwk7jRz7FKFl9RzlIULPkStffg5k6pNt5dywy4TcM=
|
||||||
github.com/charmbracelet/bubbletea v0.25.0/go.mod h1:EN3QDR1T5ZdWmdfDzYcqOCAps45+QIJbLOBxmVNWNNg=
|
github.com/charmbracelet/bubbletea v0.25.0/go.mod h1:EN3QDR1T5ZdWmdfDzYcqOCAps45+QIJbLOBxmVNWNNg=
|
||||||
|
github.com/charmbracelet/huh v0.3.0 h1:CxPplWkgW2yUTDDG0Z4S5HH8SJOosWHd4LxCvi0XsKE=
|
||||||
|
github.com/charmbracelet/huh v0.3.0/go.mod h1:fujUdKX8tC45CCSaRQdw789O6uaCRwx8l2NDyKfC4jA=
|
||||||
|
github.com/charmbracelet/huh/spinner v0.0.0-20240327025511-ec643317aa10 h1:/HZJSyFVH5rB1MlCDfkhQhRbLPD2Er29ngWXiUQ8bik=
|
||||||
|
github.com/charmbracelet/huh/spinner v0.0.0-20240327025511-ec643317aa10/go.mod h1:nrBG0YEHaxdbqHXW1xvG1hPqkuac9Eg7RTMvogiXuz0=
|
||||||
github.com/charmbracelet/lipgloss v0.9.1 h1:PNyd3jvaJbg4jRHKWXnCj1akQm4rh8dbEzN1p/u1KWg=
|
github.com/charmbracelet/lipgloss v0.9.1 h1:PNyd3jvaJbg4jRHKWXnCj1akQm4rh8dbEzN1p/u1KWg=
|
||||||
github.com/charmbracelet/lipgloss v0.9.1/go.mod h1:1mPmG4cxScwUQALAAnacHaigiiHB9Pmr+v1VEawJl6I=
|
github.com/charmbracelet/lipgloss v0.9.1/go.mod h1:1mPmG4cxScwUQALAAnacHaigiiHB9Pmr+v1VEawJl6I=
|
||||||
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
|
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
|
||||||
|
@ -60,6 +68,7 @@ github.com/chenzhuoyu/iasm v0.9.1/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLI
|
||||||
github.com/chzyer/logex v1.2.0/go.mod h1:9+9sk7u7pGNWYMkh0hdiL++6OeibzJccyQU4p4MedaY=
|
github.com/chzyer/logex v1.2.0/go.mod h1:9+9sk7u7pGNWYMkh0hdiL++6OeibzJccyQU4p4MedaY=
|
||||||
github.com/chzyer/readline v1.5.0/go.mod h1:x22KAscuvRqlLoK9CsoYsmxoXZMMFVyOl86cAH8qUic=
|
github.com/chzyer/readline v1.5.0/go.mod h1:x22KAscuvRqlLoK9CsoYsmxoXZMMFVyOl86cAH8qUic=
|
||||||
github.com/chzyer/test v0.0.0-20210722231415-061457976a23/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
github.com/chzyer/test v0.0.0-20210722231415-061457976a23/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||||
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
|
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
|
||||||
github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 h1:q2hJAaP1k2wIvVRd/hEHD7lacgqrCPS+k8g1MndzfWY=
|
github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 h1:q2hJAaP1k2wIvVRd/hEHD7lacgqrCPS+k8g1MndzfWY=
|
||||||
github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk=
|
github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk=
|
||||||
|
@ -102,8 +111,10 @@ github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkp
|
||||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||||
github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=
|
github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=
|
||||||
github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
|
github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
|
||||||
github.com/expr-lang/expr v1.16.2 h1:JvMnzUs3LeVHBvGFcXYmXo+Q6DPDmzrlcSBO6Wy3w4s=
|
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
github.com/expr-lang/expr v1.16.2/go.mod h1:uCkhfG+x7fcZ5A5sXHKuQ07jGZRl6J0FCAaf2k4PtVQ=
|
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||||
|
github.com/expr-lang/expr v1.16.3 h1:NLldf786GffptcXNxxJx5dQ+FzeWDKChBDqOOwyK8to=
|
||||||
|
github.com/expr-lang/expr v1.16.3/go.mod h1:uCkhfG+x7fcZ5A5sXHKuQ07jGZRl6J0FCAaf2k4PtVQ=
|
||||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||||
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
|
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
|
||||||
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
|
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
|
||||||
|
@ -146,8 +157,8 @@ github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91
|
||||||
github.com/go-playground/validator/v10 v10.16.0 h1:x+plE831WK4vaKHO/jpgUGsvLKIqRRkz6M78GuJAfGE=
|
github.com/go-playground/validator/v10 v10.16.0 h1:x+plE831WK4vaKHO/jpgUGsvLKIqRRkz6M78GuJAfGE=
|
||||||
github.com/go-playground/validator/v10 v10.16.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
|
github.com/go-playground/validator/v10 v10.16.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
|
||||||
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
||||||
github.com/go-sql-driver/mysql v1.8.0 h1:UtktXaU2Nb64z/pLiGIxY4431SJ4/dR5cjMmlVHgnT4=
|
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
|
||||||
github.com/go-sql-driver/mysql v1.8.0/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
|
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
|
||||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
|
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
|
||||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
|
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
|
||||||
|
@ -167,7 +178,12 @@ github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0kt
|
||||||
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
||||||
github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A=
|
github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A=
|
||||||
github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI=
|
github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI=
|
||||||
|
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||||
|
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||||
|
@ -185,8 +201,8 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
||||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
github.com/google/go-github/v60 v60.0.0 h1:oLG98PsLauFvvu4D/YPxq374jhSxFYdzQGNCyONLfn8=
|
github.com/google/go-github/v61 v61.0.0 h1:VwQCBwhyE9JclCI+22/7mLB1PuU9eowCXKY5pNlu1go=
|
||||||
github.com/google/go-github/v60 v60.0.0/go.mod h1:ByhX2dP9XT9o/ll2yXAu2VD8l5eNVg8hD4Cr0S/LmQk=
|
github.com/google/go-github/v61 v61.0.0/go.mod h1:0WR+KmsWX75G2EbpyGsGmradjo3IiciuI4BmdVCobQY=
|
||||||
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
|
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
|
||||||
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
|
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
|
||||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
|
@ -205,12 +221,17 @@ github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pw
|
||||||
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
|
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
|
||||||
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
|
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
|
||||||
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
|
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
|
||||||
|
github.com/hashicorp/go-hclog v0.14.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
|
||||||
github.com/hashicorp/go-hclog v1.2.0 h1:La19f8d7WIlm4ogzNHB0JGqs5AUDAZ2UfCY4sJXcJdM=
|
github.com/hashicorp/go-hclog v1.2.0 h1:La19f8d7WIlm4ogzNHB0JGqs5AUDAZ2UfCY4sJXcJdM=
|
||||||
github.com/hashicorp/go-hclog v1.2.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
|
github.com/hashicorp/go-hclog v1.2.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
|
||||||
|
github.com/hashicorp/go-plugin v1.4.3 h1:DXmvivbWD5qdiBts9TpBC7BYL1Aia5sxbRgQB+v6UZM=
|
||||||
|
github.com/hashicorp/go-plugin v1.4.3/go.mod h1:5fGEH17QVwTTcR0zV7yhDPLLmFX9YSZ38b18Udy6vYQ=
|
||||||
github.com/hashicorp/go-retryablehttp v0.7.5 h1:bJj+Pj19UZMIweq/iie+1u5YCdGrnxCT9yvm0e+Nd5M=
|
github.com/hashicorp/go-retryablehttp v0.7.5 h1:bJj+Pj19UZMIweq/iie+1u5YCdGrnxCT9yvm0e+Nd5M=
|
||||||
github.com/hashicorp/go-retryablehttp v0.7.5/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8=
|
github.com/hashicorp/go-retryablehttp v0.7.5/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8=
|
||||||
github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek=
|
github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek=
|
||||||
github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||||
|
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb h1:b5rjCoWHc7eqmAS4/qyk21ZsHyb6Mxv/jykxvNTkU4M=
|
||||||
|
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
|
||||||
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
||||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||||
github.com/ianlancetaylor/demangle v0.0.0-20220319035150-800ac71e25c2/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w=
|
github.com/ianlancetaylor/demangle v0.0.0-20220319035150-800ac71e25c2/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w=
|
||||||
|
@ -257,6 +278,8 @@ github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dv
|
||||||
github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||||
github.com/jellydator/ttlcache/v3 v3.2.0 h1:6lqVJ8X3ZaUwvzENqPAobDsXNExfUJd61u++uW8a3LE=
|
github.com/jellydator/ttlcache/v3 v3.2.0 h1:6lqVJ8X3ZaUwvzENqPAobDsXNExfUJd61u++uW8a3LE=
|
||||||
github.com/jellydator/ttlcache/v3 v3.2.0/go.mod h1:hi7MGFdMAwZna5n2tuvh63DvFLzVKySzCVW6+0gA2n4=
|
github.com/jellydator/ttlcache/v3 v3.2.0/go.mod h1:hi7MGFdMAwZna5n2tuvh63DvFLzVKySzCVW6+0gA2n4=
|
||||||
|
github.com/jhump/protoreflect v1.6.0 h1:h5jfMVslIg6l29nsMs0D8Wj17RDVdNYti0vDN/PZZoE=
|
||||||
|
github.com/jhump/protoreflect v1.6.0/go.mod h1:eaTn3RZAmMBcV0fifFvlm6VHNz3wSkYyXYWUh7ymB74=
|
||||||
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
|
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
|
||||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||||
|
@ -331,6 +354,9 @@ github.com/mholt/acmez v1.2.0 h1:1hhLxSgY5FvH5HCnGUuwbKY2VQVo8IU7rxXKSnZ7F30=
|
||||||
github.com/mholt/acmez v1.2.0/go.mod h1:VT9YwH1xgNX1kmYY89gY8xPJC84BFAisjo8Egigt4kE=
|
github.com/mholt/acmez v1.2.0/go.mod h1:VT9YwH1xgNX1kmYY89gY8xPJC84BFAisjo8Egigt4kE=
|
||||||
github.com/miekg/dns v1.1.57 h1:Jzi7ApEIzwEPLHWRcafCN9LZSBbqQpxjt/wpgvg7wcM=
|
github.com/miekg/dns v1.1.57 h1:Jzi7ApEIzwEPLHWRcafCN9LZSBbqQpxjt/wpgvg7wcM=
|
||||||
github.com/miekg/dns v1.1.57/go.mod h1:uqRjCRUuEAA6qsOiJvDd+CFo/vW+y5WR6SNmHE55hZk=
|
github.com/miekg/dns v1.1.57/go.mod h1:uqRjCRUuEAA6qsOiJvDd+CFo/vW+y5WR6SNmHE55hZk=
|
||||||
|
github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||||
|
github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0=
|
||||||
|
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||||
github.com/moby/moby v24.0.9+incompatible h1:Z/hFbZJqC5Fmuf6jesMLdHU71CMAgdiSJ1ZYey+bFmg=
|
github.com/moby/moby v24.0.9+incompatible h1:Z/hFbZJqC5Fmuf6jesMLdHU71CMAgdiSJ1ZYey+bFmg=
|
||||||
|
@ -345,8 +371,8 @@ github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjY
|
||||||
github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8=
|
github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8=
|
||||||
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
||||||
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
||||||
github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b h1:1XF24mVaiu7u+CFywTdcDo2ie1pzzhwjt6RHqzpMU34=
|
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI=
|
||||||
github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b/go.mod h1:fQuZ0gauxyBcmsdE3ZT4NasjaRdxmbCS0jRHsrWu3Ho=
|
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo=
|
||||||
github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
|
github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
|
||||||
github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
|
github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
|
||||||
github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
|
github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
|
||||||
|
@ -357,6 +383,8 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||||
github.com/neticdk/go-bitbucket v1.0.0 h1:FPvHEgPHoDwD2VHbpyu2R2gnoWQ867RxZd2FivS4wSw=
|
github.com/neticdk/go-bitbucket v1.0.0 h1:FPvHEgPHoDwD2VHbpyu2R2gnoWQ867RxZd2FivS4wSw=
|
||||||
github.com/neticdk/go-bitbucket v1.0.0/go.mod h1:IrHeWO1CrNi0DlOvfhAA9bGRSeNSUB6/SAfzmwbA5aU=
|
github.com/neticdk/go-bitbucket v1.0.0/go.mod h1:IrHeWO1CrNi0DlOvfhAA9bGRSeNSUB6/SAfzmwbA5aU=
|
||||||
|
github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw=
|
||||||
|
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
|
||||||
github.com/oklog/ulid/v2 v2.1.0 h1:+9lhoxAP56we25tyYETBBY1YLA2SaoLvUFgrP2miPJU=
|
github.com/oklog/ulid/v2 v2.1.0 h1:+9lhoxAP56we25tyYETBBY1YLA2SaoLvUFgrP2miPJU=
|
||||||
github.com/oklog/ulid/v2 v2.1.0/go.mod h1:rcEKHmBBKfef9DhnvX7y1HZBYxjXb0cP5ExxNsTT1QQ=
|
github.com/oklog/ulid/v2 v2.1.0/go.mod h1:rcEKHmBBKfef9DhnvX7y1HZBYxjXb0cP5ExxNsTT1QQ=
|
||||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
|
@ -382,6 +410,7 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU=
|
github.com/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU=
|
||||||
github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k=
|
github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k=
|
||||||
|
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw=
|
github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw=
|
||||||
github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI=
|
github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI=
|
||||||
github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE=
|
github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE=
|
||||||
|
@ -513,6 +542,10 @@ golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58
|
||||||
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
|
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
|
||||||
golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
|
golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
|
||||||
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
|
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
|
||||||
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
|
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
|
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||||
|
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||||
|
@ -522,7 +555,11 @@ golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91
|
||||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||||
golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
|
golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
|
||||||
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||||
|
golang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
@ -538,9 +575,11 @@ golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||||
golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc=
|
golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc=
|
||||||
golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
|
golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/oauth2 v0.18.0 h1:09qnuIAgzdx1XplqJvW6CQqMCtGZykZWcXzPMPUusvI=
|
golang.org/x/oauth2 v0.18.0 h1:09qnuIAgzdx1XplqJvW6CQqMCtGZykZWcXzPMPUusvI=
|
||||||
golang.org/x/oauth2 v0.18.0/go.mod h1:Wf7knwG0MPoWIMMBgFlEaSUDaKskp0dCfrlJRJXbBi8=
|
golang.org/x/oauth2 v0.18.0/go.mod h1:Wf7knwG0MPoWIMMBgFlEaSUDaKskp0dCfrlJRJXbBi8=
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
@ -548,6 +587,7 @@ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJ
|
||||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
|
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
|
||||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
|
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
@ -603,8 +643,11 @@ golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||||
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
||||||
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
|
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||||
golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
@ -625,10 +668,19 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||||
|
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM=
|
google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM=
|
||||||
google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds=
|
google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds=
|
||||||
|
google.golang.org/genproto v0.0.0-20170818010345-ee236bd376b0/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
|
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
|
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 h1:AjyfHzEPEFp/NpvfN5g+KDla3EMojjhRVZc1i7cj+oM=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 h1:AjyfHzEPEFp/NpvfN5g+KDla3EMojjhRVZc1i7cj+oM=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80/go.mod h1:PAREbraiVEVGVdTZsVWjSbbTtSyGbAgIIvni8a8CD5s=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80/go.mod h1:PAREbraiVEVGVdTZsVWjSbbTtSyGbAgIIvni8a8CD5s=
|
||||||
|
google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||||
|
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||||
|
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||||
|
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||||
google.golang.org/grpc v1.62.1 h1:B4n+nfKzOICUXMgyrNd19h/I9oH0L1pizfk1d4zSgTk=
|
google.golang.org/grpc v1.62.1 h1:B4n+nfKzOICUXMgyrNd19h/I9oH0L1pizfk1d4zSgTk=
|
||||||
google.golang.org/grpc v1.62.1/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE=
|
google.golang.org/grpc v1.62.1/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE=
|
||||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||||
|
@ -658,6 +710,8 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o=
|
gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o=
|
||||||
gotest.tools/v3 v3.4.0/go.mod h1:CtbdzLSsqVhDgMtKsx03ird5YTGB3ar27v0u/yKBW5g=
|
gotest.tools/v3 v3.4.0/go.mod h1:CtbdzLSsqVhDgMtKsx03ird5YTGB3ar27v0u/yKBW5g=
|
||||||
|
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||||
k8s.io/api v0.29.3 h1:2ORfZ7+bGC3YJqGpV0KSDDEVf8hdGQ6A03/50vj8pmw=
|
k8s.io/api v0.29.3 h1:2ORfZ7+bGC3YJqGpV0KSDDEVf8hdGQ6A03/50vj8pmw=
|
||||||
k8s.io/api v0.29.3/go.mod h1:y2yg2NTyHUUkIoTC+phinTnEa3KFM6RZ3szxt014a80=
|
k8s.io/api v0.29.3/go.mod h1:y2yg2NTyHUUkIoTC+phinTnEa3KFM6RZ3szxt014a80=
|
||||||
|
|
|
@ -213,7 +213,7 @@ func (e *docker) StartStep(ctx context.Context, step *backend.Step, taskUUID str
|
||||||
}
|
}
|
||||||
|
|
||||||
// add default volumes to the host configuration
|
// add default volumes to the host configuration
|
||||||
hostConfig.Binds = utils.DedupStrings(append(hostConfig.Binds, e.volumes...))
|
hostConfig.Binds = utils.DeduplicateStrings(append(hostConfig.Binds, e.volumes...))
|
||||||
|
|
||||||
_, err := e.client.ContainerCreate(ctx, config, hostConfig, nil, nil, containerName)
|
_, err := e.client.ContainerCreate(ctx, config, hostConfig, nil, nil, containerName)
|
||||||
if client.IsErrNotFound(err) {
|
if client.IsErrNotFound(err) {
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
// BackendOptions defines all the advanced options for the kubernetes backend
|
// BackendOptions defines all the advanced options for the kubernetes backend
|
||||||
type BackendOptions struct {
|
type BackendOptions struct {
|
||||||
Resources Resources `mapstructure:"resources"`
|
Resources Resources `mapstructure:"resources"`
|
||||||
|
RuntimeClassName *string `mapstructure:"runtimeClassName"`
|
||||||
ServiceAccountName string `mapstructure:"serviceAccountName"`
|
ServiceAccountName string `mapstructure:"serviceAccountName"`
|
||||||
NodeSelector map[string]string `mapstructure:"nodeSelector"`
|
NodeSelector map[string]string `mapstructure:"nodeSelector"`
|
||||||
Tolerations []Toleration `mapstructure:"tolerations"`
|
Tolerations []Toleration `mapstructure:"tolerations"`
|
||||||
|
|
|
@ -316,6 +316,9 @@ func (e *kube) TailStep(ctx context.Context, step *types.Step, taskUUID string)
|
||||||
}
|
}
|
||||||
|
|
||||||
if pod.Name == podName {
|
if pod.Name == podName {
|
||||||
|
if isImagePullBackOffState(pod) {
|
||||||
|
up <- true
|
||||||
|
}
|
||||||
switch pod.Status.Phase {
|
switch pod.Status.Phase {
|
||||||
case v1.PodRunning, v1.PodSucceeded, v1.PodFailed:
|
case v1.PodRunning, v1.PodSucceeded, v1.PodFailed:
|
||||||
up <- true
|
up <- true
|
||||||
|
|
|
@ -117,6 +117,7 @@ func podSpec(step *types.Step, config *config, options BackendOptions) (v1.PodSp
|
||||||
var err error
|
var err error
|
||||||
spec := v1.PodSpec{
|
spec := v1.PodSpec{
|
||||||
RestartPolicy: v1.RestartPolicyNever,
|
RestartPolicy: v1.RestartPolicyNever,
|
||||||
|
RuntimeClassName: options.RuntimeClassName,
|
||||||
ServiceAccountName: options.ServiceAccountName,
|
ServiceAccountName: options.ServiceAccountName,
|
||||||
ImagePullSecrets: imagePullSecretsReferences(config.ImagePullSecretNames),
|
ImagePullSecrets: imagePullSecretsReferences(config.ImagePullSecretNames),
|
||||||
HostAliases: hostAliases(step.ExtraHosts),
|
HostAliases: hostAliases(step.ExtraHosts),
|
||||||
|
@ -138,6 +139,8 @@ func podContainer(step *types.Step, podName, goos string, options BackendOptions
|
||||||
Name: podName,
|
Name: podName,
|
||||||
Image: step.Image,
|
Image: step.Image,
|
||||||
WorkingDir: step.WorkingDir,
|
WorkingDir: step.WorkingDir,
|
||||||
|
Ports: containerPorts(step.Ports),
|
||||||
|
SecurityContext: containerSecurityContext(options.SecurityContext, step.Privileged),
|
||||||
}
|
}
|
||||||
|
|
||||||
if step.Pull {
|
if step.Pull {
|
||||||
|
@ -155,8 +158,6 @@ func podContainer(step *types.Step, podName, goos string, options BackendOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
container.Env = mapToEnvVars(step.Environment)
|
container.Env = mapToEnvVars(step.Environment)
|
||||||
container.Ports = containerPorts(step.Ports)
|
|
||||||
container.SecurityContext = containerSecurityContext(options.SecurityContext, step.Privileged)
|
|
||||||
|
|
||||||
container.Resources, err = resourceRequirements(options.Resources)
|
container.Resources, err = resourceRequirements(options.Resources)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -245,6 +245,7 @@ func TestFullPod(t *testing.T) {
|
||||||
"nodeSelector": {
|
"nodeSelector": {
|
||||||
"storage": "ssd"
|
"storage": "ssd"
|
||||||
},
|
},
|
||||||
|
"runtimeClassName": "runc",
|
||||||
"serviceAccountName": "wp-svc-acc",
|
"serviceAccountName": "wp-svc-acc",
|
||||||
"securityContext": {
|
"securityContext": {
|
||||||
"runAsUser": 101,
|
"runAsUser": 101,
|
||||||
|
@ -289,6 +290,7 @@ func TestFullPod(t *testing.T) {
|
||||||
"status": {}
|
"status": {}
|
||||||
}`
|
}`
|
||||||
|
|
||||||
|
runtimeClass := "runc"
|
||||||
hostAliases := []types.HostAlias{
|
hostAliases := []types.HostAlias{
|
||||||
{Name: "cloudflare", IP: "1.1.1.1"},
|
{Name: "cloudflare", IP: "1.1.1.1"},
|
||||||
{Name: "cf.v6", IP: "2606:4700:4700::64"},
|
{Name: "cf.v6", IP: "2606:4700:4700::64"},
|
||||||
|
@ -333,6 +335,7 @@ func TestFullPod(t *testing.T) {
|
||||||
SecurityContext: SecurityContextConfig{RunAsNonRoot: false},
|
SecurityContext: SecurityContextConfig{RunAsNonRoot: false},
|
||||||
}, "wp-01he8bebctabr3kgk0qj36d2me-0", "linux/amd64", BackendOptions{
|
}, "wp-01he8bebctabr3kgk0qj36d2me-0", "linux/amd64", BackendOptions{
|
||||||
NodeSelector: map[string]string{"storage": "ssd"},
|
NodeSelector: map[string]string{"storage": "ssd"},
|
||||||
|
RuntimeClassName: &runtimeClass,
|
||||||
ServiceAccountName: "wp-svc-acc",
|
ServiceAccountName: "wp-svc-acc",
|
||||||
Tolerations: []Toleration{{Key: "net-port", Value: "100Mbit", Effect: TaintEffectNoSchedule}},
|
Tolerations: []Toleration{{Key: "net-port", Value: "100Mbit", Effect: TaintEffectNoSchedule}},
|
||||||
Resources: Resources{
|
Resources: Resources{
|
||||||
|
|
|
@ -2,28 +2,12 @@ package errors
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"go.uber.org/multierr"
|
"go.uber.org/multierr"
|
||||||
|
|
||||||
|
"go.woodpecker-ci.org/woodpecker/v2/pipeline/errors/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
type PipelineErrorType string
|
|
||||||
|
|
||||||
const (
|
|
||||||
PipelineErrorTypeLinter PipelineErrorType = "linter" // some error with the config syntax
|
|
||||||
PipelineErrorTypeDeprecation PipelineErrorType = "deprecation" // using some deprecated feature
|
|
||||||
PipelineErrorTypeCompiler PipelineErrorType = "compiler" // some error with the config semantics
|
|
||||||
PipelineErrorTypeGeneric PipelineErrorType = "generic" // some generic error
|
|
||||||
PipelineErrorTypeBadHabit PipelineErrorType = "bad_habit" // some bad-habit error
|
|
||||||
)
|
|
||||||
|
|
||||||
type PipelineError struct {
|
|
||||||
Type PipelineErrorType `json:"type"`
|
|
||||||
Message string `json:"message"`
|
|
||||||
IsWarning bool `json:"is_warning"`
|
|
||||||
Data any `json:"data"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type LinterErrorData struct {
|
type LinterErrorData struct {
|
||||||
File string `json:"file"`
|
File string `json:"file"`
|
||||||
Field string `json:"field"`
|
Field string `json:"field"`
|
||||||
|
@ -35,12 +19,8 @@ type DeprecationErrorData struct {
|
||||||
Docs string `json:"docs"`
|
Docs string `json:"docs"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *PipelineError) Error() string {
|
func GetLinterData(e *types.PipelineError) *LinterErrorData {
|
||||||
return fmt.Sprintf("[%s] %s", e.Type, e.Message)
|
if e.Type != types.PipelineErrorTypeLinter {
|
||||||
}
|
|
||||||
|
|
||||||
func (e *PipelineError) GetLinterData() *LinterErrorData {
|
|
||||||
if e.Type != PipelineErrorTypeLinter {
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,16 +31,16 @@ func (e *PipelineError) GetLinterData() *LinterErrorData {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetPipelineErrors(err error) []*PipelineError {
|
func GetPipelineErrors(err error) []*types.PipelineError {
|
||||||
var pipelineErrors []*PipelineError
|
var pipelineErrors []*types.PipelineError
|
||||||
for _, _err := range multierr.Errors(err) {
|
for _, _err := range multierr.Errors(err) {
|
||||||
var err *PipelineError
|
var err *types.PipelineError
|
||||||
if errors.As(_err, &err) {
|
if errors.As(_err, &err) {
|
||||||
pipelineErrors = append(pipelineErrors, err)
|
pipelineErrors = append(pipelineErrors, err)
|
||||||
} else {
|
} else {
|
||||||
pipelineErrors = append(pipelineErrors, &PipelineError{
|
pipelineErrors = append(pipelineErrors, &types.PipelineError{
|
||||||
Message: _err.Error(),
|
Message: _err.Error(),
|
||||||
Type: PipelineErrorTypeGeneric,
|
Type: types.PipelineErrorTypeGeneric,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"go.uber.org/multierr"
|
"go.uber.org/multierr"
|
||||||
|
|
||||||
pipeline_errors "go.woodpecker-ci.org/woodpecker/v2/pipeline/errors"
|
pipeline_errors "go.woodpecker-ci.org/woodpecker/v2/pipeline/errors"
|
||||||
|
"go.woodpecker-ci.org/woodpecker/v2/pipeline/errors/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestGetPipelineErrors(t *testing.T) {
|
func TestGetPipelineErrors(t *testing.T) {
|
||||||
|
@ -16,7 +17,7 @@ func TestGetPipelineErrors(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
title string
|
title string
|
||||||
err error
|
err error
|
||||||
expected []*pipeline_errors.PipelineError
|
expected []*types.PipelineError
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
title: "nil error",
|
title: "nil error",
|
||||||
|
@ -25,10 +26,10 @@ func TestGetPipelineErrors(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "warning",
|
title: "warning",
|
||||||
err: &pipeline_errors.PipelineError{
|
err: &types.PipelineError{
|
||||||
IsWarning: true,
|
IsWarning: true,
|
||||||
},
|
},
|
||||||
expected: []*pipeline_errors.PipelineError{
|
expected: []*types.PipelineError{
|
||||||
{
|
{
|
||||||
IsWarning: true,
|
IsWarning: true,
|
||||||
},
|
},
|
||||||
|
@ -36,10 +37,10 @@ func TestGetPipelineErrors(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "pipeline error",
|
title: "pipeline error",
|
||||||
err: &pipeline_errors.PipelineError{
|
err: &types.PipelineError{
|
||||||
IsWarning: false,
|
IsWarning: false,
|
||||||
},
|
},
|
||||||
expected: []*pipeline_errors.PipelineError{
|
expected: []*types.PipelineError{
|
||||||
{
|
{
|
||||||
IsWarning: false,
|
IsWarning: false,
|
||||||
},
|
},
|
||||||
|
@ -48,14 +49,14 @@ func TestGetPipelineErrors(t *testing.T) {
|
||||||
{
|
{
|
||||||
title: "multiple warnings",
|
title: "multiple warnings",
|
||||||
err: multierr.Combine(
|
err: multierr.Combine(
|
||||||
&pipeline_errors.PipelineError{
|
&types.PipelineError{
|
||||||
IsWarning: true,
|
IsWarning: true,
|
||||||
},
|
},
|
||||||
&pipeline_errors.PipelineError{
|
&types.PipelineError{
|
||||||
IsWarning: true,
|
IsWarning: true,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
expected: []*pipeline_errors.PipelineError{
|
expected: []*types.PipelineError{
|
||||||
{
|
{
|
||||||
IsWarning: true,
|
IsWarning: true,
|
||||||
},
|
},
|
||||||
|
@ -67,15 +68,15 @@ func TestGetPipelineErrors(t *testing.T) {
|
||||||
{
|
{
|
||||||
title: "multiple errors and warnings",
|
title: "multiple errors and warnings",
|
||||||
err: multierr.Combine(
|
err: multierr.Combine(
|
||||||
&pipeline_errors.PipelineError{
|
&types.PipelineError{
|
||||||
IsWarning: true,
|
IsWarning: true,
|
||||||
},
|
},
|
||||||
&pipeline_errors.PipelineError{
|
&types.PipelineError{
|
||||||
IsWarning: false,
|
IsWarning: false,
|
||||||
},
|
},
|
||||||
errors.New("some error"),
|
errors.New("some error"),
|
||||||
),
|
),
|
||||||
expected: []*pipeline_errors.PipelineError{
|
expected: []*types.PipelineError{
|
||||||
{
|
{
|
||||||
IsWarning: true,
|
IsWarning: true,
|
||||||
},
|
},
|
||||||
|
@ -83,7 +84,7 @@ func TestGetPipelineErrors(t *testing.T) {
|
||||||
IsWarning: false,
|
IsWarning: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Type: pipeline_errors.PipelineErrorTypeGeneric,
|
Type: types.PipelineErrorTypeGeneric,
|
||||||
IsWarning: false,
|
IsWarning: false,
|
||||||
Message: "some error",
|
Message: "some error",
|
||||||
},
|
},
|
||||||
|
@ -111,14 +112,14 @@ func TestHasBlockingErrors(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "warning",
|
title: "warning",
|
||||||
err: &pipeline_errors.PipelineError{
|
err: &types.PipelineError{
|
||||||
IsWarning: true,
|
IsWarning: true,
|
||||||
},
|
},
|
||||||
expected: false,
|
expected: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "pipeline error",
|
title: "pipeline error",
|
||||||
err: &pipeline_errors.PipelineError{
|
err: &types.PipelineError{
|
||||||
IsWarning: false,
|
IsWarning: false,
|
||||||
},
|
},
|
||||||
expected: true,
|
expected: true,
|
||||||
|
@ -126,10 +127,10 @@ func TestHasBlockingErrors(t *testing.T) {
|
||||||
{
|
{
|
||||||
title: "multiple warnings",
|
title: "multiple warnings",
|
||||||
err: multierr.Combine(
|
err: multierr.Combine(
|
||||||
&pipeline_errors.PipelineError{
|
&types.PipelineError{
|
||||||
IsWarning: true,
|
IsWarning: true,
|
||||||
},
|
},
|
||||||
&pipeline_errors.PipelineError{
|
&types.PipelineError{
|
||||||
IsWarning: true,
|
IsWarning: true,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
@ -138,10 +139,10 @@ func TestHasBlockingErrors(t *testing.T) {
|
||||||
{
|
{
|
||||||
title: "multiple errors and warnings",
|
title: "multiple errors and warnings",
|
||||||
err: multierr.Combine(
|
err: multierr.Combine(
|
||||||
&pipeline_errors.PipelineError{
|
&types.PipelineError{
|
||||||
IsWarning: true,
|
IsWarning: true,
|
||||||
},
|
},
|
||||||
&pipeline_errors.PipelineError{
|
&types.PipelineError{
|
||||||
IsWarning: false,
|
IsWarning: false,
|
||||||
},
|
},
|
||||||
errors.New("some error"),
|
errors.New("some error"),
|
||||||
|
|
24
pipeline/errors/types/errors.go
Normal file
24
pipeline/errors/types/errors.go
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
package types
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
type PipelineErrorType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
PipelineErrorTypeLinter PipelineErrorType = "linter" // some error with the config syntax
|
||||||
|
PipelineErrorTypeDeprecation PipelineErrorType = "deprecation" // using some deprecated feature
|
||||||
|
PipelineErrorTypeCompiler PipelineErrorType = "compiler" // some error with the config semantics
|
||||||
|
PipelineErrorTypeGeneric PipelineErrorType = "generic" // some generic error
|
||||||
|
PipelineErrorTypeBadHabit PipelineErrorType = "bad_habit" // some bad-habit error
|
||||||
|
)
|
||||||
|
|
||||||
|
type PipelineError struct {
|
||||||
|
Type PipelineErrorType `json:"type"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
IsWarning bool `json:"is_warning"`
|
||||||
|
Data any `json:"data"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *PipelineError) Error() string {
|
||||||
|
return fmt.Sprintf("[%s] %s", e.Type, e.Message)
|
||||||
|
}
|
|
@ -1,117 +0,0 @@
|
||||||
// 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 compiler
|
|
||||||
|
|
||||||
import (
|
|
||||||
"path"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
yaml_types "go.woodpecker-ci.org/woodpecker/v2/pipeline/frontend/yaml/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Cacher defines a compiler transform that can be used
|
|
||||||
// to implement default caching for a repository.
|
|
||||||
type Cacher interface {
|
|
||||||
Restore(repo, branch string, mounts []string) *yaml_types.Container
|
|
||||||
Rebuild(repo, branch string, mounts []string) *yaml_types.Container
|
|
||||||
}
|
|
||||||
|
|
||||||
type volumeCacher struct {
|
|
||||||
base string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *volumeCacher) Restore(repo, branch string, mounts []string) *yaml_types.Container {
|
|
||||||
return &yaml_types.Container{
|
|
||||||
Name: "rebuild_cache",
|
|
||||||
Image: "plugins/volume-cache:1.0.0",
|
|
||||||
Settings: map[string]any{
|
|
||||||
"mount": mounts,
|
|
||||||
"path": "/cache",
|
|
||||||
"restore": true,
|
|
||||||
"file": strings.ReplaceAll(branch, "/", "_") + ".tar",
|
|
||||||
"fallback_to": "main.tar",
|
|
||||||
},
|
|
||||||
Volumes: yaml_types.Volumes{
|
|
||||||
Volumes: []*yaml_types.Volume{
|
|
||||||
{
|
|
||||||
Source: path.Join(c.base, repo),
|
|
||||||
Destination: "/cache",
|
|
||||||
// TODO add access mode
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *volumeCacher) Rebuild(repo, branch string, mounts []string) *yaml_types.Container {
|
|
||||||
return &yaml_types.Container{
|
|
||||||
Name: "rebuild_cache",
|
|
||||||
Image: "plugins/volume-cache:1.0.0",
|
|
||||||
Settings: map[string]any{
|
|
||||||
"mount": mounts,
|
|
||||||
"path": "/cache",
|
|
||||||
"rebuild": true,
|
|
||||||
"flush": true,
|
|
||||||
"file": strings.ReplaceAll(branch, "/", "_") + ".tar",
|
|
||||||
},
|
|
||||||
Volumes: yaml_types.Volumes{
|
|
||||||
Volumes: []*yaml_types.Volume{
|
|
||||||
{
|
|
||||||
Source: path.Join(c.base, repo),
|
|
||||||
Destination: "/cache",
|
|
||||||
// TODO add access mode
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type s3Cacher struct {
|
|
||||||
bucket string
|
|
||||||
access string
|
|
||||||
secret string
|
|
||||||
region string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *s3Cacher) Restore(_, _ string, mounts []string) *yaml_types.Container {
|
|
||||||
return &yaml_types.Container{
|
|
||||||
Name: "rebuild_cache",
|
|
||||||
Image: "plugins/s3-cache:latest",
|
|
||||||
Settings: map[string]any{
|
|
||||||
"mount": mounts,
|
|
||||||
"access_key": c.access,
|
|
||||||
"secret_key": c.secret,
|
|
||||||
"bucket": c.bucket,
|
|
||||||
"region": c.region,
|
|
||||||
"rebuild": true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *s3Cacher) Rebuild(_, _ string, mounts []string) *yaml_types.Container {
|
|
||||||
return &yaml_types.Container{
|
|
||||||
Name: "rebuild_cache",
|
|
||||||
Image: "plugins/s3-cache:latest",
|
|
||||||
Settings: map[string]any{
|
|
||||||
"mount": mounts,
|
|
||||||
"access_key": c.access,
|
|
||||||
"secret_key": c.secret,
|
|
||||||
"bucket": c.bucket,
|
|
||||||
"region": c.region,
|
|
||||||
"rebuild": true,
|
|
||||||
"flush": true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -16,7 +16,6 @@ package compiler
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"path"
|
|
||||||
|
|
||||||
backend_types "go.woodpecker-ci.org/woodpecker/v2/pipeline/backend/types"
|
backend_types "go.woodpecker-ci.org/woodpecker/v2/pipeline/backend/types"
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/pipeline/frontend/metadata"
|
"go.woodpecker-ci.org/woodpecker/v2/pipeline/frontend/metadata"
|
||||||
|
@ -34,7 +33,6 @@ type Registry struct {
|
||||||
Hostname string
|
Hostname string
|
||||||
Username string
|
Username string
|
||||||
Password string
|
Password string
|
||||||
Token string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Secret struct {
|
type Secret struct {
|
||||||
|
@ -68,7 +66,7 @@ func (s *Secret) Match(event string) bool {
|
||||||
if len(s.Events) == 0 {
|
if len(s.Events) == 0 {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
// tread all pull events the same way
|
// treat all pull events the same way
|
||||||
if event == "pull_request_closed" {
|
if event == "pull_request_closed" {
|
||||||
event = "pull_request"
|
event = "pull_request"
|
||||||
}
|
}
|
||||||
|
@ -82,8 +80,6 @@ func (s *Secret) Match(event string) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
type secretMap map[string]Secret
|
|
||||||
|
|
||||||
type ResourceLimit struct {
|
type ResourceLimit struct {
|
||||||
MemSwapLimit int64
|
MemSwapLimit int64
|
||||||
MemLimit int64
|
MemLimit int64
|
||||||
|
@ -106,8 +102,7 @@ type Compiler struct {
|
||||||
path string
|
path string
|
||||||
metadata metadata.Metadata
|
metadata metadata.Metadata
|
||||||
registries []Registry
|
registries []Registry
|
||||||
secrets secretMap
|
secrets map[string]Secret
|
||||||
cacher Cacher
|
|
||||||
reslimit ResourceLimit
|
reslimit ResourceLimit
|
||||||
defaultCloneImage string
|
defaultCloneImage string
|
||||||
trustedPipeline bool
|
trustedPipeline bool
|
||||||
|
@ -224,11 +219,6 @@ func (c *Compiler) Compile(conf *yaml_types.Workflow) (*backend_types.Config, er
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
err := c.setupCache(conf, config)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// add services steps
|
// add services steps
|
||||||
if len(conf.Services.ContainerList) != 0 {
|
if len(conf.Services.ContainerList) != 0 {
|
||||||
stage := new(backend_types.Stage)
|
stage := new(backend_types.Stage)
|
||||||
|
@ -297,48 +287,5 @@ func (c *Compiler) Compile(conf *yaml_types.Workflow) (*backend_types.Config, er
|
||||||
|
|
||||||
config.Stages = append(config.Stages, stepStages...)
|
config.Stages = append(config.Stages, stepStages...)
|
||||||
|
|
||||||
err = c.setupCacheRebuild(conf, config)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return config, nil
|
return config, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Compiler) setupCache(conf *yaml_types.Workflow, ir *backend_types.Config) error {
|
|
||||||
if c.local || len(conf.Cache) == 0 || c.cacher == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
container := c.cacher.Restore(path.Join(c.metadata.Repo.Owner, c.metadata.Repo.Name), c.metadata.Curr.Commit.Branch, conf.Cache)
|
|
||||||
step, err := c.createProcess(container, backend_types.StepTypeCache)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
stage := new(backend_types.Stage)
|
|
||||||
stage.Steps = append(stage.Steps, step)
|
|
||||||
|
|
||||||
ir.Stages = append(ir.Stages, stage)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Compiler) setupCacheRebuild(conf *yaml_types.Workflow, ir *backend_types.Config) error {
|
|
||||||
if c.local || len(conf.Cache) == 0 || c.metadata.Curr.Event != metadata.EventPush || c.cacher == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
container := c.cacher.Rebuild(path.Join(c.metadata.Repo.Owner, c.metadata.Repo.Name), c.metadata.Curr.Commit.Branch, conf.Cache)
|
|
||||||
|
|
||||||
step, err := c.createProcess(container, backend_types.StepTypeCache)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
stage := new(backend_types.Stage)
|
|
||||||
stage.Steps = append(stage.Steps, step)
|
|
||||||
|
|
||||||
ir.Stages = append(ir.Stages, stage)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
|
@ -149,34 +149,6 @@ func WithEnviron(env map[string]string) Option {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithCacher configures the compiler with default cache settings.
|
|
||||||
func WithCacher(cacher Cacher) Option {
|
|
||||||
return func(compiler *Compiler) {
|
|
||||||
compiler.cacher = cacher
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithVolumeCacher configures the compiler with default local volume
|
|
||||||
// caching enabled.
|
|
||||||
func WithVolumeCacher(base string) Option {
|
|
||||||
return func(compiler *Compiler) {
|
|
||||||
compiler.cacher = &volumeCacher{base: base}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithS3Cacher configures the compiler with default amazon s3
|
|
||||||
// caching enabled.
|
|
||||||
func WithS3Cacher(access, secret, region, bucket string) Option {
|
|
||||||
return func(compiler *Compiler) {
|
|
||||||
compiler.cacher = &s3Cacher{
|
|
||||||
access: access,
|
|
||||||
secret: secret,
|
|
||||||
bucket: bucket,
|
|
||||||
region: region,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithNetworks configures the compiler with additional networks
|
// WithNetworks configures the compiler with additional networks
|
||||||
// to be connected to pipeline containers
|
// to be connected to pipeline containers
|
||||||
func WithNetworks(networks ...string) Option {
|
func WithNetworks(networks ...string) Option {
|
||||||
|
|
|
@ -166,30 +166,9 @@ func TestWithEnviron(t *testing.T) {
|
||||||
assert.Equal(t, "true", compiler.env["SHOW"])
|
assert.Equal(t, "true", compiler.env["SHOW"])
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWithVolumeCacher(t *testing.T) {
|
|
||||||
compiler := New(
|
|
||||||
WithVolumeCacher("/cache"),
|
|
||||||
)
|
|
||||||
cacher, ok := compiler.cacher.(*volumeCacher)
|
|
||||||
assert.True(t, ok)
|
|
||||||
assert.Equal(t, "/cache", cacher.base)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestWithDefaultCloneImage(t *testing.T) {
|
func TestWithDefaultCloneImage(t *testing.T) {
|
||||||
compiler := New(
|
compiler := New(
|
||||||
WithDefaultCloneImage("not-an-image"),
|
WithDefaultCloneImage("not-an-image"),
|
||||||
)
|
)
|
||||||
assert.Equal(t, "not-an-image", compiler.defaultCloneImage)
|
assert.Equal(t, "not-an-image", compiler.defaultCloneImage)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWithS3Cacher(t *testing.T) {
|
|
||||||
compiler := New(
|
|
||||||
WithS3Cacher("some-access-key", "some-secret-key", "some-region", "some-bucket"),
|
|
||||||
)
|
|
||||||
cacher, ok := compiler.cacher.(*s3Cacher)
|
|
||||||
assert.True(t, ok)
|
|
||||||
assert.Equal(t, "some-bucket", cacher.bucket)
|
|
||||||
assert.Equal(t, "some-access-key", cacher.access)
|
|
||||||
assert.Equal(t, "some-region", cacher.region)
|
|
||||||
assert.Equal(t, "some-secret-key", cacher.secret)
|
|
||||||
}
|
|
||||||
|
|
|
@ -16,11 +16,12 @@ package linter
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/pipeline/errors"
|
"go.woodpecker-ci.org/woodpecker/v2/pipeline/errors"
|
||||||
|
errorTypes "go.woodpecker-ci.org/woodpecker/v2/pipeline/errors/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newLinterError(message, file, field string, isWarning bool) *errors.PipelineError {
|
func newLinterError(message, file, field string, isWarning bool) *errorTypes.PipelineError {
|
||||||
return &errors.PipelineError{
|
return &errorTypes.PipelineError{
|
||||||
Type: errors.PipelineErrorTypeLinter,
|
Type: errorTypes.PipelineErrorTypeLinter,
|
||||||
Message: message,
|
Message: message,
|
||||||
Data: &errors.LinterErrorData{File: file, Field: field},
|
Data: &errors.LinterErrorData{File: file, Field: field},
|
||||||
IsWarning: isWarning,
|
IsWarning: isWarning,
|
||||||
|
|
|
@ -21,6 +21,7 @@ import (
|
||||||
"go.uber.org/multierr"
|
"go.uber.org/multierr"
|
||||||
|
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/pipeline/errors"
|
"go.woodpecker-ci.org/woodpecker/v2/pipeline/errors"
|
||||||
|
errorTypes "go.woodpecker-ci.org/woodpecker/v2/pipeline/errors/types"
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/pipeline/frontend/yaml/linter/schema"
|
"go.woodpecker-ci.org/woodpecker/v2/pipeline/frontend/yaml/linter/schema"
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/pipeline/frontend/yaml/types"
|
"go.woodpecker-ci.org/woodpecker/v2/pipeline/frontend/yaml/types"
|
||||||
)
|
)
|
||||||
|
@ -210,8 +211,8 @@ func (l *Linter) lintDeprecations(config *WorkflowConfig) (err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if parsed.PipelineDoNotUseIt.ContainerList != nil {
|
if parsed.PipelineDoNotUseIt.ContainerList != nil {
|
||||||
err = multierr.Append(err, &errors.PipelineError{
|
err = multierr.Append(err, &errorTypes.PipelineError{
|
||||||
Type: errors.PipelineErrorTypeDeprecation,
|
Type: errorTypes.PipelineErrorTypeDeprecation,
|
||||||
Message: "Please use 'steps:' instead of deprecated 'pipeline:' list",
|
Message: "Please use 'steps:' instead of deprecated 'pipeline:' list",
|
||||||
Data: errors.DeprecationErrorData{
|
Data: errors.DeprecationErrorData{
|
||||||
File: config.File,
|
File: config.File,
|
||||||
|
@ -223,8 +224,8 @@ func (l *Linter) lintDeprecations(config *WorkflowConfig) (err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if parsed.PlatformDoNotUseIt != "" {
|
if parsed.PlatformDoNotUseIt != "" {
|
||||||
err = multierr.Append(err, &errors.PipelineError{
|
err = multierr.Append(err, &errorTypes.PipelineError{
|
||||||
Type: errors.PipelineErrorTypeDeprecation,
|
Type: errorTypes.PipelineErrorTypeDeprecation,
|
||||||
Message: "Please use labels instead of deprecated 'platform' filters",
|
Message: "Please use labels instead of deprecated 'platform' filters",
|
||||||
Data: errors.DeprecationErrorData{
|
Data: errors.DeprecationErrorData{
|
||||||
File: config.File,
|
File: config.File,
|
||||||
|
@ -236,8 +237,8 @@ func (l *Linter) lintDeprecations(config *WorkflowConfig) (err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if parsed.BranchesDoNotUseIt != nil {
|
if parsed.BranchesDoNotUseIt != nil {
|
||||||
err = multierr.Append(err, &errors.PipelineError{
|
err = multierr.Append(err, &errorTypes.PipelineError{
|
||||||
Type: errors.PipelineErrorTypeDeprecation,
|
Type: errorTypes.PipelineErrorTypeDeprecation,
|
||||||
Message: "Please use global when instead of deprecated 'branches' filter",
|
Message: "Please use global when instead of deprecated 'branches' filter",
|
||||||
Data: errors.DeprecationErrorData{
|
Data: errors.DeprecationErrorData{
|
||||||
File: config.File,
|
File: config.File,
|
||||||
|
@ -250,8 +251,8 @@ func (l *Linter) lintDeprecations(config *WorkflowConfig) (err error) {
|
||||||
|
|
||||||
for _, step := range parsed.Steps.ContainerList {
|
for _, step := range parsed.Steps.ContainerList {
|
||||||
if step.Group != "" {
|
if step.Group != "" {
|
||||||
err = multierr.Append(err, &errors.PipelineError{
|
err = multierr.Append(err, &errorTypes.PipelineError{
|
||||||
Type: errors.PipelineErrorTypeDeprecation,
|
Type: errorTypes.PipelineErrorTypeDeprecation,
|
||||||
Message: "Please use depends_on instead of deprecated 'group' setting",
|
Message: "Please use depends_on instead of deprecated 'group' setting",
|
||||||
Data: errors.DeprecationErrorData{
|
Data: errors.DeprecationErrorData{
|
||||||
File: config.File,
|
File: config.File,
|
||||||
|
@ -265,8 +266,8 @@ func (l *Linter) lintDeprecations(config *WorkflowConfig) (err error) {
|
||||||
|
|
||||||
for i, c := range parsed.When.Constraints {
|
for i, c := range parsed.When.Constraints {
|
||||||
if len(c.Event.Exclude) != 0 {
|
if len(c.Event.Exclude) != 0 {
|
||||||
err = multierr.Append(err, &errors.PipelineError{
|
err = multierr.Append(err, &errorTypes.PipelineError{
|
||||||
Type: errors.PipelineErrorTypeDeprecation,
|
Type: errorTypes.PipelineErrorTypeDeprecation,
|
||||||
Message: "Please only use allow lists for events",
|
Message: "Please only use allow lists for events",
|
||||||
Data: errors.DeprecationErrorData{
|
Data: errors.DeprecationErrorData{
|
||||||
File: config.File,
|
File: config.File,
|
||||||
|
@ -281,8 +282,8 @@ func (l *Linter) lintDeprecations(config *WorkflowConfig) (err error) {
|
||||||
for _, step := range parsed.Steps.ContainerList {
|
for _, step := range parsed.Steps.ContainerList {
|
||||||
for i, c := range step.When.Constraints {
|
for i, c := range step.When.Constraints {
|
||||||
if len(c.Event.Exclude) != 0 {
|
if len(c.Event.Exclude) != 0 {
|
||||||
err = multierr.Append(err, &errors.PipelineError{
|
err = multierr.Append(err, &errorTypes.PipelineError{
|
||||||
Type: errors.PipelineErrorTypeDeprecation,
|
Type: errorTypes.PipelineErrorTypeDeprecation,
|
||||||
Message: "Please only use allow lists for events",
|
Message: "Please only use allow lists for events",
|
||||||
Data: errors.DeprecationErrorData{
|
Data: errors.DeprecationErrorData{
|
||||||
File: config.File,
|
File: config.File,
|
||||||
|
@ -298,8 +299,8 @@ func (l *Linter) lintDeprecations(config *WorkflowConfig) (err error) {
|
||||||
for _, step := range parsed.Steps.ContainerList {
|
for _, step := range parsed.Steps.ContainerList {
|
||||||
for i, c := range step.Secrets.Secrets {
|
for i, c := range step.Secrets.Secrets {
|
||||||
if c.Source != c.Target {
|
if c.Source != c.Target {
|
||||||
err = multierr.Append(err, &errors.PipelineError{
|
err = multierr.Append(err, &errorTypes.PipelineError{
|
||||||
Type: errors.PipelineErrorTypeDeprecation,
|
Type: errorTypes.PipelineErrorTypeDeprecation,
|
||||||
Message: "Secrets alternative names are deprecated, use environment with from_secret",
|
Message: "Secrets alternative names are deprecated, use environment with from_secret",
|
||||||
Data: errors.DeprecationErrorData{
|
Data: errors.DeprecationErrorData{
|
||||||
File: config.File,
|
File: config.File,
|
||||||
|
@ -348,8 +349,8 @@ func (l *Linter) lintBadHabits(config *WorkflowConfig) (err error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if field != "" {
|
if field != "" {
|
||||||
err = multierr.Append(err, &errors.PipelineError{
|
err = multierr.Append(err, &errorTypes.PipelineError{
|
||||||
Type: errors.PipelineErrorTypeBadHabit,
|
Type: errorTypes.PipelineErrorTypeBadHabit,
|
||||||
Message: "Please set an event filter on all when branches",
|
Message: "Please set an event filter on all when branches",
|
||||||
Data: errors.LinterErrorData{
|
Data: errors.LinterErrorData{
|
||||||
File: config.File,
|
File: config.File,
|
||||||
|
|
|
@ -696,6 +696,10 @@
|
||||||
},
|
},
|
||||||
"securityContext": {
|
"securityContext": {
|
||||||
"$ref": "#/definitions/step_backend_kubernetes_security_context"
|
"$ref": "#/definitions/step_backend_kubernetes_security_context"
|
||||||
|
},
|
||||||
|
"runtimeClassName": {
|
||||||
|
"description": "Read more: https://woodpecker-ci.org/docs/administration/backends/kubernetes#runtimeclassname",
|
||||||
|
"type": "string"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -19,7 +19,7 @@ import (
|
||||||
|
|
||||||
"codeberg.org/6543/xyaml"
|
"codeberg.org/6543/xyaml"
|
||||||
|
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/pipeline/errors"
|
errorTypes "go.woodpecker-ci.org/woodpecker/v2/pipeline/errors/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -116,7 +116,7 @@ func parse(raw []byte) (Matrix, error) {
|
||||||
Matrix map[string][]string
|
Matrix map[string][]string
|
||||||
}{}
|
}{}
|
||||||
if err := xyaml.Unmarshal(raw, &data); err != nil {
|
if err := xyaml.Unmarshal(raw, &data); err != nil {
|
||||||
return nil, &errors.PipelineError{Message: err.Error(), Type: errors.PipelineErrorTypeCompiler}
|
return nil, &errorTypes.PipelineError{Message: err.Error(), Type: errorTypes.PipelineErrorTypeCompiler}
|
||||||
}
|
}
|
||||||
return data.Matrix, nil
|
return data.Matrix, nil
|
||||||
}
|
}
|
||||||
|
@ -129,7 +129,7 @@ func parseList(raw []byte) ([]Axis, error) {
|
||||||
}{}
|
}{}
|
||||||
|
|
||||||
if err := xyaml.Unmarshal(raw, &data); err != nil {
|
if err := xyaml.Unmarshal(raw, &data); err != nil {
|
||||||
return nil, &errors.PipelineError{Message: err.Error(), Type: errors.PipelineErrorTypeCompiler}
|
return nil, &errorTypes.PipelineError{Message: err.Error(), Type: errorTypes.PipelineErrorTypeCompiler}
|
||||||
}
|
}
|
||||||
return data.Matrix.Include, nil
|
return data.Matrix.Include, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,6 @@ package types
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/pipeline/frontend/yaml/constraint"
|
"go.woodpecker-ci.org/woodpecker/v2/pipeline/frontend/yaml/constraint"
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/pipeline/frontend/yaml/types/base"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
|
@ -33,7 +32,6 @@ type (
|
||||||
SkipClone bool `yaml:"skip_clone"`
|
SkipClone bool `yaml:"skip_clone"`
|
||||||
|
|
||||||
// Undocumented
|
// Undocumented
|
||||||
Cache base.StringOrSlice `yaml:"cache,omitempty"`
|
|
||||||
Networks WorkflowNetworks `yaml:"networks,omitempty"`
|
Networks WorkflowNetworks `yaml:"networks,omitempty"`
|
||||||
Volumes WorkflowVolumes `yaml:"volumes,omitempty"`
|
Volumes WorkflowVolumes `yaml:"volumes,omitempty"`
|
||||||
|
|
||||||
|
|
|
@ -16,8 +16,3 @@ package proto
|
||||||
|
|
||||||
//go:generate protoc --go_out=paths=source_relative:. woodpecker.proto
|
//go:generate protoc --go_out=paths=source_relative:. woodpecker.proto
|
||||||
//go:generate protoc --go-grpc_out=paths=source_relative:. woodpecker.proto
|
//go:generate protoc --go-grpc_out=paths=source_relative:. woodpecker.proto
|
||||||
|
|
||||||
// install protoc: https://grpc.io/docs/protoc-installation/
|
|
||||||
// and get needed binary's:
|
|
||||||
// go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
|
|
||||||
// go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
|
|
||||||
|
|
|
@ -15,8 +15,8 @@
|
||||||
|
|
||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.32.0
|
// protoc-gen-go v1.33.0
|
||||||
// protoc v4.25.1
|
// protoc v4.24.4
|
||||||
// source: woodpecker.proto
|
// source: woodpecker.proto
|
||||||
|
|
||||||
package proto
|
package proto
|
||||||
|
|
|
@ -1,22 +1,7 @@
|
||||||
// Copyright 2021 Woodpecker Authors
|
|
||||||
// Copyright 2011 Drone.IO Inc.
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
|
|
||||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// - protoc-gen-go-grpc v1.3.0
|
// - protoc-gen-go-grpc v1.3.0
|
||||||
// - protoc v4.25.1
|
// - protoc v4.24.4
|
||||||
// source: woodpecker.proto
|
// source: woodpecker.proto
|
||||||
|
|
||||||
package proto
|
package proto
|
||||||
|
@ -74,7 +59,7 @@ func NewWoodpeckerClient(cc grpc.ClientConnInterface) WoodpeckerClient {
|
||||||
|
|
||||||
func (c *woodpeckerClient) Version(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*VersionResponse, error) {
|
func (c *woodpeckerClient) Version(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*VersionResponse, error) {
|
||||||
out := new(VersionResponse)
|
out := new(VersionResponse)
|
||||||
err := c.cc.Invoke(ctx, Woodpecker_Version_FullMethodName, in, out, opts...)
|
err := c.cc.Invoke(ctx, "/proto.Woodpecker/Version", in, out, opts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -83,7 +68,7 @@ func (c *woodpeckerClient) Version(ctx context.Context, in *Empty, opts ...grpc.
|
||||||
|
|
||||||
func (c *woodpeckerClient) Next(ctx context.Context, in *NextRequest, opts ...grpc.CallOption) (*NextResponse, error) {
|
func (c *woodpeckerClient) Next(ctx context.Context, in *NextRequest, opts ...grpc.CallOption) (*NextResponse, error) {
|
||||||
out := new(NextResponse)
|
out := new(NextResponse)
|
||||||
err := c.cc.Invoke(ctx, Woodpecker_Next_FullMethodName, in, out, opts...)
|
err := c.cc.Invoke(ctx, "/proto.Woodpecker/Next", in, out, opts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -92,7 +77,7 @@ func (c *woodpeckerClient) Next(ctx context.Context, in *NextRequest, opts ...gr
|
||||||
|
|
||||||
func (c *woodpeckerClient) Init(ctx context.Context, in *InitRequest, opts ...grpc.CallOption) (*Empty, error) {
|
func (c *woodpeckerClient) Init(ctx context.Context, in *InitRequest, opts ...grpc.CallOption) (*Empty, error) {
|
||||||
out := new(Empty)
|
out := new(Empty)
|
||||||
err := c.cc.Invoke(ctx, Woodpecker_Init_FullMethodName, in, out, opts...)
|
err := c.cc.Invoke(ctx, "/proto.Woodpecker/Init", in, out, opts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -101,7 +86,7 @@ func (c *woodpeckerClient) Init(ctx context.Context, in *InitRequest, opts ...gr
|
||||||
|
|
||||||
func (c *woodpeckerClient) Wait(ctx context.Context, in *WaitRequest, opts ...grpc.CallOption) (*Empty, error) {
|
func (c *woodpeckerClient) Wait(ctx context.Context, in *WaitRequest, opts ...grpc.CallOption) (*Empty, error) {
|
||||||
out := new(Empty)
|
out := new(Empty)
|
||||||
err := c.cc.Invoke(ctx, Woodpecker_Wait_FullMethodName, in, out, opts...)
|
err := c.cc.Invoke(ctx, "/proto.Woodpecker/Wait", in, out, opts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -110,7 +95,7 @@ func (c *woodpeckerClient) Wait(ctx context.Context, in *WaitRequest, opts ...gr
|
||||||
|
|
||||||
func (c *woodpeckerClient) Done(ctx context.Context, in *DoneRequest, opts ...grpc.CallOption) (*Empty, error) {
|
func (c *woodpeckerClient) Done(ctx context.Context, in *DoneRequest, opts ...grpc.CallOption) (*Empty, error) {
|
||||||
out := new(Empty)
|
out := new(Empty)
|
||||||
err := c.cc.Invoke(ctx, Woodpecker_Done_FullMethodName, in, out, opts...)
|
err := c.cc.Invoke(ctx, "/proto.Woodpecker/Done", in, out, opts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -119,7 +104,7 @@ func (c *woodpeckerClient) Done(ctx context.Context, in *DoneRequest, opts ...gr
|
||||||
|
|
||||||
func (c *woodpeckerClient) Extend(ctx context.Context, in *ExtendRequest, opts ...grpc.CallOption) (*Empty, error) {
|
func (c *woodpeckerClient) Extend(ctx context.Context, in *ExtendRequest, opts ...grpc.CallOption) (*Empty, error) {
|
||||||
out := new(Empty)
|
out := new(Empty)
|
||||||
err := c.cc.Invoke(ctx, Woodpecker_Extend_FullMethodName, in, out, opts...)
|
err := c.cc.Invoke(ctx, "/proto.Woodpecker/Extend", in, out, opts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -128,7 +113,7 @@ func (c *woodpeckerClient) Extend(ctx context.Context, in *ExtendRequest, opts .
|
||||||
|
|
||||||
func (c *woodpeckerClient) Update(ctx context.Context, in *UpdateRequest, opts ...grpc.CallOption) (*Empty, error) {
|
func (c *woodpeckerClient) Update(ctx context.Context, in *UpdateRequest, opts ...grpc.CallOption) (*Empty, error) {
|
||||||
out := new(Empty)
|
out := new(Empty)
|
||||||
err := c.cc.Invoke(ctx, Woodpecker_Update_FullMethodName, in, out, opts...)
|
err := c.cc.Invoke(ctx, "/proto.Woodpecker/Update", in, out, opts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -137,7 +122,7 @@ func (c *woodpeckerClient) Update(ctx context.Context, in *UpdateRequest, opts .
|
||||||
|
|
||||||
func (c *woodpeckerClient) Log(ctx context.Context, in *LogRequest, opts ...grpc.CallOption) (*Empty, error) {
|
func (c *woodpeckerClient) Log(ctx context.Context, in *LogRequest, opts ...grpc.CallOption) (*Empty, error) {
|
||||||
out := new(Empty)
|
out := new(Empty)
|
||||||
err := c.cc.Invoke(ctx, Woodpecker_Log_FullMethodName, in, out, opts...)
|
err := c.cc.Invoke(ctx, "/proto.Woodpecker/Log", in, out, opts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -146,7 +131,7 @@ func (c *woodpeckerClient) Log(ctx context.Context, in *LogRequest, opts ...grpc
|
||||||
|
|
||||||
func (c *woodpeckerClient) RegisterAgent(ctx context.Context, in *RegisterAgentRequest, opts ...grpc.CallOption) (*RegisterAgentResponse, error) {
|
func (c *woodpeckerClient) RegisterAgent(ctx context.Context, in *RegisterAgentRequest, opts ...grpc.CallOption) (*RegisterAgentResponse, error) {
|
||||||
out := new(RegisterAgentResponse)
|
out := new(RegisterAgentResponse)
|
||||||
err := c.cc.Invoke(ctx, Woodpecker_RegisterAgent_FullMethodName, in, out, opts...)
|
err := c.cc.Invoke(ctx, "/proto.Woodpecker/RegisterAgent", in, out, opts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -164,7 +149,7 @@ func (c *woodpeckerClient) UnregisterAgent(ctx context.Context, in *Empty, opts
|
||||||
|
|
||||||
func (c *woodpeckerClient) ReportHealth(ctx context.Context, in *ReportHealthRequest, opts ...grpc.CallOption) (*Empty, error) {
|
func (c *woodpeckerClient) ReportHealth(ctx context.Context, in *ReportHealthRequest, opts ...grpc.CallOption) (*Empty, error) {
|
||||||
out := new(Empty)
|
out := new(Empty)
|
||||||
err := c.cc.Invoke(ctx, Woodpecker_ReportHealth_FullMethodName, in, out, opts...)
|
err := c.cc.Invoke(ctx, "/proto.Woodpecker/ReportHealth", in, out, opts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -249,7 +234,7 @@ func _Woodpecker_Version_Handler(srv interface{}, ctx context.Context, dec func(
|
||||||
}
|
}
|
||||||
info := &grpc.UnaryServerInfo{
|
info := &grpc.UnaryServerInfo{
|
||||||
Server: srv,
|
Server: srv,
|
||||||
FullMethod: Woodpecker_Version_FullMethodName,
|
FullMethod: "/proto.Woodpecker/Version",
|
||||||
}
|
}
|
||||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
return srv.(WoodpeckerServer).Version(ctx, req.(*Empty))
|
return srv.(WoodpeckerServer).Version(ctx, req.(*Empty))
|
||||||
|
@ -267,7 +252,7 @@ func _Woodpecker_Next_Handler(srv interface{}, ctx context.Context, dec func(int
|
||||||
}
|
}
|
||||||
info := &grpc.UnaryServerInfo{
|
info := &grpc.UnaryServerInfo{
|
||||||
Server: srv,
|
Server: srv,
|
||||||
FullMethod: Woodpecker_Next_FullMethodName,
|
FullMethod: "/proto.Woodpecker/Next",
|
||||||
}
|
}
|
||||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
return srv.(WoodpeckerServer).Next(ctx, req.(*NextRequest))
|
return srv.(WoodpeckerServer).Next(ctx, req.(*NextRequest))
|
||||||
|
@ -285,7 +270,7 @@ func _Woodpecker_Init_Handler(srv interface{}, ctx context.Context, dec func(int
|
||||||
}
|
}
|
||||||
info := &grpc.UnaryServerInfo{
|
info := &grpc.UnaryServerInfo{
|
||||||
Server: srv,
|
Server: srv,
|
||||||
FullMethod: Woodpecker_Init_FullMethodName,
|
FullMethod: "/proto.Woodpecker/Init",
|
||||||
}
|
}
|
||||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
return srv.(WoodpeckerServer).Init(ctx, req.(*InitRequest))
|
return srv.(WoodpeckerServer).Init(ctx, req.(*InitRequest))
|
||||||
|
@ -303,7 +288,7 @@ func _Woodpecker_Wait_Handler(srv interface{}, ctx context.Context, dec func(int
|
||||||
}
|
}
|
||||||
info := &grpc.UnaryServerInfo{
|
info := &grpc.UnaryServerInfo{
|
||||||
Server: srv,
|
Server: srv,
|
||||||
FullMethod: Woodpecker_Wait_FullMethodName,
|
FullMethod: "/proto.Woodpecker/Wait",
|
||||||
}
|
}
|
||||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
return srv.(WoodpeckerServer).Wait(ctx, req.(*WaitRequest))
|
return srv.(WoodpeckerServer).Wait(ctx, req.(*WaitRequest))
|
||||||
|
@ -321,7 +306,7 @@ func _Woodpecker_Done_Handler(srv interface{}, ctx context.Context, dec func(int
|
||||||
}
|
}
|
||||||
info := &grpc.UnaryServerInfo{
|
info := &grpc.UnaryServerInfo{
|
||||||
Server: srv,
|
Server: srv,
|
||||||
FullMethod: Woodpecker_Done_FullMethodName,
|
FullMethod: "/proto.Woodpecker/Done",
|
||||||
}
|
}
|
||||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
return srv.(WoodpeckerServer).Done(ctx, req.(*DoneRequest))
|
return srv.(WoodpeckerServer).Done(ctx, req.(*DoneRequest))
|
||||||
|
@ -339,7 +324,7 @@ func _Woodpecker_Extend_Handler(srv interface{}, ctx context.Context, dec func(i
|
||||||
}
|
}
|
||||||
info := &grpc.UnaryServerInfo{
|
info := &grpc.UnaryServerInfo{
|
||||||
Server: srv,
|
Server: srv,
|
||||||
FullMethod: Woodpecker_Extend_FullMethodName,
|
FullMethod: "/proto.Woodpecker/Extend",
|
||||||
}
|
}
|
||||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
return srv.(WoodpeckerServer).Extend(ctx, req.(*ExtendRequest))
|
return srv.(WoodpeckerServer).Extend(ctx, req.(*ExtendRequest))
|
||||||
|
@ -357,7 +342,7 @@ func _Woodpecker_Update_Handler(srv interface{}, ctx context.Context, dec func(i
|
||||||
}
|
}
|
||||||
info := &grpc.UnaryServerInfo{
|
info := &grpc.UnaryServerInfo{
|
||||||
Server: srv,
|
Server: srv,
|
||||||
FullMethod: Woodpecker_Update_FullMethodName,
|
FullMethod: "/proto.Woodpecker/Update",
|
||||||
}
|
}
|
||||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
return srv.(WoodpeckerServer).Update(ctx, req.(*UpdateRequest))
|
return srv.(WoodpeckerServer).Update(ctx, req.(*UpdateRequest))
|
||||||
|
@ -375,7 +360,7 @@ func _Woodpecker_Log_Handler(srv interface{}, ctx context.Context, dec func(inte
|
||||||
}
|
}
|
||||||
info := &grpc.UnaryServerInfo{
|
info := &grpc.UnaryServerInfo{
|
||||||
Server: srv,
|
Server: srv,
|
||||||
FullMethod: Woodpecker_Log_FullMethodName,
|
FullMethod: "/proto.Woodpecker/Log",
|
||||||
}
|
}
|
||||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
return srv.(WoodpeckerServer).Log(ctx, req.(*LogRequest))
|
return srv.(WoodpeckerServer).Log(ctx, req.(*LogRequest))
|
||||||
|
@ -393,7 +378,7 @@ func _Woodpecker_RegisterAgent_Handler(srv interface{}, ctx context.Context, dec
|
||||||
}
|
}
|
||||||
info := &grpc.UnaryServerInfo{
|
info := &grpc.UnaryServerInfo{
|
||||||
Server: srv,
|
Server: srv,
|
||||||
FullMethod: Woodpecker_RegisterAgent_FullMethodName,
|
FullMethod: "/proto.Woodpecker/RegisterAgent",
|
||||||
}
|
}
|
||||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
return srv.(WoodpeckerServer).RegisterAgent(ctx, req.(*RegisterAgentRequest))
|
return srv.(WoodpeckerServer).RegisterAgent(ctx, req.(*RegisterAgentRequest))
|
||||||
|
@ -429,7 +414,7 @@ func _Woodpecker_ReportHealth_Handler(srv interface{}, ctx context.Context, dec
|
||||||
}
|
}
|
||||||
info := &grpc.UnaryServerInfo{
|
info := &grpc.UnaryServerInfo{
|
||||||
Server: srv,
|
Server: srv,
|
||||||
FullMethod: Woodpecker_ReportHealth_FullMethodName,
|
FullMethod: "/proto.Woodpecker/ReportHealth",
|
||||||
}
|
}
|
||||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
return srv.(WoodpeckerServer).ReportHealth(ctx, req.(*ReportHealthRequest))
|
return srv.(WoodpeckerServer).ReportHealth(ctx, req.(*ReportHealthRequest))
|
||||||
|
@ -493,10 +478,6 @@ var Woodpecker_ServiceDesc = grpc.ServiceDesc{
|
||||||
Metadata: "woodpecker.proto",
|
Metadata: "woodpecker.proto",
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
|
||||||
WoodpeckerAuth_Auth_FullMethodName = "/proto.WoodpeckerAuth/Auth"
|
|
||||||
)
|
|
||||||
|
|
||||||
// WoodpeckerAuthClient is the client API for WoodpeckerAuth service.
|
// WoodpeckerAuthClient is the client API for WoodpeckerAuth service.
|
||||||
//
|
//
|
||||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
||||||
|
@ -514,7 +495,7 @@ func NewWoodpeckerAuthClient(cc grpc.ClientConnInterface) WoodpeckerAuthClient {
|
||||||
|
|
||||||
func (c *woodpeckerAuthClient) Auth(ctx context.Context, in *AuthRequest, opts ...grpc.CallOption) (*AuthResponse, error) {
|
func (c *woodpeckerAuthClient) Auth(ctx context.Context, in *AuthRequest, opts ...grpc.CallOption) (*AuthResponse, error) {
|
||||||
out := new(AuthResponse)
|
out := new(AuthResponse)
|
||||||
err := c.cc.Invoke(ctx, WoodpeckerAuth_Auth_FullMethodName, in, out, opts...)
|
err := c.cc.Invoke(ctx, "/proto.WoodpeckerAuth/Auth", in, out, opts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -559,7 +540,7 @@ func _WoodpeckerAuth_Auth_Handler(srv interface{}, ctx context.Context, dec func
|
||||||
}
|
}
|
||||||
info := &grpc.UnaryServerInfo{
|
info := &grpc.UnaryServerInfo{
|
||||||
Server: srv,
|
Server: srv,
|
||||||
FullMethod: WoodpeckerAuth_Auth_FullMethodName,
|
FullMethod: "/proto.WoodpeckerAuth/Auth",
|
||||||
}
|
}
|
||||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
return srv.(WoodpeckerAuthServer).Auth(ctx, req.(*AuthRequest))
|
return srv.(WoodpeckerAuthServer).Auth(ctx, req.(*AuthRequest))
|
||||||
|
|
|
@ -20,6 +20,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/server"
|
"go.woodpecker-ci.org/woodpecker/v2/server"
|
||||||
cronScheduler "go.woodpecker-ci.org/woodpecker/v2/server/cron"
|
cronScheduler "go.woodpecker-ci.org/woodpecker/v2/server/cron"
|
||||||
|
@ -80,7 +81,7 @@ func RunCron(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
repo, newPipeline, err := cronScheduler.CreatePipeline(c, _store, server.Config.Services.Forge, cron)
|
repo, newPipeline, err := cronScheduler.CreatePipeline(c, _store, cron)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.String(http.StatusInternalServerError, "Error creating pipeline for cron %q. %s", id, err)
|
c.String(http.StatusInternalServerError, "Error creating pipeline for cron %q. %s", id, err)
|
||||||
return
|
return
|
||||||
|
@ -109,7 +110,12 @@ func PostCron(c *gin.Context) {
|
||||||
repo := session.Repo(c)
|
repo := session.Repo(c)
|
||||||
user := session.User(c)
|
user := session.User(c)
|
||||||
_store := store.FromContext(c)
|
_store := store.FromContext(c)
|
||||||
forge := server.Config.Services.Forge
|
_forge, err := server.Config.Services.Manager.ForgeFromRepo(repo)
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Err(err).Msg("Cannot get forge from repo")
|
||||||
|
c.AbortWithStatus(http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
in := new(model.Cron)
|
in := new(model.Cron)
|
||||||
if err := c.Bind(in); err != nil {
|
if err := c.Bind(in); err != nil {
|
||||||
|
@ -137,7 +143,7 @@ func PostCron(c *gin.Context) {
|
||||||
|
|
||||||
if in.Branch != "" {
|
if in.Branch != "" {
|
||||||
// check if branch exists on forge
|
// check if branch exists on forge
|
||||||
_, err := forge.BranchHead(c, user, repo, in.Branch)
|
_, err := _forge.BranchHead(c, user, repo, in.Branch)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.String(http.StatusBadRequest, "Error inserting cron. branch not resolved: %s", err)
|
c.String(http.StatusBadRequest, "Error inserting cron. branch not resolved: %s", err)
|
||||||
return
|
return
|
||||||
|
@ -166,7 +172,12 @@ func PatchCron(c *gin.Context) {
|
||||||
repo := session.Repo(c)
|
repo := session.Repo(c)
|
||||||
user := session.User(c)
|
user := session.User(c)
|
||||||
_store := store.FromContext(c)
|
_store := store.FromContext(c)
|
||||||
forge := server.Config.Services.Forge
|
_forge, err := server.Config.Services.Manager.ForgeFromRepo(repo)
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Err(err).Msg("Cannot get forge from repo")
|
||||||
|
c.AbortWithStatus(http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
id, err := strconv.ParseInt(c.Param("cron"), 10, 64)
|
id, err := strconv.ParseInt(c.Param("cron"), 10, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -188,7 +199,7 @@ func PatchCron(c *gin.Context) {
|
||||||
}
|
}
|
||||||
if in.Branch != "" {
|
if in.Branch != "" {
|
||||||
// check if branch exists on forge
|
// check if branch exists on forge
|
||||||
_, err := forge.BranchHead(c, user, repo, in.Branch)
|
_, err := _forge.BranchHead(c, user, repo, in.Branch)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.String(http.StatusBadRequest, "Error inserting cron. branch not resolved: %s", err)
|
c.String(http.StatusBadRequest, "Error inserting cron. branch not resolved: %s", err)
|
||||||
return
|
return
|
||||||
|
|
|
@ -19,6 +19,7 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/server"
|
"go.woodpecker-ci.org/woodpecker/v2/server"
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/server/forge"
|
"go.woodpecker-ci.org/woodpecker/v2/server/forge"
|
||||||
|
@ -54,7 +55,12 @@ func handleDBError(c *gin.Context, err error) {
|
||||||
// If the forge has a refresh token, the current access token may be stale.
|
// If the forge has a refresh token, the current access token may be stale.
|
||||||
// Therefore, we should refresh prior to dispatching the job.
|
// Therefore, we should refresh prior to dispatching the job.
|
||||||
func refreshUserToken(c *gin.Context, user *model.User) {
|
func refreshUserToken(c *gin.Context, user *model.User) {
|
||||||
_forge := server.Config.Services.Forge
|
|
||||||
_store := store.FromContext(c)
|
_store := store.FromContext(c)
|
||||||
|
_forge, err := server.Config.Services.Manager.ForgeFromUser(user)
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Err(err).Msg("Cannot get forge from user")
|
||||||
|
c.AbortWithStatus(http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
forge.Refresh(c, _forge, _store, user)
|
forge.Refresh(c, _forge, _store, user)
|
||||||
}
|
}
|
||||||
|
|
|
@ -104,7 +104,13 @@ func BlockTilQueueHasRunningItem(c *gin.Context) {
|
||||||
// @Param hook body object true "the webhook payload; forge is automatically detected"
|
// @Param hook body object true "the webhook payload; forge is automatically detected"
|
||||||
func PostHook(c *gin.Context) {
|
func PostHook(c *gin.Context) {
|
||||||
_store := store.FromContext(c)
|
_store := store.FromContext(c)
|
||||||
_forge := server.Config.Services.Forge
|
|
||||||
|
_forge, err := server.Config.Services.Manager.ForgeMain() // TODO: get the forge for the specific repo somehow
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Err(err).Msg("Cannot get main forge")
|
||||||
|
c.AbortWithStatus(http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// 1. Parse webhook
|
// 1. Parse webhook
|
||||||
|
|
|
@ -43,7 +43,12 @@ func HandleLogin(c *gin.Context) {
|
||||||
|
|
||||||
func HandleAuth(c *gin.Context) {
|
func HandleAuth(c *gin.Context) {
|
||||||
_store := store.FromContext(c)
|
_store := store.FromContext(c)
|
||||||
_forge := server.Config.Services.Forge
|
_forge, err := server.Config.Services.Manager.ForgeMain()
|
||||||
|
if err != nil {
|
||||||
|
_ = c.AbortWithError(http.StatusInternalServerError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
forgeID := int64(1) // TODO: replace with forge id when multiple forges are supported
|
||||||
|
|
||||||
// when dealing with redirects, we may need to adjust the content type. I
|
// when dealing with redirects, we may need to adjust the content type. I
|
||||||
// cannot, however, remember why, so need to revisit this line.
|
// cannot, however, remember why, so need to revisit this line.
|
||||||
|
@ -68,12 +73,12 @@ func HandleAuth(c *gin.Context) {
|
||||||
|
|
||||||
// get the user from the database
|
// get the user from the database
|
||||||
u, err := _store.GetUserRemoteID(tmpuser.ForgeRemoteID, tmpuser.Login)
|
u, err := _store.GetUserRemoteID(tmpuser.ForgeRemoteID, tmpuser.Login)
|
||||||
if err != nil {
|
if err != nil && !errors.Is(err, types.RecordNotExist) {
|
||||||
if !errors.Is(err, types.RecordNotExist) {
|
|
||||||
_ = c.AbortWithError(http.StatusInternalServerError, err)
|
_ = c.AbortWithError(http.StatusInternalServerError, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if errors.Is(err, types.RecordNotExist) {
|
||||||
// if self-registration is disabled we should return a not authorized error
|
// if self-registration is disabled we should return a not authorized error
|
||||||
if !server.Config.Permissions.Open && !server.Config.Permissions.Admins.IsAdmin(tmpuser) {
|
if !server.Config.Permissions.Open && !server.Config.Permissions.Admins.IsAdmin(tmpuser) {
|
||||||
log.Error().Msgf("cannot register %s. registration closed", tmpuser.Login)
|
log.Error().Msgf("cannot register %s. registration closed", tmpuser.Login)
|
||||||
|
@ -100,6 +105,7 @@ func HandleAuth(c *gin.Context) {
|
||||||
Secret: tmpuser.Secret,
|
Secret: tmpuser.Secret,
|
||||||
Email: tmpuser.Email,
|
Email: tmpuser.Email,
|
||||||
Avatar: tmpuser.Avatar,
|
Avatar: tmpuser.Avatar,
|
||||||
|
ForgeID: forgeID,
|
||||||
Hash: base32.StdEncoding.EncodeToString(
|
Hash: base32.StdEncoding.EncodeToString(
|
||||||
securecookie.GenerateRandomKey(32),
|
securecookie.GenerateRandomKey(32),
|
||||||
),
|
),
|
||||||
|
@ -129,6 +135,7 @@ func HandleAuth(c *gin.Context) {
|
||||||
Name: u.Login,
|
Name: u.Login,
|
||||||
IsUser: true,
|
IsUser: true,
|
||||||
Private: false,
|
Private: false,
|
||||||
|
ForgeID: u.ForgeID,
|
||||||
}
|
}
|
||||||
if err := _store.OrgCreate(org); err != nil {
|
if err := _store.OrgCreate(org); err != nil {
|
||||||
log.Error().Err(err).Msgf("on user creation, could create org for user")
|
log.Error().Err(err).Msgf("on user creation, could create org for user")
|
||||||
|
@ -228,14 +235,21 @@ func GetLogout(c *gin.Context) {
|
||||||
func GetLoginToken(c *gin.Context) {
|
func GetLoginToken(c *gin.Context) {
|
||||||
_store := store.FromContext(c)
|
_store := store.FromContext(c)
|
||||||
|
|
||||||
|
_forge, err := server.Config.Services.Manager.ForgeMain() // TODO: get selected forge from auth request
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Err(err).Msg("Cannot get main forge")
|
||||||
|
c.AbortWithStatus(http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
in := &tokenPayload{}
|
in := &tokenPayload{}
|
||||||
err := c.Bind(in)
|
err = c.Bind(in)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_ = c.AbortWithError(http.StatusBadRequest, err)
|
_ = c.AbortWithError(http.StatusBadRequest, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
login, err := server.Config.Services.Forge.Auth(c, in.Access, in.Refresh)
|
login, err := _forge.Auth(c, in.Access, in.Refresh)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_ = c.AbortWithError(http.StatusUnauthorized, err)
|
_ = c.AbortWithError(http.StatusUnauthorized, err)
|
||||||
return
|
return
|
||||||
|
|
|
@ -68,6 +68,13 @@ func GetOrgPermissions(c *gin.Context) {
|
||||||
user := session.User(c)
|
user := session.User(c)
|
||||||
_store := store.FromContext(c)
|
_store := store.FromContext(c)
|
||||||
|
|
||||||
|
_forge, err := server.Config.Services.Manager.ForgeFromUser(user)
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Err(err).Msg("Cannot get forge from user")
|
||||||
|
c.AbortWithStatus(http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
orgID, err := strconv.ParseInt(c.Param("org_id"), 10, 64)
|
orgID, err := strconv.ParseInt(c.Param("org_id"), 10, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.String(http.StatusBadRequest, "Error parsing org id. %s", err)
|
c.String(http.StatusBadRequest, "Error parsing org id. %s", err)
|
||||||
|
@ -96,7 +103,7 @@ func GetOrgPermissions(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
perm, err := server.Config.Services.Membership.Get(c, user, org.Name)
|
perm, err := server.Config.Services.Membership.Get(c, _forge, user, org.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.String(http.StatusInternalServerError, "Error getting membership for %d. %s", orgID, err)
|
c.String(http.StatusInternalServerError, "Error getting membership for %d. %s", orgID, err)
|
||||||
return
|
return
|
||||||
|
@ -116,6 +123,13 @@ func GetOrgPermissions(c *gin.Context) {
|
||||||
// @Param org_full_name path string true "the organizations full-name / slug"
|
// @Param org_full_name path string true "the organizations full-name / slug"
|
||||||
func LookupOrg(c *gin.Context) {
|
func LookupOrg(c *gin.Context) {
|
||||||
_store := store.FromContext(c)
|
_store := store.FromContext(c)
|
||||||
|
user := session.User(c)
|
||||||
|
_forge, err := server.Config.Services.Manager.ForgeFromUser(user)
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Err(err).Msg("Cannot get forge from user")
|
||||||
|
c.AbortWithStatus(http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
orgFullName := strings.TrimLeft(c.Param("org_full_name"), "/")
|
orgFullName := strings.TrimLeft(c.Param("org_full_name"), "/")
|
||||||
|
|
||||||
|
@ -137,7 +151,7 @@ func LookupOrg(c *gin.Context) {
|
||||||
c.AbortWithStatus(http.StatusNotFound)
|
c.AbortWithStatus(http.StatusNotFound)
|
||||||
return
|
return
|
||||||
} else if !user.Admin {
|
} else if !user.Admin {
|
||||||
perm, err := server.Config.Services.Membership.Get(c, user, org.Name)
|
perm, err := server.Config.Services.Membership.Get(c, _forge, user, org.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().Err(err).Msg("failed to check membership")
|
log.Error().Err(err).Msg("failed to check membership")
|
||||||
c.Status(http.StatusInternalServerError)
|
c.Status(http.StatusInternalServerError)
|
||||||
|
|
|
@ -27,6 +27,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/server"
|
"go.woodpecker-ci.org/woodpecker/v2/server"
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/server/model"
|
"go.woodpecker-ci.org/woodpecker/v2/server/model"
|
||||||
|
@ -48,10 +49,16 @@ import (
|
||||||
func CreatePipeline(c *gin.Context) {
|
func CreatePipeline(c *gin.Context) {
|
||||||
_store := store.FromContext(c)
|
_store := store.FromContext(c)
|
||||||
repo := session.Repo(c)
|
repo := session.Repo(c)
|
||||||
|
_forge, err := server.Config.Services.Manager.ForgeFromRepo(repo)
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Err(err).Msg("Cannot get forge from repo")
|
||||||
|
c.AbortWithStatus(http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// parse create options
|
// parse create options
|
||||||
var opts model.PipelineOptions
|
var opts model.PipelineOptions
|
||||||
err := json.NewDecoder(c.Request.Body).Decode(&opts)
|
err = json.NewDecoder(c.Request.Body).Decode(&opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_ = c.AbortWithError(http.StatusBadRequest, err)
|
_ = c.AbortWithError(http.StatusBadRequest, err)
|
||||||
return
|
return
|
||||||
|
@ -59,7 +66,7 @@ func CreatePipeline(c *gin.Context) {
|
||||||
|
|
||||||
user := session.User(c)
|
user := session.User(c)
|
||||||
|
|
||||||
lastCommit, _ := server.Config.Services.Forge.BranchHead(c, user, repo, opts.Branch)
|
lastCommit, _ := _forge.BranchHead(c, user, repo, opts.Branch)
|
||||||
|
|
||||||
tmpPipeline := createTmpPipeline(model.EventManual, lastCommit, user, &opts)
|
tmpPipeline := createTmpPipeline(model.EventManual, lastCommit, user, &opts)
|
||||||
|
|
||||||
|
@ -224,6 +231,66 @@ func GetStepLogs(c *gin.Context) {
|
||||||
c.JSON(http.StatusOK, logs)
|
c.JSON(http.StatusOK, logs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeleteStepLogs
|
||||||
|
//
|
||||||
|
// @Summary Deletes step log
|
||||||
|
// @Router /repos/{repo_id}/logs/{number}/{stepId} [delete]
|
||||||
|
// @Produce plain
|
||||||
|
// @Success 204
|
||||||
|
// @Tags Pipeline logs
|
||||||
|
// @Param Authorization header string true "Insert your personal access token" default(Bearer <personal access token>)
|
||||||
|
// @Param repo_id path int true "the repository id"
|
||||||
|
// @Param number path int true "the number of the pipeline"
|
||||||
|
// @Param stepId path int true "the step id"
|
||||||
|
func DeleteStepLogs(c *gin.Context) {
|
||||||
|
_store := store.FromContext(c)
|
||||||
|
repo := session.Repo(c)
|
||||||
|
|
||||||
|
pipelineNumber, err := strconv.ParseInt(c.Params.ByName("number"), 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
_ = c.AbortWithError(http.StatusBadRequest, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_pipeline, err := _store.GetPipelineNumber(repo, pipelineNumber)
|
||||||
|
if err != nil {
|
||||||
|
handleDBError(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
stepID, err := strconv.ParseInt(c.Params.ByName("stepId"), 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
_ = c.AbortWithError(http.StatusBadRequest, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_step, err := _store.StepLoad(stepID)
|
||||||
|
if err != nil {
|
||||||
|
handleDBError(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if _step.PipelineID != _pipeline.ID {
|
||||||
|
// make sure we cannot read arbitrary logs by id
|
||||||
|
_ = c.AbortWithError(http.StatusBadRequest, fmt.Errorf("step with id %d is not part of repo %s", stepID, repo.FullName))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch _step.State {
|
||||||
|
case model.StatusRunning, model.StatusPending:
|
||||||
|
c.String(http.StatusUnprocessableEntity, "Cannot delete logs for a pending or running step")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = _store.LogDelete(_step)
|
||||||
|
if err != nil {
|
||||||
|
handleDBError(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Status(http.StatusNoContent)
|
||||||
|
}
|
||||||
|
|
||||||
// GetPipelineConfig
|
// GetPipelineConfig
|
||||||
//
|
//
|
||||||
// @Summary Pipeline configuration
|
// @Summary Pipeline configuration
|
||||||
|
@ -272,6 +339,13 @@ func CancelPipeline(c *gin.Context) {
|
||||||
_store := store.FromContext(c)
|
_store := store.FromContext(c)
|
||||||
repo := session.Repo(c)
|
repo := session.Repo(c)
|
||||||
user := session.User(c)
|
user := session.User(c)
|
||||||
|
_forge, err := server.Config.Services.Manager.ForgeFromRepo(repo)
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Err(err).Msg("Cannot get forge from repo")
|
||||||
|
c.AbortWithStatus(http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
num, _ := strconv.ParseInt(c.Params.ByName("number"), 10, 64)
|
num, _ := strconv.ParseInt(c.Params.ByName("number"), 10, 64)
|
||||||
|
|
||||||
pl, err := _store.GetPipelineNumber(repo, num)
|
pl, err := _store.GetPipelineNumber(repo, num)
|
||||||
|
@ -280,7 +354,7 @@ func CancelPipeline(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := pipeline.Cancel(c, _store, repo, user, pl); err != nil {
|
if err := pipeline.Cancel(c, _forge, _store, repo, user, pl); err != nil {
|
||||||
handlePipelineErr(c, err)
|
handlePipelineErr(c, err)
|
||||||
} else {
|
} else {
|
||||||
c.Status(http.StatusNoContent)
|
c.Status(http.StatusNoContent)
|
||||||
|
@ -407,23 +481,22 @@ func PostPipeline(c *gin.Context) {
|
||||||
refreshUserToken(c, user)
|
refreshUserToken(c, user)
|
||||||
|
|
||||||
// make Deploy overridable
|
// make Deploy overridable
|
||||||
pl.Deploy = c.DefaultQuery("deploy_to", pl.Deploy)
|
|
||||||
|
|
||||||
// make Event overridable to deploy
|
// make Event overridable to deploy
|
||||||
// TODO refactor to use own proper API for deploy
|
// TODO refactor to use own proper API for deploy
|
||||||
if event, ok := c.GetQuery("event"); ok {
|
if event, ok := c.GetQuery("event"); ok {
|
||||||
// only allow deploy from push, tag and release
|
|
||||||
if pl.Event != model.EventPush && pl.Event != model.EventTag && pl.Event != model.EventRelease {
|
|
||||||
_ = c.AbortWithError(http.StatusBadRequest, fmt.Errorf("can only deploy push, tag and release pipelines"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
pl.Event = model.WebhookEvent(event)
|
pl.Event = model.WebhookEvent(event)
|
||||||
|
|
||||||
if pl.Event != model.EventDeploy {
|
if pl.Event != model.EventDeploy {
|
||||||
_ = c.AbortWithError(http.StatusBadRequest, model.ErrInvalidWebhookEvent)
|
_ = c.AbortWithError(http.StatusBadRequest, model.ErrInvalidWebhookEvent)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !repo.AllowDeploy {
|
||||||
|
_ = c.AbortWithError(http.StatusForbidden, fmt.Errorf("repo does not allow deployments"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
pl.Deploy = c.DefaultQuery("deploy_to", pl.Deploy)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read query string parameters into pipelineParams, exclude reserved params
|
// Read query string parameters into pipelineParams, exclude reserved params
|
||||||
|
|
|
@ -45,9 +45,14 @@ import (
|
||||||
// @Param Authorization header string true "Insert your personal access token" default(Bearer <personal access token>)
|
// @Param Authorization header string true "Insert your personal access token" default(Bearer <personal access token>)
|
||||||
// @Param forge_remote_id query string true "the id of a repository at the forge"
|
// @Param forge_remote_id query string true "the id of a repository at the forge"
|
||||||
func PostRepo(c *gin.Context) {
|
func PostRepo(c *gin.Context) {
|
||||||
forge := server.Config.Services.Forge
|
|
||||||
_store := store.FromContext(c)
|
_store := store.FromContext(c)
|
||||||
user := session.User(c)
|
user := session.User(c)
|
||||||
|
_forge, err := server.Config.Services.Manager.ForgeFromUser(user)
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Err(err).Msg("Cannot get forge from user")
|
||||||
|
c.AbortWithStatus(http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
forgeRemoteID := model.ForgeRemoteID(c.Query("forge_remote_id"))
|
forgeRemoteID := model.ForgeRemoteID(c.Query("forge_remote_id"))
|
||||||
if !forgeRemoteID.IsValid() {
|
if !forgeRemoteID.IsValid() {
|
||||||
|
@ -67,7 +72,7 @@ func PostRepo(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
from, err := forge.Repo(c, user, forgeRemoteID, "", "")
|
from, err := _forge.Repo(c, user, forgeRemoteID, "", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.String(http.StatusInternalServerError, "Could not fetch repository from forge.")
|
c.String(http.StatusInternalServerError, "Could not fetch repository from forge.")
|
||||||
return
|
return
|
||||||
|
@ -86,6 +91,7 @@ func PostRepo(c *gin.Context) {
|
||||||
} else {
|
} else {
|
||||||
repo = from
|
repo = from
|
||||||
repo.AllowPull = true
|
repo.AllowPull = true
|
||||||
|
repo.AllowDeploy = false
|
||||||
repo.NetrcOnlyTrusted = true
|
repo.NetrcOnlyTrusted = true
|
||||||
repo.CancelPreviousPipelineEvents = server.Config.Pipeline.DefaultCancelPreviousPipelineEvents
|
repo.CancelPreviousPipelineEvents = server.Config.Pipeline.DefaultCancelPreviousPipelineEvents
|
||||||
}
|
}
|
||||||
|
@ -137,7 +143,7 @@ func PostRepo(c *gin.Context) {
|
||||||
|
|
||||||
// create an org if it doesn't exist yet
|
// create an org if it doesn't exist yet
|
||||||
if errors.Is(err, types.RecordNotExist) {
|
if errors.Is(err, types.RecordNotExist) {
|
||||||
org, err = forge.Org(c, user, repo.Owner)
|
org, err = _forge.Org(c, user, repo.Owner)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
msg := "could not fetch organization from forge."
|
msg := "could not fetch organization from forge."
|
||||||
log.Error().Err(err).Msg(msg)
|
log.Error().Err(err).Msg(msg)
|
||||||
|
@ -145,6 +151,7 @@ func PostRepo(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
org.ForgeID = user.ForgeID
|
||||||
err = _store.OrgCreate(org)
|
err = _store.OrgCreate(org)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
msg := "could not create organization in store."
|
msg := "could not create organization in store."
|
||||||
|
@ -156,7 +163,7 @@ func PostRepo(c *gin.Context) {
|
||||||
|
|
||||||
repo.OrgID = org.ID
|
repo.OrgID = org.ID
|
||||||
|
|
||||||
err = forge.Activate(c, user, repo, hookURL)
|
err = _forge.Activate(c, user, repo, hookURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
msg := "could not create webhook in forge."
|
msg := "could not create webhook in forge."
|
||||||
log.Error().Err(err).Msg(msg)
|
log.Error().Err(err).Msg(msg)
|
||||||
|
@ -167,6 +174,7 @@ func PostRepo(c *gin.Context) {
|
||||||
if enabledOnce {
|
if enabledOnce {
|
||||||
err = _store.UpdateRepo(repo)
|
err = _store.UpdateRepo(repo)
|
||||||
} else {
|
} else {
|
||||||
|
repo.ForgeID = user.ForgeID // TODO: allow to use other connected forges of the user
|
||||||
err = _store.CreateRepo(repo)
|
err = _store.CreateRepo(repo)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -223,6 +231,9 @@ func PatchRepo(c *gin.Context) {
|
||||||
if in.AllowPull != nil {
|
if in.AllowPull != nil {
|
||||||
repo.AllowPull = *in.AllowPull
|
repo.AllowPull = *in.AllowPull
|
||||||
}
|
}
|
||||||
|
if in.AllowDeploy != nil {
|
||||||
|
repo.AllowDeploy = *in.AllowDeploy
|
||||||
|
}
|
||||||
if in.IsGated != nil {
|
if in.IsGated != nil {
|
||||||
repo.IsGated = *in.IsGated
|
repo.IsGated = *in.IsGated
|
||||||
}
|
}
|
||||||
|
@ -338,9 +349,14 @@ func GetRepoPermissions(c *gin.Context) {
|
||||||
func GetRepoBranches(c *gin.Context) {
|
func GetRepoBranches(c *gin.Context) {
|
||||||
repo := session.Repo(c)
|
repo := session.Repo(c)
|
||||||
user := session.User(c)
|
user := session.User(c)
|
||||||
f := server.Config.Services.Forge
|
_forge, err := server.Config.Services.Manager.ForgeFromRepo(repo)
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Err(err).Msg("Cannot get forge from repo")
|
||||||
|
c.AbortWithStatus(http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
branches, err := f.Branches(c, user, repo, session.Pagination(c))
|
branches, err := _forge.Branches(c, user, repo, session.Pagination(c))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_ = c.AbortWithError(http.StatusInternalServerError, err)
|
_ = c.AbortWithError(http.StatusInternalServerError, err)
|
||||||
return
|
return
|
||||||
|
@ -363,9 +379,14 @@ func GetRepoBranches(c *gin.Context) {
|
||||||
func GetRepoPullRequests(c *gin.Context) {
|
func GetRepoPullRequests(c *gin.Context) {
|
||||||
repo := session.Repo(c)
|
repo := session.Repo(c)
|
||||||
user := session.User(c)
|
user := session.User(c)
|
||||||
f := server.Config.Services.Forge
|
_forge, err := server.Config.Services.Manager.ForgeFromRepo(repo)
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Err(err).Msg("Cannot get forge from repo")
|
||||||
|
c.AbortWithStatus(http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
prs, err := f.PullRequests(c, user, repo, session.Pagination(c))
|
prs, err := _forge.PullRequests(c, user, repo, session.Pagination(c))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_ = c.AbortWithError(http.StatusInternalServerError, err)
|
_ = c.AbortWithError(http.StatusInternalServerError, err)
|
||||||
return
|
return
|
||||||
|
@ -386,9 +407,14 @@ func GetRepoPullRequests(c *gin.Context) {
|
||||||
func DeleteRepo(c *gin.Context) {
|
func DeleteRepo(c *gin.Context) {
|
||||||
remove, _ := strconv.ParseBool(c.Query("remove"))
|
remove, _ := strconv.ParseBool(c.Query("remove"))
|
||||||
_store := store.FromContext(c)
|
_store := store.FromContext(c)
|
||||||
|
|
||||||
repo := session.Repo(c)
|
repo := session.Repo(c)
|
||||||
user := session.User(c)
|
user := session.User(c)
|
||||||
|
_forge, err := server.Config.Services.Manager.ForgeFromRepo(repo)
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Err(err).Msg("Cannot get forge from repo")
|
||||||
|
c.AbortWithStatus(http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
repo.IsActive = false
|
repo.IsActive = false
|
||||||
repo.UserID = 0
|
repo.UserID = 0
|
||||||
|
@ -405,7 +431,7 @@ func DeleteRepo(c *gin.Context) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := server.Config.Services.Forge.Deactivate(c, user, repo, server.Config.Server.WebhookHost); err != nil {
|
if err := _forge.Deactivate(c, user, repo, server.Config.Server.WebhookHost); err != nil {
|
||||||
_ = c.AbortWithError(http.StatusInternalServerError, err)
|
_ = c.AbortWithError(http.StatusInternalServerError, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -441,10 +467,15 @@ func RepairRepo(c *gin.Context) {
|
||||||
// @Param repo_id path int true "the repository id"
|
// @Param repo_id path int true "the repository id"
|
||||||
// @Param to query string true "the username to move the repository to"
|
// @Param to query string true "the username to move the repository to"
|
||||||
func MoveRepo(c *gin.Context) {
|
func MoveRepo(c *gin.Context) {
|
||||||
forge := server.Config.Services.Forge
|
|
||||||
_store := store.FromContext(c)
|
_store := store.FromContext(c)
|
||||||
repo := session.Repo(c)
|
repo := session.Repo(c)
|
||||||
user := session.User(c)
|
user := session.User(c)
|
||||||
|
_forge, err := server.Config.Services.Manager.ForgeFromRepo(repo)
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Err(err).Msg("Cannot get forge from repo")
|
||||||
|
c.AbortWithStatus(http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
to, exists := c.GetQuery("to")
|
to, exists := c.GetQuery("to")
|
||||||
if !exists {
|
if !exists {
|
||||||
|
@ -459,7 +490,7 @@ func MoveRepo(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
from, err := forge.Repo(c, user, "", owner, name)
|
from, err := _forge.Repo(c, user, "", owner, name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_ = c.AbortWithError(http.StatusInternalServerError, err)
|
_ = c.AbortWithError(http.StatusInternalServerError, err)
|
||||||
return
|
return
|
||||||
|
@ -504,10 +535,10 @@ func MoveRepo(c *gin.Context) {
|
||||||
sig,
|
sig,
|
||||||
)
|
)
|
||||||
|
|
||||||
if err := forge.Deactivate(c, user, repo, host); err != nil {
|
if err := _forge.Deactivate(c, user, repo, host); err != nil {
|
||||||
log.Trace().Err(err).Msgf("deactivate repo '%s' for move to activate later, got an error", repo.FullName)
|
log.Trace().Err(err).Msgf("deactivate repo '%s' for move to activate later, got an error", repo.FullName)
|
||||||
}
|
}
|
||||||
if err := forge.Activate(c, user, repo, hookURL); err != nil {
|
if err := _forge.Activate(c, user, repo, hookURL); err != nil {
|
||||||
c.String(http.StatusInternalServerError, err.Error())
|
c.String(http.StatusInternalServerError, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -567,8 +598,13 @@ func RepairAllRepos(c *gin.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func repairRepo(c *gin.Context, repo *model.Repo, withPerms, skipOnErr bool) {
|
func repairRepo(c *gin.Context, repo *model.Repo, withPerms, skipOnErr bool) {
|
||||||
forge := server.Config.Services.Forge
|
|
||||||
_store := store.FromContext(c)
|
_store := store.FromContext(c)
|
||||||
|
_forge, err := server.Config.Services.Manager.ForgeFromRepo(repo)
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Err(err).Msg("Cannot get forge from repo")
|
||||||
|
c.AbortWithStatus(http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
user, err := _store.GetUser(repo.UserID)
|
user, err := _store.GetUser(repo.UserID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -599,7 +635,7 @@ func repairRepo(c *gin.Context, repo *model.Repo, withPerms, skipOnErr bool) {
|
||||||
sig,
|
sig,
|
||||||
)
|
)
|
||||||
|
|
||||||
from, err := forge.Repo(c, user, repo.ForgeRemoteID, repo.Owner, repo.Name)
|
from, err := _forge.Repo(c, user, repo.ForgeRemoteID, repo.Owner, repo.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().Err(err).Msgf("get repo '%s/%s' from forge", repo.Owner, repo.Name)
|
log.Error().Err(err).Msgf("get repo '%s/%s' from forge", repo.Owner, repo.Name)
|
||||||
if !skipOnErr {
|
if !skipOnErr {
|
||||||
|
@ -632,10 +668,10 @@ func repairRepo(c *gin.Context, repo *model.Repo, withPerms, skipOnErr bool) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := forge.Deactivate(c, user, repo, host); err != nil {
|
if err := _forge.Deactivate(c, user, repo, host); err != nil {
|
||||||
log.Trace().Err(err).Msgf("deactivate repo '%s' to repair failed", repo.FullName)
|
log.Trace().Err(err).Msgf("deactivate repo '%s' to repair failed", repo.FullName)
|
||||||
}
|
}
|
||||||
if err := forge.Activate(c, user, repo, hookURL); err != nil {
|
if err := _forge.Activate(c, user, repo, hookURL); err != nil {
|
||||||
c.String(http.StatusInternalServerError, err.Error())
|
c.String(http.StatusInternalServerError, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ import (
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/gorilla/securecookie"
|
"github.com/gorilla/securecookie"
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/server"
|
"go.woodpecker-ci.org/woodpecker/v2/server"
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/server/model"
|
"go.woodpecker-ci.org/woodpecker/v2/server/model"
|
||||||
|
@ -86,9 +87,14 @@ func GetFeed(c *gin.Context) {
|
||||||
// @Param all query bool false "query all repos, including inactive ones"
|
// @Param all query bool false "query all repos, including inactive ones"
|
||||||
func GetRepos(c *gin.Context) {
|
func GetRepos(c *gin.Context) {
|
||||||
_store := store.FromContext(c)
|
_store := store.FromContext(c)
|
||||||
_forge := server.Config.Services.Forge
|
|
||||||
|
|
||||||
user := session.User(c)
|
user := session.User(c)
|
||||||
|
_forge, err := server.Config.Services.Manager.ForgeFromUser(user)
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Err(err).Msg("Cannot get forge from user")
|
||||||
|
c.AbortWithStatus(http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
all, _ := strconv.ParseBool(c.Query("all"))
|
all, _ := strconv.ParseBool(c.Query("all"))
|
||||||
|
|
||||||
if all {
|
if all {
|
||||||
|
|
|
@ -132,6 +132,8 @@ func PostUser(c *gin.Context) {
|
||||||
Hash: base32.StdEncoding.EncodeToString(
|
Hash: base32.StdEncoding.EncodeToString(
|
||||||
securecookie.GenerateRandomKey(32),
|
securecookie.GenerateRandomKey(32),
|
||||||
),
|
),
|
||||||
|
ForgeID: 1, // TODO: replace with forge id when multiple forges are supported
|
||||||
|
ForgeRemoteID: model.ForgeRemoteID("0"), // TODO: search for the user in the forge and get the remote id
|
||||||
}
|
}
|
||||||
if err = user.Validate(); err != nil {
|
if err = user.Validate(); err != nil {
|
||||||
c.String(http.StatusBadRequest, err.Error())
|
c.String(http.StatusBadRequest, err.Error())
|
||||||
|
|
16
server/cache/membership.go
vendored
16
server/cache/membership.go
vendored
|
@ -23,39 +23,39 @@ import (
|
||||||
|
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/server/forge"
|
"go.woodpecker-ci.org/woodpecker/v2/server/forge"
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/server/model"
|
"go.woodpecker-ci.org/woodpecker/v2/server/model"
|
||||||
|
"go.woodpecker-ci.org/woodpecker/v2/server/store"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MembershipService is a service to check for user membership.
|
// MembershipService is a service to check for user membership.
|
||||||
type MembershipService interface {
|
type MembershipService interface {
|
||||||
// Get returns if the user is a member of the organization.
|
// Get returns if the user is a member of the organization.
|
||||||
Get(ctx context.Context, u *model.User, org string) (*model.OrgPerm, error)
|
Get(ctx context.Context, _forge forge.Forge, u *model.User, org string) (*model.OrgPerm, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type membershipCache struct {
|
type membershipCache struct {
|
||||||
forge forge.Forge
|
|
||||||
cache *ttlcache.Cache[string, *model.OrgPerm]
|
cache *ttlcache.Cache[string, *model.OrgPerm]
|
||||||
|
store store.Store
|
||||||
ttl time.Duration
|
ttl time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewMembershipService creates a new membership service.
|
// NewMembershipService creates a new membership service.
|
||||||
func NewMembershipService(f forge.Forge) MembershipService {
|
func NewMembershipService(_store store.Store) MembershipService {
|
||||||
//nolint:gomnd
|
|
||||||
return &membershipCache{
|
return &membershipCache{
|
||||||
ttl: 10 * time.Minute,
|
ttl: 10 * time.Minute, //nolint: gomnd
|
||||||
forge: f,
|
store: _store,
|
||||||
cache: ttlcache.New(ttlcache.WithDisableTouchOnHit[string, *model.OrgPerm]()),
|
cache: ttlcache.New(ttlcache.WithDisableTouchOnHit[string, *model.OrgPerm]()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get returns if the user is a member of the organization.
|
// Get returns if the user is a member of the organization.
|
||||||
func (c *membershipCache) Get(ctx context.Context, u *model.User, org string) (*model.OrgPerm, error) {
|
func (c *membershipCache) Get(ctx context.Context, _forge forge.Forge, u *model.User, org string) (*model.OrgPerm, error) {
|
||||||
key := fmt.Sprintf("%s-%s", u.ForgeRemoteID, org)
|
key := fmt.Sprintf("%s-%s", u.ForgeRemoteID, org)
|
||||||
item := c.cache.Get(key)
|
item := c.cache.Get(key)
|
||||||
if item != nil && !item.IsExpired() {
|
if item != nil && !item.IsExpired() {
|
||||||
return item.Value(), nil
|
return item.Value(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
perm, err := c.forge.OrgMembership(ctx, u, org)
|
perm, err := _forge.OrgMembership(ctx, u, org)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,6 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/server/cache"
|
"go.woodpecker-ci.org/woodpecker/v2/server/cache"
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/server/forge"
|
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/server/logging"
|
"go.woodpecker-ci.org/woodpecker/v2/server/logging"
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/server/model"
|
"go.woodpecker-ci.org/woodpecker/v2/server/model"
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/server/pubsub"
|
"go.woodpecker-ci.org/woodpecker/v2/server/pubsub"
|
||||||
|
@ -35,9 +34,8 @@ var Config = struct {
|
||||||
Pubsub *pubsub.Publisher
|
Pubsub *pubsub.Publisher
|
||||||
Queue queue.Queue
|
Queue queue.Queue
|
||||||
Logs logging.Log
|
Logs logging.Log
|
||||||
Forge forge.Forge
|
|
||||||
Membership cache.MembershipService
|
Membership cache.MembershipService
|
||||||
Manager *services.Manager
|
Manager services.Manager
|
||||||
}
|
}
|
||||||
Server struct {
|
Server struct {
|
||||||
Key string
|
Key string
|
||||||
|
|
|
@ -22,6 +22,7 @@ import (
|
||||||
"github.com/robfig/cron"
|
"github.com/robfig/cron"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
|
|
||||||
|
"go.woodpecker-ci.org/woodpecker/v2/server"
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/server/forge"
|
"go.woodpecker-ci.org/woodpecker/v2/server/forge"
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/server/model"
|
"go.woodpecker-ci.org/woodpecker/v2/server/model"
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/server/pipeline"
|
"go.woodpecker-ci.org/woodpecker/v2/server/pipeline"
|
||||||
|
@ -37,7 +38,7 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
// Start starts the cron scheduler loop
|
// Start starts the cron scheduler loop
|
||||||
func Start(ctx context.Context, store store.Store, forge forge.Forge) error {
|
func Start(ctx context.Context, store store.Store) error {
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
|
@ -54,7 +55,7 @@ func Start(ctx context.Context, store store.Store, forge forge.Forge) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, cron := range crons {
|
for _, cron := range crons {
|
||||||
if err := runCron(store, forge, cron, now); err != nil {
|
if err := runCron(ctx, store, cron, now); err != nil {
|
||||||
log.Error().Err(err).Int64("cronID", cron.ID).Msg("run cron failed")
|
log.Error().Err(err).Int64("cronID", cron.ID).Msg("run cron failed")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -77,9 +78,8 @@ func CalcNewNext(schedule string, now time.Time) (time.Time, error) {
|
||||||
return c.Next(now), nil
|
return c.Next(now), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func runCron(store store.Store, forge forge.Forge, cron *model.Cron, now time.Time) error {
|
func runCron(ctx context.Context, store store.Store, cron *model.Cron, now time.Time) error {
|
||||||
log.Trace().Msgf("cron: run id[%d]", cron.ID)
|
log.Trace().Msgf("cron: run id[%d]", cron.ID)
|
||||||
ctx := context.Background()
|
|
||||||
|
|
||||||
newNext, err := CalcNewNext(cron.Schedule, now)
|
newNext, err := CalcNewNext(cron.Schedule, now)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -96,7 +96,7 @@ func runCron(store store.Store, forge forge.Forge, cron *model.Cron, now time.Ti
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
repo, newPipeline, err := CreatePipeline(ctx, store, forge, cron)
|
repo, newPipeline, err := CreatePipeline(ctx, store, cron)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -105,12 +105,17 @@ func runCron(store store.Store, forge forge.Forge, cron *model.Cron, now time.Ti
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreatePipeline(ctx context.Context, store store.Store, f forge.Forge, cron *model.Cron) (*model.Repo, *model.Pipeline, error) {
|
func CreatePipeline(ctx context.Context, store store.Store, cron *model.Cron) (*model.Repo, *model.Pipeline, error) {
|
||||||
repo, err := store.GetRepo(cron.RepoID)
|
repo, err := store.GetRepo(cron.RepoID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_forge, err := server.Config.Services.Manager.ForgeFromRepo(repo)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
if cron.Branch == "" {
|
if cron.Branch == "" {
|
||||||
// fallback to the repos default branch
|
// fallback to the repos default branch
|
||||||
cron.Branch = repo.Branch
|
cron.Branch = repo.Branch
|
||||||
|
@ -124,9 +129,9 @@ func CreatePipeline(ctx context.Context, store store.Store, f forge.Forge, cron
|
||||||
// If the forge has a refresh token, the current access token
|
// If the forge has a refresh token, the current access token
|
||||||
// may be stale. Therefore, we should refresh prior to dispatching
|
// may be stale. Therefore, we should refresh prior to dispatching
|
||||||
// the pipeline.
|
// the pipeline.
|
||||||
forge.Refresh(ctx, f, store, creator)
|
forge.Refresh(ctx, _forge, store, creator)
|
||||||
|
|
||||||
commit, err := f.BranchHead(ctx, creator, repo, cron.Branch)
|
commit, err := _forge.BranchHead(ctx, creator, repo, cron.Branch)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,13 +22,16 @@ import (
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/mock"
|
"github.com/stretchr/testify/mock"
|
||||||
|
|
||||||
|
"go.woodpecker-ci.org/woodpecker/v2/server"
|
||||||
mocks_forge "go.woodpecker-ci.org/woodpecker/v2/server/forge/mocks"
|
mocks_forge "go.woodpecker-ci.org/woodpecker/v2/server/forge/mocks"
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/server/model"
|
"go.woodpecker-ci.org/woodpecker/v2/server/model"
|
||||||
|
mocks_manager "go.woodpecker-ci.org/woodpecker/v2/server/services/mocks"
|
||||||
mocks_store "go.woodpecker-ci.org/woodpecker/v2/server/store/mocks"
|
mocks_store "go.woodpecker-ci.org/woodpecker/v2/server/store/mocks"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestCreateBuild(t *testing.T) {
|
func TestCreatePipeline(t *testing.T) {
|
||||||
forge := mocks_forge.NewForge(t)
|
_manager := mocks_manager.NewManager(t)
|
||||||
|
_forge := mocks_forge.NewForge(t)
|
||||||
store := mocks_store.NewStore(t)
|
store := mocks_store.NewStore(t)
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
|
@ -47,12 +50,14 @@ func TestCreateBuild(t *testing.T) {
|
||||||
// mock things
|
// mock things
|
||||||
store.On("GetRepo", mock.Anything).Return(repo1, nil)
|
store.On("GetRepo", mock.Anything).Return(repo1, nil)
|
||||||
store.On("GetUser", mock.Anything).Return(creator, nil)
|
store.On("GetUser", mock.Anything).Return(creator, nil)
|
||||||
forge.On("BranchHead", mock.Anything, creator, repo1, "default").Return(&model.Commit{
|
_forge.On("BranchHead", mock.Anything, creator, repo1, "default").Return(&model.Commit{
|
||||||
ForgeURL: "https://example.com/sha1",
|
ForgeURL: "https://example.com/sha1",
|
||||||
SHA: "sha1",
|
SHA: "sha1",
|
||||||
}, nil)
|
}, nil)
|
||||||
|
_manager.On("ForgeFromRepo", repo1).Return(_forge, nil)
|
||||||
|
server.Config.Services.Manager = _manager
|
||||||
|
|
||||||
_, pipeline, err := CreatePipeline(ctx, store, forge, &model.Cron{
|
_, pipeline, err := CreatePipeline(ctx, store, &model.Cron{
|
||||||
Name: "test",
|
Name: "test",
|
||||||
})
|
})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
154
server/forge/addon/args.go
Normal file
154
server/forge/addon/args.go
Normal file
|
@ -0,0 +1,154 @@
|
||||||
|
// 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 addon
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go.woodpecker-ci.org/woodpecker/v2/server/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
type argumentsAuth struct {
|
||||||
|
Token string `json:"token"`
|
||||||
|
Secret string `json:"secret"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type argumentsRepo struct {
|
||||||
|
U *modelUser `json:"u"`
|
||||||
|
RemoteID model.ForgeRemoteID `json:"remote_id"`
|
||||||
|
Owner string `json:"owner"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type argumentsFileDir struct {
|
||||||
|
U *modelUser `json:"u"`
|
||||||
|
R *modelRepo `json:"r"`
|
||||||
|
B *model.Pipeline `json:"b"`
|
||||||
|
F string `json:"f"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type argumentsStatus struct {
|
||||||
|
U *modelUser `json:"u"`
|
||||||
|
R *modelRepo `json:"r"`
|
||||||
|
B *model.Pipeline `json:"b"`
|
||||||
|
P *model.Workflow `json:"p"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type argumentsNetrc struct {
|
||||||
|
U *modelUser `json:"u"`
|
||||||
|
R *modelRepo `json:"r"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type argumentsActivateDeactivate struct {
|
||||||
|
U *modelUser `json:"u"`
|
||||||
|
R *modelRepo `json:"r"`
|
||||||
|
Link string `json:"link"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type argumentsBranchesPullRequests struct {
|
||||||
|
U *modelUser `json:"u"`
|
||||||
|
R *modelRepo `json:"r"`
|
||||||
|
P *model.ListOptions `json:"p"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type argumentsBranchHead struct {
|
||||||
|
U *modelUser `json:"u"`
|
||||||
|
R *modelRepo `json:"r"`
|
||||||
|
Branch string `json:"branch"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type argumentsOrgMembershipOrg struct {
|
||||||
|
U *modelUser `json:"u"`
|
||||||
|
Org string `json:"org"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type responseHook struct {
|
||||||
|
Repo *modelRepo `json:"repo"`
|
||||||
|
Pipeline *model.Pipeline `json:"pipeline"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type responseLogin struct {
|
||||||
|
User *modelUser `json:"user"`
|
||||||
|
RedirectURL string `json:"redirect_url"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type httpRequest struct {
|
||||||
|
Method string `json:"method"`
|
||||||
|
URL string `json:"url"`
|
||||||
|
Header map[string][]string `json:"header"`
|
||||||
|
Form map[string][]string `json:"form"`
|
||||||
|
Body []byte `json:"body"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// modelUser is an extension of model.User to marshal all fields to JSON
|
||||||
|
type modelUser struct {
|
||||||
|
User *model.User `json:"user"`
|
||||||
|
|
||||||
|
ForgeRemoteID model.ForgeRemoteID `json:"forge_remote_id"`
|
||||||
|
|
||||||
|
// Token is the oauth2 token.
|
||||||
|
Token string `json:"token"`
|
||||||
|
|
||||||
|
// Secret is the oauth2 token secret.
|
||||||
|
Secret string `json:"secret"`
|
||||||
|
|
||||||
|
// Expiry is the token and secret expiration timestamp.
|
||||||
|
Expiry int64 `json:"expiry"`
|
||||||
|
|
||||||
|
// Hash is a unique token used to sign tokens.
|
||||||
|
Hash string `json:"hash"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *modelUser) asModel() *model.User {
|
||||||
|
m.User.ForgeRemoteID = m.ForgeRemoteID
|
||||||
|
m.User.Token = m.Token
|
||||||
|
m.User.Secret = m.Secret
|
||||||
|
m.User.Expiry = m.Expiry
|
||||||
|
m.User.Hash = m.Hash
|
||||||
|
return m.User
|
||||||
|
}
|
||||||
|
|
||||||
|
func modelUserFromModel(u *model.User) *modelUser {
|
||||||
|
return &modelUser{
|
||||||
|
User: u,
|
||||||
|
ForgeRemoteID: u.ForgeRemoteID,
|
||||||
|
Token: u.Token,
|
||||||
|
Secret: u.Secret,
|
||||||
|
Expiry: u.Expiry,
|
||||||
|
Hash: u.Hash,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// modelRepo is an extension of model.Repo to marshal all fields to JSON
|
||||||
|
type modelRepo struct {
|
||||||
|
Repo *model.Repo `json:"repo"`
|
||||||
|
UserID int64 `json:"user_id"`
|
||||||
|
Hash string `json:"hash"`
|
||||||
|
Perm *model.Perm `json:"perm"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *modelRepo) asModel() *model.Repo {
|
||||||
|
m.Repo.UserID = m.UserID
|
||||||
|
m.Repo.Hash = m.Hash
|
||||||
|
m.Repo.Perm = m.Perm
|
||||||
|
return m.Repo
|
||||||
|
}
|
||||||
|
|
||||||
|
func modelRepoFromModel(r *model.Repo) *modelRepo {
|
||||||
|
return &modelRepo{
|
||||||
|
Repo: r,
|
||||||
|
UserID: r.UserID,
|
||||||
|
Hash: r.Hash,
|
||||||
|
Perm: r.Perm,
|
||||||
|
}
|
||||||
|
}
|
377
server/forge/addon/client.go
Normal file
377
server/forge/addon/client.go
Normal file
|
@ -0,0 +1,377 @@
|
||||||
|
// 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 addon
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"net/rpc"
|
||||||
|
"os/exec"
|
||||||
|
|
||||||
|
"github.com/hashicorp/go-plugin"
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
|
||||||
|
"go.woodpecker-ci.org/woodpecker/v2/server/forge"
|
||||||
|
"go.woodpecker-ci.org/woodpecker/v2/server/forge/types"
|
||||||
|
"go.woodpecker-ci.org/woodpecker/v2/server/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
// make sure RPC implements forge.Forge
|
||||||
|
var _ forge.Forge = new(RPC)
|
||||||
|
|
||||||
|
func Load(file string) (forge.Forge, error) {
|
||||||
|
client := plugin.NewClient(&plugin.ClientConfig{
|
||||||
|
HandshakeConfig: HandshakeConfig,
|
||||||
|
Plugins: map[string]plugin.Plugin{
|
||||||
|
pluginKey: &Plugin{},
|
||||||
|
},
|
||||||
|
Cmd: exec.Command(file),
|
||||||
|
Logger: &clientLogger{
|
||||||
|
logger: log.With().Str("addon", file).Logger(),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
// TODO defer client.Kill()
|
||||||
|
|
||||||
|
rpcClient, err := client.Client()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
raw, err := rpcClient.Dispense(pluginKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
extension, _ := raw.(forge.Forge)
|
||||||
|
return extension, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type RPC struct {
|
||||||
|
client *rpc.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *RPC) Name() string {
|
||||||
|
var resp string
|
||||||
|
_ = g.client.Call("Plugin.Name", nil, &resp)
|
||||||
|
return resp
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *RPC) URL() string {
|
||||||
|
var resp string
|
||||||
|
_ = g.client.Call("Plugin.URL", nil, &resp)
|
||||||
|
return resp
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *RPC) Login(_ context.Context, r *types.OAuthRequest) (*model.User, string, error) {
|
||||||
|
args, err := json.Marshal(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
var jsonResp []byte
|
||||||
|
err = g.client.Call("Plugin.Login", args, &jsonResp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
var resp responseLogin
|
||||||
|
err = json.Unmarshal(jsonResp, &resp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp.User.asModel(), resp.RedirectURL, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *RPC) Auth(_ context.Context, token, secret string) (string, error) {
|
||||||
|
args, err := json.Marshal(&argumentsAuth{
|
||||||
|
Token: token,
|
||||||
|
Secret: secret,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
var resp string
|
||||||
|
return resp, g.client.Call("Plugin.Auth", args, &resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *RPC) Teams(_ context.Context, u *model.User) ([]*model.Team, error) {
|
||||||
|
args, err := json.Marshal(modelUserFromModel(u))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var jsonResp []byte
|
||||||
|
err = g.client.Call("Plugin.Teams", args, &jsonResp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var resp []*model.Team
|
||||||
|
return resp, json.Unmarshal(jsonResp, &resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *RPC) Repo(_ context.Context, u *model.User, remoteID model.ForgeRemoteID, owner, name string) (*model.Repo, error) {
|
||||||
|
args, err := json.Marshal(&argumentsRepo{
|
||||||
|
U: modelUserFromModel(u),
|
||||||
|
RemoteID: remoteID,
|
||||||
|
Owner: owner,
|
||||||
|
Name: name,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var jsonResp []byte
|
||||||
|
err = g.client.Call("Plugin.Repo", args, &jsonResp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var resp *modelRepo
|
||||||
|
err = json.Unmarshal(jsonResp, resp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return resp.asModel(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *RPC) Repos(_ context.Context, u *model.User) ([]*model.Repo, error) {
|
||||||
|
args, err := json.Marshal(modelUserFromModel(u))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var jsonResp []byte
|
||||||
|
err = g.client.Call("Plugin.Repos", args, &jsonResp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var resp []*modelRepo
|
||||||
|
err = json.Unmarshal(jsonResp, &resp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var modelRepos []*model.Repo
|
||||||
|
for _, repo := range resp {
|
||||||
|
modelRepos = append(modelRepos, repo.asModel())
|
||||||
|
}
|
||||||
|
return modelRepos, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *RPC) File(_ context.Context, u *model.User, r *model.Repo, b *model.Pipeline, f string) ([]byte, error) {
|
||||||
|
args, err := json.Marshal(&argumentsFileDir{
|
||||||
|
U: modelUserFromModel(u),
|
||||||
|
R: modelRepoFromModel(r),
|
||||||
|
B: b,
|
||||||
|
F: f,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var resp []byte
|
||||||
|
return resp, g.client.Call("Plugin.File", args, &resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *RPC) Dir(_ context.Context, u *model.User, r *model.Repo, b *model.Pipeline, f string) ([]*types.FileMeta, error) {
|
||||||
|
args, err := json.Marshal(&argumentsFileDir{
|
||||||
|
U: modelUserFromModel(u),
|
||||||
|
R: modelRepoFromModel(r),
|
||||||
|
B: b,
|
||||||
|
F: f,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var jsonResp []byte
|
||||||
|
err = g.client.Call("Plugin.Dir", args, &jsonResp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var resp []*types.FileMeta
|
||||||
|
return resp, json.Unmarshal(jsonResp, &resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *RPC) Status(_ context.Context, u *model.User, r *model.Repo, b *model.Pipeline, p *model.Workflow) error {
|
||||||
|
args, err := json.Marshal(&argumentsStatus{
|
||||||
|
U: modelUserFromModel(u),
|
||||||
|
R: modelRepoFromModel(r),
|
||||||
|
B: b,
|
||||||
|
P: p,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var jsonResp []byte
|
||||||
|
return g.client.Call("Plugin.Status", args, &jsonResp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *RPC) Netrc(u *model.User, r *model.Repo) (*model.Netrc, error) {
|
||||||
|
args, err := json.Marshal(&argumentsNetrc{
|
||||||
|
U: modelUserFromModel(u),
|
||||||
|
R: modelRepoFromModel(r),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var jsonResp []byte
|
||||||
|
err = g.client.Call("Plugin.Netrc", args, &jsonResp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var resp *model.Netrc
|
||||||
|
return resp, json.Unmarshal(jsonResp, &resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *RPC) Activate(_ context.Context, u *model.User, r *model.Repo, link string) error {
|
||||||
|
args, err := json.Marshal(&argumentsActivateDeactivate{
|
||||||
|
U: modelUserFromModel(u),
|
||||||
|
R: modelRepoFromModel(r),
|
||||||
|
Link: link,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var jsonResp []byte
|
||||||
|
return g.client.Call("Plugin.Activate", args, &jsonResp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *RPC) Deactivate(_ context.Context, u *model.User, r *model.Repo, link string) error {
|
||||||
|
args, err := json.Marshal(&argumentsActivateDeactivate{
|
||||||
|
U: modelUserFromModel(u),
|
||||||
|
R: modelRepoFromModel(r),
|
||||||
|
Link: link,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var jsonResp []byte
|
||||||
|
return g.client.Call("Plugin.Deactivate", args, &jsonResp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *RPC) Branches(_ context.Context, u *model.User, r *model.Repo, p *model.ListOptions) ([]string, error) {
|
||||||
|
args, err := json.Marshal(&argumentsBranchesPullRequests{
|
||||||
|
U: modelUserFromModel(u),
|
||||||
|
R: modelRepoFromModel(r),
|
||||||
|
P: p,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var jsonResp []byte
|
||||||
|
err = g.client.Call("Plugin.Branches", args, &jsonResp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var resp []string
|
||||||
|
return resp, json.Unmarshal(jsonResp, &resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *RPC) BranchHead(_ context.Context, u *model.User, r *model.Repo, branch string) (*model.Commit, error) {
|
||||||
|
args, err := json.Marshal(&argumentsBranchHead{
|
||||||
|
U: modelUserFromModel(u),
|
||||||
|
R: modelRepoFromModel(r),
|
||||||
|
Branch: branch,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var jsonResp []byte
|
||||||
|
err = g.client.Call("Plugin.BranchHead", args, &jsonResp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var resp *model.Commit
|
||||||
|
return resp, json.Unmarshal(jsonResp, &resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *RPC) PullRequests(_ context.Context, u *model.User, r *model.Repo, p *model.ListOptions) ([]*model.PullRequest, error) {
|
||||||
|
args, err := json.Marshal(&argumentsBranchesPullRequests{
|
||||||
|
U: modelUserFromModel(u),
|
||||||
|
R: modelRepoFromModel(r),
|
||||||
|
P: p,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var jsonResp []byte
|
||||||
|
err = g.client.Call("Plugin.PullRequests", args, &jsonResp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var resp []*model.PullRequest
|
||||||
|
return resp, json.Unmarshal(jsonResp, &resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *RPC) Hook(_ context.Context, r *http.Request) (*model.Repo, *model.Pipeline, error) {
|
||||||
|
body, err := io.ReadAll(r.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
args, err := json.Marshal(&httpRequest{
|
||||||
|
Method: r.Method,
|
||||||
|
URL: r.URL.String(),
|
||||||
|
Header: r.Header,
|
||||||
|
Form: r.Form,
|
||||||
|
Body: body,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
var jsonResp []byte
|
||||||
|
err = g.client.Call("Plugin.Hook", args, &jsonResp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
var resp responseHook
|
||||||
|
err = json.Unmarshal(jsonResp, &resp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
return resp.Repo.asModel(), resp.Pipeline, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *RPC) OrgMembership(_ context.Context, u *model.User, org string) (*model.OrgPerm, error) {
|
||||||
|
args, err := json.Marshal(&argumentsOrgMembershipOrg{
|
||||||
|
U: modelUserFromModel(u),
|
||||||
|
Org: org,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var jsonResp []byte
|
||||||
|
err = g.client.Call("Plugin.OrgMembership", args, &jsonResp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var resp *model.OrgPerm
|
||||||
|
return resp, json.Unmarshal(jsonResp, &resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *RPC) Org(_ context.Context, u *model.User, org string) (*model.Org, error) {
|
||||||
|
args, err := json.Marshal(&argumentsOrgMembershipOrg{
|
||||||
|
U: modelUserFromModel(u),
|
||||||
|
Org: org,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var jsonResp []byte
|
||||||
|
err = g.client.Call("Plugin.Org", args, &jsonResp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var resp *model.Org
|
||||||
|
return resp, json.Unmarshal(jsonResp, &resp)
|
||||||
|
}
|
165
server/forge/addon/logger.go
Normal file
165
server/forge/addon/logger.go
Normal file
|
@ -0,0 +1,165 @@
|
||||||
|
// 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 addon
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io"
|
||||||
|
stdlog "log"
|
||||||
|
|
||||||
|
"github.com/hashicorp/go-hclog"
|
||||||
|
"github.com/rs/zerolog"
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
type clientLogger struct {
|
||||||
|
logger zerolog.Logger
|
||||||
|
name string
|
||||||
|
withArgs []any
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertLvl(level hclog.Level) zerolog.Level {
|
||||||
|
switch level {
|
||||||
|
case hclog.Error:
|
||||||
|
return zerolog.ErrorLevel
|
||||||
|
case hclog.Warn:
|
||||||
|
return zerolog.WarnLevel
|
||||||
|
case hclog.Info:
|
||||||
|
return zerolog.InfoLevel
|
||||||
|
case hclog.Debug:
|
||||||
|
return zerolog.DebugLevel
|
||||||
|
case hclog.Trace:
|
||||||
|
return zerolog.TraceLevel
|
||||||
|
}
|
||||||
|
return zerolog.NoLevel
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *clientLogger) applyArgs(args []any) *zerolog.Logger {
|
||||||
|
var key string
|
||||||
|
logger := c.logger.With()
|
||||||
|
args = append(args, c.withArgs)
|
||||||
|
for i, arg := range args {
|
||||||
|
switch {
|
||||||
|
case key != "":
|
||||||
|
logger.Any(key, arg)
|
||||||
|
key = ""
|
||||||
|
case i == len(args)-1:
|
||||||
|
logger.Any(hclog.MissingKey, arg)
|
||||||
|
default:
|
||||||
|
|
||||||
|
key, _ = arg.(string)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
l := logger.Logger()
|
||||||
|
return &l
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *clientLogger) Log(level hclog.Level, msg string, args ...any) {
|
||||||
|
c.applyArgs(args).WithLevel(convertLvl(level)).Msg(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *clientLogger) Trace(msg string, args ...any) {
|
||||||
|
c.applyArgs(args).Trace().Msg(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *clientLogger) Debug(msg string, args ...any) {
|
||||||
|
c.applyArgs(args).Debug().Msg(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *clientLogger) Info(msg string, args ...any) {
|
||||||
|
c.applyArgs(args).Info().Msg(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *clientLogger) Warn(msg string, args ...any) {
|
||||||
|
c.applyArgs(args).Warn().Msg(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *clientLogger) Error(msg string, args ...any) {
|
||||||
|
c.applyArgs(args).Error().Msg(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *clientLogger) IsTrace() bool {
|
||||||
|
return log.Logger.GetLevel() >= zerolog.TraceLevel
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *clientLogger) IsDebug() bool {
|
||||||
|
return log.Logger.GetLevel() >= zerolog.DebugLevel
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *clientLogger) IsInfo() bool {
|
||||||
|
return log.Logger.GetLevel() >= zerolog.InfoLevel
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *clientLogger) IsWarn() bool {
|
||||||
|
return log.Logger.GetLevel() >= zerolog.WarnLevel
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *clientLogger) IsError() bool {
|
||||||
|
return log.Logger.GetLevel() >= zerolog.ErrorLevel
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *clientLogger) ImpliedArgs() []any {
|
||||||
|
return c.withArgs
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *clientLogger) With(args ...any) hclog.Logger {
|
||||||
|
return &clientLogger{
|
||||||
|
logger: c.logger,
|
||||||
|
name: c.name,
|
||||||
|
withArgs: args,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *clientLogger) Name() string {
|
||||||
|
return c.name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *clientLogger) Named(name string) hclog.Logger {
|
||||||
|
curr := c.name
|
||||||
|
if curr != "" {
|
||||||
|
curr = c.name + "."
|
||||||
|
}
|
||||||
|
return c.ResetNamed(curr + name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *clientLogger) ResetNamed(name string) hclog.Logger {
|
||||||
|
return &clientLogger{
|
||||||
|
logger: c.logger,
|
||||||
|
name: name,
|
||||||
|
withArgs: c.withArgs,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *clientLogger) SetLevel(level hclog.Level) {
|
||||||
|
c.logger = c.logger.Level(convertLvl(level))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *clientLogger) StandardLogger(opts *hclog.StandardLoggerOptions) *stdlog.Logger {
|
||||||
|
return stdlog.New(c.StandardWriter(opts), "", 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *clientLogger) StandardWriter(*hclog.StandardLoggerOptions) io.Writer {
|
||||||
|
return ioAdapter{logger: c.logger}
|
||||||
|
}
|
||||||
|
|
||||||
|
type ioAdapter struct {
|
||||||
|
logger zerolog.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i ioAdapter) Write(p []byte) (n int, err error) {
|
||||||
|
str := string(bytes.TrimRight(p, " \t\n"))
|
||||||
|
i.logger.Log().Msg(str)
|
||||||
|
return len(p), nil
|
||||||
|
}
|
43
server/forge/addon/plugin.go
Normal file
43
server/forge/addon/plugin.go
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
// 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 addon
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/rpc"
|
||||||
|
|
||||||
|
"github.com/hashicorp/go-plugin"
|
||||||
|
|
||||||
|
"go.woodpecker-ci.org/woodpecker/v2/server/forge"
|
||||||
|
)
|
||||||
|
|
||||||
|
const pluginKey = "forge"
|
||||||
|
|
||||||
|
var HandshakeConfig = plugin.HandshakeConfig{
|
||||||
|
ProtocolVersion: 1,
|
||||||
|
MagicCookieKey: "WOODPECKER_FORGE_ADDON_PLUGIN",
|
||||||
|
MagicCookieValue: "woodpecker-plugin-magic-cookie-value",
|
||||||
|
}
|
||||||
|
|
||||||
|
type Plugin struct {
|
||||||
|
Impl forge.Forge
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Plugin) Server(*plugin.MuxBroker) (any, error) {
|
||||||
|
return &RPCServer{Impl: p.Impl}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*Plugin) Client(_ *plugin.MuxBroker, c *rpc.Client) (any, error) {
|
||||||
|
return &RPC{client: c}, nil
|
||||||
|
}
|
278
server/forge/addon/server.go
Normal file
278
server/forge/addon/server.go
Normal file
|
@ -0,0 +1,278 @@
|
||||||
|
// 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 addon
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/hashicorp/go-plugin"
|
||||||
|
|
||||||
|
"go.woodpecker-ci.org/woodpecker/v2/server/forge"
|
||||||
|
"go.woodpecker-ci.org/woodpecker/v2/server/forge/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Serve(impl forge.Forge) {
|
||||||
|
plugin.Serve(&plugin.ServeConfig{
|
||||||
|
HandshakeConfig: HandshakeConfig,
|
||||||
|
Plugins: map[string]plugin.Plugin{
|
||||||
|
pluginKey: &Plugin{Impl: impl},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func mkCtx() context.Context {
|
||||||
|
return context.Background()
|
||||||
|
}
|
||||||
|
|
||||||
|
type RPCServer struct {
|
||||||
|
Impl forge.Forge
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *RPCServer) Name(_ []byte, resp *string) error {
|
||||||
|
*resp = s.Impl.Name()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *RPCServer) URL(_ []byte, resp *string) error {
|
||||||
|
*resp = s.Impl.URL()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *RPCServer) Teams(args []byte, resp *[]byte) error {
|
||||||
|
var a *modelUser
|
||||||
|
err := json.Unmarshal(args, a)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
teams, err := s.Impl.Teams(mkCtx(), a.asModel())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*resp, err = json.Marshal(teams)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *RPCServer) Repo(args []byte, resp *[]byte) error {
|
||||||
|
var a argumentsRepo
|
||||||
|
err := json.Unmarshal(args, &a)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
repos, err := s.Impl.Repo(mkCtx(), a.U.asModel(), a.RemoteID, a.Owner, a.Name)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*resp, err = json.Marshal(modelRepoFromModel(repos))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *RPCServer) Repos(args []byte, resp *[]byte) error {
|
||||||
|
var a *modelUser
|
||||||
|
err := json.Unmarshal(args, a)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
repos, err := s.Impl.Repos(mkCtx(), a.asModel())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var modelRepos []*modelRepo
|
||||||
|
for _, repo := range repos {
|
||||||
|
modelRepos = append(modelRepos, modelRepoFromModel(repo))
|
||||||
|
}
|
||||||
|
*resp, err = json.Marshal(modelRepos)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *RPCServer) File(args []byte, resp *[]byte) error {
|
||||||
|
var a argumentsFileDir
|
||||||
|
err := json.Unmarshal(args, &a)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*resp, err = s.Impl.File(mkCtx(), a.U.asModel(), a.R.asModel(), a.B, a.F)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *RPCServer) Dir(args []byte, resp *[]byte) error {
|
||||||
|
var a argumentsFileDir
|
||||||
|
err := json.Unmarshal(args, &a)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
meta, err := s.Impl.Dir(mkCtx(), a.U.asModel(), a.R.asModel(), a.B, a.F)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*resp, err = json.Marshal(meta)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *RPCServer) Status(args []byte, resp *[]byte) error {
|
||||||
|
var a argumentsStatus
|
||||||
|
err := json.Unmarshal(args, &a)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*resp = []byte{}
|
||||||
|
return s.Impl.Status(mkCtx(), a.U.asModel(), a.R.asModel(), a.B, a.P)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *RPCServer) Netrc(args []byte, resp *[]byte) error {
|
||||||
|
var a argumentsNetrc
|
||||||
|
err := json.Unmarshal(args, &a)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
netrc, err := s.Impl.Netrc(a.U.asModel(), a.R.asModel())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*resp, err = json.Marshal(netrc)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *RPCServer) Activate(args []byte, resp *[]byte) error {
|
||||||
|
var a argumentsActivateDeactivate
|
||||||
|
err := json.Unmarshal(args, &a)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*resp = []byte{}
|
||||||
|
return s.Impl.Activate(mkCtx(), a.U.asModel(), a.R.asModel(), a.Link)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *RPCServer) Deactivate(args []byte, resp *[]byte) error {
|
||||||
|
var a argumentsActivateDeactivate
|
||||||
|
err := json.Unmarshal(args, &a)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*resp = []byte{}
|
||||||
|
return s.Impl.Deactivate(mkCtx(), a.U.asModel(), a.R.asModel(), a.Link)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *RPCServer) Branches(args []byte, resp *[]byte) error {
|
||||||
|
var a argumentsBranchesPullRequests
|
||||||
|
err := json.Unmarshal(args, &a)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
branches, err := s.Impl.Branches(mkCtx(), a.U.asModel(), a.R.asModel(), a.P)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*resp, err = json.Marshal(branches)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *RPCServer) BranchHead(args []byte, resp *[]byte) error {
|
||||||
|
var a argumentsBranchHead
|
||||||
|
err := json.Unmarshal(args, &a)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
commit, err := s.Impl.BranchHead(mkCtx(), a.U.asModel(), a.R.asModel(), a.Branch)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*resp, err = json.Marshal(commit)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *RPCServer) PullRequests(args []byte, resp *[]byte) error {
|
||||||
|
var a argumentsBranchesPullRequests
|
||||||
|
err := json.Unmarshal(args, &a)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
prs, err := s.Impl.PullRequests(mkCtx(), a.U.asModel(), a.R.asModel(), a.P)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*resp, err = json.Marshal(prs)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *RPCServer) OrgMembership(args []byte, resp *[]byte) error {
|
||||||
|
var a argumentsOrgMembershipOrg
|
||||||
|
err := json.Unmarshal(args, &a)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
org, err := s.Impl.OrgMembership(mkCtx(), a.U.asModel(), a.Org)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*resp, err = json.Marshal(org)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *RPCServer) Org(args []byte, resp *[]byte) error {
|
||||||
|
var a argumentsOrgMembershipOrg
|
||||||
|
err := json.Unmarshal(args, &a)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
org, err := s.Impl.Org(mkCtx(), a.U.asModel(), a.Org)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*resp, err = json.Marshal(org)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *RPCServer) Hook(args []byte, resp *[]byte) error {
|
||||||
|
var a httpRequest
|
||||||
|
err := json.Unmarshal(args, &a)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
req, err := http.NewRequest(a.Method, a.URL, bytes.NewBuffer(a.Body))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
req.Header = a.Header
|
||||||
|
req.Form = a.Form
|
||||||
|
repo, pipeline, err := s.Impl.Hook(mkCtx(), req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*resp, err = json.Marshal(&responseHook{
|
||||||
|
Repo: modelRepoFromModel(repo),
|
||||||
|
Pipeline: pipeline,
|
||||||
|
})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *RPCServer) Login(args []byte, resp *[]byte) error {
|
||||||
|
var a *types.OAuthRequest
|
||||||
|
err := json.Unmarshal(args, a)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
user, red, err := s.Impl.Login(mkCtx(), a)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*resp, err = json.Marshal(&responseLogin{
|
||||||
|
User: modelUserFromModel(user),
|
||||||
|
RedirectURL: red,
|
||||||
|
})
|
||||||
|
return err
|
||||||
|
}
|
|
@ -17,6 +17,7 @@ package bitbucket
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
@ -232,9 +233,15 @@ func (c *config) Repos(ctx context.Context, u *model.User) ([]*model.Repo, error
|
||||||
func (c *config) File(ctx context.Context, u *model.User, r *model.Repo, p *model.Pipeline, f string) ([]byte, error) {
|
func (c *config) File(ctx context.Context, u *model.User, r *model.Repo, p *model.Pipeline, f string) ([]byte, error) {
|
||||||
config, err := c.newClient(ctx, u).FindSource(r.Owner, r.Name, p.Commit, f)
|
config, err := c.newClient(ctx, u).FindSource(r.Owner, r.Name, p.Commit, f)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
var rspErr internal.Error
|
||||||
|
if ok := errors.As(err, &rspErr); ok && rspErr.Status == http.StatusNotFound {
|
||||||
|
return nil, &forge_types.ErrConfigNotFound{
|
||||||
|
Configs: []string{f},
|
||||||
|
}
|
||||||
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return []byte(*config), err
|
return []byte(*config), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dir fetches a folder from the bitbucket repository
|
// Dir fetches a folder from the bitbucket repository
|
||||||
|
@ -245,6 +252,12 @@ func (c *config) Dir(ctx context.Context, u *model.User, r *model.Repo, p *model
|
||||||
for {
|
for {
|
||||||
filesResp, err := client.GetRepoFiles(r.Owner, r.Name, p.Commit, f, page)
|
filesResp, err := client.GetRepoFiles(r.Owner, r.Name, p.Commit, f, page)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
var rspErr internal.Error
|
||||||
|
if ok := errors.As(err, &rspErr); ok && rspErr.Status == http.StatusNotFound {
|
||||||
|
return nil, &forge_types.ErrConfigNotFound{
|
||||||
|
Configs: []string{f},
|
||||||
|
}
|
||||||
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
for _, file := range filesResp.Values {
|
for _, file := range filesResp.Values {
|
||||||
|
|
|
@ -18,6 +18,7 @@ package bitbucket
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"testing"
|
"testing"
|
||||||
|
@ -179,6 +180,7 @@ func Test_bitbucket(t *testing.T) {
|
||||||
g.It("Should handle not found error", func() {
|
g.It("Should handle not found error", func() {
|
||||||
_, err := c.File(ctx, fakeUser, fakeRepo, fakePipeline, "file_not_found")
|
_, err := c.File(ctx, fakeUser, fakeRepo, fakePipeline, "file_not_found")
|
||||||
g.Assert(err).IsNotNil()
|
g.Assert(err).IsNotNil()
|
||||||
|
g.Assert(errors.Is(err, &types.ErrConfigNotFound{})).IsTrue()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -222,8 +224,9 @@ func Test_bitbucket(t *testing.T) {
|
||||||
g.Assert(string(files[0].Data)).Equal("dummy payload")
|
g.Assert(string(files[0].Data)).Equal("dummy payload")
|
||||||
})
|
})
|
||||||
g.It("Should handle not found errors", func() {
|
g.It("Should handle not found errors", func() {
|
||||||
_, err := c.Dir(ctx, fakeUser, fakeRepo, fakePipeline, "/dir_not_found")
|
_, err := c.Dir(ctx, fakeUser, fakeRepo, fakePipeline, "dir_not_found/")
|
||||||
g.Assert(err).IsNotNil()
|
g.Assert(err).IsNotNil()
|
||||||
|
g.Assert(errors.Is(err, &types.ErrConfigNotFound{})).IsTrue()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,6 @@
|
||||||
|
|
||||||
package forge
|
package forge
|
||||||
|
|
||||||
//go:generate go install github.com/vektra/mockery/v2@latest
|
|
||||||
//go:generate mockery --name Forge --output mocks --case underscore
|
//go:generate mockery --name Forge --output mocks --case underscore
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
|
@ -118,7 +118,7 @@ func getChangedFilesFromPushHook(hook *pushHook) []string {
|
||||||
files = append(files, hook.HeadCommit.Removed...)
|
files = append(files, hook.HeadCommit.Removed...)
|
||||||
files = append(files, hook.HeadCommit.Modified...)
|
files = append(files, hook.HeadCommit.Modified...)
|
||||||
|
|
||||||
return utils.DedupStrings(files)
|
return utils.DeduplicateStrings(files)
|
||||||
}
|
}
|
||||||
|
|
||||||
// helper function that extracts the Pipeline data from a Gitea tag hook
|
// helper function that extracts the Pipeline data from a Gitea tag hook
|
||||||
|
|
|
@ -18,7 +18,7 @@ package github
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/google/go-github/v60/github"
|
"github.com/google/go-github/v61/github"
|
||||||
|
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/server/model"
|
"go.woodpecker-ci.org/woodpecker/v2/server/model"
|
||||||
)
|
)
|
||||||
|
|
|
@ -19,7 +19,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/franela/goblin"
|
"github.com/franela/goblin"
|
||||||
"github.com/google/go-github/v60/github"
|
"github.com/google/go-github/v61/github"
|
||||||
|
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/server/model"
|
"go.woodpecker-ci.org/woodpecker/v2/server/model"
|
||||||
)
|
)
|
||||||
|
|
|
@ -26,7 +26,7 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/google/go-github/v60/github"
|
"github.com/google/go-github/v61/github"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
"golang.org/x/oauth2"
|
"golang.org/x/oauth2"
|
||||||
|
|
||||||
|
@ -51,6 +51,7 @@ type Opts struct {
|
||||||
Secret string // GitHub oauth client secret.
|
Secret string // GitHub oauth client secret.
|
||||||
SkipVerify bool // Skip ssl verification.
|
SkipVerify bool // Skip ssl verification.
|
||||||
MergeRef bool // Clone pull requests using the merge ref.
|
MergeRef bool // Clone pull requests using the merge ref.
|
||||||
|
OnlyPublic bool // Only obtain OAuth tokens with access to public repos.
|
||||||
}
|
}
|
||||||
|
|
||||||
// New returns a Forge implementation that integrates with a GitHub Cloud or
|
// New returns a Forge implementation that integrates with a GitHub Cloud or
|
||||||
|
@ -63,6 +64,7 @@ func New(opts Opts) (forge.Forge, error) {
|
||||||
Secret: opts.Secret,
|
Secret: opts.Secret,
|
||||||
SkipVerify: opts.SkipVerify,
|
SkipVerify: opts.SkipVerify,
|
||||||
MergeRef: opts.MergeRef,
|
MergeRef: opts.MergeRef,
|
||||||
|
OnlyPublic: opts.OnlyPublic,
|
||||||
}
|
}
|
||||||
if opts.URL != defaultURL {
|
if opts.URL != defaultURL {
|
||||||
r.url = strings.TrimSuffix(opts.URL, "/")
|
r.url = strings.TrimSuffix(opts.URL, "/")
|
||||||
|
@ -79,6 +81,7 @@ type client struct {
|
||||||
Secret string
|
Secret string
|
||||||
SkipVerify bool
|
SkipVerify bool
|
||||||
MergeRef bool
|
MergeRef bool
|
||||||
|
OnlyPublic bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Name returns the string name of this driver
|
// Name returns the string name of this driver
|
||||||
|
@ -405,10 +408,17 @@ func (c *client) newContext(ctx context.Context) context.Context {
|
||||||
|
|
||||||
// helper function to return the GitHub oauth2 config
|
// helper function to return the GitHub oauth2 config
|
||||||
func (c *client) newConfig() *oauth2.Config {
|
func (c *client) newConfig() *oauth2.Config {
|
||||||
|
scopes := []string{"user:email", "read:org"}
|
||||||
|
if c.OnlyPublic {
|
||||||
|
scopes = append(scopes, []string{"admin:repo_hook", "repo:status"}...)
|
||||||
|
} else {
|
||||||
|
scopes = append(scopes, "repo")
|
||||||
|
}
|
||||||
|
|
||||||
return &oauth2.Config{
|
return &oauth2.Config{
|
||||||
ClientID: c.Client,
|
ClientID: c.Client,
|
||||||
ClientSecret: c.Secret,
|
ClientSecret: c.Secret,
|
||||||
Scopes: []string{"repo", "user:email", "read:org"},
|
Scopes: scopes,
|
||||||
Endpoint: oauth2.Endpoint{
|
Endpoint: oauth2.Endpoint{
|
||||||
AuthURL: fmt.Sprintf("%s/login/oauth/authorize", c.url),
|
AuthURL: fmt.Sprintf("%s/login/oauth/authorize", c.url),
|
||||||
TokenURL: fmt.Sprintf("%s/login/oauth/access_token", c.url),
|
TokenURL: fmt.Sprintf("%s/login/oauth/access_token", c.url),
|
||||||
|
@ -620,7 +630,7 @@ func (c *client) loadChangedFilesFromPullRequest(ctx context.Context, pull *gith
|
||||||
|
|
||||||
opts.Page = resp.NextPage
|
opts.Page = resp.NextPage
|
||||||
}
|
}
|
||||||
return utils.DedupStrings(fileList), nil
|
return utils.DeduplicateStrings(fileList), nil
|
||||||
})
|
})
|
||||||
|
|
||||||
return pipeline, err
|
return pipeline, err
|
||||||
|
|
|
@ -22,7 +22,7 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/google/go-github/v60/github"
|
"github.com/google/go-github/v61/github"
|
||||||
|
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/server/forge/types"
|
"go.woodpecker-ci.org/woodpecker/v2/server/forge/types"
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/server/model"
|
"go.woodpecker-ci.org/woodpecker/v2/server/model"
|
||||||
|
@ -215,5 +215,5 @@ func getChangedFilesFromCommits(commits []*github.HeadCommit) []string {
|
||||||
files = append(files, cm.Removed...)
|
files = append(files, cm.Removed...)
|
||||||
files = append(files, cm.Modified...)
|
files = append(files, cm.Modified...)
|
||||||
}
|
}
|
||||||
return utils.DedupStrings(files)
|
return utils.DeduplicateStrings(files)
|
||||||
}
|
}
|
||||||
|
|
|
@ -189,7 +189,7 @@ func convertPushHook(hook *gitlab.PushEvent) (*model.Repo, *model.Pipeline, erro
|
||||||
files = append(files, cm.Removed...)
|
files = append(files, cm.Removed...)
|
||||||
files = append(files, cm.Modified...)
|
files = append(files, cm.Modified...)
|
||||||
}
|
}
|
||||||
pipeline.ChangedFiles = utils.DedupStrings(files)
|
pipeline.ChangedFiles = utils.DeduplicateStrings(files)
|
||||||
|
|
||||||
return repo, pipeline, nil
|
return repo, pipeline, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -808,7 +808,7 @@ func (g *GitLab) loadChangedFilesFromMergeRequest(ctx context.Context, tmpRepo *
|
||||||
for _, file := range changes {
|
for _, file := range changes {
|
||||||
files = append(files, file.NewPath, file.OldPath)
|
files = append(files, file.NewPath, file.OldPath)
|
||||||
}
|
}
|
||||||
pipeline.ChangedFiles = utils.DedupStrings(files)
|
pipeline.ChangedFiles = utils.DeduplicateStrings(files)
|
||||||
|
|
||||||
return pipeline, nil
|
return pipeline, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// Code generated by mockery v2.42.1. DO NOT EDIT.
|
// Code generated by mockery. DO NOT EDIT.
|
||||||
|
|
||||||
package mocks
|
package mocks
|
||||||
|
|
||||||
|
|
134
server/forge/setup/setup.go
Normal file
134
server/forge/setup/setup.go
Normal file
|
@ -0,0 +1,134 @@
|
||||||
|
package setup
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
|
||||||
|
"go.woodpecker-ci.org/woodpecker/v2/server/forge"
|
||||||
|
"go.woodpecker-ci.org/woodpecker/v2/server/forge/addon"
|
||||||
|
"go.woodpecker-ci.org/woodpecker/v2/server/forge/bitbucket"
|
||||||
|
"go.woodpecker-ci.org/woodpecker/v2/server/forge/bitbucketdatacenter"
|
||||||
|
"go.woodpecker-ci.org/woodpecker/v2/server/forge/gitea"
|
||||||
|
"go.woodpecker-ci.org/woodpecker/v2/server/forge/github"
|
||||||
|
"go.woodpecker-ci.org/woodpecker/v2/server/forge/gitlab"
|
||||||
|
"go.woodpecker-ci.org/woodpecker/v2/server/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Forge(forge *model.Forge) (forge.Forge, error) {
|
||||||
|
switch forge.Type {
|
||||||
|
case model.ForgeTypeAddon:
|
||||||
|
return setupAddon(forge)
|
||||||
|
case model.ForgeTypeGithub:
|
||||||
|
return setupGitHub(forge)
|
||||||
|
case model.ForgeTypeGitlab:
|
||||||
|
return setupGitLab(forge)
|
||||||
|
case model.ForgeTypeBitbucket:
|
||||||
|
return setupBitbucket(forge)
|
||||||
|
case model.ForgeTypeGitea:
|
||||||
|
return setupGitea(forge)
|
||||||
|
case model.ForgeTypeBitbucketDatacenter:
|
||||||
|
return setupBitbucketDatacenter(forge)
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("forge not configured")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupBitbucket(forge *model.Forge) (forge.Forge, error) {
|
||||||
|
opts := &bitbucket.Opts{
|
||||||
|
Client: forge.Client,
|
||||||
|
Secret: forge.ClientSecret,
|
||||||
|
}
|
||||||
|
log.Trace().Msgf("Forge (bitbucket) opts: %#v", opts)
|
||||||
|
return bitbucket.New(opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupGitea(forge *model.Forge) (forge.Forge, error) {
|
||||||
|
server, err := url.Parse(forge.URL)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
oauthURL, ok := forge.AdditionalOptions["oauth-server"].(string)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("missing oauth-server")
|
||||||
|
}
|
||||||
|
|
||||||
|
opts := gitea.Opts{
|
||||||
|
URL: strings.TrimRight(server.String(), "/"),
|
||||||
|
Client: forge.Client,
|
||||||
|
Secret: forge.ClientSecret,
|
||||||
|
SkipVerify: forge.SkipVerify,
|
||||||
|
OAuth2URL: oauthURL,
|
||||||
|
}
|
||||||
|
if len(opts.URL) == 0 {
|
||||||
|
return nil, fmt.Errorf("WOODPECKER_GITEA_URL must be set")
|
||||||
|
}
|
||||||
|
log.Trace().Msgf("Forge (gitea) opts: %#v", opts)
|
||||||
|
return gitea.New(opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupGitLab(forge *model.Forge) (forge.Forge, error) {
|
||||||
|
return gitlab.New(gitlab.Opts{
|
||||||
|
URL: forge.URL,
|
||||||
|
ClientID: forge.Client,
|
||||||
|
ClientSecret: forge.ClientSecret,
|
||||||
|
SkipVerify: forge.SkipVerify,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupGitHub(forge *model.Forge) (forge.Forge, error) {
|
||||||
|
mergeRef, ok := forge.AdditionalOptions["merge-ref"].(bool)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("missing merge-ref")
|
||||||
|
}
|
||||||
|
|
||||||
|
publicOnly, ok := forge.AdditionalOptions["public-only"].(bool)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("missing public-only")
|
||||||
|
}
|
||||||
|
|
||||||
|
opts := github.Opts{
|
||||||
|
URL: forge.URL,
|
||||||
|
Client: forge.Client,
|
||||||
|
Secret: forge.ClientSecret,
|
||||||
|
SkipVerify: forge.SkipVerify,
|
||||||
|
MergeRef: mergeRef,
|
||||||
|
OnlyPublic: publicOnly,
|
||||||
|
}
|
||||||
|
log.Trace().Msgf("Forge (github) opts: %#v", opts)
|
||||||
|
return github.New(opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupBitbucketDatacenter(forge *model.Forge) (forge.Forge, error) {
|
||||||
|
gitUsername, ok := forge.AdditionalOptions["git-username"].(string)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("missing git-username")
|
||||||
|
}
|
||||||
|
gitPassword, ok := forge.AdditionalOptions["git-password"].(string)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("missing git-password")
|
||||||
|
}
|
||||||
|
|
||||||
|
opts := bitbucketdatacenter.Opts{
|
||||||
|
URL: forge.URL,
|
||||||
|
ClientID: forge.Client,
|
||||||
|
ClientSecret: forge.ClientSecret,
|
||||||
|
Username: gitUsername,
|
||||||
|
Password: gitPassword,
|
||||||
|
}
|
||||||
|
log.Trace().Msgf("Forge (bitbucketdatacenter) opts: %#v", opts)
|
||||||
|
return bitbucketdatacenter.New(opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupAddon(forge *model.Forge) (forge.Forge, error) {
|
||||||
|
executable, ok := forge.AdditionalOptions["executable"].(string)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("missing git-username")
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Trace().Msgf("Forge (addon) executable: %#v", executable)
|
||||||
|
return addon.Load(executable)
|
||||||
|
}
|
|
@ -31,6 +31,7 @@ import (
|
||||||
grpcMetadata "google.golang.org/grpc/metadata"
|
grpcMetadata "google.golang.org/grpc/metadata"
|
||||||
|
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/pipeline/rpc"
|
"go.woodpecker-ci.org/woodpecker/v2/pipeline/rpc"
|
||||||
|
"go.woodpecker-ci.org/woodpecker/v2/server"
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/server/forge"
|
"go.woodpecker-ci.org/woodpecker/v2/server/forge"
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/server/logging"
|
"go.woodpecker-ci.org/woodpecker/v2/server/logging"
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/server/model"
|
"go.woodpecker-ci.org/woodpecker/v2/server/model"
|
||||||
|
@ -41,7 +42,6 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type RPC struct {
|
type RPC struct {
|
||||||
forge forge.Forge
|
|
||||||
queue queue.Queue
|
queue queue.Queue
|
||||||
pubsub *pubsub.Publisher
|
pubsub *pubsub.Publisher
|
||||||
logger logging.Log
|
logger logging.Log
|
||||||
|
@ -525,11 +525,17 @@ func (s *RPC) updateForgeStatus(ctx context.Context, repo *model.Repo, pipeline
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
forge.Refresh(ctx, s.forge, s.store, user)
|
_forge, err := server.Config.Services.Manager.ForgeFromRepo(repo)
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Err(err).Msgf("can not get forge for repo '%s'", repo.FullName)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
forge.Refresh(ctx, _forge, s.store, user)
|
||||||
|
|
||||||
// only do status updates for parent steps
|
// only do status updates for parent steps
|
||||||
if workflow != nil {
|
if workflow != nil {
|
||||||
err = s.forge.Status(ctx, user, repo, pipeline, workflow)
|
err = _forge.Status(ctx, user, repo, pipeline, workflow)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().Err(err).Msgf("error setting commit status for %s/%d", repo.FullName, pipeline.Number)
|
log.Error().Err(err).Msgf("error setting commit status for %s/%d", repo.FullName, pipeline.Number)
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,6 @@ import (
|
||||||
|
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/pipeline/rpc"
|
"go.woodpecker-ci.org/woodpecker/v2/pipeline/rpc"
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/pipeline/rpc/proto"
|
"go.woodpecker-ci.org/woodpecker/v2/pipeline/rpc/proto"
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/server/forge"
|
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/server/logging"
|
"go.woodpecker-ci.org/woodpecker/v2/server/logging"
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/server/pubsub"
|
"go.woodpecker-ci.org/woodpecker/v2/server/pubsub"
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/server/queue"
|
"go.woodpecker-ci.org/woodpecker/v2/server/queue"
|
||||||
|
@ -37,7 +36,7 @@ type WoodpeckerServer struct {
|
||||||
peer RPC
|
peer RPC
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewWoodpeckerServer(forge forge.Forge, queue queue.Queue, logger logging.Log, pubsub *pubsub.Publisher, store store.Store) proto.WoodpeckerServer {
|
func NewWoodpeckerServer(queue queue.Queue, logger logging.Log, pubsub *pubsub.Publisher, store store.Store) proto.WoodpeckerServer {
|
||||||
pipelineTime := promauto.NewGaugeVec(prometheus.GaugeOpts{
|
pipelineTime := promauto.NewGaugeVec(prometheus.GaugeOpts{
|
||||||
Namespace: "woodpecker",
|
Namespace: "woodpecker",
|
||||||
Name: "pipeline_time",
|
Name: "pipeline_time",
|
||||||
|
@ -49,7 +48,6 @@ func NewWoodpeckerServer(forge forge.Forge, queue queue.Queue, logger logging.Lo
|
||||||
Help: "Pipeline count.",
|
Help: "Pipeline count.",
|
||||||
}, []string{"repo", "branch", "status", "pipeline"})
|
}, []string{"repo", "branch", "status", "pipeline"})
|
||||||
peer := RPC{
|
peer := RPC{
|
||||||
forge: forge,
|
|
||||||
store: store,
|
store: store,
|
||||||
queue: queue,
|
queue: queue,
|
||||||
pubsub: pubsub,
|
pubsub: pubsub,
|
||||||
|
|
36
server/model/forge.go
Normal file
36
server/model/forge.go
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
// 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 model
|
||||||
|
|
||||||
|
type ForgeType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
ForgeTypeGithub ForgeType = "github"
|
||||||
|
ForgeTypeGitlab ForgeType = "gitlab"
|
||||||
|
ForgeTypeGitea ForgeType = "gitea"
|
||||||
|
ForgeTypeBitbucket ForgeType = "bitbucket"
|
||||||
|
ForgeTypeBitbucketDatacenter ForgeType = "bitbucket-dc"
|
||||||
|
ForgeTypeAddon ForgeType = "addon"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Forge struct {
|
||||||
|
ID int64 `xorm:"pk autoincr 'id'"`
|
||||||
|
Type ForgeType `xorm:"VARCHAR(250)"`
|
||||||
|
URL string `xorm:"VARCHAR(500) 'url'"`
|
||||||
|
Client string `xorm:"VARCHAR(250)"`
|
||||||
|
ClientSecret string `xorm:"VARCHAR(250)"`
|
||||||
|
SkipVerify bool `xorm:"bool"`
|
||||||
|
AdditionalOptions map[string]any `xorm:"json"`
|
||||||
|
}
|
|
@ -17,6 +17,7 @@ package model
|
||||||
// Org represents an organization.
|
// Org represents an organization.
|
||||||
type Org struct {
|
type Org struct {
|
||||||
ID int64 `json:"id,omitempty" xorm:"pk autoincr 'id'"`
|
ID int64 `json:"id,omitempty" xorm:"pk autoincr 'id'"`
|
||||||
|
ForgeID int64 `json:"forge_id,omitempty" xorm:"forge_id"`
|
||||||
Name string `json:"name" xorm:"UNIQUE 'name'"`
|
Name string `json:"name" xorm:"UNIQUE 'name'"`
|
||||||
IsUser bool `json:"is_user" xorm:"is_user"`
|
IsUser bool `json:"is_user" xorm:"is_user"`
|
||||||
// if name lookup has to check for membership or not
|
// if name lookup has to check for membership or not
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
package model
|
package model
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/pipeline/errors"
|
"go.woodpecker-ci.org/woodpecker/v2/pipeline/errors/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Pipeline struct {
|
type Pipeline struct {
|
||||||
|
@ -27,7 +27,7 @@ type Pipeline struct {
|
||||||
Parent int64 `json:"parent" xorm:"pipeline_parent"`
|
Parent int64 `json:"parent" xorm:"pipeline_parent"`
|
||||||
Event WebhookEvent `json:"event" xorm:"pipeline_event"`
|
Event WebhookEvent `json:"event" xorm:"pipeline_event"`
|
||||||
Status StatusValue `json:"status" xorm:"INDEX 'pipeline_status'"`
|
Status StatusValue `json:"status" xorm:"INDEX 'pipeline_status'"`
|
||||||
Errors []*errors.PipelineError `json:"errors" xorm:"json 'pipeline_errors'"`
|
Errors []*types.PipelineError `json:"errors" xorm:"json 'pipeline_errors'"`
|
||||||
Created int64 `json:"created_at" xorm:"pipeline_created"`
|
Created int64 `json:"created_at" xorm:"pipeline_created"`
|
||||||
Updated int64 `json:"updated_at" xorm:"updated NOT NULL DEFAULT 0 'updated'"`
|
Updated int64 `json:"updated_at" xorm:"updated NOT NULL DEFAULT 0 'updated'"`
|
||||||
Started int64 `json:"started_at" xorm:"pipeline_started"`
|
Started int64 `json:"started_at" xorm:"pipeline_started"`
|
||||||
|
|
|
@ -24,6 +24,7 @@ import (
|
||||||
type Repo struct {
|
type Repo struct {
|
||||||
ID int64 `json:"id,omitempty" xorm:"pk autoincr 'repo_id'"`
|
ID int64 `json:"id,omitempty" xorm:"pk autoincr 'repo_id'"`
|
||||||
UserID int64 `json:"-" xorm:"INDEX repo_user_id"`
|
UserID int64 `json:"-" xorm:"INDEX repo_user_id"`
|
||||||
|
ForgeID int64 `json:"forge_id,omitempty" xorm:"forge_id"`
|
||||||
// ForgeRemoteID is the unique identifier for the repository on the forge.
|
// ForgeRemoteID is the unique identifier for the repository on the forge.
|
||||||
ForgeRemoteID ForgeRemoteID `json:"forge_remote_id" xorm:"forge_remote_id"`
|
ForgeRemoteID ForgeRemoteID `json:"forge_remote_id" xorm:"forge_remote_id"`
|
||||||
OrgID int64 `json:"org_id" xorm:"INDEX repo_org_id"`
|
OrgID int64 `json:"org_id" xorm:"INDEX repo_org_id"`
|
||||||
|
@ -44,6 +45,7 @@ type Repo struct {
|
||||||
IsGated bool `json:"gated" xorm:"repo_gated"`
|
IsGated bool `json:"gated" xorm:"repo_gated"`
|
||||||
IsActive bool `json:"active" xorm:"repo_active"`
|
IsActive bool `json:"active" xorm:"repo_active"`
|
||||||
AllowPull bool `json:"allow_pr" xorm:"repo_allow_pr"`
|
AllowPull bool `json:"allow_pr" xorm:"repo_allow_pr"`
|
||||||
|
AllowDeploy bool `json:"allow_deploy" xorm:"repo_allow_deploy"`
|
||||||
Config string `json:"config_file" xorm:"varchar(500) 'repo_config_path'"`
|
Config string `json:"config_file" xorm:"varchar(500) 'repo_config_path'"`
|
||||||
Hash string `json:"-" xorm:"varchar(500) 'repo_hash'"`
|
Hash string `json:"-" xorm:"varchar(500) 'repo_hash'"`
|
||||||
Perm *Perm `json:"-" xorm:"-"`
|
Perm *Perm `json:"-" xorm:"-"`
|
||||||
|
@ -112,6 +114,7 @@ type RepoPatch struct {
|
||||||
Timeout *int64 `json:"timeout,omitempty"`
|
Timeout *int64 `json:"timeout,omitempty"`
|
||||||
Visibility *string `json:"visibility,omitempty"`
|
Visibility *string `json:"visibility,omitempty"`
|
||||||
AllowPull *bool `json:"allow_pr,omitempty"`
|
AllowPull *bool `json:"allow_pr,omitempty"`
|
||||||
|
AllowDeploy *bool `json:"allow_deploy,omitempty"`
|
||||||
CancelPreviousPipelineEvents *[]WebhookEvent `json:"cancel_previous_pipeline_events"`
|
CancelPreviousPipelineEvents *[]WebhookEvent `json:"cancel_previous_pipeline_events"`
|
||||||
NetrcOnlyTrusted *bool `json:"netrc_only_trusted"`
|
NetrcOnlyTrusted *bool `json:"netrc_only_trusted"`
|
||||||
} // @name RepoPatch
|
} // @name RepoPatch
|
||||||
|
|
|
@ -34,6 +34,8 @@ type User struct {
|
||||||
// required: true
|
// required: true
|
||||||
ID int64 `json:"id" xorm:"pk autoincr 'user_id'"`
|
ID int64 `json:"id" xorm:"pk autoincr 'user_id'"`
|
||||||
|
|
||||||
|
ForgeID int64 `json:"forge_id,omitempty" xorm:"forge_id"`
|
||||||
|
|
||||||
ForgeRemoteID ForgeRemoteID `json:"-" xorm:"forge_remote_id"`
|
ForgeRemoteID ForgeRemoteID `json:"-" xorm:"forge_remote_id"`
|
||||||
|
|
||||||
// Login is the username for this user.
|
// Login is the username for this user.
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue