diff --git a/cli/secret/secret_info.go b/cli/secret/secret_info.go index 4f69cbada9b..f645741b9c6 100644 --- a/cli/secret/secret_info.go +++ b/cli/secret/secret_info.go @@ -16,6 +16,7 @@ package secret import ( "context" + "fmt" "html/template" "os" @@ -51,6 +52,11 @@ func secretInfo(ctx context.Context, c *cli.Command) error { secretName = c.String("name") format = c.String("format") + "\n" ) + + if secretName == "" { + return fmt.Errorf("secret name is missing") + } + client, err := internal.NewClient(ctx, c) if err != nil { return err diff --git a/cmd/server/docs/docs.go b/cmd/server/docs/docs.go index 3113408cc6f..2b8437fd1d5 100644 --- a/cmd/server/docs/docs.go +++ b/cmd/server/docs/docs.go @@ -4415,8 +4415,7 @@ const docTemplate = `{ "branch": { "type": "string" }, - "created_at": { - "description": "TODO change JSON field to \"created\" in 3.0", + "created": { "type": "integer" }, "creator_id": { @@ -4458,15 +4457,13 @@ const docTemplate = `{ "commit": { "type": "string" }, - "created_at": { - "description": "TODO change JSON field to \"created\" in 3.0", + "created": { "type": "integer" }, "event": { "type": "string" }, - "finished_at": { - "description": "TODO change JSON field to \"finished\" in 3.0", + "finished": { "type": "integer" }, "id": { @@ -4487,8 +4484,7 @@ const docTemplate = `{ "repo_id": { "type": "integer" }, - "started_at": { - "description": "TODO change JSON field to \"started\" in 3.0", + "started": { "type": "integer" }, "status": { @@ -4645,8 +4641,7 @@ const docTemplate = `{ "commit": { "type": "string" }, - "created_at": { - "description": "TODO change JSON field to \"created\" in 3.0", + "created": { "type": "integer" }, "deploy_task": { @@ -4664,8 +4659,7 @@ const docTemplate = `{ "event": { "$ref": "#/definitions/WebhookEvent" }, - "finished_at": { - "description": "TODO change JSON field to \"finished\" in 3.0", + "finished": { "type": "integer" }, "forge_url": { @@ -4698,8 +4692,7 @@ const docTemplate = `{ "refspec": { "type": "string" }, - "reviewed_at": { - "description": "TODO change JSON field to \"reviewed\" in 3.0", + "reviewed": { "type": "integer" }, "reviewed_by": { @@ -4709,8 +4702,7 @@ const docTemplate = `{ "description": "uses reported user for webhooks and name of cron for cron pipelines", "type": "string" }, - "started_at": { - "description": "TODO change JSON field to \"started\" in 3.0", + "started": { "type": "integer" }, "status": { @@ -4722,8 +4714,7 @@ const docTemplate = `{ "title": { "type": "string" }, - "updated_at": { - "description": "TODO change JSON field to \"updated\" in 3.0", + "updated": { "type": "integer" }, "variables": { @@ -5012,15 +5003,15 @@ const docTemplate = `{ "Step": { "type": "object", "properties": { - "end_time": { - "type": "integer" - }, "error": { "type": "string" }, "exit_code": { "type": "integer" }, + "finished": { + "type": "integer" + }, "id": { "type": "integer" }, @@ -5036,7 +5027,7 @@ const docTemplate = `{ "ppid": { "type": "integer" }, - "start_time": { + "started": { "type": "integer" }, "state": { @@ -5196,9 +5187,6 @@ const docTemplate = `{ "$ref": "#/definitions/Step" } }, - "end_time": { - "type": "integer" - }, "environ": { "type": "object", "additionalProperties": { @@ -5208,6 +5196,9 @@ const docTemplate = `{ "error": { "type": "string" }, + "finished": { + "type": "integer" + }, "id": { "type": "integer" }, @@ -5223,7 +5214,7 @@ const docTemplate = `{ "platform": { "type": "string" }, - "start_time": { + "started": { "type": "integer" }, "state": { diff --git a/docker/Dockerfile.agent.alpine.multiarch b/docker/Dockerfile.agent.alpine.multiarch index c15d113f811..d873a683e4b 100644 --- a/docker/Dockerfile.agent.alpine.multiarch +++ b/docker/Dockerfile.agent.alpine.multiarch @@ -10,6 +10,8 @@ RUN --mount=type=cache,target=/root/.cache/go-build \ FROM docker.io/alpine:3.20 RUN apk add -U --no-cache ca-certificates ENV GODEBUG=netdns=go +# Internal setting do NOT change! Signals that woodpecker is running inside a container +ENV WOODPECKER_IN_CONTAINER=true EXPOSE 3000 COPY --from=build /src/dist/woodpecker-agent /bin/ diff --git a/docker/Dockerfile.agent.multiarch b/docker/Dockerfile.agent.multiarch index 6349a3d5082..52a58a0cf08 100644 --- a/docker/Dockerfile.agent.multiarch +++ b/docker/Dockerfile.agent.multiarch @@ -10,6 +10,8 @@ RUN mkdir -p /etc/woodpecker FROM scratch ENV GODEBUG=netdns=go +# Internal setting do NOT change! Signals that woodpecker is running inside a container +ENV WOODPECKER_IN_CONTAINER=true EXPOSE 3000 # copy certs from build image diff --git a/docs/docs/20-usage/45-cron.md b/docs/docs/20-usage/45-cron.md index 95ee8202e3c..2cb088122e8 100644 --- a/docs/docs/20-usage/45-cron.md +++ b/docs/docs/20-usage/45-cron.md @@ -23,12 +23,6 @@ To configure cron jobs you need at least push access to the repository. ![cron settings](./cron-settings.png) - The supported schedule syntax can be found at . If you need general understanding of the cron syntax is a good place to start and experiment. + The supported schedule syntax can be found at . If you need general understanding of the cron syntax is a good place to start and experiment. - Examples: `@every 5m`, `@daily`, `0 30 * * * *` ... - - :::info - Woodpeckers cron syntax starts with seconds instead of minutes as used by most linux cron schedulers. - - Example: "At minute 30 every hour" would be `0 30 * * * *` instead of `30 * * * *` - ::: + Examples: `@every 5m`, `@daily`, `30 * * * *` ... diff --git a/docs/docs/30-administration/22-backends/40-kubernetes.md b/docs/docs/30-administration/22-backends/40-kubernetes.md index 5637eb43bf3..f2e11ff8094 100644 --- a/docs/docs/30-administration/22-backends/40-kubernetes.md +++ b/docs/docs/30-administration/22-backends/40-kubernetes.md @@ -197,7 +197,7 @@ backend_options: ``` :::note -AppArmor syntax follows [KEP-24](https://github.com/kubernetes/enhancements/blob/fddcbb9cbf3df39ded03bad71228265ac6e5215f/keps/sig-node/24-apparmor/README.md). +The feature requires Kubernetes v1.30 or above. ::: ### Annotations and labels diff --git a/docs/docs/91-migrations.md b/docs/docs/91-migrations.md index 5bd8db9acc0..9e8603be846 100644 --- a/docs/docs/91-migrations.md +++ b/docs/docs/91-migrations.md @@ -2,13 +2,6 @@ Some versions need some changes to the server configuration or the pipeline configuration files. - - ## `next` - Removed `WOODPECKER_DEV_OAUTH_HOST` and `WOODPECKER_DEV_GITEA_OAUTH_URL` use `WOODPECKER_EXPERT_FORGE_OAUTH_HOST` @@ -16,6 +9,7 @@ Some versions need some changes to the server configuration or the pipeline conf - Deprecated `steps.[name].group` in favor of `steps.[name].depends_on` (see [workflow syntax](./20-usage/20-workflow-syntax.md#depends_on) to learn how to set dependencies) - Removed `WOODPECKER_ROOT_PATH` and `WOODPECKER_ROOT_URL` config variables. Use `WOODPECKER_HOST` with a path instead - Pipelines without a config file will now be skipped instead of failing +- Removed implicitly defined `regcred` image pull secret name. Set it explicitly via `WOODPECKER_BACKEND_K8S_PULL_SECRET_NAMES` - Deprecated `includes` and `excludes` support from **event** filter - Deprecated uppercasing all secret env vars, instead, the value of the `secrets` property is used. [Read more](./20-usage/40-secrets.md#use-secrets-in-commands) - Deprecated alternative names for secrets, use `environment` with `from_secret` @@ -23,6 +17,9 @@ Some versions need some changes to the server configuration or the pipeline conf - Deprecated `environment` filter, use `when.evaluate` - Removed `WOODPECKER_WEBHOOK_HOST` in favor of `WOODPECKER_EXPERT_WEBHOOK_HOST` - Migrated to rfc9421 for webhook signatures +- Renamed `start_time`, `end_time`, `created_at`, `started_at`, `finished_at` and `reviewed_at` JSON fields to `started`, `finished`, `created`, `started`, `finished`, `reviewed` +- Update all webhooks by pressing the "Repair all" button in the admin settings as the webhook token claims have changed +- Crons now use standard Linux syntax without seconds ## 2.0.0 diff --git a/go.mod b/go.mod index 47362655d86..a3cb369b487 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ toolchain go1.22.5 require ( al.essio.dev/pkg/shellescape v1.5.0 - code.gitea.io/sdk/gitea v0.18.0 + code.gitea.io/sdk/gitea v0.19.0 codeberg.org/6543/go-yaml2json v1.0.0 codeberg.org/6543/xyaml v1.1.0 codeberg.org/mvdkleijn/forgejo-sdk/forgejo v1.1.1 @@ -26,6 +26,7 @@ require ( github.com/expr-lang/expr v1.16.9 github.com/franela/goblin v0.0.0-20211003143422-0a4f594942bf github.com/fsnotify/fsnotify v1.7.0 + github.com/gdgvda/cron v0.2.0 github.com/getkin/kin-openapi v0.126.0 github.com/gin-gonic/gin v1.10.0 github.com/gitsight/go-vcsurl v1.0.1 @@ -49,7 +50,6 @@ require ( github.com/oklog/ulid/v2 v2.1.0 github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.19.1 - github.com/robfig/cron v1.2.0 github.com/rs/zerolog v1.33.0 github.com/stretchr/testify v1.9.0 github.com/swaggo/files v1.0.1 @@ -62,12 +62,12 @@ require ( github.com/yaronf/httpsign v0.3.1 github.com/zalando/go-keyring v0.2.5 go.uber.org/multierr v1.11.0 - golang.org/x/crypto v0.25.0 + golang.org/x/crypto v0.26.0 golang.org/x/net v0.27.0 golang.org/x/oauth2 v0.21.0 - golang.org/x/sync v0.7.0 - golang.org/x/term v0.22.0 - golang.org/x/text v0.16.0 + golang.org/x/sync v0.8.0 + golang.org/x/term v0.23.0 + golang.org/x/text v0.17.0 google.golang.org/grpc v1.65.0 google.golang.org/protobuf v1.34.2 gopkg.in/yaml.v3 v3.0.1 @@ -205,7 +205,7 @@ require ( go.uber.org/zap v1.27.0 // indirect golang.org/x/arch v0.8.0 // indirect golang.org/x/mod v0.17.0 // indirect - golang.org/x/sys v0.22.0 // indirect + golang.org/x/sys v0.23.0 // indirect golang.org/x/time v0.5.0 // indirect golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240722135656-d784300faade // indirect diff --git a/go.sum b/go.sum index 964957cdfa8..2be07558662 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,7 @@ al.essio.dev/pkg/shellescape v1.5.0 h1:7oTvSsQ5kg9WksA9O58y9wjYnY4jP0CL82/Q8WLUGKk= al.essio.dev/pkg/shellescape v1.5.0/go.mod h1:6sIqp7X2P6mThCQ7twERpZTuigpr6KbZWtls1U8I890= -code.gitea.io/sdk/gitea v0.18.0 h1:+zZrwVmujIrgobt6wVBWCqITz6bn1aBjnCUHmpZrerI= -code.gitea.io/sdk/gitea v0.18.0/go.mod h1:IG9xZJoltDNeDSW0qiF2Vqx5orMWa7OhVWrjvrd5NpI= +code.gitea.io/sdk/gitea v0.19.0 h1:8I6s1s4RHgzxiPHhOQdgim1RWIRcr0LVMbHBjBFXq4Y= +code.gitea.io/sdk/gitea v0.19.0/go.mod h1:IG9xZJoltDNeDSW0qiF2Vqx5orMWa7OhVWrjvrd5NpI= codeberg.org/6543/go-yaml2json v1.0.0 h1:heGqo9VEi7gY2yNqjj7X4ADs5nzlFIbGsJtgYDLrnig= codeberg.org/6543/go-yaml2json v1.0.0/go.mod h1:mz61q14LWF4ZABrgMEDMmk3t9dPi6zgR1uBh2VKV2RQ= codeberg.org/6543/xyaml v1.1.0 h1:0PWTy8OUqshshjrrnAXFWXSPUEa8R49DIh2ah07SxFc= @@ -145,6 +145,8 @@ github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nos github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= +github.com/gdgvda/cron v0.2.0 h1:oX8qdLZq4tC5StnCsZsTNs2BIzaRjcjmPZ4o+BArKX4= +github.com/gdgvda/cron v0.2.0/go.mod h1:VEwidZXB255kESB5DcUGRWTYZS8KkOBYD1YBn8Wiyx8= github.com/getkin/kin-openapi v0.126.0 h1:c2cSgLnAsS0xYfKsgt5oBV6MYRM/giU8/RtwUY4wyfY= github.com/getkin/kin-openapi v0.126.0/go.mod h1:7mONz8IwmSRg6RttPu6v8U/OJ+gr+J99qSFNjPGSQqw= github.com/gin-contrib/gzip v0.0.6 h1:NjcunTcGAj5CO1gn4N8jHOSIeRFHIbn51z6K+xaN4d4= @@ -459,8 +461,6 @@ github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qq github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= -github.com/robfig/cron v1.2.0 h1:ZjScXvvxeQ63Dbyxy76Fj3AT3Ut0aKsyd2/tl3DTMuQ= -github.com/robfig/cron v1.2.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= @@ -599,8 +599,8 @@ golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 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.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= -golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= +golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= +golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= @@ -633,8 +633,8 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= -golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -664,14 +664,14 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= -golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM= +golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk= -golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4= +golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU= +golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -679,8 +679,8 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= -golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= +golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= +golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/pipeline/backend/kubernetes/flags.go b/pipeline/backend/kubernetes/flags.go index 8ba9b079c85..910f526249b 100644 --- a/pipeline/backend/kubernetes/flags.go +++ b/pipeline/backend/kubernetes/flags.go @@ -82,7 +82,6 @@ var Flags = []cli.Flag{ Sources: cli.EnvVars("WOODPECKER_BACKEND_K8S_PULL_SECRET_NAMES"), Name: "backend-k8s-pod-image-pull-secret-names", Usage: "backend k8s pull secret names for private registries", - Value: []string{"regcred"}, }, &cli.BoolFlag{ Sources: cli.EnvVars("WOODPECKER_BACKEND_K8S_ALLOW_NATIVE_SECRETS"), diff --git a/pipeline/backend/kubernetes/kubernetes.go b/pipeline/backend/kubernetes/kubernetes.go index dadf913c547..7cde9f68cab 100644 --- a/pipeline/backend/kubernetes/kubernetes.go +++ b/pipeline/backend/kubernetes/kubernetes.go @@ -100,10 +100,6 @@ func configFromCliContext(ctx context.Context) (*config, error) { }, NativeSecretsAllowFromStep: c.Bool("backend-k8s-allow-native-secrets"), } - // TODO: remove in next major - if len(config.ImagePullSecretNames) == 1 && config.ImagePullSecretNames[0] == "regcred" { - log.Warn().Msg("WOODPECKER_BACKEND_K8S_PULL_SECRET_NAMES is set to the default ('regcred'). It will default to empty in Woodpecker 3.0. Set it explicitly before then.") - } // Unmarshal label and annotation settings here to ensure they're valid on startup if labels := c.String("backend-k8s-pod-labels"); labels != "" { if err := yaml.Unmarshal([]byte(labels), &config.PodLabels); err != nil { diff --git a/pipeline/backend/kubernetes/pod.go b/pipeline/backend/kubernetes/pod.go index 67954347081..53efbc8cd83 100644 --- a/pipeline/backend/kubernetes/pod.go +++ b/pipeline/backend/kubernetes/pod.go @@ -84,7 +84,7 @@ func podMeta(step *types.Step, config *config, options BackendOptions, podName s meta := meta_v1.ObjectMeta{ Name: podName, Namespace: config.Namespace, - Annotations: podAnnotations(config, options, podName), + Annotations: podAnnotations(config, options), } meta.Labels, err = podLabels(step, config, options) @@ -126,7 +126,7 @@ func stepLabel(step *types.Step) (string, error) { return toDNSName(step.Name) } -func podAnnotations(config *config, options BackendOptions, podName string) map[string]string { +func podAnnotations(config *config, options BackendOptions) map[string]string { annotations := make(map[string]string) if len(options.Annotations) > 0 { @@ -141,13 +141,6 @@ func podAnnotations(config *config, options BackendOptions, podName string) map[ 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 } @@ -377,11 +370,12 @@ func toleration(backendToleration Toleration) v1.Toleration { func podSecurityContext(sc *SecurityContext, secCtxConf SecurityContextConfig, stepPrivileged bool) *v1.PodSecurityContext { var ( - nonRoot *bool - user *int64 - group *int64 - fsGroup *int64 - seccomp *v1.SeccompProfile + nonRoot *bool + user *int64 + group *int64 + fsGroup *int64 + seccomp *v1.SeccompProfile + apparmor *v1.AppArmorProfile ) if secCtxConf.RunAsNonRoot { @@ -410,6 +404,7 @@ func podSecurityContext(sc *SecurityContext, secCtxConf SecurityContextConfig, s } seccomp = seccompProfile(sc.SeccompProfile) + apparmor = apparmorProfile(sc.ApparmorProfile) } if nonRoot == nil && user == nil && group == nil && fsGroup == nil && seccomp == nil { @@ -417,11 +412,12 @@ func podSecurityContext(sc *SecurityContext, secCtxConf SecurityContextConfig, s } securityContext := &v1.PodSecurityContext{ - RunAsNonRoot: nonRoot, - RunAsUser: user, - RunAsGroup: group, - FSGroup: fsGroup, - SeccompProfile: seccomp, + RunAsNonRoot: nonRoot, + RunAsUser: user, + RunAsGroup: group, + FSGroup: fsGroup, + SeccompProfile: seccomp, + AppArmorProfile: apparmor, } log.Trace().Msgf("pod security context that will be used: %v", securityContext) return securityContext @@ -443,6 +439,22 @@ func seccompProfile(scp *SecProfile) *v1.SeccompProfile { return seccompProfile } +func apparmorProfile(scp *SecProfile) *v1.AppArmorProfile { + if scp == nil || len(scp.Type) == 0 { + return nil + } + log.Trace().Msgf("using AppArmor profile: %v", scp) + + apparmorProfile := &v1.AppArmorProfile{ + Type: v1.AppArmorProfileType(scp.Type), + } + if len(scp.LocalhostProfile) > 0 { + apparmorProfile.LocalhostProfile = &scp.LocalhostProfile + } + + return apparmorProfile +} + func containerSecurityContext(sc *SecurityContext, stepPrivileged bool) *v1.SecurityContext { if !stepPrivileged { return nil @@ -471,36 +483,6 @@ func containerSecurityContext(sc *SecurityContext, stepPrivileged bool) *v1.Secu return nil } -func apparmorAnnotation(containerName string, scp *SecProfile) (*string, *string) { - if scp == nil { - return nil, nil - } - log.Trace().Msgf("using AppArmor profile: %v", scp) - - var ( - profileType string - profilePath string - ) - - if scp.Type == SecProfileTypeRuntimeDefault { - profileType = "runtime" - profilePath = "default" - } - - if scp.Type == SecProfileTypeLocalhost { - profileType = "localhost" - profilePath = scp.LocalhostProfile - } - - if len(profileType) == 0 { - return nil, nil - } - - key := v1.DeprecatedAppArmorBetaContainerAnnotationKeyPrefix + containerName - value := profileType + "/" + profilePath - return &key, &value -} - func mapToEnvVars(m map[string]string) []v1.EnvVar { var ev []v1.EnvVar for k, v := range m { diff --git a/pipeline/backend/kubernetes/pod_test.go b/pipeline/backend/kubernetes/pod_test.go index 8b824154598..4f7732878fa 100644 --- a/pipeline/backend/kubernetes/pod_test.go +++ b/pipeline/backend/kubernetes/pod_test.go @@ -162,7 +162,6 @@ func TestFullPod(t *testing.T) { }, "annotations": { "apps.kubernetes.io/pod-index": "0", - "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" } }, @@ -250,9 +249,13 @@ func TestFullPod(t *testing.T) { "runAsGroup": 101, "runAsNonRoot": true, "fsGroup": 101, + "appArmorProfile": { + "type": "Localhost", + "localhostProfile": "k8s-apparmor-example-deny-write" + }, "seccompProfile": { - "type": "Localhost", - "localhostProfile": "profiles/audit.json" + "type": "Localhost", + "localhostProfile": "profiles/audit.json" } }, "imagePullSecrets": [ diff --git a/pipeline/backend/local/local.go b/pipeline/backend/local/local.go index 3ea42ba0f2a..5e11f68f0a6 100644 --- a/pipeline/backend/local/local.go +++ b/pipeline/backend/local/local.go @@ -62,8 +62,14 @@ func (e *local) Name() string { return "local" } -func (e *local) IsAvailable(context.Context) bool { - return true +func (e *local) IsAvailable(ctx context.Context) bool { + if c, ok := ctx.Value(types.CliCommand).(*cli.Command); ok { + if c.String("backend-engine") == e.Name() { + return true + } + } + _, inContainer := os.LookupEnv("WOODPECKER_IN_CONTAINER") + return !inContainer } func (e *local) Flags() []cli.Flag { diff --git a/server/api/hook.go b/server/api/hook.go index 654acc93d86..713fca39b11 100644 --- a/server/api/hook.go +++ b/server/api/hook.go @@ -250,11 +250,8 @@ func PostHook(c *gin.Context) { func getRepoFromToken(store store.Store, t *token.Token) (*model.Repo, error) { // try to get the repo by the repo-id repoID, err := strconv.ParseInt(t.Get("repo-id"), 10, 64) - if err == nil { - return store.GetRepo(repoID) + if err != nil { + return nil, err } - - // try to get the repo by the repo name or by its redirection - repoName := t.Get("text") - return store.GetRepoName(repoName) + return store.GetRepo(repoID) } diff --git a/server/api/repo.go b/server/api/repo.go index b5cefa699f9..84b9b800287 100644 --- a/server/api/repo.go +++ b/server/api/repo.go @@ -360,7 +360,8 @@ func GetRepoBranches(c *gin.Context) { branches, err := _forge.Branches(c, user, repo, session.Pagination(c)) if err != nil { - _ = c.AbortWithError(http.StatusInternalServerError, err) + log.Error().Err(err).Msg("failed to load branches") + c.String(http.StatusInternalServerError, "failed to load branches: %s", err) return } diff --git a/server/cron/cron.go b/server/cron/cron.go index 0dc05ee655f..9fa35aa8cfa 100644 --- a/server/cron/cron.go +++ b/server/cron/cron.go @@ -19,7 +19,7 @@ import ( "fmt" "time" - "github.com/robfig/cron" + "github.com/gdgvda/cron" "github.com/rs/zerolog/log" "go.woodpecker-ci.org/woodpecker/v2/server" @@ -31,7 +31,7 @@ import ( const ( // Specifies the interval woodpecker checks for new crons to exec. - checkTime = 10 * time.Second + checkTime = time.Minute // Specifies the batch size of crons to retrieve per check from database. checkItems = 10 @@ -71,7 +71,7 @@ func CalcNewNext(schedule string, now time.Time) (time.Time, error) { // TODO: allow the users / the admin to set a specific timezone - c, err := cron.Parse(schedule) + c, err := cron.ParseStandard(schedule) if err != nil { return time.Time{}, fmt.Errorf("cron parse schedule: %w", err) } diff --git a/server/forge/bitbucketdatacenter/bitbucketdatacenter.go b/server/forge/bitbucketdatacenter/bitbucketdatacenter.go index cd3b58bc4cf..b04abeeac38 100644 --- a/server/forge/bitbucketdatacenter/bitbucketdatacenter.go +++ b/server/forge/bitbucketdatacenter/bitbucketdatacenter.go @@ -211,7 +211,7 @@ func (c *client) Repos(ctx context.Context, u *model.User) ([]*model.Repo, error } opts := &bb.RepositorySearchOptions{Permission: bb.PermissionRepoWrite, ListOptions: bb.ListOptions{Limit: listLimit}} - var all []*model.Repo + all := make([]*model.Repo, 0) for { repos, resp, err := bc.Projects.SearchRepositories(ctx, opts) if err != nil { @@ -277,7 +277,7 @@ func (c *client) Dir(ctx context.Context, u *model.User, r *model.Repo, p *model } opts := &bb.FilesListOptions{At: p.Commit} - var all []*forge_types.FileMeta + all := make([]*forge_types.FileMeta, 0) for { list, resp, err := bc.Projects.ListFiles(ctx, r.Owner, r.Name, path, opts) if err != nil { @@ -341,7 +341,7 @@ func (c *client) Branches(ctx context.Context, u *model.User, r *model.Repo, p * } opts := &bb.BranchSearchOptions{ListOptions: convertListOptions(p)} - var all []string + all := make([]string, 0) for { branches, resp, err := bc.Projects.SearchBranches(ctx, r.Owner, r.Name, opts) if err != nil { @@ -389,7 +389,7 @@ func (c *client) PullRequests(ctx context.Context, u *model.User, r *model.Repo, } opts := &bb.PullRequestSearchOptions{ListOptions: convertListOptions(p)} - var all []*model.PullRequest + all := make([]*model.PullRequest, 0) for { prs, resp, err := bc.Projects.SearchPullRequests(ctx, r.Owner, r.Name, opts) if err != nil { diff --git a/server/model/cron.go b/server/model/cron.go index 42467de3573..cdce73ef352 100644 --- a/server/model/cron.go +++ b/server/model/cron.go @@ -17,18 +17,18 @@ package model import ( "fmt" - "github.com/robfig/cron" + "github.com/gdgvda/cron" ) type Cron struct { - ID int64 `json:"id" xorm:"pk autoincr 'id'"` - Name string `json:"name" xorm:"name UNIQUE(s) INDEX"` - RepoID int64 `json:"repo_id" xorm:"repo_id UNIQUE(s) INDEX"` - CreatorID int64 `json:"creator_id" xorm:"creator_id INDEX"` - NextExec int64 `json:"next_exec" xorm:"next_exec"` - Schedule string `json:"schedule" xorm:"schedule NOT NULL"` // @weekly, 3min, ... - Created int64 `json:"created_at" xorm:"created NOT NULL DEFAULT 0"` // TODO change JSON field to "created" in 3.0 - Branch string `json:"branch" xorm:"branch"` + ID int64 `json:"id" xorm:"pk autoincr 'id'"` + Name string `json:"name" xorm:"name UNIQUE(s) INDEX"` + RepoID int64 `json:"repo_id" xorm:"repo_id UNIQUE(s) INDEX"` + CreatorID int64 `json:"creator_id" xorm:"creator_id INDEX"` + NextExec int64 `json:"next_exec" xorm:"next_exec"` + Schedule string `json:"schedule" xorm:"schedule NOT NULL"` // @weekly, 3min, ... + Created int64 `json:"created" xorm:"created NOT NULL DEFAULT 0"` + Branch string `json:"branch" xorm:"branch"` } // @name Cron // TableName returns the database table name for xorm. @@ -46,7 +46,7 @@ func (c *Cron) Validate() error { return fmt.Errorf("schedule is required") } - _, err := cron.Parse(c.Schedule) + _, err := cron.ParseStandard(c.Schedule) if err != nil { return fmt.Errorf("can't parse schedule: %w", err) } diff --git a/server/model/feed.go b/server/model/feed.go index 0dd4b5d3a33..04357fc4ec2 100644 --- a/server/model/feed.go +++ b/server/model/feed.go @@ -22,9 +22,9 @@ type Feed struct { Number int64 `json:"number,omitempty" xorm:"pipeline_number"` Event string `json:"event,omitempty" xorm:"pipeline_event"` Status string `json:"status,omitempty" xorm:"pipeline_status"` - Created int64 `json:"created_at,omitempty" xorm:"pipeline_created"` // TODO change JSON field to "created" in 3.0 - Started int64 `json:"started_at,omitempty" xorm:"pipeline_started"` // TODO change JSON field to "started" in 3.0 - Finished int64 `json:"finished_at,omitempty" xorm:"pipeline_finished"` // TODO change JSON field to "finished" in 3.0 + Created int64 `json:"created,omitempty" xorm:"pipeline_created"` + Started int64 `json:"started,omitempty" xorm:"pipeline_started"` + Finished int64 `json:"finished,omitempty" xorm:"pipeline_finished"` Commit string `json:"commit,omitempty" xorm:"pipeline_commit"` Branch string `json:"branch,omitempty" xorm:"pipeline_branch"` Ref string `json:"ref,omitempty" xorm:"pipeline_ref"` diff --git a/server/model/pipeline.go b/server/model/pipeline.go index 5106308deef..a073ad15fbf 100644 --- a/server/model/pipeline.go +++ b/server/model/pipeline.go @@ -28,10 +28,10 @@ type Pipeline struct { Event WebhookEvent `json:"event" xorm:"event"` Status StatusValue `json:"status" xorm:"INDEX 'status'"` Errors []*types.PipelineError `json:"errors" xorm:"json 'errors'"` - Created int64 `json:"created_at" xorm:"'created' NOT NULL DEFAULT 0 created"` // TODO change JSON field to "created" in 3.0 - Updated int64 `json:"updated_at" xorm:"'updated' NOT NULL DEFAULT 0 updated"` // TODO change JSON field to "updated" in 3.0 - Started int64 `json:"started_at" xorm:"started"` // TODO change JSON field to "started" in 3.0 - Finished int64 `json:"finished_at" xorm:"finished"` // TODO change JSON field to "finished" in 3.0 + Created int64 `json:"created" xorm:"'created' NOT NULL DEFAULT 0 created"` + Updated int64 `json:"updated" xorm:"'updated' NOT NULL DEFAULT 0 updated"` + Started int64 `json:"started" xorm:"started"` + Finished int64 `json:"finished" xorm:"finished"` DeployTo string `json:"deploy_to" xorm:"deploy"` DeployTask string `json:"deploy_task" xorm:"deploy_task"` Commit string `json:"commit" xorm:"commit"` @@ -46,7 +46,7 @@ type Pipeline struct { Email string `json:"author_email" xorm:"email"` ForgeURL string `json:"forge_url" xorm:"forge_url"` Reviewer string `json:"reviewed_by" xorm:"reviewer"` - Reviewed int64 `json:"reviewed_at" xorm:"reviewed"` // TODO change JSON field to "reviewed" in 3.0 + Reviewed int64 `json:"reviewed" xorm:"reviewed"` Workflows []*Workflow `json:"workflows,omitempty" xorm:"-"` ChangedFiles []string `json:"changed_files,omitempty" xorm:"LONGTEXT 'changed_files'"` AdditionalVariables map[string]string `json:"variables,omitempty" xorm:"json 'additional_variables'"` diff --git a/server/model/step.go b/server/model/step.go index 97189e733fa..dba0d3bac86 100644 --- a/server/model/step.go +++ b/server/model/step.go @@ -36,8 +36,8 @@ type Step struct { Error string `json:"error,omitempty" xorm:"TEXT 'error'"` Failure string `json:"-" xorm:"failure"` ExitCode int `json:"exit_code" xorm:"exit_code"` - Started int64 `json:"start_time,omitempty" xorm:"started"` - Finished int64 `json:"end_time,omitempty" xorm:"stopped"` + Started int64 `json:"started,omitempty" xorm:"started"` + Finished int64 `json:"finished,omitempty" xorm:"finished"` Type StepType `json:"type,omitempty" xorm:"type"` } // @name Step diff --git a/server/model/workflow.go b/server/model/workflow.go index 7efd5f15178..e6029ab69c1 100644 --- a/server/model/workflow.go +++ b/server/model/workflow.go @@ -23,8 +23,8 @@ type Workflow struct { Name string `json:"name" xorm:"name"` State StatusValue `json:"state" xorm:"state"` Error string `json:"error,omitempty" xorm:"TEXT 'error'"` - Started int64 `json:"start_time,omitempty" xorm:"started"` - Finished int64 `json:"end_time,omitempty" xorm:"stopped"` + Started int64 `json:"started,omitempty" xorm:"started"` + Finished int64 `json:"finished,omitempty" xorm:"finished"` AgentID int64 `json:"agent_id,omitempty" xorm:"agent_id"` Platform string `json:"platform,omitempty" xorm:"platform"` Environ map[string]string `json:"environ,omitempty" xorm:"json 'environ'"` diff --git a/server/store/datastore/migration/033_cron_without_sec.go b/server/store/datastore/migration/033_cron_without_sec.go new file mode 100644 index 00000000000..a170401b30d --- /dev/null +++ b/server/store/datastore/migration/033_cron_without_sec.go @@ -0,0 +1,53 @@ +// Copyright 2024 Woodpecker Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package migration + +import ( + "fmt" + "strings" + + "src.techknowlogick.com/xormigrate" + "xorm.io/xorm" + + "go.woodpecker-ci.org/woodpecker/v2/server/model" +) + +var cronWithoutSec = xormigrate.Migration{ + ID: "cron-without-sec", + MigrateSession: func(sess *xorm.Session) error { + if err := sess.Sync(new(model.Cron)); err != nil { + return fmt.Errorf("sync new models failed: %w", err) + } + + var crons []*model.Cron + if err := sess.Find(&crons); err != nil { + return err + } + + for _, c := range crons { + if strings.HasPrefix(strings.TrimSpace(c.Schedule), "@") { + // something like "@daily" + continue + } + + c.Schedule = strings.SplitN(strings.TrimSpace(c.Schedule), " ", 2)[1] + if _, err := sess.Update(c); err != nil { + return err + } + } + + return nil + }, +} diff --git a/server/store/datastore/migration/034_rename_start_end_time.go b/server/store/datastore/migration/034_rename_start_end_time.go new file mode 100644 index 00000000000..2bc968f6bfe --- /dev/null +++ b/server/store/datastore/migration/034_rename_start_end_time.go @@ -0,0 +1,59 @@ +// Copyright 2024 Woodpecker Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package migration + +import ( + "fmt" + + "src.techknowlogick.com/xormigrate" + "xorm.io/xorm" +) + +type stepV033 struct { + Finished int64 `xorm:"stopped"` +} + +func (stepV033) TableName() string { + return "steps" +} + +type workflowV033 struct { + Finished int64 `xorm:"stopped"` +} + +func (workflowV033) TableName() string { + return "workflows" +} + +var renameStartEndTime = xormigrate.Migration{ + ID: "rename-start-end-time", + MigrateSession: func(sess *xorm.Session) (err error) { + if err := sess.Sync(new(stepV033), new(workflowV033)); err != nil { + return fmt.Errorf("sync models failed: %w", err) + } + + // Step + if err := renameColumn(sess, "steps", "stopped", "finished"); err != nil { + return err + } + + // Workflow + if err := renameColumn(sess, "workflows", "stopped", "finished"); err != nil { + return err + } + + return nil + }, +} diff --git a/server/store/datastore/migration/migration.go b/server/store/datastore/migration/migration.go index 4c905e66e03..da9b1f7327d 100644 --- a/server/store/datastore/migration/migration.go +++ b/server/store/datastore/migration/migration.go @@ -63,6 +63,8 @@ var migrationTasks = []*xormigrate.Migration{ &setForgeID, &unifyColumnsTables, &alterTableRegistriesFixRequiredFields, + &cronWithoutSec, + &renameStartEndTime, } var allBeans = []any{ diff --git a/web/src/assets/locales/en.json b/web/src/assets/locales/en.json index a009b1997cc..530a8f0fdad 100644 --- a/web/src/assets/locales/en.json +++ b/web/src/assets/locales/en.json @@ -38,7 +38,8 @@ "desc": "Specify additional variables to use in your pipeline. Variables with the same name will be overwritten.", "name": "Variable name", "value": "Variable value" - } + }, + "show_pipelines": "Show pipelines" }, "deploy_pipeline": { "title": "Trigger deployment event for current pipeline #{pipelineId}", diff --git a/web/src/components/layout/popups/ManualPipelinePopup.vue b/web/src/components/layout/popups/ManualPipelinePopup.vue deleted file mode 100644 index 4c7ba0e9c36..00000000000 --- a/web/src/components/layout/popups/ManualPipelinePopup.vue +++ /dev/null @@ -1,134 +0,0 @@ - - - diff --git a/web/src/components/repo/pipeline/PipelineLog.vue b/web/src/components/repo/pipeline/PipelineLog.vue index 3a9bca98709..5fa128cb635 100644 --- a/web/src/components/repo/pipeline/PipelineLog.vue +++ b/web/src/components/repo/pipeline/PipelineLog.vue @@ -13,7 +13,7 @@
{{ $t('repo.pipeline.actions.canceled') }} - {{ $t('repo.pipeline.step_not_started') }} + {{ $t('repo.pipeline.step_not_started') }}
{{ $t('repo.pipeline.loading') }}
{{ $t('repo.pipeline.no_logs') }}
@@ -350,7 +350,7 @@ watch(stepSlug, async () => { watch(step, async (newStep, oldStep) => { if (oldStep?.name === newStep?.name) { - if (oldStep?.end_time !== newStep?.end_time && autoScroll.value) { + if (oldStep?.finished !== newStep?.finished && autoScroll.value) { scrollDown(); } diff --git a/web/src/components/repo/pipeline/PipelineStepDuration.vue b/web/src/components/repo/pipeline/PipelineStepDuration.vue index 8fd6e03d390..0b79a4bd1a1 100644 --- a/web/src/components/repo/pipeline/PipelineStepDuration.vue +++ b/web/src/components/repo/pipeline/PipelineStepDuration.vue @@ -19,8 +19,8 @@ const workflow = toRef(props, 'workflow'); const { durationAsNumber } = useDate(); const durationRaw = computed(() => { - const start = (step.value ? step.value?.start_time : workflow.value?.start_time) || 0; - const end = (step.value ? step.value?.end_time : workflow.value?.end_time) || 0; + const start = (step.value ? step.value?.started : workflow.value?.started) || 0; + const end = (step.value ? step.value?.finished : workflow.value?.finished) || 0; if (end === 0 && start === 0) { return undefined; @@ -43,5 +43,5 @@ const duration = computed(() => { return durationAsNumber(durationElapsed.value || 0); }); -const started = computed(() => (step.value ? step.value?.start_time : workflow.value?.start_time) !== undefined); +const started = computed(() => (step.value ? step.value?.started : workflow.value?.started) !== undefined); diff --git a/web/src/components/repo/pipeline/PipelineStepList.vue b/web/src/components/repo/pipeline/PipelineStepList.vue index 18e117a4ce4..50006de2560 100644 --- a/web/src/components/repo/pipeline/PipelineStepList.vue +++ b/web/src/components/repo/pipeline/PipelineStepList.vue @@ -82,7 +82,7 @@ {{ workflow.name }} diff --git a/web/src/components/repo/settings/CronTab.vue b/web/src/components/repo/settings/CronTab.vue index 98d27cdef95..e19c9f0a0c7 100644 --- a/web/src/components/repo/settings/CronTab.vue +++ b/web/src/components/repo/settings/CronTab.vue @@ -60,7 +60,7 @@ (action: (...a: T) => void | Promise) { +export function useAsyncAction( + action: (...a: T) => void | Promise, + onerror: ((error: any) => void) | undefined = undefined, +) { const isLoading = ref(false); async function doSubmit(...a: T) { @@ -16,7 +15,10 @@ export function useAsyncAction(action: (...a: T) => void | try { await action(...a); } catch (error) { - notifications.notify({ title: (error as Error).message, type: 'error' }); + console.error(error); + if (onerror) { + onerror(error); + } } isLoading.value = false; } diff --git a/web/src/compositions/usePipeline.ts b/web/src/compositions/usePipeline.ts index d02409fdbb5..b4c8a6804d2 100644 --- a/web/src/compositions/usePipeline.ts +++ b/web/src/compositions/usePipeline.ts @@ -14,7 +14,7 @@ export default (pipeline: Ref) => { return undefined; } - const start = pipeline.value.created_at || 0; + const start = pipeline.value.created || 0; return start * 1000; }); @@ -44,8 +44,8 @@ export default (pipeline: Ref) => { return undefined; } - const start = pipeline.value.started_at || 0; - const end = pipeline.value.finished_at || pipeline.value.updated_at || 0; + const start = pipeline.value.started || 0; + const end = pipeline.value.finished || pipeline.value.updated || 0; if (start === 0 || end === 0) { return 0; @@ -109,7 +109,7 @@ export default (pipeline: Ref) => { return undefined; } - const start = pipeline.value.created_at || 0; + const start = pipeline.value.created || 0; return toLocaleString(new Date(start * 1000)); }); diff --git a/web/src/lib/api/client.ts b/web/src/lib/api/client.ts index 1fd5741fd6b..88731465487 100644 --- a/web/src/lib/api/client.ts +++ b/web/src/lib/api/client.ts @@ -52,14 +52,19 @@ export default class ApiClient { }); if (!res.ok) { + let message = res.statusText; + const resText = await res.text(); + if (resText) { + message = `${res.statusText}: ${resText}`; + } const error: ApiError = { status: res.status, - message: res.statusText, + message, }; if (this.onerror) { this.onerror(error); } - throw new Error(res.statusText); + throw new Error(message); } const contentType = res.headers.get('Content-Type'); diff --git a/web/src/lib/api/types/pipeline.ts b/web/src/lib/api/types/pipeline.ts index 18b784e6d6c..2ca9ccf0d84 100644 --- a/web/src/lib/api/types/pipeline.ts +++ b/web/src/lib/api/types/pipeline.ts @@ -25,16 +25,16 @@ export interface Pipeline { errors?: PipelineError[]; // When the pipeline request was received. - created_at: number; + created: number; // When the pipeline was updated last time in database. - updated_at: number; + updated: number; // When the pipeline began execution. - started_at: number; + started: number; // When the pipeline was finished. - finished_at: number; + finished: number; // Where the deployment should go. deploy_to: string; @@ -76,13 +76,9 @@ export interface Pipeline { // This url will point to the repository state associated with the pipeline's commit. forge_url: string; - signed: boolean; - - verified: boolean; - reviewed_by: string; - reviewed_at: number; + reviewed: number; // The steps associated with this pipeline. // A pipeline will have multiple steps if a matrix pipeline was used or if a rebuild was requested. @@ -110,8 +106,8 @@ export interface PipelineWorkflow { name: string; state: PipelineStatus; environ?: Record; - start_time?: number; - end_time?: number; + started?: number; + finished?: number; agent_id?: number; error?: string; children: PipelineStep[]; @@ -126,8 +122,8 @@ export interface PipelineStep { name: string; state: PipelineStatus; exit_code: number; - start_time?: number; - end_time?: number; + started?: number; + finished?: number; error?: string; type?: StepType; } diff --git a/web/src/router.ts b/web/src/router.ts index cb00abc462e..c71c831d921 100644 --- a/web/src/router.ts +++ b/web/src/router.ts @@ -103,6 +103,12 @@ const routes: RouteRecordRaw[] = [ meta: { authentication: 'required' }, props: true, }, + { + path: 'manual', + name: 'repo-manual', + component: (): Component => import('~/views/repo/RepoManualPipeline.vue'), + meta: { authentication: 'required', repoHeader: true }, + }, ], }, { diff --git a/web/src/utils/helpers.ts b/web/src/utils/helpers.ts index ecd2b47f23f..2efb9818b6d 100644 --- a/web/src/utils/helpers.ts +++ b/web/src/utils/helpers.ts @@ -46,7 +46,7 @@ export function isStepRunning(step: PipelineStep): boolean { * @returns {number} 0 if created at the same time, < 0 if b was create before a, > 0 otherwise */ export function comparePipelines(a: Pipeline, b: Pipeline): number { - return (b.created_at || -1) - (a.created_at || -1); + return (b.created || -1) - (a.created || -1); } /** diff --git a/web/src/views/repo/RepoManualPipeline.vue b/web/src/views/repo/RepoManualPipeline.vue new file mode 100644 index 00000000000..df569448fd3 --- /dev/null +++ b/web/src/views/repo/RepoManualPipeline.vue @@ -0,0 +1,149 @@ + + + diff --git a/web/src/views/repo/RepoWrapper.vue b/web/src/views/repo/RepoWrapper.vue index 123a788ee56..d5655c142c0 100644 --- a/web/src/views/repo/RepoWrapper.vue +++ b/web/src/views/repo/RepoWrapper.vue @@ -30,11 +30,17 @@ @@ -54,7 +60,6 @@ import { useRoute, useRouter } from 'vue-router'; import Button from '~/components/atomic/Button.vue'; import type { IconNames } from '~/components/atomic/Icon.vue'; import IconButton from '~/components/atomic/IconButton.vue'; -import ManualPipelinePopup from '~/components/layout/popups/ManualPipelinePopup.vue'; import Scaffold from '~/components/layout/scaffold/Scaffold.vue'; import Tab from '~/components/layout/scaffold/Tab.vue'; import useApiClient from '~/compositions/useApiClient'; @@ -97,8 +102,6 @@ const forgeIcon = computed(() => { return 'repo'; }); -const showManualPipelinePopup = ref(false); - async function loadRepo() { repoPermissions.value = await apiClient.getRepoPermissions(repositoryId.value); if (!repoPermissions.value.pull) { diff --git a/woodpecker-go/woodpecker/types.go b/woodpecker-go/woodpecker/types.go index b9622ddb6b6..09bf183cab2 100644 --- a/woodpecker-go/woodpecker/types.go +++ b/woodpecker-go/woodpecker/types.go @@ -69,35 +69,32 @@ type ( // Pipeline defines a pipeline object. Pipeline struct { - ID int64 `json:"id"` - Number int64 `json:"number"` - Parent int64 `json:"parent"` - Event string `json:"event"` - Status string `json:"status"` - Errors []*PipelineError `json:"errors"` - // Deprecated TODO remove in 3.x - Enqueued int64 `json:"enqueued_at"` - Created int64 `json:"created_at"` - Updated int64 `json:"updated_at"` - Started int64 `json:"started_at"` - Finished int64 `json:"finished_at"` - Deploy string `json:"deploy_to"` - Commit string `json:"commit"` - Branch string `json:"branch"` - Ref string `json:"ref"` - Refspec string `json:"refspec"` - CloneURL string `json:"clone_url"` - Title string `json:"title"` - Message string `json:"message"` - Timestamp int64 `json:"timestamp"` - Sender string `json:"sender"` - Author string `json:"author"` - Avatar string `json:"author_avatar"` - Email string `json:"author_email"` - ForgeURL string `json:"forge_url"` - Reviewer string `json:"reviewed_by"` - Reviewed int64 `json:"reviewed_at"` - Workflows []*Workflow `json:"workflows,omitempty"` + ID int64 `json:"id"` + Number int64 `json:"number"` + Parent int64 `json:"parent"` + Event string `json:"event"` + Status string `json:"status"` + Errors []*PipelineError `json:"errors"` + Created int64 `json:"created_at"` + Updated int64 `json:"updated_at"` + Started int64 `json:"started_at"` + Finished int64 `json:"finished_at"` + Deploy string `json:"deploy_to"` + Commit string `json:"commit"` + Branch string `json:"branch"` + Ref string `json:"ref"` + Refspec string `json:"refspec"` + Title string `json:"title"` + Message string `json:"message"` + Timestamp int64 `json:"timestamp"` + Sender string `json:"sender"` + Author string `json:"author"` + Avatar string `json:"author_avatar"` + Email string `json:"author_email"` + ForgeURL string `json:"forge_url"` + Reviewer string `json:"reviewed_by"` + Reviewed int64 `json:"reviewed_at"` + Workflows []*Workflow `json:"workflows,omitempty"` } // Workflow represents a workflow in the pipeline. @@ -137,10 +134,6 @@ type ( Address string `json:"address"` Username string `json:"username"` Password string `json:"password,omitempty"` - // Deprecated - Email string `json:"email"` // TODO: remove in 3.x - // Deprecated - Token string `json:"token"` // TODO: remove in 3.x } // Secret represents a secret variable, such as a password or token. @@ -161,9 +154,9 @@ type ( Number int64 `json:"number,omitempty"` Event string `json:"event,omitempty"` Status string `json:"status,omitempty"` - Created int64 `json:"created_at,omitempty"` - Started int64 `json:"started_at,omitempty"` - Finished int64 `json:"finished_at,omitempty"` + Created int64 `json:"created,omitempty"` + Started int64 `json:"started,omitempty"` + Finished int64 `json:"finished,omitempty"` Commit string `json:"commit,omitempty"` Branch string `json:"branch,omitempty"` Ref string `json:"ref,omitempty"` @@ -183,31 +176,21 @@ type ( Commit string `json:"commit,omitempty"` } - //nolint:godot - // TODO: use dedicated struct in 3.x - // QueueStats struct { - // Workers int `json:"worker_count"` - // Pending int `json:"pending_count"` - // WaitingOnDeps int `json:"waiting_on_deps_count"` - // Running int `json:"running_count"` - // Complete int `json:"completed_count"` - // } + QueueStats struct { + Workers int `json:"worker_count"` + Pending int `json:"pending_count"` + WaitingOnDeps int `json:"waiting_on_deps_count"` + Running int `json:"running_count"` + Complete int `json:"completed_count"` + } // Info provides queue stats. Info struct { - Pending []Task `json:"pending"` - WaitingOnDeps []Task `json:"waiting_on_deps"` - Running []Task `json:"running"` - // TODO: use dedicated struct in 3.x - // Stats QueueStats `json:"stats"` - Stats struct { - Workers int `json:"worker_count"` - Pending int `json:"pending_count"` - WaitingOnDeps int `json:"waiting_on_deps_count"` - Running int `json:"running_count"` - Complete int `json:"completed_count"` - } `json:"stats"` - Paused bool `json:"paused,omitempty"` + Pending []Task `json:"pending"` + WaitingOnDeps []Task `json:"waiting_on_deps"` + Running []Task `json:"running"` + Stats QueueStats `json:"stats"` + Paused bool `json:"paused,omitempty"` } // LogLevel is for checking/setting logging level. @@ -233,7 +216,7 @@ type ( CreatorID int64 `json:"creator_id"` NextExec int64 `json:"next_exec"` Schedule string `json:"schedule"` - Created int64 `json:"created_at"` + Created int64 `json:"created"` Branch string `json:"branch"` }