mirror of
https://github.com/woodpecker-ci/woodpecker.git
synced 2024-06-02 21:49:25 +00:00
Merge branch 'main' into service-use
This commit is contained in:
commit
9ed07b0e9d
|
@ -55,5 +55,5 @@ ci:
|
|||
autoupdate_commit_msg: '[pre-commit.ci] pre-commit autoupdate'
|
||||
autoupdate_schedule: quarterly
|
||||
# NB: hadolint not included in pre-commit.ci
|
||||
skip: [check-hooks-apply, check-useless-excludes, hadolint, prettier]
|
||||
skip: [check-hooks-apply, check-useless-excludes, hadolint, prettier, golangci-lint]
|
||||
submodules: false
|
||||
|
|
|
@ -4,8 +4,7 @@ when:
|
|||
variables:
|
||||
- &golang_image 'docker.io/golang:1.22.2'
|
||||
- &node_image 'docker.io/node:22-alpine'
|
||||
# TODO: switch back to upstream image after https://github.com/techknowlogick/xgo/pull/224 got merged
|
||||
- &xgo_image 'docker.io/pats22/xgo:go-1.22.1'
|
||||
- &xgo_image 'docker.io/techknowlogick/xgo:go-1.22.2'
|
||||
- &xgo_version 'go-1.21.2'
|
||||
|
||||
steps:
|
||||
|
@ -91,7 +90,7 @@ steps:
|
|||
release:
|
||||
depends_on:
|
||||
- checksums
|
||||
image: woodpeckerci/plugin-github-release:1.1.2
|
||||
image: woodpeckerci/plugin-github-release:1.2.0
|
||||
settings:
|
||||
api_key:
|
||||
from_secret: github_token
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
variables:
|
||||
- &golang_image 'docker.io/golang:1.22.2'
|
||||
- &node_image 'docker.io/node:22-alpine'
|
||||
# TODO: switch back to upstream image after https://github.com/techknowlogick/xgo/pull/224 got merged
|
||||
- &xgo_image 'docker.io/pats22/xgo:go-1.22.1'
|
||||
- &xgo_image 'docker.io/techknowlogick/xgo:go-1.22.2'
|
||||
- &xgo_version 'go-1.21.2'
|
||||
- &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'
|
||||
|
|
|
@ -6,7 +6,7 @@ when:
|
|||
- event: tag
|
||||
|
||||
steps:
|
||||
mastodon-toot:
|
||||
- name: mastodon-toot
|
||||
image: docker.io/woodpeckerci/plugin-mastodon-post
|
||||
settings:
|
||||
server: https://floss.social
|
||||
|
@ -16,10 +16,18 @@ steps:
|
|||
ai_token:
|
||||
from_secret: openai_token
|
||||
ai_prompt: |
|
||||
We want to present the next version of our app on Twitter.
|
||||
We want to present the next version of our app on Mastodon.
|
||||
Therefore we want to post a catching text, so users will know why they should
|
||||
update to the newest version. If there is no special feature included
|
||||
just summarize the changes in a few sentences. Use #WoodpeckerCI, #release and
|
||||
additional fitting hashtags and emojis to make the post more appealing.
|
||||
update to the newest version. Highlight the most special features. If there is no special feature
|
||||
included just summarize the changes in a few sentences. The whole text should not be longer than 240
|
||||
characters. Avoid naming contributors from. Use #WoodpeckerCI, #release and
|
||||
additional fitting hashtags and emojis to make the post more appealing
|
||||
|
||||
The changelog entry: {{ changelog }}
|
||||
- name: discord
|
||||
image: docker.io/appleboy/drone-discord:1.3.1
|
||||
settings:
|
||||
webhook_id: 1236558396820295711
|
||||
webhook_token:
|
||||
from_secret: discord_token
|
||||
message: '**{{ build.tag }}** was released: <https://github.com/woodpecker-ci/woodpecker/releases/tag/{{ build.tag }}>'
|
||||
|
|
|
@ -75,14 +75,29 @@ func FormatFlag(tmpl string, hidden ...bool) *cli.StringFlag {
|
|||
}
|
||||
}
|
||||
|
||||
// OutputFlags returns a slice of cli.Flag containing output format options.
|
||||
func OutputFlags(def string) []cli.Flag {
|
||||
return []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "output",
|
||||
Usage: "output format",
|
||||
Value: def,
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "output-no-headers",
|
||||
Usage: "don't print headers",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
var RepoFlag = &cli.StringFlag{
|
||||
Name: "repository",
|
||||
Aliases: []string{"repo"},
|
||||
Usage: "repository id or full-name (e.g. 134 or octocat/hello-world)",
|
||||
Usage: "repository id or full name (e.g. 134 or octocat/hello-world)",
|
||||
}
|
||||
|
||||
var OrgFlag = &cli.StringFlag{
|
||||
Name: "organization",
|
||||
Aliases: []string{"org"},
|
||||
Usage: "organization id or full-name (e.g. 123 or octocat)",
|
||||
Usage: "organization id or full name (e.g. 123 or octocat)",
|
||||
}
|
||||
|
|
|
@ -204,6 +204,10 @@ var flags = []cli.Flag{
|
|||
EnvVars: []string{"CI_PIPELINE_TARGET"},
|
||||
Name: "pipeline-target",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
EnvVars: []string{"CI_PIPELINE_TASK"},
|
||||
Name: "pipeline-task",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
EnvVars: []string{"CI_COMMIT_SHA"},
|
||||
Name: "commit-sha",
|
||||
|
|
|
@ -61,6 +61,7 @@ func metadataFromContext(c *cli.Context, axis matrix.Axis) metadata.Metadata {
|
|||
Event: c.String("pipeline-event"),
|
||||
ForgeURL: c.String("pipeline-url"),
|
||||
Target: c.String("pipeline-target"),
|
||||
Task: c.String("pipeline-task"),
|
||||
Commit: metadata.Commit{
|
||||
Sha: c.String("commit-sha"),
|
||||
Ref: c.String("commit-ref"),
|
||||
|
|
24
cli/output/output.go
Normal file
24
cli/output/output.go
Normal file
|
@ -0,0 +1,24 @@
|
|||
package output
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var ErrOutputOptionRequired = errors.New("output option required")
|
||||
|
||||
func ParseOutputOptions(out string) (string, []string) {
|
||||
out, opt, found := strings.Cut(out, "=")
|
||||
|
||||
if !found {
|
||||
return out, nil
|
||||
}
|
||||
|
||||
var optList []string
|
||||
|
||||
if opt != "" {
|
||||
optList = strings.Split(opt, ",")
|
||||
}
|
||||
|
||||
return out, optList
|
||||
}
|
203
cli/output/table.go
Normal file
203
cli/output/table.go
Normal file
|
@ -0,0 +1,203 @@
|
|||
package output
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
"unicode"
|
||||
|
||||
"github.com/mitchellh/mapstructure"
|
||||
)
|
||||
|
||||
// NewTable creates a new Table.
|
||||
func NewTable(out io.Writer) *Table {
|
||||
padding := 2
|
||||
|
||||
return &Table{
|
||||
w: tabwriter.NewWriter(out, 0, 0, padding, ' ', 0),
|
||||
columns: map[string]bool{},
|
||||
fieldMapping: map[string]FieldFn{},
|
||||
fieldAlias: map[string]string{},
|
||||
allowedFields: map[string]bool{},
|
||||
}
|
||||
}
|
||||
|
||||
type FieldFn func(obj any) string
|
||||
|
||||
type writerFlusher interface {
|
||||
io.Writer
|
||||
Flush() error
|
||||
}
|
||||
|
||||
// Table is a generic way to format object as a table.
|
||||
type Table struct {
|
||||
w writerFlusher
|
||||
columns map[string]bool
|
||||
fieldMapping map[string]FieldFn
|
||||
fieldAlias map[string]string
|
||||
allowedFields map[string]bool
|
||||
}
|
||||
|
||||
// Columns returns a list of known output columns.
|
||||
func (o *Table) Columns() (cols []string) {
|
||||
for c := range o.columns {
|
||||
cols = append(cols, c)
|
||||
}
|
||||
sort.Strings(cols)
|
||||
return
|
||||
}
|
||||
|
||||
// AddFieldAlias overrides the field name to allow custom column headers.
|
||||
func (o *Table) AddFieldAlias(field, alias string) *Table {
|
||||
o.fieldAlias[field] = alias
|
||||
return o
|
||||
}
|
||||
|
||||
// AddFieldFn adds a function which handles the output of the specified field.
|
||||
func (o *Table) AddFieldFn(field string, fn FieldFn) *Table {
|
||||
o.fieldMapping[field] = fn
|
||||
o.allowedFields[field] = true
|
||||
o.columns[field] = true
|
||||
return o
|
||||
}
|
||||
|
||||
// AddAllowedFields reads all first level fieldnames of the struct and allows them to be used.
|
||||
func (o *Table) AddAllowedFields(obj any) (*Table, error) {
|
||||
v := reflect.ValueOf(obj)
|
||||
if v.Kind() != reflect.Struct {
|
||||
return o, fmt.Errorf("AddAllowedFields input must be a struct")
|
||||
}
|
||||
t := v.Type()
|
||||
for i := 0; i < v.NumField(); i++ {
|
||||
k := t.Field(i).Type.Kind()
|
||||
if k != reflect.Bool &&
|
||||
k != reflect.Float32 &&
|
||||
k != reflect.Float64 &&
|
||||
k != reflect.String &&
|
||||
k != reflect.Int &&
|
||||
k != reflect.Int64 {
|
||||
// only allow simple values
|
||||
// complex values need to be mapped via a FieldFn
|
||||
continue
|
||||
}
|
||||
o.allowedFields[strings.ToLower(t.Field(i).Name)] = true
|
||||
o.allowedFields[fieldName(t.Field(i).Name)] = true
|
||||
o.columns[fieldName(t.Field(i).Name)] = true
|
||||
}
|
||||
return o, nil
|
||||
}
|
||||
|
||||
// RemoveAllowedField removes fields from the allowed list.
|
||||
func (o *Table) RemoveAllowedField(fields ...string) *Table {
|
||||
for _, field := range fields {
|
||||
delete(o.allowedFields, field)
|
||||
delete(o.columns, field)
|
||||
}
|
||||
return o
|
||||
}
|
||||
|
||||
// ValidateColumns returns an error if invalid columns are specified.
|
||||
func (o *Table) ValidateColumns(cols []string) error {
|
||||
var invalidCols []string
|
||||
for _, col := range cols {
|
||||
if _, ok := o.allowedFields[strings.ToLower(col)]; !ok {
|
||||
invalidCols = append(invalidCols, col)
|
||||
}
|
||||
}
|
||||
if len(invalidCols) > 0 {
|
||||
return fmt.Errorf("invalid table columns: %s", strings.Join(invalidCols, ","))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteHeader writes the table header.
|
||||
func (o *Table) WriteHeader(columns []string) {
|
||||
var header []string
|
||||
for _, col := range columns {
|
||||
if alias, ok := o.fieldAlias[col]; ok {
|
||||
col = alias
|
||||
}
|
||||
header = append(header, strings.ReplaceAll(strings.ToUpper(col), "_", " "))
|
||||
}
|
||||
_, _ = fmt.Fprintln(o.w, strings.Join(header, "\t"))
|
||||
}
|
||||
|
||||
func (o *Table) Flush() error {
|
||||
return o.w.Flush()
|
||||
}
|
||||
|
||||
// Write writes a table line.
|
||||
func (o *Table) Write(columns []string, obj any) error {
|
||||
var data map[string]any
|
||||
|
||||
if err := mapstructure.Decode(obj, &data); err != nil {
|
||||
return fmt.Errorf("failed to decode object: %w", err)
|
||||
}
|
||||
|
||||
dataL := map[string]any{}
|
||||
for key, value := range data {
|
||||
dataL[strings.ToLower(key)] = value
|
||||
}
|
||||
|
||||
var out []string
|
||||
for _, col := range columns {
|
||||
colName := strings.ToLower(col)
|
||||
if alias, ok := o.fieldAlias[colName]; ok {
|
||||
if fn, ok := o.fieldMapping[alias]; ok {
|
||||
out = append(out, fn(obj))
|
||||
continue
|
||||
}
|
||||
}
|
||||
if fn, ok := o.fieldMapping[colName]; ok {
|
||||
out = append(out, fn(obj))
|
||||
continue
|
||||
}
|
||||
if value, ok := dataL[strings.ReplaceAll(colName, "_", "")]; ok {
|
||||
if value == nil {
|
||||
out = append(out, NA(""))
|
||||
continue
|
||||
}
|
||||
if b, ok := value.(bool); ok {
|
||||
out = append(out, YesNo(b))
|
||||
continue
|
||||
}
|
||||
if s, ok := value.(string); ok {
|
||||
out = append(out, NA(s))
|
||||
continue
|
||||
}
|
||||
out = append(out, fmt.Sprintf("%v", value))
|
||||
}
|
||||
}
|
||||
_, _ = fmt.Fprintln(o.w, strings.Join(out, "\t"))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func NA(s string) string {
|
||||
if s == "" {
|
||||
return "-"
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func YesNo(b bool) string {
|
||||
if b {
|
||||
return "yes"
|
||||
}
|
||||
return "no"
|
||||
}
|
||||
|
||||
func fieldName(name string) string {
|
||||
r := []rune(name)
|
||||
var out []rune
|
||||
for i := range r {
|
||||
if i > 0 && (unicode.IsUpper(r[i])) && (i+1 < len(r) && unicode.IsLower(r[i+1]) || unicode.IsLower(r[i-1])) {
|
||||
out = append(out, '_')
|
||||
}
|
||||
out = append(out, unicode.ToLower(r[i]))
|
||||
}
|
||||
return string(out)
|
||||
}
|
75
cli/output/table_test.go
Normal file
75
cli/output/table_test.go
Normal file
|
@ -0,0 +1,75 @@
|
|||
package output
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type writerFlusherStub struct {
|
||||
bytes.Buffer
|
||||
}
|
||||
|
||||
func (s writerFlusherStub) Flush() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type testFieldsStruct struct {
|
||||
Name string
|
||||
Number int
|
||||
}
|
||||
|
||||
func TestTableOutput(t *testing.T) {
|
||||
var wfs writerFlusherStub
|
||||
to := NewTable(os.Stdout)
|
||||
to.w = &wfs
|
||||
|
||||
t.Run("AddAllowedFields", func(t *testing.T) {
|
||||
_, _ = to.AddAllowedFields(testFieldsStruct{})
|
||||
if _, ok := to.allowedFields["name"]; !ok {
|
||||
t.Error("name should be a allowed field")
|
||||
}
|
||||
})
|
||||
t.Run("AddFieldAlias", func(t *testing.T) {
|
||||
to.AddFieldAlias("woodpecker_ci", "woodpecker ci")
|
||||
if alias, ok := to.fieldAlias["woodpecker_ci"]; !ok || alias != "woodpecker ci" {
|
||||
t.Errorf("woodpecker_ci alias should be 'woodpecker ci', is: %v", alias)
|
||||
}
|
||||
})
|
||||
t.Run("AddFieldOutputFn", func(t *testing.T) {
|
||||
to.AddFieldFn("woodpecker ci", FieldFn(func(_ any) string {
|
||||
return "WOODPECKER CI!!!"
|
||||
}))
|
||||
if _, ok := to.fieldMapping["woodpecker ci"]; !ok {
|
||||
t.Errorf("'woodpecker ci' field output fn should be set")
|
||||
}
|
||||
})
|
||||
t.Run("ValidateColumns", func(t *testing.T) {
|
||||
err := to.ValidateColumns([]string{"non-existent", "NAME"})
|
||||
if err == nil ||
|
||||
strings.Contains(err.Error(), "name") ||
|
||||
!strings.Contains(err.Error(), "non-existent") {
|
||||
t.Errorf("error should contain 'non-existent' but not 'name': %v", err)
|
||||
}
|
||||
})
|
||||
t.Run("WriteHeader", func(t *testing.T) {
|
||||
to.WriteHeader([]string{"woodpecker_ci", "name"})
|
||||
if wfs.String() != "WOODPECKER CI\tNAME\n" {
|
||||
t.Errorf("written header should be 'WOODPECKER CI\\tNAME\\n', is: %q", wfs.String())
|
||||
}
|
||||
wfs.Reset()
|
||||
})
|
||||
t.Run("WriteLine", func(t *testing.T) {
|
||||
_ = to.Write([]string{"woodpecker_ci", "name", "number"}, &testFieldsStruct{"test123", 1000000000})
|
||||
if wfs.String() != "WOODPECKER CI!!!\ttest123\t1000000000\n" {
|
||||
t.Errorf("written line should be 'WOODPECKER CI!!!\\ttest123\\t1000000000\\n', is: %q", wfs.String())
|
||||
}
|
||||
wfs.Reset()
|
||||
})
|
||||
t.Run("Columns", func(t *testing.T) {
|
||||
if len(to.Columns()) != 3 {
|
||||
t.Errorf("unexpected number of columns: %v", to.Columns())
|
||||
}
|
||||
})
|
||||
}
|
|
@ -15,9 +15,7 @@
|
|||
package pipeline
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
|
||||
|
@ -31,8 +29,7 @@ var pipelineCreateCmd = &cli.Command{
|
|||
Usage: "create new pipeline",
|
||||
ArgsUsage: "<repo-id|repo-full-name>",
|
||||
Action: pipelineCreate,
|
||||
Flags: []cli.Flag{
|
||||
common.FormatFlag(tmplPipelineList),
|
||||
Flags: append(common.OutputFlags("table"), []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "branch",
|
||||
Usage: "branch to create pipeline from",
|
||||
|
@ -42,7 +39,7 @@ var pipelineCreateCmd = &cli.Command{
|
|||
Name: "var",
|
||||
Usage: "key=value",
|
||||
},
|
||||
},
|
||||
}...),
|
||||
}
|
||||
|
||||
func pipelineCreate(c *cli.Context) error {
|
||||
|
@ -76,10 +73,5 @@ func pipelineCreate(c *cli.Context) error {
|
|||
return err
|
||||
}
|
||||
|
||||
tmpl, err := template.New("_").Parse(c.String("format") + "\n")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return tmpl.Execute(os.Stdout, pipeline)
|
||||
return pipelineOutput(c, []woodpecker.Pipeline{*pipeline})
|
||||
}
|
||||
|
|
|
@ -15,14 +15,13 @@
|
|||
package pipeline
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strconv"
|
||||
"text/template"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
|
||||
"go.woodpecker-ci.org/woodpecker/v2/cli/common"
|
||||
"go.woodpecker-ci.org/woodpecker/v2/cli/internal"
|
||||
"go.woodpecker-ci.org/woodpecker/v2/woodpecker-go/woodpecker"
|
||||
)
|
||||
|
||||
var pipelineInfoCmd = &cli.Command{
|
||||
|
@ -30,7 +29,7 @@ var pipelineInfoCmd = &cli.Command{
|
|||
Usage: "show pipeline details",
|
||||
ArgsUsage: "<repo-id|repo-full-name> [pipeline]",
|
||||
Action: pipelineInfo,
|
||||
Flags: []cli.Flag{common.FormatFlag(tmplPipelineInfo)},
|
||||
Flags: common.OutputFlags("table"),
|
||||
}
|
||||
|
||||
func pipelineInfo(c *cli.Context) error {
|
||||
|
@ -65,20 +64,5 @@ func pipelineInfo(c *cli.Context) error {
|
|||
return err
|
||||
}
|
||||
|
||||
tmpl, err := template.New("_").Parse(c.String("format"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return tmpl.Execute(os.Stdout, pipeline)
|
||||
return pipelineOutput(c, []woodpecker.Pipeline{*pipeline})
|
||||
}
|
||||
|
||||
// template for pipeline information
|
||||
var tmplPipelineInfo = `Number: {{ .Number }}
|
||||
Status: {{ .Status }}
|
||||
Event: {{ .Event }}
|
||||
Commit: {{ .Commit }}
|
||||
Branch: {{ .Branch }}
|
||||
Ref: {{ .Ref }}
|
||||
Message: {{ .Message }}
|
||||
Author: {{ .Author }}
|
||||
`
|
||||
|
|
|
@ -15,13 +15,11 @@
|
|||
package pipeline
|
||||
|
||||
import (
|
||||
"os"
|
||||
"text/template"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
|
||||
"go.woodpecker-ci.org/woodpecker/v2/cli/common"
|
||||
"go.woodpecker-ci.org/woodpecker/v2/cli/internal"
|
||||
"go.woodpecker-ci.org/woodpecker/v2/woodpecker-go/woodpecker"
|
||||
)
|
||||
|
||||
var pipelineLastCmd = &cli.Command{
|
||||
|
@ -29,14 +27,13 @@ var pipelineLastCmd = &cli.Command{
|
|||
Usage: "show latest pipeline details",
|
||||
ArgsUsage: "<repo-id|repo-full-name>",
|
||||
Action: pipelineLast,
|
||||
Flags: []cli.Flag{
|
||||
common.FormatFlag(tmplPipelineInfo),
|
||||
Flags: append(common.OutputFlags("table"), []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "branch",
|
||||
Usage: "branch name",
|
||||
Value: "main",
|
||||
},
|
||||
},
|
||||
}...),
|
||||
}
|
||||
|
||||
func pipelineLast(c *cli.Context) error {
|
||||
|
@ -55,9 +52,5 @@ func pipelineLast(c *cli.Context) error {
|
|||
return err
|
||||
}
|
||||
|
||||
tmpl, err := template.New("_").Parse(c.String("format"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return tmpl.Execute(os.Stdout, pipeline)
|
||||
return pipelineOutput(c, []woodpecker.Pipeline{*pipeline})
|
||||
}
|
||||
|
|
|
@ -15,13 +15,11 @@
|
|||
package pipeline
|
||||
|
||||
import (
|
||||
"os"
|
||||
"text/template"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
|
||||
"go.woodpecker-ci.org/woodpecker/v2/cli/common"
|
||||
"go.woodpecker-ci.org/woodpecker/v2/cli/internal"
|
||||
"go.woodpecker-ci.org/woodpecker/v2/woodpecker-go/woodpecker"
|
||||
)
|
||||
|
||||
//nolint:gomnd
|
||||
|
@ -29,9 +27,8 @@ var pipelineListCmd = &cli.Command{
|
|||
Name: "ls",
|
||||
Usage: "show pipeline history",
|
||||
ArgsUsage: "<repo-id|repo-full-name>",
|
||||
Action: pipelineList,
|
||||
Flags: []cli.Flag{
|
||||
common.FormatFlag(tmplPipelineList),
|
||||
Action: List,
|
||||
Flags: append(common.OutputFlags("table"), []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "branch",
|
||||
Usage: "branch filter",
|
||||
|
@ -49,28 +46,33 @@ var pipelineListCmd = &cli.Command{
|
|||
Usage: "limit the list size",
|
||||
Value: 25,
|
||||
},
|
||||
},
|
||||
}...),
|
||||
}
|
||||
|
||||
func pipelineList(c *cli.Context) error {
|
||||
repoIDOrFullName := c.Args().First()
|
||||
func List(c *cli.Context) error {
|
||||
client, err := internal.NewClient(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
repoID, err := internal.ParseRepo(client, repoIDOrFullName)
|
||||
resources, err := pipelineList(c, client)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return pipelineOutput(c, resources)
|
||||
}
|
||||
|
||||
func pipelineList(c *cli.Context, client woodpecker.Client) ([]woodpecker.Pipeline, error) {
|
||||
resources := make([]woodpecker.Pipeline, 0)
|
||||
|
||||
repoIDOrFullName := c.Args().First()
|
||||
repoID, err := internal.ParseRepo(client, repoIDOrFullName)
|
||||
if err != nil {
|
||||
return resources, err
|
||||
}
|
||||
|
||||
pipelines, err := client.PipelineList(repoID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tmpl, err := template.New("_").Parse(c.String("format") + "\n")
|
||||
if err != nil {
|
||||
return err
|
||||
return resources, err
|
||||
}
|
||||
|
||||
branch := c.String("branch")
|
||||
|
@ -92,21 +94,9 @@ func pipelineList(c *cli.Context) error {
|
|||
if status != "" && pipeline.Status != status {
|
||||
continue
|
||||
}
|
||||
if err := tmpl.Execute(os.Stdout, pipeline); err != nil {
|
||||
return err
|
||||
}
|
||||
resources = append(resources, *pipeline)
|
||||
count++
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// template for pipeline list information
|
||||
var tmplPipelineList = "\x1b[33mPipeline #{{ .Number }} \x1b[0m" + `
|
||||
Status: {{ .Status }}
|
||||
Event: {{ .Event }}
|
||||
Commit: {{ .Commit }}
|
||||
Branch: {{ .Branch }}
|
||||
Ref: {{ .Ref }}
|
||||
Author: {{ .Author }} {{ if .Email }}<{{.Email}}>{{ end }}
|
||||
Message: {{ .Message }}
|
||||
`
|
||||
return resources, nil
|
||||
}
|
||||
|
|
132
cli/pipeline/list_test.go
Normal file
132
cli/pipeline/list_test.go
Normal file
|
@ -0,0 +1,132 @@
|
|||
package pipeline
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/urfave/cli/v2"
|
||||
|
||||
"go.woodpecker-ci.org/woodpecker/v2/woodpecker-go/woodpecker"
|
||||
"go.woodpecker-ci.org/woodpecker/v2/woodpecker-go/woodpecker/mocks"
|
||||
)
|
||||
|
||||
func TestPipelineList(t *testing.T) {
|
||||
testtases := []struct {
|
||||
name string
|
||||
repoID int64
|
||||
repoErr error
|
||||
pipelines []*woodpecker.Pipeline
|
||||
pipelineErr error
|
||||
args []string
|
||||
expected []woodpecker.Pipeline
|
||||
wantErr error
|
||||
}{
|
||||
{
|
||||
name: "success",
|
||||
repoID: 1,
|
||||
pipelines: []*woodpecker.Pipeline{
|
||||
{ID: 1, Branch: "main", Event: "push", Status: "success"},
|
||||
{ID: 2, Branch: "develop", Event: "pull_request", Status: "running"},
|
||||
{ID: 3, Branch: "main", Event: "push", Status: "failure"},
|
||||
},
|
||||
args: []string{"ls", "repo/name"},
|
||||
expected: []woodpecker.Pipeline{
|
||||
{ID: 1, Branch: "main", Event: "push", Status: "success"},
|
||||
{ID: 2, Branch: "develop", Event: "pull_request", Status: "running"},
|
||||
{ID: 3, Branch: "main", Event: "push", Status: "failure"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "filter by branch",
|
||||
repoID: 1,
|
||||
pipelines: []*woodpecker.Pipeline{
|
||||
{ID: 1, Branch: "main", Event: "push", Status: "success"},
|
||||
{ID: 2, Branch: "develop", Event: "pull_request", Status: "running"},
|
||||
{ID: 3, Branch: "main", Event: "push", Status: "failure"},
|
||||
},
|
||||
args: []string{"ls", "--branch", "main", "repo/name"},
|
||||
expected: []woodpecker.Pipeline{
|
||||
{ID: 1, Branch: "main", Event: "push", Status: "success"},
|
||||
{ID: 3, Branch: "main", Event: "push", Status: "failure"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "filter by event",
|
||||
repoID: 1,
|
||||
pipelines: []*woodpecker.Pipeline{
|
||||
{ID: 1, Branch: "main", Event: "push", Status: "success"},
|
||||
{ID: 2, Branch: "develop", Event: "pull_request", Status: "running"},
|
||||
{ID: 3, Branch: "main", Event: "push", Status: "failure"},
|
||||
},
|
||||
args: []string{"ls", "--event", "push", "repo/name"},
|
||||
expected: []woodpecker.Pipeline{
|
||||
{ID: 1, Branch: "main", Event: "push", Status: "success"},
|
||||
{ID: 3, Branch: "main", Event: "push", Status: "failure"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "filter by status",
|
||||
repoID: 1,
|
||||
pipelines: []*woodpecker.Pipeline{
|
||||
{ID: 1, Branch: "main", Event: "push", Status: "success"},
|
||||
{ID: 2, Branch: "develop", Event: "pull_request", Status: "running"},
|
||||
{ID: 3, Branch: "main", Event: "push", Status: "failure"},
|
||||
},
|
||||
args: []string{"ls", "--status", "success", "repo/name"},
|
||||
expected: []woodpecker.Pipeline{
|
||||
{ID: 1, Branch: "main", Event: "push", Status: "success"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "limit results",
|
||||
repoID: 1,
|
||||
pipelines: []*woodpecker.Pipeline{
|
||||
{ID: 1, Branch: "main", Event: "push", Status: "success"},
|
||||
{ID: 2, Branch: "develop", Event: "pull_request", Status: "running"},
|
||||
{ID: 3, Branch: "main", Event: "push", Status: "failure"},
|
||||
},
|
||||
args: []string{"ls", "--limit", "2", "repo/name"},
|
||||
expected: []woodpecker.Pipeline{
|
||||
{ID: 1, Branch: "main", Event: "push", Status: "success"},
|
||||
{ID: 2, Branch: "develop", Event: "pull_request", Status: "running"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "pipeline list error",
|
||||
repoID: 1,
|
||||
pipelineErr: errors.New("pipeline error"),
|
||||
args: []string{"ls", "repo/name"},
|
||||
wantErr: errors.New("pipeline error"),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range testtases {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
mockClient := mocks.NewClient(t)
|
||||
mockClient.On("PipelineList", mock.Anything).Return(tt.pipelines, tt.pipelineErr)
|
||||
mockClient.On("RepoLookup", mock.Anything).Return(&woodpecker.Repo{ID: tt.repoID}, nil)
|
||||
|
||||
app := &cli.App{Writer: io.Discard}
|
||||
c := cli.NewContext(app, nil, nil)
|
||||
|
||||
command := pipelineListCmd
|
||||
command.Action = func(c *cli.Context) error {
|
||||
pipelines, err := pipelineList(c, mockClient)
|
||||
if tt.wantErr != nil {
|
||||
assert.EqualError(t, err, tt.wantErr.Error())
|
||||
return nil
|
||||
}
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, tt.expected, pipelines)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
_ = command.Run(c, tt.args...)
|
||||
})
|
||||
}
|
||||
}
|
|
@ -15,7 +15,15 @@
|
|||
package pipeline
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"text/template"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
|
||||
"go.woodpecker-ci.org/woodpecker/v2/cli/output"
|
||||
"go.woodpecker-ci.org/woodpecker/v2/woodpecker-go/woodpecker"
|
||||
)
|
||||
|
||||
// Command exports the pipeline command set.
|
||||
|
@ -37,3 +45,53 @@ var Command = &cli.Command{
|
|||
pipelineCreateCmd,
|
||||
},
|
||||
}
|
||||
|
||||
func pipelineOutput(c *cli.Context, resources []woodpecker.Pipeline, fd ...io.Writer) error {
|
||||
outfmt, outopt := output.ParseOutputOptions(c.String("output"))
|
||||
noHeader := c.Bool("output-no-headers")
|
||||
|
||||
var out io.Writer
|
||||
switch len(fd) {
|
||||
case 0:
|
||||
out = os.Stdout
|
||||
case 1:
|
||||
out = fd[0]
|
||||
default:
|
||||
out = os.Stdout
|
||||
}
|
||||
|
||||
switch outfmt {
|
||||
case "go-template":
|
||||
if len(outopt) < 1 {
|
||||
return fmt.Errorf("%w: missing template", output.ErrOutputOptionRequired)
|
||||
}
|
||||
|
||||
tmpl, err := template.New("_").Parse(outopt[0] + "\n")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tmpl.Execute(out, resources); err != nil {
|
||||
return err
|
||||
}
|
||||
case "table":
|
||||
fallthrough
|
||||
default:
|
||||
table := output.NewTable(out)
|
||||
cols := []string{"Number", "Status", "Event", "Branch", "Commit", "Author"}
|
||||
|
||||
if len(outopt) > 0 {
|
||||
cols = outopt
|
||||
}
|
||||
if !noHeader {
|
||||
table.WriteHeader(cols)
|
||||
}
|
||||
for _, resource := range resources {
|
||||
if err := table.Write(cols, resource); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
table.Flush()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
86
cli/pipeline/pipeline_test.go
Normal file
86
cli/pipeline/pipeline_test.go
Normal file
|
@ -0,0 +1,86 @@
|
|||
package pipeline
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/urfave/cli/v2"
|
||||
|
||||
"go.woodpecker-ci.org/woodpecker/v2/cli/common"
|
||||
"go.woodpecker-ci.org/woodpecker/v2/woodpecker-go/woodpecker"
|
||||
)
|
||||
|
||||
func TestPipelineOutput(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
args []string
|
||||
expected string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "table output with default columns",
|
||||
args: []string{},
|
||||
expected: "NUMBER STATUS EVENT BRANCH COMMIT AUTHOR\n1 success push main abcdef John Doe\n",
|
||||
},
|
||||
{
|
||||
name: "table output with custom columns",
|
||||
args: []string{"output", "--output", "table=Number,Status,Branch"},
|
||||
expected: "NUMBER STATUS BRANCH\n1 success main\n",
|
||||
},
|
||||
{
|
||||
name: "table output with no header",
|
||||
args: []string{"output", "--output-no-headers"},
|
||||
expected: "1 success push main abcdef John Doe\n",
|
||||
},
|
||||
{
|
||||
name: "go-template output",
|
||||
args: []string{"output", "--output", "go-template={{range . }}{{.Number}} {{.Status}} {{.Branch}}{{end}}"},
|
||||
expected: "1 success main\n",
|
||||
},
|
||||
{
|
||||
name: "invalid go-template",
|
||||
args: []string{"output", "--output", "go-template={{.InvalidField}}"},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
pipelines := []woodpecker.Pipeline{
|
||||
{
|
||||
Number: 1,
|
||||
Status: "success",
|
||||
Event: "push",
|
||||
Branch: "main",
|
||||
Commit: "abcdef",
|
||||
Author: "John Doe",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
app := &cli.App{Writer: io.Discard}
|
||||
c := cli.NewContext(app, nil, nil)
|
||||
|
||||
command := &cli.Command{}
|
||||
command.Name = "output"
|
||||
command.Flags = common.OutputFlags("table")
|
||||
command.Action = func(c *cli.Context) error {
|
||||
var buf bytes.Buffer
|
||||
err := pipelineOutput(c, pipelines, &buf)
|
||||
|
||||
if tt.wantErr {
|
||||
assert.Error(t, err)
|
||||
return nil
|
||||
}
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, tt.expected, buf.String())
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
_ = command.Run(c, tt.args...)
|
||||
})
|
||||
}
|
||||
}
|
|
@ -26,7 +26,7 @@ const docTemplate = `{
|
|||
"tags": [
|
||||
"Agents"
|
||||
],
|
||||
"summary": "Get agent list",
|
||||
"summary": "List agents",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
|
@ -64,13 +64,14 @@ const docTemplate = `{
|
|||
}
|
||||
},
|
||||
"post": {
|
||||
"description": "Creates a new agent with a random token",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Agents"
|
||||
],
|
||||
"summary": "Create a new agent with a random token so a new agent can connect to the server",
|
||||
"summary": "Create a new agent",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
|
@ -108,7 +109,7 @@ const docTemplate = `{
|
|||
"tags": [
|
||||
"Agents"
|
||||
],
|
||||
"summary": "Get agent information",
|
||||
"summary": "Get an agent",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
|
@ -173,7 +174,7 @@ const docTemplate = `{
|
|||
"tags": [
|
||||
"Agents"
|
||||
],
|
||||
"summary": "Update agent information",
|
||||
"summary": "Update an agent",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
|
@ -218,7 +219,7 @@ const docTemplate = `{
|
|||
"tags": [
|
||||
"Agents"
|
||||
],
|
||||
"summary": "Get agent tasks",
|
||||
"summary": "List agent tasks",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
|
@ -283,7 +284,7 @@ const docTemplate = `{
|
|||
"tags": [
|
||||
"Badges"
|
||||
],
|
||||
"summary": "Get status badge, SVG format",
|
||||
"summary": "Get status of pipeline as SVG badge",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "integer",
|
||||
|
@ -744,7 +745,7 @@ const docTemplate = `{
|
|||
"tags": [
|
||||
"Organizations"
|
||||
],
|
||||
"summary": "Lookup organization by full-name",
|
||||
"summary": "Lookup an organization by full name",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
|
@ -756,7 +757,7 @@ const docTemplate = `{
|
|||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "the organizations full-name / slug",
|
||||
"description": "the organizations full name / slug",
|
||||
"name": "org_full_name",
|
||||
"in": "path",
|
||||
"required": true
|
||||
|
@ -781,7 +782,7 @@ const docTemplate = `{
|
|||
"tags": [
|
||||
"Orgs"
|
||||
],
|
||||
"summary": "Get all orgs",
|
||||
"summary": "List organizations",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
|
@ -828,7 +829,7 @@ const docTemplate = `{
|
|||
"tags": [
|
||||
"Orgs"
|
||||
],
|
||||
"summary": "Delete an org",
|
||||
"summary": "Delete an organization",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
|
@ -861,7 +862,7 @@ const docTemplate = `{
|
|||
"tags": [
|
||||
"Organization"
|
||||
],
|
||||
"summary": "Get organization by id",
|
||||
"summary": "Get an organization",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
|
@ -900,7 +901,7 @@ const docTemplate = `{
|
|||
"tags": [
|
||||
"Organization permissions"
|
||||
],
|
||||
"summary": "Get the permissions of the current user in the given organization",
|
||||
"summary": "Get the permissions of the currently authenticated user for the given organization",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
|
@ -939,7 +940,7 @@ const docTemplate = `{
|
|||
"tags": [
|
||||
"Organization secrets"
|
||||
],
|
||||
"summary": "Get the organization secret list",
|
||||
"summary": "List organization secrets",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
|
@ -990,7 +991,7 @@ const docTemplate = `{
|
|||
"tags": [
|
||||
"Organization secrets"
|
||||
],
|
||||
"summary": "Persist/create an organization secret",
|
||||
"summary": "Create an organization secret",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
|
@ -1035,7 +1036,7 @@ const docTemplate = `{
|
|||
"tags": [
|
||||
"Organization secrets"
|
||||
],
|
||||
"summary": "Get the named organization secret",
|
||||
"summary": "Get a organization secret by name",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
|
@ -1076,7 +1077,7 @@ const docTemplate = `{
|
|||
"tags": [
|
||||
"Organization secrets"
|
||||
],
|
||||
"summary": "Delete the named secret from an organization",
|
||||
"summary": "Delete an organization secret by name",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
|
@ -1114,7 +1115,7 @@ const docTemplate = `{
|
|||
"tags": [
|
||||
"Organization secrets"
|
||||
],
|
||||
"summary": "Update an organization secret",
|
||||
"summary": "Update an organization secret by name",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
|
@ -1166,7 +1167,7 @@ const docTemplate = `{
|
|||
"tags": [
|
||||
"Pipeline queues"
|
||||
],
|
||||
"summary": "List pipeline queues",
|
||||
"summary": "List pipelines in queue",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
|
@ -1257,7 +1258,7 @@ const docTemplate = `{
|
|||
"tags": [
|
||||
"Pipeline queues"
|
||||
],
|
||||
"summary": "Pause a pipeline queue",
|
||||
"summary": "Pause the pipeline queue",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
|
@ -1283,7 +1284,7 @@ const docTemplate = `{
|
|||
"tags": [
|
||||
"Pipeline queues"
|
||||
],
|
||||
"summary": "Resume a pipeline queue",
|
||||
"summary": "Resume the pipeline queue",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
|
@ -1303,13 +1304,14 @@ const docTemplate = `{
|
|||
},
|
||||
"/repos": {
|
||||
"get": {
|
||||
"description": "Returns a list of all repositories. Requires admin rights.",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Repositories"
|
||||
],
|
||||
"summary": "List all repositories on the server. Requires admin rights.",
|
||||
"summary": "List all repositories on the server",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
|
@ -1395,7 +1397,7 @@ const docTemplate = `{
|
|||
"tags": [
|
||||
"Repositories"
|
||||
],
|
||||
"summary": "Get repository by full-name",
|
||||
"summary": "Lookup a repository by full name",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
|
@ -1407,7 +1409,7 @@ const docTemplate = `{
|
|||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "the repository full-name / slug",
|
||||
"description": "the repository full name / slug",
|
||||
"name": "repo_full_name",
|
||||
"in": "path",
|
||||
"required": true
|
||||
|
@ -1425,13 +1427,14 @@ const docTemplate = `{
|
|||
},
|
||||
"/repos/repair": {
|
||||
"post": {
|
||||
"description": "Executes a repair process on all repositories. Requires admin rights.",
|
||||
"produces": [
|
||||
"text/plain"
|
||||
],
|
||||
"tags": [
|
||||
"Repositories"
|
||||
],
|
||||
"summary": "Repair all repositories on the server. Requires admin rights.",
|
||||
"summary": "Repair all repositories on the server",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
|
@ -1457,7 +1460,7 @@ const docTemplate = `{
|
|||
"tags": [
|
||||
"Repositories"
|
||||
],
|
||||
"summary": "Get repository information",
|
||||
"summary": "Get a repository",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
|
@ -1525,7 +1528,7 @@ const docTemplate = `{
|
|||
"tags": [
|
||||
"Repositories"
|
||||
],
|
||||
"summary": "Change a repository",
|
||||
"summary": "Update a repository",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
|
@ -1570,7 +1573,7 @@ const docTemplate = `{
|
|||
"tags": [
|
||||
"Repositories"
|
||||
],
|
||||
"summary": "Get repository branches",
|
||||
"summary": "Get branches of a repository",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
|
@ -1623,7 +1626,7 @@ const docTemplate = `{
|
|||
"tags": [
|
||||
"Repositories"
|
||||
],
|
||||
"summary": "Change a repository's owner, to the one holding the access token",
|
||||
"summary": "Change a repository's owner to the currently authenticated user",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
|
@ -1659,7 +1662,7 @@ const docTemplate = `{
|
|||
"tags": [
|
||||
"Repository cron jobs"
|
||||
],
|
||||
"summary": "Get the cron job list",
|
||||
"summary": "List cron jobs",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
|
@ -1710,7 +1713,7 @@ const docTemplate = `{
|
|||
"tags": [
|
||||
"Repository cron jobs"
|
||||
],
|
||||
"summary": "Persist/creat a cron job",
|
||||
"summary": "Create a cron job",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
|
@ -1755,7 +1758,7 @@ const docTemplate = `{
|
|||
"tags": [
|
||||
"Repository cron jobs"
|
||||
],
|
||||
"summary": "Get a cron job by id",
|
||||
"summary": "Get a cron job",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
|
@ -1837,7 +1840,7 @@ const docTemplate = `{
|
|||
"tags": [
|
||||
"Repository cron jobs"
|
||||
],
|
||||
"summary": "Delete a cron job by id",
|
||||
"summary": "Delete a cron job",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
|
@ -1927,7 +1930,7 @@ const docTemplate = `{
|
|||
"tags": [
|
||||
"Pipeline logs"
|
||||
],
|
||||
"summary": "Deletes log",
|
||||
"summary": "Deletes all logs of a pipeline",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
|
@ -1967,7 +1970,7 @@ const docTemplate = `{
|
|||
"tags": [
|
||||
"Pipeline logs"
|
||||
],
|
||||
"summary": "Log information",
|
||||
"summary": "Get logs for a pipeline step",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
|
@ -2020,7 +2023,7 @@ const docTemplate = `{
|
|||
"tags": [
|
||||
"Pipeline logs"
|
||||
],
|
||||
"summary": "Deletes step log",
|
||||
"summary": "Delete step logs of a pipeline",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
|
@ -2108,7 +2111,7 @@ const docTemplate = `{
|
|||
"tags": [
|
||||
"Repositories"
|
||||
],
|
||||
"summary": "Repository permission information",
|
||||
"summary": "Check current authenticated users access to the repository",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
|
@ -2138,13 +2141,14 @@ const docTemplate = `{
|
|||
},
|
||||
"/repos/{repo_id}/pipelines": {
|
||||
"get": {
|
||||
"description": "Get a list of pipelines for a repository.",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Pipelines"
|
||||
],
|
||||
"summary": "Get pipelines, current running and past ones",
|
||||
"summary": "List repository pipelines",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
|
@ -2207,7 +2211,7 @@ const docTemplate = `{
|
|||
"tags": [
|
||||
"Pipelines"
|
||||
],
|
||||
"summary": "Run/trigger a pipelines",
|
||||
"summary": "Trigger a manual pipeline",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
|
@ -2252,7 +2256,7 @@ const docTemplate = `{
|
|||
"tags": [
|
||||
"Pipelines"
|
||||
],
|
||||
"summary": "Pipeline information by number",
|
||||
"summary": "Get a repositories pipeline",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
|
@ -2347,7 +2351,7 @@ const docTemplate = `{
|
|||
"tags": [
|
||||
"Pipelines"
|
||||
],
|
||||
"summary": "Delete pipeline",
|
||||
"summary": "Delete a pipeline",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
|
@ -2387,7 +2391,7 @@ const docTemplate = `{
|
|||
"tags": [
|
||||
"Pipelines"
|
||||
],
|
||||
"summary": "Start pipelines in gated repos",
|
||||
"summary": "Approve and start a pipeline",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
|
@ -2430,7 +2434,7 @@ const docTemplate = `{
|
|||
"tags": [
|
||||
"Pipelines"
|
||||
],
|
||||
"summary": "Cancels a pipeline",
|
||||
"summary": "Cancel a pipeline",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
|
@ -2470,7 +2474,7 @@ const docTemplate = `{
|
|||
"tags": [
|
||||
"Pipelines"
|
||||
],
|
||||
"summary": "Pipeline configuration",
|
||||
"summary": "Get configuration files for a pipeline",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
|
@ -2516,7 +2520,7 @@ const docTemplate = `{
|
|||
"tags": [
|
||||
"Pipelines"
|
||||
],
|
||||
"summary": "Decline pipelines in gated repos",
|
||||
"summary": "Decline a pipeline",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
|
@ -2559,7 +2563,7 @@ const docTemplate = `{
|
|||
"tags": [
|
||||
"Repositories"
|
||||
],
|
||||
"summary": "List active pull requests",
|
||||
"summary": "List active pull requests of a repository",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
|
@ -2612,7 +2616,7 @@ const docTemplate = `{
|
|||
"tags": [
|
||||
"Repository registries"
|
||||
],
|
||||
"summary": "Get the registry list",
|
||||
"summary": "List registries",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
|
@ -2663,7 +2667,7 @@ const docTemplate = `{
|
|||
"tags": [
|
||||
"Repository registries"
|
||||
],
|
||||
"summary": "Persist/create a registry",
|
||||
"summary": "Create a registry",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
|
@ -2708,7 +2712,7 @@ const docTemplate = `{
|
|||
"tags": [
|
||||
"Repository registries"
|
||||
],
|
||||
"summary": "Get a named registry",
|
||||
"summary": "Get a registry by name",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
|
@ -2749,7 +2753,7 @@ const docTemplate = `{
|
|||
"tags": [
|
||||
"Repository registries"
|
||||
],
|
||||
"summary": "Delete a named registry",
|
||||
"summary": "Delete a registry by name",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
|
@ -2787,7 +2791,7 @@ const docTemplate = `{
|
|||
"tags": [
|
||||
"Repository registries"
|
||||
],
|
||||
"summary": "Update a named registry",
|
||||
"summary": "Update a registry by name",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
|
@ -2872,7 +2876,7 @@ const docTemplate = `{
|
|||
"tags": [
|
||||
"Repository secrets"
|
||||
],
|
||||
"summary": "Get the secret list",
|
||||
"summary": "List repository secrets",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
|
@ -2923,7 +2927,7 @@ const docTemplate = `{
|
|||
"tags": [
|
||||
"Repository secrets"
|
||||
],
|
||||
"summary": "Persist/create a secret",
|
||||
"summary": "Create a repository secret",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
|
@ -2968,7 +2972,7 @@ const docTemplate = `{
|
|||
"tags": [
|
||||
"Repository secrets"
|
||||
],
|
||||
"summary": "Get a named secret",
|
||||
"summary": "Get a repository secret by name",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
|
@ -3009,7 +3013,7 @@ const docTemplate = `{
|
|||
"tags": [
|
||||
"Repository secrets"
|
||||
],
|
||||
"summary": "Delete a named secret",
|
||||
"summary": "Delete a repository secret by name",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
|
@ -3047,7 +3051,7 @@ const docTemplate = `{
|
|||
"tags": [
|
||||
"Repository secrets"
|
||||
],
|
||||
"summary": "Update a named secret",
|
||||
"summary": "Update a repository secret by name",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
|
@ -3099,7 +3103,7 @@ const docTemplate = `{
|
|||
"tags": [
|
||||
"Secrets"
|
||||
],
|
||||
"summary": "Get the global secret list",
|
||||
"summary": "List global secrets",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
|
@ -3143,7 +3147,7 @@ const docTemplate = `{
|
|||
"tags": [
|
||||
"Secrets"
|
||||
],
|
||||
"summary": "Persist/create a global secret",
|
||||
"summary": "Create a global secret",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
|
@ -3311,14 +3315,14 @@ const docTemplate = `{
|
|||
},
|
||||
"/stream/events": {
|
||||
"get": {
|
||||
"description": "event source streaming for compatibility with quic and http2",
|
||||
"description": "With quic and http2 support",
|
||||
"produces": [
|
||||
"text/plain"
|
||||
],
|
||||
"tags": [
|
||||
"Events"
|
||||
],
|
||||
"summary": "Event stream",
|
||||
"summary": "Stream events like pipeline updates",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK"
|
||||
|
@ -3334,7 +3338,7 @@ const docTemplate = `{
|
|||
"tags": [
|
||||
"Pipeline logs"
|
||||
],
|
||||
"summary": "Log stream",
|
||||
"summary": "Stream logs of a pipeline step",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "integer",
|
||||
|
@ -3373,7 +3377,7 @@ const docTemplate = `{
|
|||
"tags": [
|
||||
"User"
|
||||
],
|
||||
"summary": "Returns the currently authenticated user.",
|
||||
"summary": "Get the currently authenticated user",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
|
@ -3396,14 +3400,14 @@ const docTemplate = `{
|
|||
},
|
||||
"/user/feed": {
|
||||
"get": {
|
||||
"description": "Feed entries can be used to display information on the latest builds.",
|
||||
"description": "The feed lists the most recent pipeline for the currently authenticated user.",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"User"
|
||||
],
|
||||
"summary": "A feed entry for a build.",
|
||||
"summary": "Get the currently authenticaed users pipeline feed",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
|
@ -3418,7 +3422,10 @@ const docTemplate = `{
|
|||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/Feed"
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/Feed"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3433,7 +3440,7 @@ const docTemplate = `{
|
|||
"tags": [
|
||||
"User"
|
||||
],
|
||||
"summary": "Get user's repos",
|
||||
"summary": "Get user's repositories",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
|
@ -3523,7 +3530,7 @@ const docTemplate = `{
|
|||
"tags": [
|
||||
"Users"
|
||||
],
|
||||
"summary": "Get all users",
|
||||
"summary": "List users",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
|
@ -3677,7 +3684,7 @@ const docTemplate = `{
|
|||
"tags": [
|
||||
"Users"
|
||||
],
|
||||
"summary": "Change a user",
|
||||
"summary": "Update a user",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
|
@ -4016,6 +4023,9 @@ const docTemplate = `{
|
|||
"created_at": {
|
||||
"type": "integer"
|
||||
},
|
||||
"deploy_task": {
|
||||
"type": "string"
|
||||
},
|
||||
"deploy_to": {
|
||||
"type": "string"
|
||||
},
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
# Workflow syntax
|
||||
|
||||
The workflow section defines a list of steps to build, test and deploy your code. Steps are executed serially, in the order in which they are defined. If a step returns a non-zero exit code, the workflow and therefore all other workflows and the pipeline immediately aborts and returns a failure status.
|
||||
The Workflow section defines a list of steps to build, test and deploy your code. The steps are executed serially in the order in which they are defined. If a step returns a non-zero exit code, the workflow and therefore the entire pipeline terminates immediately and returns an error status.
|
||||
|
||||
:::note
|
||||
An exception to this rule are steps with a [`status: [failure]`](#status) condition, which ensures that they are executed in the case of a failed run.
|
||||
:::
|
||||
|
||||
Example steps:
|
||||
|
||||
|
@ -161,6 +165,9 @@ Only build steps can define commands. You cannot use commands with plugins or se
|
|||
|
||||
Allows you to specify the entrypoint for containers. Note that this must be a list of the command and its arguments (e.g. `["/bin/sh", "-c"]`).
|
||||
|
||||
If you define [`commands`](#commands), the default entrypoint will be `["/bin/sh", "-c", "echo $CI_SCRIPT | base64 -d | /bin/sh -e"]`.
|
||||
You can also use a custom shell with `CI_SCRIPT` (Base64-encoded) if you set `commands`.
|
||||
|
||||
### `environment`
|
||||
|
||||
Woodpecker provides the ability to pass environment variables to individual steps.
|
||||
|
|
|
@ -85,6 +85,7 @@ This is the reference list of all environment variables available to your pipeli
|
|||
| `CI_PIPELINE_URL` | link to the web UI for the pipeline |
|
||||
| `CI_PIPELINE_FORGE_URL` | link to the forge's web UI for the commit(s) or tag that triggered the pipeline |
|
||||
| `CI_PIPELINE_DEPLOY_TARGET` | pipeline deploy target for `deployment` events (i.e. production) |
|
||||
| `CI_PIPELINE_DEPLOY_TASK` | pipeline deploy task for `deployment` events (i.e. migration) |
|
||||
| `CI_PIPELINE_STATUS` | pipeline status (success, failure) |
|
||||
| `CI_PIPELINE_CREATED` | pipeline created UNIX timestamp |
|
||||
| `CI_PIPELINE_STARTED` | pipeline started UNIX timestamp |
|
||||
|
@ -118,6 +119,7 @@ This is the reference list of all environment variables available to your pipeli
|
|||
| `CI_PREV_PIPELINE_URL` | previous pipeline link in CI |
|
||||
| `CI_PREV_PIPELINE_FORGE_URL` | previous pipeline link to event in forge |
|
||||
| `CI_PREV_PIPELINE_DEPLOY_TARGET` | previous pipeline deploy target for `deployment` events (ie production) |
|
||||
| `CI_PREV_PIPELINE_DEPLOY_TASK` | previous pipeline deploy task for `deployment` events (ie migration) |
|
||||
| `CI_PREV_PIPELINE_STATUS` | previous pipeline status (success, failure) |
|
||||
| `CI_PREV_PIPELINE_CREATED` | previous pipeline created UNIX timestamp |
|
||||
| `CI_PREV_PIPELINE_STARTED` | previous pipeline started UNIX timestamp |
|
||||
|
|
|
@ -52,8 +52,9 @@ If you have some missing resources, please feel free to [open a pull-request](ht
|
|||
- [Quest For CICD - WoodpeckerCI](https://omaramin.me/posts/woodpecker/)
|
||||
- [Getting started with Woodpecker CI](https://systeemkabouter.eu/getting-started-with-woodpecker-ci.html)
|
||||
- [Installing gitea and woodpecker using binary packages](https://neelex.com/2023/03/26/Installing-gitea-using-binary-packages/)
|
||||
- [Deploying mdbook to codeberg pages using woodpecker CI](https://www.markpitblado.me/blog/ci-deployment-of-mdbook)
|
||||
- [Deploying mdbook to codeberg pages using woodpecker CI](https://www.markpitblado.me/blog/deploying-mdbook-to-codeberg-pages-using-woodpecker-ci/)
|
||||
- [Deploy a Fly app with Woodpecker CI](https://joeroe.io/2024/01/09/deploy-fly-woodpecker-ci.html)
|
||||
- [Ansible - using Woodpecker as an alternative to Semaphore](https://pat-s.me/ansible-using-woodpecker-as-an-alternative-to-semaphore/)
|
||||
|
||||
## Videos
|
||||
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
## ORM
|
||||
|
||||
Woodpecker uses [Xorm](https://xorm.io/) as ORM for the database connection.
|
||||
You can find its documentation at [gobook.io/read/gitea.com/xorm](https://gobook.io/read/gitea.com/xorm/manual-en-US/).
|
||||
|
||||
## Add a new migration
|
||||
|
||||
|
|
|
@ -60,6 +60,11 @@
|
|||
"docs": "https://raw.githubusercontent.com/markopolo123/gitea-comment-plugin/main/docs.md",
|
||||
"verified": false
|
||||
},
|
||||
{
|
||||
"name": "Gitea publisher-golang",
|
||||
"docs": "https://raw.githubusercontent.com/woodpecker-kit/woodpecker-gitea-publisher-golang/main/doc/docs.md",
|
||||
"verified": false
|
||||
},
|
||||
{
|
||||
"name": "Git Push",
|
||||
"docs": "https://raw.githubusercontent.com/appleboy/drone-git-push/master/DOCS.md",
|
||||
|
|
8
go.sum
8
go.sum
|
@ -540,8 +540,6 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y
|
|||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
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.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
|
||||
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
|
||||
golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
|
||||
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
|
@ -575,8 +573,6 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug
|
|||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
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.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc=
|
||||
golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
|
||||
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
|
||||
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
|
@ -623,8 +619,6 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
|
||||
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
|
||||
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
|
@ -633,8 +627,6 @@ golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuX
|
|||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||
golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
|
||||
golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8=
|
||||
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
|
||||
golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q=
|
||||
golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
|
|
|
@ -18,21 +18,19 @@ import (
|
|||
"encoding/base64"
|
||||
)
|
||||
|
||||
func GenerateContainerConf(commands []string, goos string) (env map[string]string, entry []string, cmd string) {
|
||||
func GenerateContainerConf(commands []string, goos string) (env map[string]string, entry []string) {
|
||||
env = make(map[string]string)
|
||||
if goos == "windows" {
|
||||
env["CI_SCRIPT"] = base64.StdEncoding.EncodeToString([]byte(generateScriptWindows(commands)))
|
||||
env["HOME"] = "c:\\root"
|
||||
env["SHELL"] = "powershell.exe"
|
||||
entry = []string{"powershell", "-noprofile", "-noninteractive", "-command"}
|
||||
cmd = "[System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($Env:CI_SCRIPT)) | iex"
|
||||
entry = []string{"powershell", "-noprofile", "-noninteractive", "-command", "[System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($Env:CI_SCRIPT)) | iex"}
|
||||
} else {
|
||||
env["CI_SCRIPT"] = base64.StdEncoding.EncodeToString([]byte(generateScriptPosix(commands)))
|
||||
env["HOME"] = "/root"
|
||||
env["SHELL"] = "/bin/sh"
|
||||
entry = []string{"/bin/sh", "-c"}
|
||||
cmd = "echo $CI_SCRIPT | base64 -d | /bin/sh -e"
|
||||
entry = []string{"/bin/sh", "-c", "echo $CI_SCRIPT | base64 -d | /bin/sh -e"}
|
||||
}
|
||||
|
||||
return env, entry, cmd
|
||||
return env, entry
|
||||
}
|
||||
|
|
|
@ -12,16 +12,14 @@ const (
|
|||
)
|
||||
|
||||
func TestGenerateContainerConf(t *testing.T) {
|
||||
gotEnv, gotEntry, gotCmd := GenerateContainerConf([]string{"echo hello world"}, "windows")
|
||||
gotEnv, gotEntry := GenerateContainerConf([]string{"echo hello world"}, "windows")
|
||||
assert.Equal(t, windowsScriptBase64, gotEnv["CI_SCRIPT"])
|
||||
assert.Equal(t, "c:\\root", gotEnv["HOME"])
|
||||
assert.Equal(t, "powershell.exe", gotEnv["SHELL"])
|
||||
assert.Equal(t, []string{"powershell", "-noprofile", "-noninteractive", "-command"}, gotEntry)
|
||||
assert.Equal(t, "[System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($Env:CI_SCRIPT)) | iex", gotCmd)
|
||||
gotEnv, gotEntry, gotCmd = GenerateContainerConf([]string{"echo hello world"}, "linux")
|
||||
assert.Equal(t, []string{"powershell", "-noprofile", "-noninteractive", "-command", "[System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($Env:CI_SCRIPT)) | iex"}, gotEntry)
|
||||
gotEnv, gotEntry = GenerateContainerConf([]string{"echo hello world"}, "linux")
|
||||
assert.Equal(t, posixScriptBase64, gotEnv["CI_SCRIPT"])
|
||||
assert.Equal(t, "/root", gotEnv["HOME"])
|
||||
assert.Equal(t, "/bin/sh", gotEnv["SHELL"])
|
||||
assert.Equal(t, []string{"/bin/sh", "-c"}, gotEntry)
|
||||
assert.Equal(t, "echo $CI_SCRIPT | base64 -d | /bin/sh -e", gotCmd)
|
||||
assert.Equal(t, []string{"/bin/sh", "-c", "echo $CI_SCRIPT | base64 -d | /bin/sh -e"}, gotEntry)
|
||||
}
|
||||
|
|
|
@ -45,16 +45,15 @@ func (e *docker) toConfig(step *types.Step) *container.Config {
|
|||
configEnv := make(map[string]string)
|
||||
maps.Copy(configEnv, step.Environment)
|
||||
|
||||
if len(step.Commands) != 0 {
|
||||
env, entry, cmd := common.GenerateContainerConf(step.Commands, e.info.OSType)
|
||||
if len(step.Commands) > 0 {
|
||||
env, entry := common.GenerateContainerConf(step.Commands, e.info.OSType)
|
||||
for k, v := range env {
|
||||
configEnv[k] = v
|
||||
}
|
||||
if len(step.Entrypoint) > 0 {
|
||||
entry = step.Entrypoint
|
||||
}
|
||||
config.Entrypoint = entry
|
||||
config.Cmd = []string{cmd}
|
||||
}
|
||||
if len(step.Entrypoint) > 0 {
|
||||
config.Entrypoint = step.Entrypoint
|
||||
}
|
||||
|
||||
if len(configEnv) != 0 {
|
||||
|
|
|
@ -105,8 +105,7 @@ func TestToConfigSmall(t *testing.T) {
|
|||
assert.EqualValues(t, &container.Config{
|
||||
AttachStdout: true,
|
||||
AttachStderr: true,
|
||||
Cmd: []string{"echo $CI_SCRIPT | base64 -d | /bin/sh -e"},
|
||||
Entrypoint: []string{"/bin/sh", "-c"},
|
||||
Entrypoint: []string{"/bin/sh", "-c", "echo $CI_SCRIPT | base64 -d | /bin/sh -e"},
|
||||
Labels: map[string]string{
|
||||
"wp_step": "test",
|
||||
"wp_uuid": "09238932",
|
||||
|
@ -162,8 +161,7 @@ func TestToConfigFull(t *testing.T) {
|
|||
WorkingDir: "/src/abc",
|
||||
AttachStdout: true,
|
||||
AttachStderr: true,
|
||||
Cmd: []string{"echo $CI_SCRIPT | base64 -d | /bin/sh -e"},
|
||||
Entrypoint: []string{"/bin/sh", "-c"},
|
||||
Entrypoint: []string{"/bin/sh", "-c", "echo $CI_SCRIPT | base64 -d | /bin/sh -e"},
|
||||
Labels: map[string]string{
|
||||
"wp_step": "test",
|
||||
"wp_uuid": "09238932",
|
||||
|
|
|
@ -11,6 +11,8 @@ type BackendOptions struct {
|
|||
Resources Resources `mapstructure:"resources"`
|
||||
RuntimeClassName *string `mapstructure:"runtimeClassName"`
|
||||
ServiceAccountName string `mapstructure:"serviceAccountName"`
|
||||
Labels map[string]string `mapstructure:"labels"`
|
||||
Annotations map[string]string `mapstructure:"annotations"`
|
||||
NodeSelector map[string]string `mapstructure:"nodeSelector"`
|
||||
Tolerations []Toleration `mapstructure:"tolerations"`
|
||||
SecurityContext *SecurityContext `mapstructure:"securityContext"`
|
||||
|
|
|
@ -20,6 +20,8 @@ func Test_parseBackendOptions(t *testing.T) {
|
|||
"kubernetes": map[string]any{
|
||||
"nodeSelector": map[string]string{"storage": "ssd"},
|
||||
"serviceAccountName": "wp-svc-acc",
|
||||
"labels": map[string]string{"app": "test"},
|
||||
"annotations": map[string]string{"apps.kubernetes.io/pod-index": "0"},
|
||||
"tolerations": []map[string]any{
|
||||
{"key": "net-port", "value": "100Mbit", "effect": TaintEffectNoSchedule},
|
||||
},
|
||||
|
@ -49,6 +51,8 @@ func Test_parseBackendOptions(t *testing.T) {
|
|||
assert.Equal(t, BackendOptions{
|
||||
NodeSelector: map[string]string{"storage": "ssd"},
|
||||
ServiceAccountName: "wp-svc-acc",
|
||||
Labels: map[string]string{"app": "test"},
|
||||
Annotations: map[string]string{"apps.kubernetes.io/pod-index": "0"},
|
||||
Tolerations: []Toleration{{Key: "net-port", Value: "100Mbit", Effect: TaintEffectNoSchedule}},
|
||||
Resources: Resources{
|
||||
Requests: map[string]string{"memory": "128Mi", "cpu": "1000m"},
|
||||
|
|
|
@ -46,15 +46,27 @@ var Flags = []cli.Flag{
|
|||
&cli.StringFlag{
|
||||
EnvVars: []string{"WOODPECKER_BACKEND_K8S_POD_LABELS"},
|
||||
Name: "backend-k8s-pod-labels",
|
||||
Usage: "backend k8s additional worker pod labels",
|
||||
Usage: "backend k8s additional Agent-wide worker pod labels",
|
||||
Value: "",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
EnvVars: []string{"WOODPECKER_BACKEND_K8S_POD_LABELS_ALLOW_FROM_STEP"},
|
||||
Name: "backend-k8s-pod-labels-allow-from-step",
|
||||
Usage: "whether to allow using labels from step's backend options",
|
||||
Value: false,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
EnvVars: []string{"WOODPECKER_BACKEND_K8S_POD_ANNOTATIONS"},
|
||||
Name: "backend-k8s-pod-annotations",
|
||||
Usage: "backend k8s additional worker pod annotations",
|
||||
Usage: "backend k8s additional Agent-wide worker pod annotations",
|
||||
Value: "",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
EnvVars: []string{"WOODPECKER_BACKEND_K8S_POD_ANNOTATIONS_ALLOW_FROM_STEP"},
|
||||
Name: "backend-k8s-pod-annotations-allow-from-step",
|
||||
Usage: "whether to allow using annotations from step's backend options",
|
||||
Value: false,
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
EnvVars: []string{"WOODPECKER_BACKEND_K8S_SECCTX_NONROOT"},
|
||||
Name: "backend-k8s-secctx-nonroot",
|
||||
|
|
|
@ -55,14 +55,16 @@ type kube struct {
|
|||
}
|
||||
|
||||
type config struct {
|
||||
Namespace string
|
||||
StorageClass string
|
||||
VolumeSize string
|
||||
StorageRwx bool
|
||||
PodLabels map[string]string
|
||||
PodAnnotations map[string]string
|
||||
ImagePullSecretNames []string
|
||||
SecurityContext SecurityContextConfig
|
||||
Namespace string
|
||||
StorageClass string
|
||||
VolumeSize string
|
||||
StorageRwx bool
|
||||
PodLabels map[string]string
|
||||
PodLabelsAllowFromStep bool
|
||||
PodAnnotations map[string]string
|
||||
PodAnnotationsAllowFromStep bool
|
||||
ImagePullSecretNames []string
|
||||
SecurityContext SecurityContextConfig
|
||||
}
|
||||
type SecurityContextConfig struct {
|
||||
RunAsNonRoot bool
|
||||
|
@ -82,13 +84,15 @@ func configFromCliContext(ctx context.Context) (*config, error) {
|
|||
if ctx != nil {
|
||||
if c, ok := ctx.Value(types.CliContext).(*cli.Context); ok {
|
||||
config := config{
|
||||
Namespace: c.String("backend-k8s-namespace"),
|
||||
StorageClass: c.String("backend-k8s-storage-class"),
|
||||
VolumeSize: c.String("backend-k8s-volume-size"),
|
||||
StorageRwx: c.Bool("backend-k8s-storage-rwx"),
|
||||
PodLabels: make(map[string]string), // just init empty map to prevent nil panic
|
||||
PodAnnotations: make(map[string]string), // just init empty map to prevent nil panic
|
||||
ImagePullSecretNames: c.StringSlice("backend-k8s-pod-image-pull-secret-names"),
|
||||
Namespace: c.String("backend-k8s-namespace"),
|
||||
StorageClass: c.String("backend-k8s-storage-class"),
|
||||
VolumeSize: c.String("backend-k8s-volume-size"),
|
||||
StorageRwx: c.Bool("backend-k8s-storage-rwx"),
|
||||
PodLabels: make(map[string]string), // just init empty map to prevent nil panic
|
||||
PodLabelsAllowFromStep: c.Bool("backend-k8s-pod-labels-allow-from-step"),
|
||||
PodAnnotations: make(map[string]string), // just init empty map to prevent nil panic
|
||||
PodAnnotationsAllowFromStep: c.Bool("backend-k8s-pod-annotations-allow-from-step"),
|
||||
ImagePullSecretNames: c.StringSlice("backend-k8s-pod-image-pull-secret-names"),
|
||||
SecurityContext: SecurityContextConfig{
|
||||
RunAsNonRoot: c.Bool("backend-k8s-secctx-nonroot"),
|
||||
},
|
||||
|
|
|
@ -76,43 +76,76 @@ func podName(step *types.Step) (string, error) {
|
|||
func podMeta(step *types.Step, config *config, options BackendOptions, podName string) (metav1.ObjectMeta, error) {
|
||||
var err error
|
||||
meta := metav1.ObjectMeta{
|
||||
Name: podName,
|
||||
Namespace: config.Namespace,
|
||||
Name: podName,
|
||||
Namespace: config.Namespace,
|
||||
Annotations: podAnnotations(config, options, podName),
|
||||
}
|
||||
|
||||
meta.Labels = config.PodLabels
|
||||
if meta.Labels == nil {
|
||||
meta.Labels = make(map[string]string, 1)
|
||||
}
|
||||
meta.Labels[StepLabel], err = stepLabel(step)
|
||||
meta.Labels, err = podLabels(step, config, options)
|
||||
if err != nil {
|
||||
return meta, err
|
||||
}
|
||||
|
||||
if step.Type == types.StepTypeService {
|
||||
meta.Labels[ServiceLabel], _ = serviceName(step)
|
||||
}
|
||||
|
||||
meta.Annotations = config.PodAnnotations
|
||||
if meta.Annotations == nil {
|
||||
meta.Annotations = make(map[string]string)
|
||||
}
|
||||
|
||||
securityContext := options.SecurityContext
|
||||
if securityContext != nil {
|
||||
key, value := apparmorAnnotation(podName, securityContext.ApparmorProfile)
|
||||
if key != nil && value != nil {
|
||||
meta.Annotations[*key] = *value
|
||||
}
|
||||
}
|
||||
|
||||
return meta, nil
|
||||
}
|
||||
|
||||
func podLabels(step *types.Step, config *config, options BackendOptions) (map[string]string, error) {
|
||||
var err error
|
||||
labels := make(map[string]string)
|
||||
|
||||
if len(options.Labels) > 0 {
|
||||
if config.PodLabelsAllowFromStep {
|
||||
log.Trace().Msgf("using labels from the backend options: %v", options.Labels)
|
||||
maps.Copy(labels, options.Labels)
|
||||
} else {
|
||||
log.Debug().Msg("Pod labels were defined in backend options, but its using disallowed by instance configuration")
|
||||
}
|
||||
}
|
||||
if len(config.PodLabels) > 0 {
|
||||
log.Trace().Msgf("using labels from the configuration: %v", config.PodLabels)
|
||||
maps.Copy(labels, config.PodLabels)
|
||||
}
|
||||
if step.Type == types.StepTypeService {
|
||||
labels[ServiceLabel], _ = serviceName(step)
|
||||
}
|
||||
labels[StepLabel], err = stepLabel(step)
|
||||
if err != nil {
|
||||
return labels, err
|
||||
}
|
||||
|
||||
return labels, nil
|
||||
}
|
||||
|
||||
func stepLabel(step *types.Step) (string, error) {
|
||||
return toDNSName(step.Name)
|
||||
}
|
||||
|
||||
func podAnnotations(config *config, options BackendOptions, podName string) map[string]string {
|
||||
annotations := make(map[string]string)
|
||||
|
||||
if len(options.Annotations) > 0 {
|
||||
if config.PodAnnotationsAllowFromStep {
|
||||
log.Trace().Msgf("using annotations from the backend options: %v", options.Annotations)
|
||||
maps.Copy(annotations, options.Annotations)
|
||||
} else {
|
||||
log.Debug().Msg("Pod annotations were defined in backend options, but its using disallowed by instance configuration ")
|
||||
}
|
||||
}
|
||||
if len(config.PodAnnotations) > 0 {
|
||||
log.Trace().Msgf("using annotations from the configuration: %v", config.PodAnnotations)
|
||||
maps.Copy(annotations, config.PodAnnotations)
|
||||
}
|
||||
securityContext := options.SecurityContext
|
||||
if securityContext != nil {
|
||||
key, value := apparmorAnnotation(podName, securityContext.ApparmorProfile)
|
||||
if key != nil && value != nil {
|
||||
annotations[*key] = *value
|
||||
}
|
||||
}
|
||||
|
||||
return annotations
|
||||
}
|
||||
|
||||
func podSpec(step *types.Step, config *config, options BackendOptions) (v1.PodSpec, error) {
|
||||
var err error
|
||||
spec := v1.PodSpec{
|
||||
|
@ -147,15 +180,14 @@ func podContainer(step *types.Step, podName, goos string, options BackendOptions
|
|||
container.ImagePullPolicy = v1.PullAlways
|
||||
}
|
||||
|
||||
if len(step.Commands) != 0 {
|
||||
scriptEnv, command, args := common.GenerateContainerConf(step.Commands, goos)
|
||||
if len(step.Entrypoint) > 0 {
|
||||
command = step.Entrypoint
|
||||
}
|
||||
if len(step.Commands) > 0 {
|
||||
scriptEnv, command := common.GenerateContainerConf(step.Commands, goos)
|
||||
container.Command = command
|
||||
container.Args = []string{args}
|
||||
maps.Copy(step.Environment, scriptEnv)
|
||||
}
|
||||
if len(step.Entrypoint) > 0 {
|
||||
container.Command = step.Entrypoint
|
||||
}
|
||||
|
||||
container.Env = mapToEnvVars(step.Environment)
|
||||
|
||||
|
|
|
@ -90,9 +90,7 @@ func TestTinyPod(t *testing.T) {
|
|||
"image": "gradle:8.4.0-jdk21",
|
||||
"command": [
|
||||
"/bin/sh",
|
||||
"-c"
|
||||
],
|
||||
"args": [
|
||||
"-c",
|
||||
"echo $CI_SCRIPT | base64 -d | /bin/sh -e"
|
||||
],
|
||||
"workingDir": "/woodpecker/src",
|
||||
|
@ -159,11 +157,13 @@ func TestFullPod(t *testing.T) {
|
|||
"creationTimestamp": null,
|
||||
"labels": {
|
||||
"app": "test",
|
||||
"part-of": "woodpecker-ci",
|
||||
"step": "go-test"
|
||||
},
|
||||
"annotations": {
|
||||
"apps.kubernetes.io/pod-index": "0",
|
||||
"container.apparmor.security.beta.kubernetes.io/wp-01he8bebctabr3kgk0qj36d2me-0": "localhost/k8s-apparmor-example-deny-write"
|
||||
"container.apparmor.security.beta.kubernetes.io/wp-01he8bebctabr3kgk0qj36d2me-0": "localhost/k8s-apparmor-example-deny-write",
|
||||
"kubernetes.io/limit-ranger": "LimitRanger plugin set: cpu, memory request and limit for container"
|
||||
}
|
||||
},
|
||||
"spec": {
|
||||
|
@ -183,9 +183,6 @@ func TestFullPod(t *testing.T) {
|
|||
"/bin/sh",
|
||||
"-c"
|
||||
],
|
||||
"args": [
|
||||
"echo $CI_SCRIPT | base64 -d | /bin/sh -e"
|
||||
],
|
||||
"workingDir": "/woodpecker/src",
|
||||
"ports": [
|
||||
{
|
||||
|
@ -328,12 +325,16 @@ func TestFullPod(t *testing.T) {
|
|||
ExtraHosts: hostAliases,
|
||||
Ports: ports,
|
||||
}, &config{
|
||||
Namespace: "woodpecker",
|
||||
ImagePullSecretNames: []string{"regcred", "another-pull-secret"},
|
||||
PodLabels: map[string]string{"app": "test"},
|
||||
PodAnnotations: map[string]string{"apps.kubernetes.io/pod-index": "0"},
|
||||
SecurityContext: SecurityContextConfig{RunAsNonRoot: false},
|
||||
Namespace: "woodpecker",
|
||||
ImagePullSecretNames: []string{"regcred", "another-pull-secret"},
|
||||
PodLabels: map[string]string{"app": "test"},
|
||||
PodLabelsAllowFromStep: true,
|
||||
PodAnnotations: map[string]string{"apps.kubernetes.io/pod-index": "0"},
|
||||
PodAnnotationsAllowFromStep: true,
|
||||
SecurityContext: SecurityContextConfig{RunAsNonRoot: false},
|
||||
}, "wp-01he8bebctabr3kgk0qj36d2me-0", "linux/amd64", BackendOptions{
|
||||
Labels: map[string]string{"part-of": "woodpecker-ci"},
|
||||
Annotations: map[string]string{"kubernetes.io/limit-ranger": "LimitRanger plugin set: cpu, memory request and limit for container"},
|
||||
NodeSelector: map[string]string{"storage": "ssd"},
|
||||
RuntimeClassName: &runtimeClass,
|
||||
ServiceAccountName: "wp-svc-acc",
|
||||
|
@ -415,3 +416,49 @@ func TestPodPrivilege(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
assert.Equal(t, true, *pod.Spec.SecurityContext.RunAsNonRoot)
|
||||
}
|
||||
|
||||
func TestScratchPod(t *testing.T) {
|
||||
expected := `
|
||||
{
|
||||
"metadata": {
|
||||
"name": "wp-01he8bebctabr3kgk0qj36d2me-0",
|
||||
"namespace": "woodpecker",
|
||||
"creationTimestamp": null,
|
||||
"labels": {
|
||||
"step": "curl-google"
|
||||
}
|
||||
},
|
||||
"spec": {
|
||||
"containers": [
|
||||
{
|
||||
"name": "wp-01he8bebctabr3kgk0qj36d2me-0",
|
||||
"image": "quay.io/curl/curl",
|
||||
"command": [
|
||||
"/usr/bin/curl",
|
||||
"-v",
|
||||
"google.com"
|
||||
],
|
||||
"resources": {}
|
||||
}
|
||||
],
|
||||
"restartPolicy": "Never"
|
||||
},
|
||||
"status": {}
|
||||
}`
|
||||
|
||||
pod, err := mkPod(&types.Step{
|
||||
Name: "curl-google",
|
||||
Image: "quay.io/curl/curl",
|
||||
Entrypoint: []string{"/usr/bin/curl", "-v", "google.com"},
|
||||
}, &config{
|
||||
Namespace: "woodpecker",
|
||||
}, "wp-01he8bebctabr3kgk0qj36d2me-0", "linux/amd64", BackendOptions{})
|
||||
assert.NoError(t, err)
|
||||
|
||||
podJSON, err := json.Marshal(pod)
|
||||
assert.NoError(t, err)
|
||||
|
||||
ja := jsonassert.New(t)
|
||||
t.Log(string(podJSON))
|
||||
ja.Assertf(string(podJSON), expected)
|
||||
}
|
||||
|
|
|
@ -77,6 +77,7 @@ func (m *Metadata) Environ() map[string]string {
|
|||
"CI_PIPELINE_URL": m.getPipelineWebURL(m.Curr, 0),
|
||||
"CI_PIPELINE_FORGE_URL": m.Curr.ForgeURL,
|
||||
"CI_PIPELINE_DEPLOY_TARGET": m.Curr.Target,
|
||||
"CI_PIPELINE_DEPLOY_TASK": m.Curr.Task,
|
||||
"CI_PIPELINE_STATUS": m.Curr.Status,
|
||||
"CI_PIPELINE_CREATED": strconv.FormatInt(m.Curr.Created, 10),
|
||||
"CI_PIPELINE_STARTED": strconv.FormatInt(m.Curr.Started, 10),
|
||||
|
@ -108,6 +109,7 @@ func (m *Metadata) Environ() map[string]string {
|
|||
"CI_PREV_PIPELINE_URL": m.getPipelineWebURL(m.Prev, 0),
|
||||
"CI_PREV_PIPELINE_FORGE_URL": m.Prev.ForgeURL,
|
||||
"CI_PREV_PIPELINE_DEPLOY_TARGET": m.Prev.Target,
|
||||
"CI_PREV_PIPELINE_DEPLOY_TASK": m.Prev.Task,
|
||||
"CI_PREV_PIPELINE_STATUS": m.Prev.Status,
|
||||
"CI_PREV_PIPELINE_CREATED": strconv.FormatInt(m.Prev.Created, 10),
|
||||
"CI_PREV_PIPELINE_STARTED": strconv.FormatInt(m.Prev.Started, 10),
|
||||
|
|
|
@ -53,6 +53,7 @@ type (
|
|||
Event string `json:"event,omitempty"`
|
||||
ForgeURL string `json:"forge_url,omitempty"`
|
||||
Target string `json:"target,omitempty"`
|
||||
Task string `json:"task,omitempty"`
|
||||
Trusted bool `json:"trusted,omitempty"`
|
||||
Commit Commit `json:"commit,omitempty"`
|
||||
Parent int64 `json:"parent,omitempty"`
|
||||
|
|
|
@ -14,6 +14,14 @@ steps:
|
|||
image: golang
|
||||
commands: go test
|
||||
|
||||
entrypoint:
|
||||
image: alpine
|
||||
entrypoint: ['some_entry', '--some-flag']
|
||||
|
||||
singla-entrypoint:
|
||||
image: alpine
|
||||
entrypoint: some_entry
|
||||
|
||||
commands:
|
||||
privileged: true
|
||||
image: golang
|
||||
|
|
|
@ -371,6 +371,21 @@
|
|||
},
|
||||
"backend_options": {
|
||||
"$ref": "#/definitions/step_backend_options"
|
||||
},
|
||||
"entrypoint": {
|
||||
"description": "Defines container entrypoint.",
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "array",
|
||||
"minLength": 1,
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -691,8 +706,17 @@
|
|||
"description": "Advanced options for the kubernetes agent backends",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"resources": {
|
||||
"$ref": "#/definitions/step_backend_kubernetes_resources"
|
||||
"labels": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": ["boolean", "string", "number"]
|
||||
}
|
||||
},
|
||||
"annotations": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": ["boolean", "string", "number"]
|
||||
}
|
||||
},
|
||||
"securityContext": {
|
||||
"$ref": "#/definitions/step_backend_kubernetes_security_context"
|
||||
|
|
|
@ -141,7 +141,7 @@ func TestUnmarshalContainer(t *testing.T) {
|
|||
assert.EqualValues(t, want, got, "problem parsing container")
|
||||
}
|
||||
|
||||
// TestUnmarshalContainersErr unmarshals a map of containers. The order is
|
||||
// TestUnmarshalContainers unmarshals a map of containers. The order is
|
||||
// retained and the container key may be used as the container name if a
|
||||
// name is not explicitly provided.
|
||||
func TestUnmarshalContainers(t *testing.T) {
|
||||
|
|
|
@ -30,7 +30,7 @@ import (
|
|||
|
||||
// GetAgents
|
||||
//
|
||||
// @Summary Get agent list
|
||||
// @Summary List agents
|
||||
// @Router /agents [get]
|
||||
// @Produce json
|
||||
// @Success 200 {array} Agent
|
||||
|
@ -49,7 +49,7 @@ func GetAgents(c *gin.Context) {
|
|||
|
||||
// GetAgent
|
||||
//
|
||||
// @Summary Get agent information
|
||||
// @Summary Get an agent
|
||||
// @Router /agents/{agent} [get]
|
||||
// @Produce json
|
||||
// @Success 200 {object} Agent
|
||||
|
@ -73,7 +73,7 @@ func GetAgent(c *gin.Context) {
|
|||
|
||||
// GetAgentTasks
|
||||
//
|
||||
// @Summary Get agent tasks
|
||||
// @Summary List agent tasks
|
||||
// @Router /agents/{agent}/tasks [get]
|
||||
// @Produce json
|
||||
// @Success 200 {array} Task
|
||||
|
@ -106,7 +106,7 @@ func GetAgentTasks(c *gin.Context) {
|
|||
|
||||
// PatchAgent
|
||||
//
|
||||
// @Summary Update agent information
|
||||
// @Summary Update an agent
|
||||
// @Router /agents/{agent} [patch]
|
||||
// @Produce json
|
||||
// @Success 200 {object} Agent
|
||||
|
@ -152,7 +152,8 @@ func PatchAgent(c *gin.Context) {
|
|||
|
||||
// PostAgent
|
||||
//
|
||||
// @Summary Create a new agent with a random token so a new agent can connect to the server
|
||||
// @Summary Create a new agent
|
||||
// @Description Creates a new agent with a random token
|
||||
// @Router /agents [post]
|
||||
// @Produce json
|
||||
// @Success 200 {object} Agent
|
||||
|
|
|
@ -37,7 +37,7 @@ import (
|
|||
|
||||
// GetBadge
|
||||
//
|
||||
// @Summary Get status badge, SVG format
|
||||
// @Summary Get status of pipeline as SVG badge
|
||||
// @Router /badges/{repo_id}/status.svg [get]
|
||||
// @Produce image/svg+xml
|
||||
// @Success 200
|
||||
|
|
|
@ -32,7 +32,7 @@ import (
|
|||
|
||||
// GetCron
|
||||
//
|
||||
// @Summary Get a cron job by id
|
||||
// @Summary Get a cron job
|
||||
// @Router /repos/{repo_id}/cron/{cron} [get]
|
||||
// @Produce json
|
||||
// @Success 200 {object} Cron
|
||||
|
@ -98,7 +98,7 @@ func RunCron(c *gin.Context) {
|
|||
|
||||
// PostCron
|
||||
//
|
||||
// @Summary Persist/creat a cron job
|
||||
// @Summary Create a cron job
|
||||
// @Router /repos/{repo_id}/cron [post]
|
||||
// @Produce json
|
||||
// @Success 200 {object} Cron
|
||||
|
@ -233,7 +233,7 @@ func PatchCron(c *gin.Context) {
|
|||
|
||||
// GetCronList
|
||||
//
|
||||
// @Summary Get the cron job list
|
||||
// @Summary List cron jobs
|
||||
// @Router /repos/{repo_id}/cron [get]
|
||||
// @Produce json
|
||||
// @Success 200 {array} Cron
|
||||
|
@ -254,7 +254,7 @@ func GetCronList(c *gin.Context) {
|
|||
|
||||
// DeleteCron
|
||||
//
|
||||
// @Summary Delete a cron job by id
|
||||
// @Summary Delete a cron job
|
||||
// @Router /repos/{repo_id}/cron/{cron} [delete]
|
||||
// @Produce plain
|
||||
// @Success 204
|
||||
|
|
|
@ -26,7 +26,7 @@ import (
|
|||
|
||||
// GetGlobalSecretList
|
||||
//
|
||||
// @Summary Get the global secret list
|
||||
// @Summary List global secrets
|
||||
// @Router /secrets [get]
|
||||
// @Produce json
|
||||
// @Success 200 {array} Secret
|
||||
|
@ -71,7 +71,7 @@ func GetGlobalSecret(c *gin.Context) {
|
|||
|
||||
// PostGlobalSecret
|
||||
//
|
||||
// @Summary Persist/create a global secret
|
||||
// @Summary Create a global secret
|
||||
// @Router /secrets [post]
|
||||
// @Produce json
|
||||
// @Success 200 {object} Secret
|
||||
|
|
|
@ -52,7 +52,7 @@ func GetQueueInfo(c *gin.Context) {
|
|||
|
||||
// PauseQueue
|
||||
//
|
||||
// @Summary Pause a pipeline queue
|
||||
// @Summary Pause the pipeline queue
|
||||
// @Router /queue/pause [post]
|
||||
// @Produce plain
|
||||
// @Success 204
|
||||
|
@ -65,7 +65,7 @@ func PauseQueue(c *gin.Context) {
|
|||
|
||||
// ResumeQueue
|
||||
//
|
||||
// @Summary Resume a pipeline queue
|
||||
// @Summary Resume the pipeline queue
|
||||
// @Router /queue/resume [post]
|
||||
// @Produce plain
|
||||
// @Success 204
|
||||
|
|
|
@ -30,7 +30,7 @@ import (
|
|||
|
||||
// GetOrg
|
||||
//
|
||||
// @Summary Get organization by id
|
||||
// @Summary Get an organization
|
||||
// @Router /orgs/{org_id} [get]
|
||||
// @Produce json
|
||||
// @Success 200 {array} Org
|
||||
|
@ -57,7 +57,7 @@ func GetOrg(c *gin.Context) {
|
|||
|
||||
// GetOrgPermissions
|
||||
//
|
||||
// @Summary Get the permissions of the current user in the given organization
|
||||
// @Summary Get the permissions of the currently authenticated user for the given organization
|
||||
// @Router /orgs/{org_id}/permissions [get]
|
||||
// @Produce json
|
||||
// @Success 200 {array} OrgPerm
|
||||
|
@ -114,13 +114,13 @@ func GetOrgPermissions(c *gin.Context) {
|
|||
|
||||
// LookupOrg
|
||||
//
|
||||
// @Summary Lookup organization by full-name
|
||||
// @Summary Lookup an organization by full name
|
||||
// @Router /org/lookup/{org_full_name} [get]
|
||||
// @Produce json
|
||||
// @Success 200 {object} Org
|
||||
// @Tags Organizations
|
||||
// @Param Authorization header string true "Insert your personal access token" default(Bearer <personal access token>)
|
||||
// @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) {
|
||||
_store := store.FromContext(c)
|
||||
user := session.User(c)
|
||||
|
|
|
@ -27,7 +27,7 @@ import (
|
|||
|
||||
// GetOrgSecret
|
||||
//
|
||||
// @Summary Get the named organization secret
|
||||
// @Summary Get a organization secret by name
|
||||
// @Router /orgs/{org_id}/secrets/{secret} [get]
|
||||
// @Produce json
|
||||
// @Success 200 {object} Secret
|
||||
|
@ -55,7 +55,7 @@ func GetOrgSecret(c *gin.Context) {
|
|||
|
||||
// GetOrgSecretList
|
||||
//
|
||||
// @Summary Get the organization secret list
|
||||
// @Summary List organization secrets
|
||||
// @Router /orgs/{org_id}/secrets [get]
|
||||
// @Produce json
|
||||
// @Success 200 {array} Secret
|
||||
|
@ -87,7 +87,7 @@ func GetOrgSecretList(c *gin.Context) {
|
|||
|
||||
// PostOrgSecret
|
||||
//
|
||||
// @Summary Persist/create an organization secret
|
||||
// @Summary Create an organization secret
|
||||
// @Router /orgs/{org_id}/secrets [post]
|
||||
// @Produce json
|
||||
// @Success 200 {object} Secret
|
||||
|
@ -129,7 +129,7 @@ func PostOrgSecret(c *gin.Context) {
|
|||
|
||||
// PatchOrgSecret
|
||||
//
|
||||
// @Summary Update an organization secret
|
||||
// @Summary Update an organization secret by name
|
||||
// @Router /orgs/{org_id}/secrets/{secret} [patch]
|
||||
// @Produce json
|
||||
// @Success 200 {object} Secret
|
||||
|
@ -183,7 +183,7 @@ func PatchOrgSecret(c *gin.Context) {
|
|||
|
||||
// DeleteOrgSecret
|
||||
//
|
||||
// @Summary Delete the named secret from an organization
|
||||
// @Summary Delete an organization secret by name
|
||||
// @Router /orgs/{org_id}/secrets/{secret} [delete]
|
||||
// @Produce plain
|
||||
// @Success 204
|
||||
|
|
|
@ -26,7 +26,7 @@ import (
|
|||
|
||||
// GetOrgs
|
||||
//
|
||||
// @Summary Get all orgs
|
||||
// @Summary List organizations
|
||||
// @Description Returns all registered orgs in the system. Requires admin rights.
|
||||
// @Router /orgs [get]
|
||||
// @Produce json
|
||||
|
@ -46,7 +46,7 @@ func GetOrgs(c *gin.Context) {
|
|||
|
||||
// DeleteOrg
|
||||
//
|
||||
// @Summary Delete an org
|
||||
// @Summary Delete an organization
|
||||
// @Description Deletes the given org. Requires admin rights.
|
||||
// @Router /orgs/{id} [delete]
|
||||
// @Produce plain
|
||||
|
|
|
@ -38,7 +38,7 @@ import (
|
|||
|
||||
// CreatePipeline
|
||||
//
|
||||
// @Summary Run/trigger a pipelines
|
||||
// @Summary Trigger a manual pipeline
|
||||
// @Router /repos/{repo_id}/pipelines [post]
|
||||
// @Produce json
|
||||
// @Success 200 {object} Pipeline
|
||||
|
@ -100,7 +100,8 @@ func createTmpPipeline(event model.WebhookEvent, commit *model.Commit, user *mod
|
|||
|
||||
// GetPipelines
|
||||
//
|
||||
// @Summary Get pipelines, current running and past ones
|
||||
// @Summary List repository pipelines
|
||||
// @Description Get a list of pipelines for a repository.
|
||||
// @Router /repos/{repo_id}/pipelines [get]
|
||||
// @Produce json
|
||||
// @Success 200 {array} Pipeline
|
||||
|
@ -146,7 +147,7 @@ func GetPipelines(c *gin.Context) {
|
|||
|
||||
// DeletePipeline
|
||||
//
|
||||
// @Summary Delete pipeline
|
||||
// @Summary Delete a pipeline
|
||||
// @Router /repos/{repo_id}/pipelines/{number} [delete]
|
||||
// @Produce plain
|
||||
// @Success 204
|
||||
|
@ -186,7 +187,7 @@ func DeletePipeline(c *gin.Context) {
|
|||
|
||||
// GetPipeline
|
||||
//
|
||||
// @Summary Pipeline information by number
|
||||
// @Summary Get a repositories pipeline
|
||||
// @Router /repos/{repo_id}/pipelines/{number} [get]
|
||||
// @Produce json
|
||||
// @Success 200 {object} Pipeline
|
||||
|
@ -241,7 +242,7 @@ func GetPipelineLast(c *gin.Context) {
|
|||
|
||||
// GetStepLogs
|
||||
//
|
||||
// @Summary Log information
|
||||
// @Summary Get logs for a pipeline step
|
||||
// @Router /repos/{repo_id}/logs/{number}/{stepID} [get]
|
||||
// @Produce json
|
||||
// @Success 200 {array} LogEntry
|
||||
|
@ -297,7 +298,7 @@ func GetStepLogs(c *gin.Context) {
|
|||
|
||||
// DeleteStepLogs
|
||||
//
|
||||
// @Summary Deletes step log
|
||||
// @Summary Delete step logs of a pipeline
|
||||
// @Router /repos/{repo_id}/logs/{number}/{stepId} [delete]
|
||||
// @Produce plain
|
||||
// @Success 204
|
||||
|
@ -357,7 +358,7 @@ func DeleteStepLogs(c *gin.Context) {
|
|||
|
||||
// GetPipelineConfig
|
||||
//
|
||||
// @Summary Pipeline configuration
|
||||
// @Summary Get configuration files for a pipeline
|
||||
// @Router /repos/{repo_id}/pipelines/{number}/config [get]
|
||||
// @Produce json
|
||||
// @Success 200 {array} Config
|
||||
|
@ -391,7 +392,7 @@ func GetPipelineConfig(c *gin.Context) {
|
|||
|
||||
// CancelPipeline
|
||||
//
|
||||
// @Summary Cancels a pipeline
|
||||
// @Summary Cancel a pipeline
|
||||
// @Router /repos/{repo_id}/pipelines/{number}/cancel [post]
|
||||
// @Produce plain
|
||||
// @Success 200
|
||||
|
@ -427,7 +428,7 @@ func CancelPipeline(c *gin.Context) {
|
|||
|
||||
// PostApproval
|
||||
//
|
||||
// @Summary Start pipelines in gated repos
|
||||
// @Summary Approve and start a pipeline
|
||||
// @Router /repos/{repo_id}/pipelines/{number}/approve [post]
|
||||
// @Produce json
|
||||
// @Success 200 {object} Pipeline
|
||||
|
@ -459,7 +460,7 @@ func PostApproval(c *gin.Context) {
|
|||
|
||||
// PostDecline
|
||||
//
|
||||
// @Summary Decline pipelines in gated repos
|
||||
// @Summary Decline a pipeline
|
||||
// @Router /repos/{repo_id}/pipelines/{number}/decline [post]
|
||||
// @Produce json
|
||||
// @Success 200 {object} Pipeline
|
||||
|
@ -491,7 +492,7 @@ func PostDecline(c *gin.Context) {
|
|||
|
||||
// GetPipelineQueue
|
||||
//
|
||||
// @Summary List pipeline queues
|
||||
// @Summary List pipelines in queue
|
||||
// @Router /pipelines [get]
|
||||
// @Produce json
|
||||
// @Success 200 {array} Feed
|
||||
|
@ -546,6 +547,9 @@ func PostPipeline(c *gin.Context) {
|
|||
|
||||
// make Deploy overridable
|
||||
|
||||
// make Deploy task overridable
|
||||
pl.DeployTask = c.DefaultQuery("deploy_task", pl.DeployTask)
|
||||
|
||||
// make Event overridable to deploy
|
||||
// TODO refactor to use own proper API for deploy
|
||||
if event, ok := c.GetQuery("event"); ok {
|
||||
|
@ -588,7 +592,7 @@ func PostPipeline(c *gin.Context) {
|
|||
|
||||
// DeletePipelineLogs
|
||||
//
|
||||
// @Summary Deletes log
|
||||
// @Summary Deletes all logs of a pipeline
|
||||
// @Router /repos/{repo_id}/logs/{number} [delete]
|
||||
// @Produce plain
|
||||
// @Success 204
|
||||
|
|
|
@ -26,7 +26,7 @@ import (
|
|||
|
||||
// GetRegistry
|
||||
//
|
||||
// @Summary Get a named registry
|
||||
// @Summary Get a registry by name
|
||||
// @Router /repos/{repo_id}/registry/{registry} [get]
|
||||
// @Produce json
|
||||
// @Success 200 {object} Registry
|
||||
|
@ -49,7 +49,7 @@ func GetRegistry(c *gin.Context) {
|
|||
|
||||
// PostRegistry
|
||||
//
|
||||
// @Summary Persist/create a registry
|
||||
// @Summary Create a registry
|
||||
// @Router /repos/{repo_id}/registry [post]
|
||||
// @Produce json
|
||||
// @Success 200 {object} Registry
|
||||
|
@ -86,7 +86,7 @@ func PostRegistry(c *gin.Context) {
|
|||
|
||||
// PatchRegistry
|
||||
//
|
||||
// @Summary Update a named registry
|
||||
// @Summary Update a registry by name
|
||||
// @Router /repos/{repo_id}/registry/{registry} [patch]
|
||||
// @Produce json
|
||||
// @Success 200 {object} Registry
|
||||
|
@ -134,7 +134,7 @@ func PatchRegistry(c *gin.Context) {
|
|||
|
||||
// GetRegistryList
|
||||
//
|
||||
// @Summary Get the registry list
|
||||
// @Summary List registries
|
||||
// @Router /repos/{repo_id}/registry [get]
|
||||
// @Produce json
|
||||
// @Success 200 {array} Registry
|
||||
|
@ -161,7 +161,7 @@ func GetRegistryList(c *gin.Context) {
|
|||
|
||||
// DeleteRegistry
|
||||
//
|
||||
// @Summary Delete a named registry
|
||||
// @Summary Delete a registry by name
|
||||
// @Router /repos/{repo_id}/registry/{registry} [delete]
|
||||
// @Produce plain
|
||||
// @Success 204
|
||||
|
|
|
@ -199,7 +199,7 @@ func PostRepo(c *gin.Context) {
|
|||
|
||||
// PatchRepo
|
||||
//
|
||||
// @Summary Change a repository
|
||||
// @Summary Update a repository
|
||||
// @Router /repos/{repo_id} [patch]
|
||||
// @Produce json
|
||||
// @Success 200 {object} Repo
|
||||
|
@ -273,7 +273,7 @@ func PatchRepo(c *gin.Context) {
|
|||
|
||||
// ChownRepo
|
||||
//
|
||||
// @Summary Change a repository's owner, to the one holding the access token
|
||||
// @Summary Change a repository's owner to the currently authenticated user
|
||||
// @Router /repos/{repo_id}/chown [post]
|
||||
// @Produce json
|
||||
// @Success 200 {object} Repo
|
||||
|
@ -296,20 +296,20 @@ func ChownRepo(c *gin.Context) {
|
|||
|
||||
// LookupRepo
|
||||
//
|
||||
// @Summary Get repository by full-name
|
||||
// @Summary Lookup a repository by full name
|
||||
// @Router /repos/lookup/{repo_full_name} [get]
|
||||
// @Produce json
|
||||
// @Success 200 {object} Repo
|
||||
// @Tags Repositories
|
||||
// @Param Authorization header string true "Insert your personal access token" default(Bearer <personal access token>)
|
||||
// @Param repo_full_name path string true "the repository full-name / slug"
|
||||
// @Param repo_full_name path string true "the repository full name / slug"
|
||||
func LookupRepo(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, session.Repo(c))
|
||||
}
|
||||
|
||||
// GetRepo
|
||||
//
|
||||
// @Summary Get repository information
|
||||
// @Summary Get a repository
|
||||
// @Router /repos/{repo_id} [get]
|
||||
// @Produce json
|
||||
// @Success 200 {object} Repo
|
||||
|
@ -322,7 +322,7 @@ func GetRepo(c *gin.Context) {
|
|||
|
||||
// GetRepoPermissions
|
||||
//
|
||||
// @Summary Repository permission information
|
||||
// @Summary Check current authenticated users access to the repository
|
||||
// @Description The repository permission, according to the used access token.
|
||||
// @Router /repos/{repo_id}/permissions [get]
|
||||
// @Produce json
|
||||
|
@ -337,7 +337,7 @@ func GetRepoPermissions(c *gin.Context) {
|
|||
|
||||
// GetRepoBranches
|
||||
//
|
||||
// @Summary Get repository branches
|
||||
// @Summary Get branches of a repository
|
||||
// @Router /repos/{repo_id}/branches [get]
|
||||
// @Produce json
|
||||
// @Success 200 {array} string
|
||||
|
@ -367,7 +367,7 @@ func GetRepoBranches(c *gin.Context) {
|
|||
|
||||
// GetRepoPullRequests
|
||||
//
|
||||
// @Summary List active pull requests
|
||||
// @Summary List active pull requests of a repository
|
||||
// @Router /repos/{repo_id}/pull_requests [get]
|
||||
// @Produce json
|
||||
// @Success 200 {array} PullRequest
|
||||
|
@ -547,7 +547,8 @@ func MoveRepo(c *gin.Context) {
|
|||
|
||||
// GetAllRepos
|
||||
//
|
||||
// @Summary List all repositories on the server. Requires admin rights.
|
||||
// @Summary List all repositories on the server
|
||||
// @Description Returns a list of all repositories. Requires admin rights.
|
||||
// @Router /repos [get]
|
||||
// @Produce json
|
||||
// @Success 200 {array} Repo
|
||||
|
@ -572,7 +573,8 @@ func GetAllRepos(c *gin.Context) {
|
|||
|
||||
// RepairAllRepos
|
||||
//
|
||||
// @Summary Repair all repositories on the server. Requires admin rights.
|
||||
// @Summary Repair all repositories on the server
|
||||
// @Description Executes a repair process on all repositories. Requires admin rights.
|
||||
// @Router /repos/repair [post]
|
||||
// @Produce plain
|
||||
// @Success 204
|
||||
|
|
|
@ -26,7 +26,7 @@ import (
|
|||
|
||||
// GetSecret
|
||||
//
|
||||
// @Summary Get a named secret
|
||||
// @Summary Get a repository secret by name
|
||||
// @Router /repos/{repo_id}/secrets/{secretName} [get]
|
||||
// @Produce json
|
||||
// @Success 200 {object} Secret
|
||||
|
@ -49,7 +49,7 @@ func GetSecret(c *gin.Context) {
|
|||
|
||||
// PostSecret
|
||||
//
|
||||
// @Summary Persist/create a secret
|
||||
// @Summary Create a repository secret
|
||||
// @Router /repos/{repo_id}/secrets [post]
|
||||
// @Produce json
|
||||
// @Success 200 {object} Secret
|
||||
|
@ -87,7 +87,7 @@ func PostSecret(c *gin.Context) {
|
|||
|
||||
// PatchSecret
|
||||
//
|
||||
// @Summary Update a named secret
|
||||
// @Summary Update a repository secret by name
|
||||
// @Router /repos/{repo_id}/secrets/{secretName} [patch]
|
||||
// @Produce json
|
||||
// @Success 200 {object} Secret
|
||||
|
@ -138,7 +138,7 @@ func PatchSecret(c *gin.Context) {
|
|||
|
||||
// GetSecretList
|
||||
//
|
||||
// @Summary Get the secret list
|
||||
// @Summary List repository secrets
|
||||
// @Router /repos/{repo_id}/secrets [get]
|
||||
// @Produce json
|
||||
// @Success 200 {array} Secret
|
||||
|
@ -165,7 +165,7 @@ func GetSecretList(c *gin.Context) {
|
|||
|
||||
// DeleteSecret
|
||||
//
|
||||
// @Summary Delete a named secret
|
||||
// @Summary Delete a repository secret by name
|
||||
// @Router /repos/{repo_id}/secrets/{secretName} [delete]
|
||||
// @Produce plain
|
||||
// @Success 204
|
||||
|
|
|
@ -36,8 +36,8 @@ import (
|
|||
|
||||
// EventStreamSSE
|
||||
//
|
||||
// @Summary Event stream
|
||||
// @Description event source streaming for compatibility with quic and http2
|
||||
// @Summary Stream events like pipeline updates
|
||||
// @Description With quic and http2 support
|
||||
// @Router /stream/events [get]
|
||||
// @Produce plain
|
||||
// @Success 200
|
||||
|
@ -124,7 +124,7 @@ func EventStreamSSE(c *gin.Context) {
|
|||
|
||||
// LogStreamSSE
|
||||
//
|
||||
// @Summary Log stream
|
||||
// @Summary Stream logs of a pipeline step
|
||||
// @Router /stream/logs/{repo_id}/{pipeline}/{stepID} [get]
|
||||
// @Produce plain
|
||||
// @Success 200
|
||||
|
|
|
@ -32,7 +32,7 @@ import (
|
|||
|
||||
// GetSelf
|
||||
//
|
||||
// @Summary Returns the currently authenticated user.
|
||||
// @Summary Get the currently authenticated user
|
||||
// @Router /user [get]
|
||||
// @Produce json
|
||||
// @Success 200 {object} User
|
||||
|
@ -44,11 +44,11 @@ func GetSelf(c *gin.Context) {
|
|||
|
||||
// GetFeed
|
||||
//
|
||||
// @Summary A feed entry for a build.
|
||||
// @Description Feed entries can be used to display information on the latest builds.
|
||||
// @Summary Get the currently authenticaed users pipeline feed
|
||||
// @Description The feed lists the most recent pipeline for the currently authenticated user.
|
||||
// @Router /user/feed [get]
|
||||
// @Produce json
|
||||
// @Success 200 {object} Feed
|
||||
// @Success 200 {array} Feed
|
||||
// @Tags User
|
||||
// @Param Authorization header string true "Insert your personal access token" default(Bearer <personal access token>)
|
||||
func GetFeed(c *gin.Context) {
|
||||
|
@ -77,7 +77,7 @@ func GetFeed(c *gin.Context) {
|
|||
|
||||
// GetRepos
|
||||
//
|
||||
// @Summary Get user's repos
|
||||
// @Summary Get user's repositories
|
||||
// @Description Retrieve the currently authenticated User's Repository list
|
||||
// @Router /user/repos [get]
|
||||
// @Produce json
|
||||
|
|
|
@ -28,7 +28,7 @@ import (
|
|||
|
||||
// GetUsers
|
||||
//
|
||||
// @Summary Get all users
|
||||
// @Summary List users
|
||||
// @Description Returns all registered, active users in the system. Requires admin rights.
|
||||
// @Router /users [get]
|
||||
// @Produce json
|
||||
|
@ -67,7 +67,7 @@ func GetUser(c *gin.Context) {
|
|||
|
||||
// PatchUser
|
||||
//
|
||||
// @Summary Change a user
|
||||
// @Summary Update a user
|
||||
// @Description Changes the data of an existing user. Requires admin rights.
|
||||
// @Router /users/{login} [patch]
|
||||
// @Produce json
|
||||
|
|
|
@ -217,14 +217,14 @@ func Test_bitbucket(t *testing.T) {
|
|||
|
||||
g.Describe("When requesting repo directory contents", func() {
|
||||
g.It("Should return the details", func() {
|
||||
files, err := c.Dir(ctx, fakeUser, fakeRepo, fakePipeline, "/dir")
|
||||
files, err := c.Dir(ctx, fakeUser, fakeRepo, fakePipeline, "dir")
|
||||
g.Assert(err).IsNil()
|
||||
g.Assert(len(files)).Equal(3)
|
||||
g.Assert(files[0].Name).Equal("README.md")
|
||||
g.Assert(string(files[0].Data)).Equal("dummy payload")
|
||||
})
|
||||
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(errors.Is(err, &types.ErrConfigNotFound{})).IsTrue()
|
||||
})
|
||||
|
|
|
@ -141,7 +141,7 @@ func convertUser(from *internal.Account, token *oauth2.Token) *model.User {
|
|||
}
|
||||
}
|
||||
|
||||
// convertTeamList is a helper function used to convert a Bitbucket team list
|
||||
// convertWorkspaceList is a helper function used to convert a Bitbucket team list
|
||||
// structure to the Woodpecker Team structure.
|
||||
func convertWorkspaceList(from []*internal.Workspace) []*model.Team {
|
||||
var teams []*model.Team
|
||||
|
@ -151,7 +151,7 @@ func convertWorkspaceList(from []*internal.Workspace) []*model.Team {
|
|||
return teams
|
||||
}
|
||||
|
||||
// convertTeam is a helper function used to convert a Bitbucket team account
|
||||
// convertWorkspace is a helper function used to convert a Bitbucket team account
|
||||
// structure to the Woodpecker Team structure.
|
||||
func convertWorkspace(from *internal.Workspace) *model.Team {
|
||||
return &model.Team{
|
||||
|
|
|
@ -114,7 +114,7 @@ func getRepoFile(c *gin.Context) {
|
|||
switch c.Param("file") {
|
||||
case "dir":
|
||||
c.String(http.StatusOK, repoDirPayload)
|
||||
case "dir_not_found/":
|
||||
case "dir_not_found":
|
||||
c.String(http.StatusNotFound, "")
|
||||
case "file_not_found":
|
||||
c.String(http.StatusNotFound, "")
|
||||
|
|
|
@ -52,7 +52,7 @@ const (
|
|||
pathOrgPerms = "%s/2.0/workspaces/%s/permissions?%s"
|
||||
pathPullRequests = "%s/2.0/repositories/%s/%s/pullrequests?%s"
|
||||
pathBranchCommits = "%s/2.0/repositories/%s/%s/commits/%s"
|
||||
pathDir = "%s/2.0/repositories/%s/%s/src/%s%s"
|
||||
pathDir = "%s/2.0/repositories/%s/%s/src/%s/%s"
|
||||
pageSize = 100
|
||||
)
|
||||
|
||||
|
|
|
@ -262,8 +262,14 @@ func (c *client) File(ctx context.Context, u *model.User, r *model.Repo, p *mode
|
|||
return nil, fmt.Errorf("unable to create bitbucket client: %w", err)
|
||||
}
|
||||
|
||||
b, _, err := bc.Projects.GetTextFileContent(ctx, r.Owner, r.Name, f, p.Commit)
|
||||
b, resp, err := bc.Projects.GetTextFileContent(ctx, r.Owner, r.Name, f, p.Commit)
|
||||
if err != nil {
|
||||
if resp.StatusCode == http.StatusNotFound {
|
||||
// requested directory might not exist
|
||||
return nil, &forge_types.ErrConfigNotFound{
|
||||
Configs: []string{f},
|
||||
}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return b, nil
|
||||
|
@ -281,7 +287,10 @@ func (c *client) Dir(ctx context.Context, u *model.User, r *model.Repo, p *model
|
|||
list, resp, err := bc.Projects.ListFiles(ctx, r.Owner, r.Name, path, opts)
|
||||
if err != nil {
|
||||
if resp.StatusCode == http.StatusNotFound {
|
||||
break // requested directory might not exist
|
||||
// requested directory might not exist
|
||||
return nil, &forge_types.ErrConfigNotFound{
|
||||
Configs: []string{path},
|
||||
}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -161,7 +161,7 @@ func convertRepoHook(eventRepo *github.PushEventRepository) *model.Repo {
|
|||
return repo
|
||||
}
|
||||
|
||||
// covertLabels is a helper function used to convert a GitHub label list to
|
||||
// convertLabels is a helper function used to convert a GitHub label list to
|
||||
// the common Woodpecker label structure.
|
||||
func convertLabels(from []*github.Label) []string {
|
||||
labels := make([]string, len(from))
|
||||
|
|
|
@ -224,6 +224,7 @@ func Test_helper(t *testing.T) {
|
|||
from := &github.DeploymentEvent{Deployment: &github.Deployment{}, Sender: &github.User{}}
|
||||
from.Deployment.Description = github.String(":shipit:")
|
||||
from.Deployment.Environment = github.String("production")
|
||||
from.Deployment.Task = github.String("deploy")
|
||||
from.Deployment.ID = github.Int64(42)
|
||||
from.Deployment.Ref = github.String("main")
|
||||
from.Deployment.SHA = github.String("f72fc19")
|
||||
|
|
|
@ -120,16 +120,17 @@ func parsePushHook(hook *github.PushEvent) (*model.Repo, *model.Pipeline) {
|
|||
// If the commit type is unsupported nil values are returned.
|
||||
func parseDeployHook(hook *github.DeploymentEvent) (*model.Repo, *model.Pipeline) {
|
||||
pipeline := &model.Pipeline{
|
||||
Event: model.EventDeploy,
|
||||
Commit: hook.GetDeployment().GetSHA(),
|
||||
ForgeURL: hook.GetDeployment().GetURL(),
|
||||
Message: hook.GetDeployment().GetDescription(),
|
||||
Ref: hook.GetDeployment().GetRef(),
|
||||
Branch: hook.GetDeployment().GetRef(),
|
||||
Deploy: hook.GetDeployment().GetEnvironment(),
|
||||
Avatar: hook.GetSender().GetAvatarURL(),
|
||||
Author: hook.GetSender().GetLogin(),
|
||||
Sender: hook.GetSender().GetLogin(),
|
||||
Event: model.EventDeploy,
|
||||
Commit: hook.GetDeployment().GetSHA(),
|
||||
ForgeURL: hook.GetDeployment().GetURL(),
|
||||
Message: hook.GetDeployment().GetDescription(),
|
||||
Ref: hook.GetDeployment().GetRef(),
|
||||
Branch: hook.GetDeployment().GetRef(),
|
||||
Deploy: hook.GetDeployment().GetEnvironment(),
|
||||
Avatar: hook.GetSender().GetAvatarURL(),
|
||||
Author: hook.GetSender().GetLogin(),
|
||||
Sender: hook.GetSender().GetLogin(),
|
||||
DeployTask: hook.GetDeployment().GetTask(),
|
||||
}
|
||||
// if the ref is a sha or short sha we need to manually construct the ref.
|
||||
if strings.HasPrefix(pipeline.Commit, pipeline.Ref) || pipeline.Commit == pipeline.Ref {
|
||||
|
|
|
@ -119,6 +119,8 @@ func Test_parser(t *testing.T) {
|
|||
g.Assert(b).IsNotNil()
|
||||
g.Assert(p).IsNil()
|
||||
g.Assert(b.Event).Equal(model.EventDeploy)
|
||||
g.Assert(b.Deploy).Equal("production")
|
||||
g.Assert(b.DeployTask).Equal("deploy")
|
||||
})
|
||||
})
|
||||
|
||||
|
|
|
@ -33,6 +33,7 @@ type Pipeline struct {
|
|||
Started int64 `json:"started_at" xorm:"pipeline_started"`
|
||||
Finished int64 `json:"finished_at" xorm:"pipeline_finished"`
|
||||
Deploy string `json:"deploy_to" xorm:"pipeline_deploy"`
|
||||
DeployTask string `json:"deploy_task" xorm:"pipeline_deploy_task"`
|
||||
Commit string `json:"commit" xorm:"pipeline_commit"`
|
||||
Branch string `json:"branch" xorm:"pipeline_branch"`
|
||||
Ref string `json:"ref" xorm:"pipeline_ref"`
|
||||
|
|
|
@ -64,7 +64,7 @@ func Restart(ctx context.Context, store store.Store, lastPipeline *model.Pipelin
|
|||
}
|
||||
|
||||
newPipeline := createNewOutOfOld(lastPipeline)
|
||||
newPipeline.Parent = lastPipeline.ID
|
||||
newPipeline.Parent = lastPipeline.Number
|
||||
|
||||
err = store.CreatePipeline(newPipeline)
|
||||
if err != nil {
|
||||
|
|
|
@ -47,11 +47,11 @@ func TestMetadataFromStruct(t *testing.T) {
|
|||
"CI_COMMIT_AUTHOR": "", "CI_COMMIT_AUTHOR_AVATAR": "", "CI_COMMIT_AUTHOR_EMAIL": "", "CI_COMMIT_BRANCH": "",
|
||||
"CI_COMMIT_MESSAGE": "", "CI_COMMIT_PULL_REQUEST": "", "CI_COMMIT_PULL_REQUEST_LABELS": "", "CI_COMMIT_REF": "", "CI_COMMIT_REFSPEC": "", "CI_COMMIT_SHA": "", "CI_COMMIT_SOURCE_BRANCH": "",
|
||||
"CI_COMMIT_TAG": "", "CI_COMMIT_TARGET_BRANCH": "", "CI_COMMIT_URL": "", "CI_FORGE_TYPE": "", "CI_FORGE_URL": "",
|
||||
"CI_PIPELINE_CREATED": "0", "CI_PIPELINE_DEPLOY_TARGET": "", "CI_PIPELINE_EVENT": "", "CI_PIPELINE_FINISHED": "0", "CI_PIPELINE_FILES": "[]", "CI_PIPELINE_NUMBER": "0",
|
||||
"CI_PIPELINE_CREATED": "0", "CI_PIPELINE_DEPLOY_TARGET": "", "CI_PIPELINE_DEPLOY_TASK": "", "CI_PIPELINE_EVENT": "", "CI_PIPELINE_FINISHED": "0", "CI_PIPELINE_FILES": "[]", "CI_PIPELINE_NUMBER": "0",
|
||||
"CI_PIPELINE_PARENT": "0", "CI_PIPELINE_STARTED": "0", "CI_PIPELINE_STATUS": "", "CI_PIPELINE_URL": "/repos/0/pipeline/0", "CI_PIPELINE_FORGE_URL": "",
|
||||
"CI_PREV_COMMIT_AUTHOR": "", "CI_PREV_COMMIT_AUTHOR_AVATAR": "", "CI_PREV_COMMIT_AUTHOR_EMAIL": "", "CI_PREV_COMMIT_BRANCH": "",
|
||||
"CI_PREV_COMMIT_MESSAGE": "", "CI_PREV_COMMIT_REF": "", "CI_PREV_COMMIT_REFSPEC": "", "CI_PREV_COMMIT_SHA": "", "CI_PREV_COMMIT_URL": "", "CI_PREV_PIPELINE_CREATED": "0",
|
||||
"CI_PREV_PIPELINE_DEPLOY_TARGET": "", "CI_PREV_PIPELINE_EVENT": "", "CI_PREV_PIPELINE_FINISHED": "0", "CI_PREV_PIPELINE_NUMBER": "0", "CI_PREV_PIPELINE_PARENT": "0",
|
||||
"CI_PREV_PIPELINE_DEPLOY_TARGET": "", "CI_PREV_PIPELINE_DEPLOY_TASK": "", "CI_PREV_PIPELINE_EVENT": "", "CI_PREV_PIPELINE_FINISHED": "0", "CI_PREV_PIPELINE_NUMBER": "0", "CI_PREV_PIPELINE_PARENT": "0",
|
||||
"CI_PREV_PIPELINE_STARTED": "0", "CI_PREV_PIPELINE_STATUS": "", "CI_PREV_PIPELINE_URL": "/repos/0/pipeline/0", "CI_PREV_PIPELINE_FORGE_URL": "", "CI_REPO": "", "CI_REPO_CLONE_URL": "", "CI_REPO_CLONE_SSH_URL": "", "CI_REPO_DEFAULT_BRANCH": "", "CI_REPO_REMOTE_ID": "",
|
||||
"CI_REPO_NAME": "", "CI_REPO_OWNER": "", "CI_REPO_PRIVATE": "false", "CI_REPO_SCM": "git", "CI_REPO_TRUSTED": "false", "CI_REPO_URL": "", "CI_STEP_FINISHED": "",
|
||||
"CI_STEP_NAME": "", "CI_STEP_NUMBER": "0", "CI_STEP_STARTED": "", "CI_STEP_STATUS": "", "CI_STEP_URL": "/repos/0/pipeline/0", "CI_SYSTEM_HOST": "", "CI_SYSTEM_NAME": "woodpecker",
|
||||
|
@ -82,11 +82,11 @@ func TestMetadataFromStruct(t *testing.T) {
|
|||
"CI_COMMIT_AUTHOR": "", "CI_COMMIT_AUTHOR_AVATAR": "", "CI_COMMIT_AUTHOR_EMAIL": "", "CI_COMMIT_BRANCH": "",
|
||||
"CI_COMMIT_MESSAGE": "", "CI_COMMIT_PULL_REQUEST": "", "CI_COMMIT_PULL_REQUEST_LABELS": "", "CI_COMMIT_REF": "", "CI_COMMIT_REFSPEC": "", "CI_COMMIT_SHA": "", "CI_COMMIT_SOURCE_BRANCH": "",
|
||||
"CI_COMMIT_TAG": "", "CI_COMMIT_TARGET_BRANCH": "", "CI_COMMIT_URL": "", "CI_FORGE_TYPE": "gitea", "CI_FORGE_URL": "https://gitea.com",
|
||||
"CI_PIPELINE_CREATED": "0", "CI_PIPELINE_DEPLOY_TARGET": "", "CI_PIPELINE_EVENT": "", "CI_PIPELINE_FINISHED": "0", "CI_PIPELINE_FILES": `["test.go","markdown file.md"]`,
|
||||
"CI_PIPELINE_CREATED": "0", "CI_PIPELINE_DEPLOY_TARGET": "", "CI_PIPELINE_DEPLOY_TASK": "", "CI_PIPELINE_EVENT": "", "CI_PIPELINE_FINISHED": "0", "CI_PIPELINE_FILES": `["test.go","markdown file.md"]`,
|
||||
"CI_PIPELINE_NUMBER": "3", "CI_PIPELINE_PARENT": "0", "CI_PIPELINE_STARTED": "0", "CI_PIPELINE_STATUS": "", "CI_PIPELINE_URL": "https://example.com/repos/0/pipeline/3", "CI_PIPELINE_FORGE_URL": "",
|
||||
"CI_PREV_COMMIT_AUTHOR": "", "CI_PREV_COMMIT_AUTHOR_AVATAR": "", "CI_PREV_COMMIT_AUTHOR_EMAIL": "", "CI_PREV_COMMIT_BRANCH": "",
|
||||
"CI_PREV_COMMIT_MESSAGE": "", "CI_PREV_COMMIT_REF": "", "CI_PREV_COMMIT_REFSPEC": "", "CI_PREV_COMMIT_SHA": "", "CI_PREV_COMMIT_URL": "", "CI_PREV_PIPELINE_CREATED": "0",
|
||||
"CI_PREV_PIPELINE_DEPLOY_TARGET": "", "CI_PREV_PIPELINE_EVENT": "", "CI_PREV_PIPELINE_FINISHED": "0", "CI_PREV_PIPELINE_NUMBER": "2", "CI_PREV_PIPELINE_PARENT": "0",
|
||||
"CI_PREV_PIPELINE_DEPLOY_TARGET": "", "CI_PREV_PIPELINE_DEPLOY_TASK": "", "CI_PREV_PIPELINE_EVENT": "", "CI_PREV_PIPELINE_FINISHED": "0", "CI_PREV_PIPELINE_NUMBER": "2", "CI_PREV_PIPELINE_PARENT": "0",
|
||||
"CI_PREV_PIPELINE_STARTED": "0", "CI_PREV_PIPELINE_STATUS": "", "CI_PREV_PIPELINE_URL": "https://example.com/repos/0/pipeline/2", "CI_PREV_PIPELINE_FORGE_URL": "", "CI_REPO": "testUser/testRepo", "CI_REPO_CLONE_URL": "https://gitea.com/testUser/testRepo.git", "CI_REPO_CLONE_SSH_URL": "git@gitea.com:testUser/testRepo.git",
|
||||
"CI_REPO_DEFAULT_BRANCH": "main", "CI_REPO_NAME": "testRepo", "CI_REPO_OWNER": "testUser", "CI_REPO_PRIVATE": "true", "CI_REPO_REMOTE_ID": "",
|
||||
"CI_REPO_SCM": "git", "CI_REPO_TRUSTED": "false", "CI_REPO_URL": "https://gitea.com/testUser/testRepo", "CI_STEP_FINISHED": "",
|
||||
|
|
|
@ -66,7 +66,7 @@ func SetRepo() gin.HandlerFunc {
|
|||
repo, err = _store.GetRepoName(fullName)
|
||||
}
|
||||
|
||||
if repo != nil {
|
||||
if repo != nil && err == nil {
|
||||
c.Set("repo", repo)
|
||||
c.Next()
|
||||
return
|
||||
|
|
|
@ -92,7 +92,7 @@ func (f *forgeFetcherContext) fetch(c context.Context, config string) ([]*types.
|
|||
|
||||
fileMetas, err := f.getFirstAvailableConfig(ctx, configs)
|
||||
if err == nil {
|
||||
return fileMetas, err
|
||||
return fileMetas, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("user defined config '%s' not found: %w", config, err)
|
||||
|
@ -102,7 +102,7 @@ func (f *forgeFetcherContext) fetch(c context.Context, config string) ([]*types.
|
|||
// for the order see shared/constants/constants.go
|
||||
fileMetas, err := f.getFirstAvailableConfig(ctx, constant.DefaultConfigOrder[:])
|
||||
if err == nil {
|
||||
return fileMetas, err
|
||||
return fileMetas, nil
|
||||
}
|
||||
|
||||
select {
|
||||
|
|
|
@ -197,7 +197,8 @@
|
|||
"title": "Zusätzliche Pipeline-Variablen",
|
||||
"value": "Variablenwert",
|
||||
"delete": "Variable löschen"
|
||||
}
|
||||
},
|
||||
"enter_task": "Aufgabe des Deployments"
|
||||
},
|
||||
"enable": {
|
||||
"disabled": "Deaktiviert",
|
||||
|
@ -228,8 +229,8 @@
|
|||
"cancel_success": "Pipeline abgebrochen",
|
||||
"canceled": "Dieser Schritt wurde abgebrochen.",
|
||||
"deploy": "Deploy",
|
||||
"log_auto_scroll": "Automatisches folgen",
|
||||
"log_auto_scroll_off": "Schalte automatisches folgen aus",
|
||||
"log_auto_scroll": "Automatisches Folgen",
|
||||
"log_auto_scroll_off": "Schalte automatisches Folgen aus",
|
||||
"log_download": "Herunterladen",
|
||||
"restart": "Neustarten",
|
||||
"restart_success": "Pipeline neu gestartet",
|
||||
|
@ -397,7 +398,7 @@
|
|||
},
|
||||
"allow_deploy": {
|
||||
"allow": "Deployments erlauben",
|
||||
"desc": "Deployments von erolgreichen Pipelines erlauben. Nur benutzen, wenn du allen Nutzern mit Push-Zugriff vertraust."
|
||||
"desc": "Deployments von erfolgreichen Pipelines erlauben. Nur benutzen, wenn du allen Nutzern mit Push-Zugriff vertraust."
|
||||
}
|
||||
},
|
||||
"not_allowed": "Zugriff auf die Einstellungen dieses Repositorys nicht erlaubt",
|
||||
|
|
|
@ -48,6 +48,7 @@
|
|||
"deploy_pipeline": {
|
||||
"title": "Trigger deployment event for current pipeline #{pipelineId}",
|
||||
"enter_target": "Target deployment environment",
|
||||
"enter_task": "Deployment task",
|
||||
"trigger": "Deploy",
|
||||
"variables": {
|
||||
"delete": "Delete variable",
|
||||
|
|
|
@ -1,309 +1,337 @@
|
|||
{
|
||||
"admin": {
|
||||
"settings": {
|
||||
"not_allowed": "Ви не маєте права доступу до налаштувань сервера",
|
||||
"secrets": {
|
||||
"add": "Додати секрет",
|
||||
"created": "Створено глобальний секрет",
|
||||
"deleted": "Глобальну таємницю видалено",
|
||||
"desc": "Глобальні секрети можуть бути передані всім сховищам, окремим крокам конвеєра під час виконання як змінні середовища.",
|
||||
"events": {
|
||||
"events": "Доступно на наступних заходах",
|
||||
"pr_warning": "Будь ласка, будьте обережні з цією опцією, оскільки зловмисник може надіслати зловмисний запит, який розкриє ваші секрети."
|
||||
},
|
||||
"images": {
|
||||
"desc": "Через кому список зображень, для яких доступний цей секрет, залишити порожнім, щоб дозволити всі зображення",
|
||||
"images": "Доступно для наступних зображень"
|
||||
},
|
||||
"name": "Найменування",
|
||||
"none": "Глобальних секретів поки що не існує.",
|
||||
"save": "Зберегти секрет",
|
||||
"saved": "Глобальний секрет збережено",
|
||||
"secrets": "Секрети",
|
||||
"show": "Показати секрети",
|
||||
"value": "Значення",
|
||||
"warning": "Ці секрети будуть доступні для всіх користувачів сервера."
|
||||
},
|
||||
"settings": "Налаштування"
|
||||
}
|
||||
},
|
||||
"back": "Назад",
|
||||
"cancel": "Скасувати",
|
||||
"docs": "Документи",
|
||||
"documentation_for": "Документація для \"{topic}\"",
|
||||
"errors": {
|
||||
"not_found": "Серверу не вдалося знайти запитуваний об'єкт"
|
||||
},
|
||||
"login": "Логін",
|
||||
"logout": "Вихід",
|
||||
"not_found": {
|
||||
"back_home": "Повертаємося додому",
|
||||
"not_found": "Ого 404, або ми щось зламали, або у вас помилка при наборі тексту :-/"
|
||||
},
|
||||
"org": {
|
||||
"settings": {
|
||||
"not_allowed": "Ви не маєте права доступу до налаштувань цієї організації",
|
||||
"secrets": {
|
||||
"add": "Додати секрет",
|
||||
"created": "Секрет організації збережено",
|
||||
"deleted": "Організаційну таємницю видалено",
|
||||
"desc": "Секрети організації можуть бути передані всім окремим крокам конвеєра сховища організації під час виконання як змінні середовища.",
|
||||
"events": {
|
||||
"events": "Доступно на наступних заходах",
|
||||
"pr_warning": "Будь ласка, будьте обережні з цією опцією, оскільки зловмисник може надіслати зловмисний запит, який розкриє ваші секрети."
|
||||
},
|
||||
"images": {
|
||||
"desc": "Через кому список зображень, для яких доступний цей секрет, залишити порожнім, щоб дозволити всі зображення",
|
||||
"images": "Доступно для наступних зображень"
|
||||
},
|
||||
"name": "Найменування",
|
||||
"none": "Секретів організації поки що немає.",
|
||||
"save": "Зберегти секрет",
|
||||
"saved": "Секрет організації збережено",
|
||||
"secrets": "Секрети",
|
||||
"show": "Показати секрети",
|
||||
"value": "Значення"
|
||||
},
|
||||
"settings": "Налаштування"
|
||||
}
|
||||
},
|
||||
"password": "Пароль",
|
||||
"pipeline_feed": "Трубопровідна подача",
|
||||
"repo": {
|
||||
"activity": "Активність",
|
||||
"add": "Додати репозиторій",
|
||||
"branches": "Відділення",
|
||||
"deploy_pipeline": {
|
||||
"enter_target": "Цільове середовище розгортання",
|
||||
"variables": {
|
||||
"add": "Додати змінну",
|
||||
"desc": "Вкажіть додаткові змінні для використання у конвеєрі. Змінні з однаковими іменами буде перезаписано.",
|
||||
"title": "Додаткові змінні конвеєра"
|
||||
}
|
||||
},
|
||||
"enable": {
|
||||
"enable": "Увімкнути",
|
||||
"enabled": "Вже увімкнено",
|
||||
"list_reloaded": "Оновлений список репозиторіїв",
|
||||
"reload": "Перезавантажити репозиторії",
|
||||
"success": "Репозиторій увімкнено"
|
||||
},
|
||||
"manual_pipeline": {
|
||||
"select_branch": "Оберіть відділення",
|
||||
"title": "Запустити ручний прогін трубопроводу",
|
||||
"trigger": "Запустити трубопровід",
|
||||
"variables": {
|
||||
"add": "Додати змінну",
|
||||
"desc": "Вкажіть додаткові змінні для використання в конвеєрі. Змінні з однаковими іменами будуть перезаписані.",
|
||||
"name": "Ім'я змінної",
|
||||
"title": "Додаткові змінні трубопроводу",
|
||||
"value": "Значення змінної"
|
||||
}
|
||||
},
|
||||
"not_allowed": "Ви не маєте права доступу до цього сховища",
|
||||
"open_in_forge": "Відкрити репозиторій у системі керування версіями",
|
||||
"pipeline": {
|
||||
"actions": {
|
||||
"cancel": "Скасувати",
|
||||
"cancel_success": "Трубопровід скасовано",
|
||||
"canceled": "Цей крок було скасовано.",
|
||||
"log_auto_scroll": "Автоматична прокрутка вниз",
|
||||
"log_auto_scroll_off": "Вимкнути автоматичну прокрутку",
|
||||
"log_download": "Завантажити",
|
||||
"restart": "Перезапуск",
|
||||
"restart_success": "Трубопровід перезапущено"
|
||||
},
|
||||
"config": "Конфіг",
|
||||
"event": {
|
||||
"cron": "Крон",
|
||||
"deploy": "Розгорнути",
|
||||
"manual": "Посібник",
|
||||
"pr": "Запит на вилучення",
|
||||
"push": "Натисни",
|
||||
"tag": "Тег"
|
||||
},
|
||||
"exit_code": "код виходу {exitCode}",
|
||||
"files": "Змінені файли ({files})",
|
||||
"loading": "Загрузка…",
|
||||
"log_download_error": "Виникла помилка при завантаженні лог-файлу",
|
||||
"no_files": "Жодні файли не були змінені.",
|
||||
"no_pipeline_steps": "Сходинки трубопроводу відсутні!",
|
||||
"no_pipelines": "Жоден трубопровід ще не був запущений.",
|
||||
"pipeline": "Трубопровід #{pipelineId}",
|
||||
"pipelines_for": "Трубопроводи для відгалуження \"{branch}\"",
|
||||
"protected": {
|
||||
"approve": "Затвердити",
|
||||
"approve_success": "Трубопровід схвалено",
|
||||
"awaits": "Цей трубопровід чекає на погодження якогось експлуатаційника!",
|
||||
"decline": "Спад",
|
||||
"decline_success": "Трубопровід відхилено",
|
||||
"declined": "Від цього газопроводу відмовилися!"
|
||||
},
|
||||
"step_not_started": "Цей крок ще не розпочався.",
|
||||
"tasks": "Задачі"
|
||||
},
|
||||
"settings": {
|
||||
"actions": {
|
||||
"actions": "Дії",
|
||||
"delete": {
|
||||
"confirm": "Всі дані будуть втрачені після цієї дії!!!\n\nВи дійсно хочете продовжити?",
|
||||
"delete": "Видалити сховище",
|
||||
"success": "Репозиторій видалено"
|
||||
},
|
||||
"disable": {
|
||||
"disable": "Відключити репозиторій",
|
||||
"success": "Репозиторій відключено"
|
||||
},
|
||||
"repair": {
|
||||
"repair": "Ремонтний репозиторій",
|
||||
"success": "Сховище відремонтовано"
|
||||
"admin": {
|
||||
"settings": {
|
||||
"not_allowed": "Ви не маєте права доступу до налаштувань сервера",
|
||||
"secrets": {
|
||||
"add": "Додати секрет",
|
||||
"created": "Створено глобальний секрет",
|
||||
"deleted": "Глобальну таємницю видалено",
|
||||
"desc": "Глобальні секрети можуть бути передані всім сховищам, окремим крокам конвеєра під час виконання як змінні середовища.",
|
||||
"events": {
|
||||
"events": "Доступно на наступних заходах",
|
||||
"pr_warning": "Будь ласка, будьте обережні з цією опцією, оскільки зловмисник може надіслати зловмисний запит, який розкриє ваші секрети."
|
||||
},
|
||||
"images": {
|
||||
"desc": "Через кому список зображень, для яких доступний цей секрет, залишити порожнім, щоб дозволити всі зображення",
|
||||
"images": "Доступно для наступних зображень"
|
||||
},
|
||||
"name": "Найменування",
|
||||
"none": "Глобальних секретів поки що не існує.",
|
||||
"save": "Зберегти секрет",
|
||||
"saved": "Глобальний секрет збережено",
|
||||
"secrets": "Секрети",
|
||||
"show": "Показати секрети",
|
||||
"value": "Значення",
|
||||
"warning": "Ці секрети будуть доступні для всіх користувачів сервера."
|
||||
},
|
||||
"settings": "Налаштування"
|
||||
}
|
||||
},
|
||||
"badge": {
|
||||
"badge": "Бейдж",
|
||||
"branch": "Філія",
|
||||
"type": "Синтаксис",
|
||||
"type_html": "HTML",
|
||||
"type_markdown": "Уцінка",
|
||||
"type_url": "URL"
|
||||
},
|
||||
"crons": {
|
||||
"add": "Додати cron",
|
||||
"branch": {
|
||||
"placeholder": "Гілка (використовує гілку за замовчуванням, якщо порожня)",
|
||||
"title": "Філія"
|
||||
},
|
||||
"created": "Створено Cron",
|
||||
"crons": "Крони",
|
||||
"delete": "Видалити cron",
|
||||
"deleted": "Cron видалено",
|
||||
"desc": "Завдання Cron можна використовувати для регулярного запуску трубопроводів.",
|
||||
"edit": "Редагувати cron",
|
||||
"name": {
|
||||
"name": "Назва",
|
||||
"placeholder": "Назва cron завдання"
|
||||
},
|
||||
"next_exec": "Наступне виконання",
|
||||
"none": "Крон поки що немає.",
|
||||
"not_executed_yet": "Ще не виконано",
|
||||
"save": "Зберегти cron",
|
||||
"saved": "Cron збережено",
|
||||
"schedule": {
|
||||
"placeholder": "Розклад",
|
||||
"title": "Розклад (на основі UTC)"
|
||||
},
|
||||
"show": "Показати крони"
|
||||
},
|
||||
"general": {
|
||||
"allow_pr": {
|
||||
"allow": "Дозволити запити на витяг",
|
||||
"desc": "Конвеєри можуть працювати на основі запитів."
|
||||
},
|
||||
"cancel_prev": {
|
||||
"cancel": "Скасувати попередні трубопроводи",
|
||||
"desc": "Дозволяє скасовувати відкладені та запущені конвеєри однієї і тієї ж події та контексту перед запуском нового конвеєра."
|
||||
},
|
||||
"general": "Генерал",
|
||||
"pipeline_path": {
|
||||
"default": "За замовчуванням: .woodpecker/*.yml -> .woodpecker.yml",
|
||||
"path": "Траса трубопроводу"
|
||||
},
|
||||
"project": "Налаштування проекту",
|
||||
"protected": {
|
||||
"desc": "Кожен трубопровід повинен бути схвалений перед початком будівництва.",
|
||||
"protected": "Захищений"
|
||||
},
|
||||
"save": "Зберегти настройки",
|
||||
"success": "Оновлено налаштування репозиторію",
|
||||
"timeout": {
|
||||
"minutes": "хвилини",
|
||||
"timeout": "Таймаут"
|
||||
},
|
||||
"trusted": {
|
||||
"desc": "Кожен трубопровід повинен бути схвалений перед початком будівництва.",
|
||||
"trusted": "Довірені"
|
||||
},
|
||||
"visibility": {
|
||||
"internal": {
|
||||
"desc": "Цей проект можуть бачити лише авторизовані користувачі інстанції Woodpecker.",
|
||||
"internal": "Внутрішній"
|
||||
},
|
||||
"private": {
|
||||
"desc": "Цей проект можете бачити тільки ви та інші власники сховища.",
|
||||
"private": "Приватний"
|
||||
},
|
||||
"public": {
|
||||
"desc": "Кожен користувач може побачити ваш проект, не входячи в систему.",
|
||||
"public": "Публічні"
|
||||
},
|
||||
"visibility": "Прозорість проекту"
|
||||
}
|
||||
},
|
||||
"not_allowed": "Ви не маєте права доступу до налаштувань цього сховища",
|
||||
"registries": {
|
||||
"add": "Додати реєстр",
|
||||
"address": {
|
||||
"address": "Адреса",
|
||||
"placeholder": "Адреса реєстру (наприклад, docker.io)"
|
||||
},
|
||||
"created": "Створено облікові дані реєстру",
|
||||
"credentials": "Реквізити реєстру",
|
||||
"delete": "Видалення реєстру",
|
||||
"deleted": "Видалено облікові дані реєстру",
|
||||
"desc": "Облікові дані реєстрів можуть бути додані для використання приватних зображень для вашого конвеєра.",
|
||||
"edit": "Редагування реєстру",
|
||||
"none": "Повноважень реєстру поки що немає.",
|
||||
"registries": "Реєстри",
|
||||
"save": "Зберегти реєстр",
|
||||
"saved": "Облікові дані реєстру збережено",
|
||||
"show": "Показати реєстри"
|
||||
},
|
||||
"secrets": {
|
||||
"add": "Додати секрет",
|
||||
"created": "Секрет створено",
|
||||
"delete": "Видалити секрет",
|
||||
"deleted": "Секрет видалено",
|
||||
"desc": "Секрети можуть бути передані окремим етапам конвеєра під час виконання як змінні середовища.",
|
||||
"edit": "Секрет редагування",
|
||||
"events": {
|
||||
"events": "Доступно на наступних заходах",
|
||||
"pr_warning": "Будь ласка, будьте обережні з цією опцією, оскільки зловмисник може надіслати зловмисний запит, який розкриє ваші секрети."
|
||||
},
|
||||
"images": {
|
||||
"desc": "Через кому список зображень, для яких доступний цей секрет, залишити порожнім, щоб дозволити всі зображення",
|
||||
"images": "Доступно для наступних зображень"
|
||||
},
|
||||
"name": "Назва",
|
||||
"none": "Секретів поки що немає.",
|
||||
"save": "Зберегти секрет",
|
||||
"saved": "Секрет збережено",
|
||||
"secrets": "Секрети",
|
||||
"show": "Показати секрети",
|
||||
"value": "Значення"
|
||||
},
|
||||
"settings": "Налаштування"
|
||||
},
|
||||
"user_none": "Ця організація/користувач ще не має проектів."
|
||||
},
|
||||
"repos": "Репо",
|
||||
"repositories": "Сховища",
|
||||
"search": "Обшук…",
|
||||
"time": {
|
||||
"days_short": "д",
|
||||
"hours_short": "г",
|
||||
"min_short": "хв",
|
||||
"not_started": "ще не розпочато",
|
||||
"sec_short": "сек",
|
||||
"template": "MMM D, РРРР, ГГ:п z",
|
||||
"weeks_short": "т"
|
||||
},
|
||||
"unknown_error": "Виникла невідома помилка",
|
||||
"url": "URL",
|
||||
"user": {
|
||||
"access_denied": "Ви не авторизовані для входу",
|
||||
"internal_error": "Виникла внутрішня помилка",
|
||||
"oauth_error": "Помилка під час автентифікації у провайдера OAuth"
|
||||
},
|
||||
"username": "Ім'я користувача",
|
||||
"welcome": "Ласкаво просимо до Woodpcker"
|
||||
"back": "Назад",
|
||||
"cancel": "Скасувати",
|
||||
"docs": "Документи",
|
||||
"documentation_for": "Документація для \"{topic}\"",
|
||||
"errors": {
|
||||
"not_found": "Серверу не вдалося знайти запитуваний об'єкт"
|
||||
},
|
||||
"login": "Логін",
|
||||
"logout": "Вихід",
|
||||
"not_found": {
|
||||
"back_home": "Повертаємося додому",
|
||||
"not_found": "Ого 404, або ми щось зламали, або у вас помилка при наборі тексту :-/"
|
||||
},
|
||||
"org": {
|
||||
"settings": {
|
||||
"not_allowed": "Ви не маєте права доступу до налаштувань цієї організації",
|
||||
"secrets": {
|
||||
"add": "Додати секрет",
|
||||
"created": "Секрет організації збережено",
|
||||
"deleted": "Організаційну таємницю видалено",
|
||||
"desc": "Секрети організації можуть бути передані всім окремим крокам конвеєра сховища організації під час виконання як змінні середовища.",
|
||||
"events": {
|
||||
"events": "Доступно на наступних заходах",
|
||||
"pr_warning": "Будь ласка, будьте обережні з цією опцією, оскільки зловмисник може надіслати зловмисний запит, який розкриє ваші секрети."
|
||||
},
|
||||
"images": {
|
||||
"desc": "Через кому список зображень, для яких доступний цей секрет, залишити порожнім, щоб дозволити всі зображення",
|
||||
"images": "Доступно для наступних зображень"
|
||||
},
|
||||
"name": "Найменування",
|
||||
"none": "Секретів організації поки що немає.",
|
||||
"save": "Зберегти секрет",
|
||||
"saved": "Секрет організації збережено",
|
||||
"secrets": "Секрети",
|
||||
"show": "Показати секрети",
|
||||
"value": "Значення"
|
||||
},
|
||||
"settings": "Налаштування"
|
||||
}
|
||||
},
|
||||
"password": "Пароль",
|
||||
"pipeline_feed": "Трубопровідна подача",
|
||||
"repo": {
|
||||
"activity": "Активність",
|
||||
"add": "Додати репозиторій",
|
||||
"branches": "Відділення",
|
||||
"deploy_pipeline": {
|
||||
"enter_target": "Цільове середовище розгортання",
|
||||
"variables": {
|
||||
"add": "Додати змінну",
|
||||
"desc": "Вкажіть додаткові змінні для використання у конвеєрі. Змінні з однаковими іменами буде перезаписано.",
|
||||
"title": "Додаткові змінні конвеєра",
|
||||
"delete": "Видалити змінну",
|
||||
"name": "Ім'я змінної",
|
||||
"value": "Змінне значення"
|
||||
},
|
||||
"enter_task": "Завдання на розгортання",
|
||||
"title": "Ініціювати подію розгортання для поточного конвеєра #{pipelineId}",
|
||||
"trigger": "Розгорнути"
|
||||
},
|
||||
"enable": {
|
||||
"enable": "Увімкнути",
|
||||
"enabled": "Вже увімкнено",
|
||||
"list_reloaded": "Оновлений список репозиторіїв",
|
||||
"reload": "Перезавантажити репозиторії",
|
||||
"success": "Репозиторій увімкнено",
|
||||
"disabled": "Вимкнено"
|
||||
},
|
||||
"manual_pipeline": {
|
||||
"select_branch": "Оберіть відділення",
|
||||
"title": "Запустити ручний прогін трубопроводу",
|
||||
"trigger": "Запуск конвеєра",
|
||||
"variables": {
|
||||
"add": "Додати змінну",
|
||||
"desc": "Вкажіть додаткові змінні для використання в конвеєрі. Змінні з однаковими іменами будуть перезаписані.",
|
||||
"name": "Ім'я змінної",
|
||||
"title": "Додаткові змінні трубопроводу",
|
||||
"value": "Значення змінної",
|
||||
"delete": "Вилучити змінну"
|
||||
}
|
||||
},
|
||||
"not_allowed": "Ви не маєте права доступу до цього сховища",
|
||||
"open_in_forge": "Відкрити репозиторій у системі керування версіями",
|
||||
"pipeline": {
|
||||
"actions": {
|
||||
"cancel": "Скасувати",
|
||||
"cancel_success": "Трубопровід скасовано",
|
||||
"canceled": "Цей крок було скасовано.",
|
||||
"log_auto_scroll": "Автоматична прокрутка вниз",
|
||||
"log_auto_scroll_off": "Вимкнути автоматичну прокрутку",
|
||||
"log_download": "Завантажити",
|
||||
"restart": "Перезапуск",
|
||||
"restart_success": "Трубопровід перезапущено"
|
||||
},
|
||||
"config": "Конфіг",
|
||||
"event": {
|
||||
"cron": "Крон",
|
||||
"deploy": "Розгорнути",
|
||||
"manual": "Посібник",
|
||||
"pr": "Запит на вилучення",
|
||||
"push": "Натисни",
|
||||
"tag": "Тег"
|
||||
},
|
||||
"exit_code": "код виходу {exitCode}",
|
||||
"files": "Змінені файли ({files})",
|
||||
"loading": "Загрузка…",
|
||||
"log_download_error": "Виникла помилка при завантаженні лог-файлу",
|
||||
"no_files": "Жодні файли не були змінені.",
|
||||
"no_pipeline_steps": "Сходинки трубопроводу відсутні!",
|
||||
"no_pipelines": "Жоден трубопровід ще не був запущений.",
|
||||
"pipeline": "Трубопровід #{pipelineId}",
|
||||
"pipelines_for": "Трубопроводи для відгалуження \"{branch}\"",
|
||||
"protected": {
|
||||
"approve": "Затвердити",
|
||||
"approve_success": "Трубопровід схвалено",
|
||||
"awaits": "Цей трубопровід чекає на погодження якогось експлуатаційника!",
|
||||
"decline": "Спад",
|
||||
"decline_success": "Трубопровід відхилено",
|
||||
"declined": "Від цього газопроводу відмовилися!"
|
||||
},
|
||||
"step_not_started": "Цей крок ще не розпочався.",
|
||||
"tasks": "Задачі",
|
||||
"pipelines_for_pr": "Конвеєри для запиту на пул #{index}"
|
||||
},
|
||||
"settings": {
|
||||
"actions": {
|
||||
"actions": "Дії",
|
||||
"delete": {
|
||||
"confirm": "Всі дані будуть втрачені після цієї дії!!!\n\nВи дійсно хочете продовжити?",
|
||||
"delete": "Видалити сховище",
|
||||
"success": "Репозиторій видалено"
|
||||
},
|
||||
"disable": {
|
||||
"disable": "Відключити репозиторій",
|
||||
"success": "Репозиторій відключено"
|
||||
},
|
||||
"repair": {
|
||||
"repair": "Ремонтний репозиторій",
|
||||
"success": "Сховище відремонтовано"
|
||||
},
|
||||
"enable": {
|
||||
"enable": "Увімкнути репозиторій",
|
||||
"success": "Репозиторій увімкнено"
|
||||
}
|
||||
},
|
||||
"badge": {
|
||||
"badge": "Бейдж",
|
||||
"branch": "Філія",
|
||||
"type": "Синтаксис",
|
||||
"type_html": "HTML",
|
||||
"type_markdown": "Уцінка",
|
||||
"type_url": "URL"
|
||||
},
|
||||
"crons": {
|
||||
"add": "Додати cron",
|
||||
"branch": {
|
||||
"placeholder": "Гілка (використовує гілку за замовчуванням, якщо порожня)",
|
||||
"title": "Філія"
|
||||
},
|
||||
"created": "Створено Cron",
|
||||
"crons": "Крони",
|
||||
"delete": "Видалити cron",
|
||||
"deleted": "Cron видалено",
|
||||
"desc": "Завдання Cron можна використовувати для регулярного запуску трубопроводів.",
|
||||
"edit": "Редагувати cron",
|
||||
"name": {
|
||||
"name": "Назва",
|
||||
"placeholder": "Назва cron завдання"
|
||||
},
|
||||
"next_exec": "Наступне виконання",
|
||||
"none": "Крон поки що немає.",
|
||||
"not_executed_yet": "Ще не виконано",
|
||||
"save": "Зберегти cron",
|
||||
"saved": "Cron збережено",
|
||||
"schedule": {
|
||||
"placeholder": "Розклад",
|
||||
"title": "Розклад (на основі UTC)"
|
||||
},
|
||||
"show": "Показати крони",
|
||||
"run": "Виконати зараз"
|
||||
},
|
||||
"general": {
|
||||
"allow_pr": {
|
||||
"allow": "Дозволити запити на витяг",
|
||||
"desc": "Конвеєри можуть працювати на основі запитів."
|
||||
},
|
||||
"cancel_prev": {
|
||||
"cancel": "Скасувати попередні трубопроводи",
|
||||
"desc": "Дозволяє скасовувати відкладені та запущені конвеєри однієї і тієї ж події та контексту перед запуском нового конвеєра."
|
||||
},
|
||||
"general": "Генерал",
|
||||
"pipeline_path": {
|
||||
"default": "За замовчуванням: .woodpecker/*.yml -> .woodpecker.yml",
|
||||
"path": "Траса трубопроводу",
|
||||
"desc_path_example": "мій/шлях/"
|
||||
},
|
||||
"project": "Налаштування проекту",
|
||||
"protected": {
|
||||
"desc": "Кожен трубопровід повинен бути схвалений перед початком будівництва.",
|
||||
"protected": "Захищений"
|
||||
},
|
||||
"save": "Зберегти настройки",
|
||||
"success": "Оновлено налаштування репозиторію",
|
||||
"timeout": {
|
||||
"minutes": "хвилини",
|
||||
"timeout": "Таймаут"
|
||||
},
|
||||
"trusted": {
|
||||
"desc": "Кожен трубопровід повинен бути схвалений перед початком будівництва.",
|
||||
"trusted": "Довірені"
|
||||
},
|
||||
"visibility": {
|
||||
"internal": {
|
||||
"desc": "Цей проект можуть бачити лише авторизовані користувачі інстанції Woodpecker.",
|
||||
"internal": "Внутрішній"
|
||||
},
|
||||
"private": {
|
||||
"desc": "Цей проект можете бачити тільки ви та інші власники сховища.",
|
||||
"private": "Приватний"
|
||||
},
|
||||
"public": {
|
||||
"desc": "Кожен користувач може побачити ваш проект, не входячи в систему.",
|
||||
"public": "Публічні"
|
||||
},
|
||||
"visibility": "Прозорість проекту"
|
||||
},
|
||||
"netrc_only_trusted": {
|
||||
"netrc_only_trusted": "Вставляйте облікові дані netrc лише в надійні контейнери",
|
||||
"desc": "Вставляйте облікові дані netrc лише в надійні контейнери (рекомендовано)."
|
||||
},
|
||||
"allow_deploy": {
|
||||
"allow": "Дозволити розгортання",
|
||||
"desc": "Дозволити розгортання з успішних конвеєрів. Використовуйте лише в тому випадку, якщо ви довіряєте всім користувачам із доступом push."
|
||||
}
|
||||
},
|
||||
"not_allowed": "Ви не маєте права доступу до налаштувань цього сховища",
|
||||
"registries": {
|
||||
"add": "Додати реєстр",
|
||||
"address": {
|
||||
"address": "Адреса",
|
||||
"placeholder": "Адреса реєстру (наприклад, docker.io)"
|
||||
},
|
||||
"created": "Створено облікові дані реєстру",
|
||||
"credentials": "Реквізити реєстру",
|
||||
"delete": "Видалення реєстру",
|
||||
"deleted": "Видалено облікові дані реєстру",
|
||||
"desc": "Облікові дані реєстрів можуть бути додані для використання приватних зображень для вашого конвеєра.",
|
||||
"edit": "Редагування реєстру",
|
||||
"none": "Повноважень реєстру поки що немає.",
|
||||
"registries": "Реєстри",
|
||||
"save": "Зберегти реєстр",
|
||||
"saved": "Облікові дані реєстру збережено",
|
||||
"show": "Показати реєстри"
|
||||
},
|
||||
"secrets": {
|
||||
"add": "Додати секрет",
|
||||
"created": "Секрет створено",
|
||||
"delete": "Видалити секрет",
|
||||
"deleted": "Секрет видалено",
|
||||
"desc": "Секрети можуть бути передані окремим етапам конвеєра під час виконання як змінні середовища.",
|
||||
"edit": "Секрет редагування",
|
||||
"events": {
|
||||
"events": "Доступно на наступних заходах",
|
||||
"pr_warning": "Будь ласка, будьте обережні з цією опцією, оскільки зловмисник може надіслати зловмисний запит, який розкриє ваші секрети."
|
||||
},
|
||||
"images": {
|
||||
"desc": "Через кому список зображень, для яких доступний цей секрет, залишити порожнім, щоб дозволити всі зображення",
|
||||
"images": "Доступно для наступних зображень"
|
||||
},
|
||||
"name": "Назва",
|
||||
"none": "Секретів поки що немає.",
|
||||
"save": "Зберегти секрет",
|
||||
"saved": "Секрет збережено",
|
||||
"secrets": "Секрети",
|
||||
"show": "Показати секрети",
|
||||
"value": "Значення",
|
||||
"delete_confirm": "Ви справді хочете видалити цей секрет?",
|
||||
"plugins_only": "Доступно лише для плагінів"
|
||||
},
|
||||
"settings": "Налаштування"
|
||||
},
|
||||
"user_none": "Ця організація/користувач ще не має проектів.",
|
||||
"pull_requests": "Запит на пул"
|
||||
},
|
||||
"repos": "Репо",
|
||||
"repositories": "Сховища",
|
||||
"search": "Обшук…",
|
||||
"time": {
|
||||
"days_short": "д",
|
||||
"hours_short": "г",
|
||||
"min_short": "хв",
|
||||
"not_started": "ще не розпочато",
|
||||
"sec_short": "сек",
|
||||
"template": "MMM D, РРРР, ГГ:п z",
|
||||
"weeks_short": "т"
|
||||
},
|
||||
"unknown_error": "Виникла невідома помилка",
|
||||
"url": "URL",
|
||||
"user": {
|
||||
"access_denied": "Ви не авторизовані для входу",
|
||||
"internal_error": "Виникла внутрішня помилка",
|
||||
"oauth_error": "Помилка під час автентифікації у провайдера OAuth"
|
||||
},
|
||||
"username": "Ім'я користувача",
|
||||
"welcome": "Ласкаво просимо до Woodpcker",
|
||||
"api": "API",
|
||||
"empty_list": "{entity} не знайдено!"
|
||||
}
|
||||
|
|
|
@ -158,7 +158,7 @@ const selectedAgent = ref<Partial<Agent>>();
|
|||
const isEditingAgent = computed(() => !!selectedAgent.value?.id);
|
||||
|
||||
async function loadAgents(page: number): Promise<Agent[] | null> {
|
||||
return apiClient.getAgents(page);
|
||||
return apiClient.getAgents({ page });
|
||||
}
|
||||
|
||||
const { resetPage, data: agents } = usePagination(loadAgents, () => !selectedAgent.value);
|
||||
|
|
|
@ -50,7 +50,7 @@ const notifications = useNotifications();
|
|||
const { t } = useI18n();
|
||||
|
||||
async function loadOrgs(page: number): Promise<Org[] | null> {
|
||||
return apiClient.getOrgs(page);
|
||||
return apiClient.getOrgs({ page });
|
||||
}
|
||||
|
||||
const { resetPage, data: orgs } = usePagination(loadOrgs);
|
||||
|
|
|
@ -56,7 +56,7 @@ const notifications = useNotifications();
|
|||
const i18n = useI18n();
|
||||
|
||||
async function loadRepos(page: number): Promise<Repo[] | null> {
|
||||
return apiClient.getAllRepos(page);
|
||||
return apiClient.getAllRepos({ page });
|
||||
}
|
||||
|
||||
const { data: repos } = usePagination(loadRepos);
|
||||
|
|
|
@ -65,7 +65,7 @@ const selectedSecret = ref<Partial<Secret>>();
|
|||
const isEditingSecret = computed(() => !!selectedSecret.value?.id);
|
||||
|
||||
async function loadSecrets(page: number): Promise<Secret[] | null> {
|
||||
return apiClient.getGlobalSecretList(page);
|
||||
return apiClient.getGlobalSecretList({ page });
|
||||
}
|
||||
|
||||
const { resetPage, data: secrets } = usePagination(loadSecrets, () => !selectedSecret.value);
|
||||
|
|
|
@ -107,7 +107,7 @@ const selectedUser = ref<Partial<User>>();
|
|||
const isEditingUser = computed(() => !!selectedUser.value?.id);
|
||||
|
||||
async function loadUsers(page: number): Promise<User[] | null> {
|
||||
return apiClient.getUsers(page);
|
||||
return apiClient.getUsers({ page });
|
||||
}
|
||||
|
||||
const { resetPage, data: users } = usePagination(loadUsers, () => !selectedUser.value);
|
||||
|
|
|
@ -8,6 +8,9 @@
|
|||
<InputField v-slot="{ id }" :label="$t('repo.deploy_pipeline.enter_target')">
|
||||
<TextField :id="id" v-model="payload.environment" required />
|
||||
</InputField>
|
||||
<InputField v-slot="{ id }" :label="$t('repo.deploy_pipeline.enter_task')">
|
||||
<TextField :id="id" v-model="payload.task" />
|
||||
</InputField>
|
||||
<InputField v-slot="{ id }" :label="$t('repo.deploy_pipeline.variables.title')">
|
||||
<span class="text-sm text-wp-text-alt-100 mb-2">{{ $t('repo.deploy_pipeline.variables.desc') }}</span>
|
||||
<div class="flex flex-col gap-2">
|
||||
|
@ -69,9 +72,10 @@ const repo = inject('repo');
|
|||
|
||||
const router = useRouter();
|
||||
|
||||
const payload = ref<{ id: string; environment: string; variables: { name: string; value: string }[] }>({
|
||||
const payload = ref<{ id: string; environment: string; task: string; variables: { name: string; value: string }[] }>({
|
||||
id: '',
|
||||
environment: '',
|
||||
task: '',
|
||||
variables: [
|
||||
{
|
||||
name: '',
|
||||
|
|
|
@ -90,7 +90,7 @@ const pipelineOptions = computed(() => {
|
|||
|
||||
const loading = ref(true);
|
||||
onMounted(async () => {
|
||||
const data = await usePaginate((page) => apiClient.getRepoBranches(repo.value.id, page));
|
||||
const data = await usePaginate((page) => apiClient.getRepoBranches(repo.value.id, { page }));
|
||||
branches.value = data.map((e) => ({
|
||||
text: e,
|
||||
value: e,
|
||||
|
|
|
@ -69,7 +69,7 @@ async function loadSecrets(page: number): Promise<Secret[] | null> {
|
|||
throw new Error("Unexpected: Can't load org");
|
||||
}
|
||||
|
||||
return apiClient.getOrgSecretList(org.value.id, page);
|
||||
return apiClient.getOrgSecretList(org.value.id, { page });
|
||||
}
|
||||
|
||||
const { resetPage, data: secrets } = usePagination(loadSecrets, () => !selectedSecret.value);
|
||||
|
|
|
@ -70,7 +70,7 @@ async function loadBranches() {
|
|||
throw new Error('Unexpected: "repo" should be provided at this place');
|
||||
}
|
||||
|
||||
branches.value = (await usePaginate((page) => apiClient.getRepoBranches(repo.value.id, page)))
|
||||
branches.value = (await usePaginate((page) => apiClient.getRepoBranches(repo.value.id, { page })))
|
||||
.map((b) => ({
|
||||
value: b,
|
||||
text: b,
|
||||
|
|
|
@ -121,7 +121,7 @@ async function loadCrons(page: number): Promise<Cron[] | null> {
|
|||
throw new Error("Unexpected: Can't load repo");
|
||||
}
|
||||
|
||||
return apiClient.getCronList(repo.value.id, page);
|
||||
return apiClient.getCronList(repo.value.id, { page });
|
||||
}
|
||||
|
||||
const { resetPage, data: crons } = usePagination(loadCrons, () => !selectedCron.value);
|
||||
|
|
|
@ -104,7 +104,7 @@ async function loadRegistries(page: number): Promise<Registry[] | null> {
|
|||
throw new Error("Unexpected: Can't load repo");
|
||||
}
|
||||
|
||||
return apiClient.getRegistryList(repo.value.id, page);
|
||||
return apiClient.getRegistryList(repo.value.id, { page });
|
||||
}
|
||||
|
||||
const { resetPage, data: registries } = usePagination(loadRegistries, () => !selectedRegistry.value);
|
||||
|
|
|
@ -71,11 +71,11 @@ async function loadSecrets(page: number, level: 'repo' | 'org' | 'global'): Prom
|
|||
|
||||
switch (level) {
|
||||
case 'repo':
|
||||
return apiClient.getSecretList(repo.value.id, page);
|
||||
return apiClient.getSecretList(repo.value.id, { page });
|
||||
case 'org':
|
||||
return apiClient.getOrgSecretList(repo.value.org_id, page);
|
||||
return apiClient.getOrgSecretList(repo.value.org_id, { page });
|
||||
case 'global':
|
||||
return apiClient.getGlobalSecretList(page);
|
||||
return apiClient.getGlobalSecretList({ page });
|
||||
default:
|
||||
throw new Error(`Unexpected level: ${level}`);
|
||||
}
|
||||
|
|
|
@ -78,7 +78,7 @@ async function loadSecrets(page: number): Promise<Secret[] | null> {
|
|||
throw new Error('Unexpected: Unauthenticated');
|
||||
}
|
||||
|
||||
return apiClient.getOrgSecretList(user.org_id, page);
|
||||
return apiClient.getOrgSecretList(user.org_id, { page });
|
||||
}
|
||||
|
||||
const { resetPage, data: secrets } = usePagination(loadSecrets, () => !selectedSecret.value);
|
||||
|
|
|
@ -31,9 +31,15 @@ type PipelineOptions = {
|
|||
type DeploymentOptions = {
|
||||
id: string;
|
||||
environment: string;
|
||||
task: string;
|
||||
variables: Record<string, string>;
|
||||
};
|
||||
|
||||
type PaginationOptions = {
|
||||
page?: number;
|
||||
perPage?: number;
|
||||
};
|
||||
|
||||
export default class WoodpeckerClient extends ApiClient {
|
||||
getRepoList(opts?: RepoListOptions): Promise<Repo[]> {
|
||||
const query = encodeQueryString(opts);
|
||||
|
@ -52,12 +58,14 @@ export default class WoodpeckerClient extends ApiClient {
|
|||
return this._get(`/api/repos/${repoId}/permissions`) as Promise<RepoPermissions>;
|
||||
}
|
||||
|
||||
getRepoBranches(repoId: number, page: number): Promise<string[]> {
|
||||
return this._get(`/api/repos/${repoId}/branches?page=${page}`) as Promise<string[]>;
|
||||
getRepoBranches(repoId: number, opts?: PaginationOptions): Promise<string[]> {
|
||||
const query = encodeQueryString(opts);
|
||||
return this._get(`/api/repos/${repoId}/branches?${query}`) as Promise<string[]>;
|
||||
}
|
||||
|
||||
getRepoPullRequests(repoId: number, page: number): Promise<PullRequest[]> {
|
||||
return this._get(`/api/repos/${repoId}/pull_requests?page=${page}`) as Promise<PullRequest[]>;
|
||||
getRepoPullRequests(repoId: number, opts?: PaginationOptions): Promise<PullRequest[]> {
|
||||
const query = encodeQueryString(opts);
|
||||
return this._get(`/api/repos/${repoId}/pull_requests?${query}`) as Promise<PullRequest[]>;
|
||||
}
|
||||
|
||||
activateRepo(forgeRemoteId: string): Promise<Repo> {
|
||||
|
@ -82,18 +90,19 @@ export default class WoodpeckerClient extends ApiClient {
|
|||
}
|
||||
|
||||
// Deploy triggers a deployment for an existing pipeline using the
|
||||
// specified target environment.
|
||||
// specified target environment and task.
|
||||
deployPipeline(repoId: number, pipelineNumber: string, options: DeploymentOptions): Promise<Pipeline> {
|
||||
const vars = {
|
||||
...options.variables,
|
||||
event: 'deployment',
|
||||
deploy_to: options.environment,
|
||||
deploy_task: options.task,
|
||||
};
|
||||
const query = encodeQueryString(vars);
|
||||
return this._post(`/api/repos/${repoId}/pipelines/${pipelineNumber}?${query}`) as Promise<Pipeline>;
|
||||
}
|
||||
|
||||
getPipelineList(repoId: number, opts?: Record<string, string | number | boolean>): Promise<Pipeline[]> {
|
||||
getPipelineList(repoId: number, opts?: PaginationOptions & { before?: string; after?: string }): Promise<Pipeline[]> {
|
||||
const query = encodeQueryString(opts);
|
||||
return this._get(`/api/repos/${repoId}/pipelines?${query}`) as Promise<Pipeline[]>;
|
||||
}
|
||||
|
@ -106,9 +115,8 @@ export default class WoodpeckerClient extends ApiClient {
|
|||
return this._get(`/api/repos/${repoId}/pipelines/${pipelineNumber}/config`) as Promise<PipelineConfig[]>;
|
||||
}
|
||||
|
||||
getPipelineFeed(opts?: Record<string, string | number | boolean>): Promise<PipelineFeed[]> {
|
||||
const query = encodeQueryString(opts);
|
||||
return this._get(`/api/user/feed?${query}`) as Promise<PipelineFeed[]>;
|
||||
getPipelineFeed(): Promise<PipelineFeed[]> {
|
||||
return this._get(`/api/user/feed`) as Promise<PipelineFeed[]>;
|
||||
}
|
||||
|
||||
cancelPipeline(repoId: number, pipelineNumber: number): Promise<unknown> {
|
||||
|
@ -126,7 +134,7 @@ export default class WoodpeckerClient extends ApiClient {
|
|||
restartPipeline(
|
||||
repoId: number,
|
||||
pipeline: string,
|
||||
opts?: Record<string, string | number | boolean>,
|
||||
opts?: { event?: string; deploy_to?: string; fork?: boolean },
|
||||
): Promise<Pipeline> {
|
||||
const query = encodeQueryString(opts);
|
||||
return this._post(`/api/repos/${repoId}/pipelines/${pipeline}?${query}`) as Promise<Pipeline>;
|
||||
|
@ -140,8 +148,9 @@ export default class WoodpeckerClient extends ApiClient {
|
|||
return this._delete(`/api/repos/${repoId}/logs/${pipeline}/${step}`);
|
||||
}
|
||||
|
||||
getSecretList(repoId: number, page: number): Promise<Secret[] | null> {
|
||||
return this._get(`/api/repos/${repoId}/secrets?page=${page}`) as Promise<Secret[] | null>;
|
||||
getSecretList(repoId: number, opts?: PaginationOptions): Promise<Secret[] | null> {
|
||||
const query = encodeQueryString(opts);
|
||||
return this._get(`/api/repos/${repoId}/secrets?${query}`) as Promise<Secret[] | null>;
|
||||
}
|
||||
|
||||
createSecret(repoId: number, secret: Partial<Secret>): Promise<unknown> {
|
||||
|
@ -158,8 +167,9 @@ export default class WoodpeckerClient extends ApiClient {
|
|||
return this._delete(`/api/repos/${repoId}/secrets/${name}`);
|
||||
}
|
||||
|
||||
getRegistryList(repoId: number, page: number): Promise<Registry[] | null> {
|
||||
return this._get(`/api/repos/${repoId}/registry?page=${page}`) as Promise<Registry[] | null>;
|
||||
getRegistryList(repoId: number, opts?: PaginationOptions): Promise<Registry[] | null> {
|
||||
const query = encodeQueryString(opts);
|
||||
return this._get(`/api/repos/${repoId}/registry?${query}`) as Promise<Registry[] | null>;
|
||||
}
|
||||
|
||||
createRegistry(repoId: number, registry: Partial<Registry>): Promise<unknown> {
|
||||
|
@ -174,8 +184,9 @@ export default class WoodpeckerClient extends ApiClient {
|
|||
return this._delete(`/api/repos/${repoId}/registry/${registryAddress}`);
|
||||
}
|
||||
|
||||
getCronList(repoId: number, page: number): Promise<Cron[] | null> {
|
||||
return this._get(`/api/repos/${repoId}/cron?page=${page}`) as Promise<Cron[] | null>;
|
||||
getCronList(repoId: number, opts?: PaginationOptions): Promise<Cron[] | null> {
|
||||
const query = encodeQueryString(opts);
|
||||
return this._get(`/api/repos/${repoId}/cron?${query}`) as Promise<Cron[] | null>;
|
||||
}
|
||||
|
||||
createCron(repoId: number, cron: Partial<Cron>): Promise<unknown> {
|
||||
|
@ -206,8 +217,9 @@ export default class WoodpeckerClient extends ApiClient {
|
|||
return this._get(`/api/orgs/${orgId}/permissions`) as Promise<OrgPermissions>;
|
||||
}
|
||||
|
||||
getOrgSecretList(orgId: number, page: number): Promise<Secret[] | null> {
|
||||
return this._get(`/api/orgs/${orgId}/secrets?page=${page}`) as Promise<Secret[] | null>;
|
||||
getOrgSecretList(orgId: number, opts?: PaginationOptions): Promise<Secret[] | null> {
|
||||
const query = encodeQueryString(opts);
|
||||
return this._get(`/api/orgs/${orgId}/secrets?${query}`) as Promise<Secret[] | null>;
|
||||
}
|
||||
|
||||
createOrgSecret(orgId: number, secret: Partial<Secret>): Promise<unknown> {
|
||||
|
@ -224,8 +236,9 @@ export default class WoodpeckerClient extends ApiClient {
|
|||
return this._delete(`/api/orgs/${orgId}/secrets/${name}`);
|
||||
}
|
||||
|
||||
getGlobalSecretList(page: number): Promise<Secret[] | null> {
|
||||
return this._get(`/api/secrets?page=${page}`) as Promise<Secret[] | null>;
|
||||
getGlobalSecretList(opts?: PaginationOptions): Promise<Secret[] | null> {
|
||||
const query = encodeQueryString(opts);
|
||||
return this._get(`/api/secrets?${query}`) as Promise<Secret[] | null>;
|
||||
}
|
||||
|
||||
createGlobalSecret(secret: Partial<Secret>): Promise<unknown> {
|
||||
|
@ -250,8 +263,9 @@ export default class WoodpeckerClient extends ApiClient {
|
|||
return this._post('/api/user/token') as Promise<string>;
|
||||
}
|
||||
|
||||
getAgents(page: number): Promise<Agent[] | null> {
|
||||
return this._get(`/api/agents?page=${page}`) as Promise<Agent[] | null>;
|
||||
getAgents(opts?: PaginationOptions): Promise<Agent[] | null> {
|
||||
const query = encodeQueryString(opts);
|
||||
return this._get(`/api/agents?${query}`) as Promise<Agent[] | null>;
|
||||
}
|
||||
|
||||
getAgent(agentId: Agent['id']): Promise<Agent> {
|
||||
|
@ -282,8 +296,9 @@ export default class WoodpeckerClient extends ApiClient {
|
|||
return this._post('/api/queue/resume');
|
||||
}
|
||||
|
||||
getUsers(page: number): Promise<User[] | null> {
|
||||
return this._get(`/api/users?page=${page}`) as Promise<User[] | null>;
|
||||
getUsers(opts?: PaginationOptions): Promise<User[] | null> {
|
||||
const query = encodeQueryString(opts);
|
||||
return this._get(`/api/users?${query}`) as Promise<User[] | null>;
|
||||
}
|
||||
|
||||
getUser(username: string): Promise<User> {
|
||||
|
@ -306,16 +321,18 @@ export default class WoodpeckerClient extends ApiClient {
|
|||
return this._delete('/api/user/token') as Promise<string>;
|
||||
}
|
||||
|
||||
getOrgs(page: number): Promise<Org[] | null> {
|
||||
return this._get(`/api/orgs?page=${page}`) as Promise<Org[] | null>;
|
||||
getOrgs(opts?: PaginationOptions): Promise<Org[] | null> {
|
||||
const query = encodeQueryString(opts);
|
||||
return this._get(`/api/orgs?${query}`) as Promise<Org[] | null>;
|
||||
}
|
||||
|
||||
deleteOrg(org: Org): Promise<unknown> {
|
||||
return this._delete(`/api/orgs/${org.id}`);
|
||||
}
|
||||
|
||||
getAllRepos(page: number): Promise<Repo[] | null> {
|
||||
return this._get(`/api/repos?page=${page}`) as Promise<Repo[] | null>;
|
||||
getAllRepos(opts?: PaginationOptions): Promise<Repo[] | null> {
|
||||
const query = encodeQueryString(opts);
|
||||
return this._get(`/api/repos?${query}`) as Promise<Repo[] | null>;
|
||||
}
|
||||
|
||||
repairAllRepos(): Promise<unknown> {
|
||||
|
|
|
@ -46,8 +46,8 @@ export const usePipelineStore = defineStore('pipelines', () => {
|
|||
setPipeline(repoId, pipeline);
|
||||
}
|
||||
|
||||
async function loadRepoPipelines(repoId: number) {
|
||||
const _pipelines = await apiClient.getPipelineList(repoId);
|
||||
async function loadRepoPipelines(repoId: number, page?: number) {
|
||||
const _pipelines = await apiClient.getPipelineList(repoId, { page });
|
||||
_pipelines.forEach((pipeline) => {
|
||||
setPipeline(repoId, pipeline);
|
||||
});
|
||||
|
|
|
@ -41,7 +41,7 @@ async function loadBranches(page: number): Promise<string[]> {
|
|||
throw new Error('Unexpected: "repo" should be provided at this place');
|
||||
}
|
||||
|
||||
return apiClient.getRepoBranches(repo.value.id, page);
|
||||
return apiClient.getRepoBranches(repo.value.id, { page });
|
||||
}
|
||||
|
||||
const { resetPage, data: branches, loading } = usePagination(loadBranches);
|
||||
|
|
|
@ -48,7 +48,7 @@ async function loadPullRequests(page: number): Promise<PullRequest[]> {
|
|||
throw new Error('Unexpected: "repo" should be provided at this place');
|
||||
}
|
||||
|
||||
return apiClient.getRepoPullRequests(repo.value.id, page);
|
||||
return apiClient.getRepoPullRequests(repo.value.id, { page });
|
||||
}
|
||||
|
||||
const { resetPage, data: pullRequests, loading } = usePagination(loadPullRequests);
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
<a v-if="badgeUrl" :href="badgeUrl" target="_blank">
|
||||
<img :src="badgeUrl" />
|
||||
</a>
|
||||
<IconButton :href="repo.forge_url" :title="$t('repo.open_in_forge')" :icon="forge ?? 'repo'" />
|
||||
<IconButton :href="repo.forge_url" :title="$t('repo.open_in_forge')" :icon="forge ?? 'repo'" class="forge" />
|
||||
<IconButton
|
||||
v-if="repoPermissions.admin"
|
||||
:to="{ name: 'repo-settings' }"
|
||||
|
|
|
@ -7,7 +7,7 @@ const (
|
|||
pathGlobalSecret = "%s/api/secrets/%s"
|
||||
)
|
||||
|
||||
// GlobalOrgSecret returns an global secret by name.
|
||||
// GlobalSecret returns an global secret by name.
|
||||
func (c *client) GlobalSecret(secret string) (*Secret, error) {
|
||||
out := new(Secret)
|
||||
uri := fmt.Sprintf(pathGlobalSecret, c.addr, secret)
|
||||
|
|
|
@ -18,6 +18,8 @@ import (
|
|||
"net/http"
|
||||
)
|
||||
|
||||
//go:generate mockery --name Client --output mocks --case underscore
|
||||
|
||||
// Client is used to communicate with a Woodpecker server.
|
||||
type Client interface {
|
||||
// SetClient sets the http.Client.
|
||||
|
|
1851
woodpecker-go/woodpecker/mocks/client.go
Normal file
1851
woodpecker-go/woodpecker/mocks/client.go
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue