mirror of
https://github.com/woodpecker-ci/woodpecker.git
synced 2024-12-24 10:07:21 +02:00
Merge branch 'main' into make-cli-exec-work-with-local-backend
This commit is contained in:
commit
bc0fcdd58e
@ -129,6 +129,7 @@
|
||||
"nosniff",
|
||||
"ntfy",
|
||||
"octocat",
|
||||
"openapi",
|
||||
"opensource",
|
||||
"Pacman",
|
||||
"picus",
|
||||
@ -250,7 +251,7 @@
|
||||
"flake.lock",
|
||||
"pnpm-lock.yaml",
|
||||
"**/node_modules/**/*",
|
||||
"cmd/server/docs/docs.go",
|
||||
"cmd/server/openapi/docs.go",
|
||||
"renovate.json",
|
||||
// TODO: remove the following
|
||||
"docs/**/*.js",
|
||||
|
5
.ecrc
5
.ecrc
@ -1,14 +1,15 @@
|
||||
{
|
||||
"Exclude": [
|
||||
".git",
|
||||
"go.mod", "go.sum",
|
||||
"go.mod",
|
||||
"go.sum",
|
||||
"vendor",
|
||||
"fixtures",
|
||||
"LICENSE",
|
||||
"node_modules",
|
||||
"server/store/datastore/migration/test-files/sqlite.db",
|
||||
"server/store/datastore/feed.go",
|
||||
"cmd/server/docs/docs.go",
|
||||
"cmd/server/openapi/docs.go",
|
||||
"_test.go",
|
||||
"Makefile"
|
||||
]
|
||||
|
1
.github/renovate.json
vendored
1
.github/renovate.json
vendored
@ -2,7 +2,6 @@
|
||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||
"extends": ["github>woodpecker-ci/renovate-config"],
|
||||
"automergeType": "pr",
|
||||
"enabledManagers": ["woodpecker"],
|
||||
"customManagers": [
|
||||
{
|
||||
"customType": "regex",
|
||||
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -51,4 +51,7 @@ docs/venv
|
||||
|
||||
### Generated by CI ###
|
||||
docs/docs/40-cli.md
|
||||
docs/openapi.json
|
||||
|
||||
# Removed once v3.0.x is minimum version to be touched
|
||||
docs/swagger.json
|
||||
|
@ -10,11 +10,11 @@ repos:
|
||||
- id: end-of-file-fixer
|
||||
- id: trailing-whitespace
|
||||
- repo: https://github.com/golangci/golangci-lint
|
||||
rev: v1.61.0
|
||||
rev: v1.62.2
|
||||
hooks:
|
||||
- id: golangci-lint
|
||||
- repo: https://github.com/igorshubovych/markdownlint-cli
|
||||
rev: v0.42.0
|
||||
rev: v0.43.0
|
||||
hooks:
|
||||
- id: markdownlint
|
||||
exclude: '^(docs/versioned_docs/.*|CHANGELOG.md)$'
|
||||
|
@ -2,7 +2,9 @@ when:
|
||||
- event: tag
|
||||
- event: pull_request
|
||||
branch: ${CI_REPO_DEFAULT_BRANCH}
|
||||
path: Makefile
|
||||
path:
|
||||
- Makefile
|
||||
- .woodpecker/binaries.yaml
|
||||
|
||||
variables:
|
||||
- &golang_image 'docker.io/golang:1.23'
|
||||
@ -97,7 +99,7 @@ steps:
|
||||
release:
|
||||
depends_on:
|
||||
- checksums
|
||||
image: woodpeckerci/plugin-release:0.2.1
|
||||
image: woodpeckerci/plugin-release:0.2.2
|
||||
settings:
|
||||
api_key:
|
||||
from_secret: github_token
|
||||
|
@ -2,7 +2,7 @@ variables:
|
||||
- &golang_image 'docker.io/golang:1.23'
|
||||
- &node_image 'docker.io/node:23-alpine'
|
||||
- &xgo_image 'docker.io/techknowlogick/xgo:go-1.23.x'
|
||||
- &buildx_plugin 'docker.io/woodpeckerci/plugin-docker-buildx:5.0.0'
|
||||
- &buildx_plugin 'docker.io/woodpeckerci/plugin-docker-buildx:5.1.0'
|
||||
- &platforms_release 'linux/arm/v6,linux/arm/v7,linux/arm64/v8,linux/386,linux/amd64,linux/ppc64le,linux/riscv64,linux/s390x,freebsd/arm64,freebsd/amd64,openbsd/arm64,openbsd/amd64'
|
||||
- &platforms_server 'linux/arm/v7,linux/arm64/v8,linux/amd64,linux/ppc64le,linux/riscv64'
|
||||
- &platforms_preview 'linux/amd64'
|
||||
@ -41,9 +41,6 @@ variables:
|
||||
|
||||
when:
|
||||
- event: [pull_request, tag]
|
||||
- event: push
|
||||
branch:
|
||||
- renovate/*
|
||||
- event: push
|
||||
branch: ${CI_REPO_DEFAULT_BRANCH}
|
||||
path: *when_path
|
||||
@ -61,7 +58,6 @@ steps:
|
||||
path: *when_path
|
||||
- branch:
|
||||
- ${CI_REPO_DEFAULT_BRANCH}
|
||||
- renovate/*
|
||||
event: [push, tag]
|
||||
path: *when_path
|
||||
|
||||
@ -82,7 +78,6 @@ steps:
|
||||
path: *when_path
|
||||
- branch:
|
||||
- ${CI_REPO_DEFAULT_BRANCH}
|
||||
- renovate/*
|
||||
event: [push, tag]
|
||||
path: *when_path
|
||||
|
||||
@ -104,9 +99,6 @@ steps:
|
||||
evaluate: 'CI_COMMIT_PULL_REQUEST_LABELS contains "build_pr_images"'
|
||||
- event: pull_request
|
||||
path: *when_path
|
||||
- event: push
|
||||
path: *when_path
|
||||
branch: renovate/*
|
||||
|
||||
cross-compile-server:
|
||||
depends_on:
|
||||
@ -169,9 +161,6 @@ steps:
|
||||
- evaluate: 'not (CI_COMMIT_PULL_REQUEST_LABELS contains "build_pr_images")'
|
||||
event: pull_request
|
||||
path: *when_path
|
||||
- event: push
|
||||
path: *when_path
|
||||
branch: renovate/*
|
||||
|
||||
publish-next-server:
|
||||
depends_on:
|
||||
|
@ -1,7 +1,7 @@
|
||||
variables:
|
||||
- &golang_image 'docker.io/golang:1.23'
|
||||
- &node_image 'docker.io/node:23-alpine'
|
||||
- &alpine_image 'docker.io/alpine:3.20'
|
||||
- &alpine_image 'docker.io/alpine:3.21'
|
||||
- path: &when_path
|
||||
- 'docs/**'
|
||||
- '.woodpecker/docs.yaml'
|
||||
@ -31,7 +31,6 @@ when:
|
||||
- <<: *docker_path
|
||||
branch:
|
||||
- ${CI_REPO_DEFAULT_BRANCH}
|
||||
- renovate/*
|
||||
- event: pull_request_closed
|
||||
path: *when_path
|
||||
- event: manual
|
||||
@ -60,7 +59,7 @@ steps:
|
||||
- event: manual
|
||||
|
||||
deploy-preview:
|
||||
image: docker.io/woodpeckerci/plugin-surge-preview:1.3.2
|
||||
image: docker.io/woodpeckerci/plugin-surge-preview:1.3.3
|
||||
settings:
|
||||
path: 'docs/build/'
|
||||
surge_token:
|
||||
@ -74,8 +73,9 @@ steps:
|
||||
|
||||
deploy-prepare:
|
||||
image: *alpine_image
|
||||
secrets:
|
||||
- BOT_PRIVATE_KEY
|
||||
environment:
|
||||
BOT_PRIVATE_KEY:
|
||||
from_secret: BOT_PRIVATE_KEY
|
||||
commands:
|
||||
- apk add openssh-client git
|
||||
- mkdir -p $HOME/.ssh
|
||||
@ -127,8 +127,9 @@ steps:
|
||||
|
||||
deploy:
|
||||
image: *alpine_image
|
||||
secrets:
|
||||
- BOT_PRIVATE_KEY
|
||||
environment:
|
||||
BOT_PRIVATE_KEY:
|
||||
from_secret: BOT_PRIVATE_KEY
|
||||
commands:
|
||||
- apk add openssh-client rsync git
|
||||
- mkdir -p $HOME/.ssh
|
||||
|
31
.woodpecker/links.yaml
Normal file
31
.woodpecker/links.yaml
Normal file
@ -0,0 +1,31 @@
|
||||
when:
|
||||
- event: cron
|
||||
cron: links
|
||||
|
||||
steps:
|
||||
- name: links
|
||||
image: docker.io/lycheeverse/lychee:0.15.1
|
||||
failure: ignore
|
||||
depends_on: []
|
||||
commands:
|
||||
- lychee pipeline/frontend/yaml/linter/schema/schema.json > links.md
|
||||
- lychee --exclude localhost docs/docs/ >> links.md
|
||||
- lychee --exclude localhost docs/src/pages/ >> links.md
|
||||
- echo -e "\nLast checked:$(date)" >> links.md
|
||||
|
||||
- name: Update issue
|
||||
image: docker.io/alpine:3.21
|
||||
depends_on: links
|
||||
environment:
|
||||
GITHUB_TOKEN:
|
||||
from_secret: github_token
|
||||
commands:
|
||||
- apk add -q --no-cache jq curl
|
||||
- export ISSUE_NUMBER=4514
|
||||
- export DESCRIPTION=$(cat links.md)
|
||||
- |
|
||||
curl -X PATCH \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
-H "Accept: application/vnd.github.v3+json" \
|
||||
https://api.github.com/repos/${CI_REPO}/issues/$ISSUE_NUMBER \
|
||||
-d "$(jq -n --arg body "$DESCRIPTION" '{body: $body}')"
|
@ -1,6 +1,6 @@
|
||||
steps:
|
||||
- name: release-helper
|
||||
image: woodpeckerci/plugin-ready-release-go:2.0.0
|
||||
image: docker.io/woodpeckerci/plugin-ready-release-go:3.1.0
|
||||
settings:
|
||||
release_branch: ${CI_COMMIT_BRANCH}
|
||||
forge_type: github
|
||||
@ -13,5 +13,3 @@ when:
|
||||
branch:
|
||||
- ${CI_REPO_DEFAULT_BRANCH}
|
||||
- release/*
|
||||
- event: manual
|
||||
evaluate: 'TASK == "release-helper"'
|
||||
|
@ -1,12 +1,11 @@
|
||||
when:
|
||||
- event: [pull_request, cron]
|
||||
- event: [pull_request]
|
||||
- event: push
|
||||
branch:
|
||||
- ${CI_REPO_DEFAULT_BRANCH}
|
||||
- renovate/*
|
||||
|
||||
variables:
|
||||
- &trivy_plugin docker.io/woodpeckerci/plugin-trivy:1.2.0
|
||||
- &trivy_plugin docker.io/woodpeckerci/plugin-trivy:1.3.0
|
||||
|
||||
steps:
|
||||
backend:
|
||||
@ -35,11 +34,8 @@ steps:
|
||||
services:
|
||||
server:
|
||||
image: *trivy_plugin
|
||||
# settings:
|
||||
# service: true
|
||||
# db-repository: docker.io/aquasec/trivy-db:2
|
||||
environment:
|
||||
PLUGIN_SERVICE: 'true'
|
||||
PLUGIN_DB_REPOSITORY: 'docker.io/aquasec/trivy-db:2'
|
||||
settings:
|
||||
service: true
|
||||
db-repository: docker.io/aquasec/trivy-db:2
|
||||
ports:
|
||||
- 10000
|
||||
|
@ -1,16 +1,12 @@
|
||||
when:
|
||||
- event: pull_request
|
||||
- event: push
|
||||
branch: renovate/*
|
||||
|
||||
steps:
|
||||
- name: lint-editorconfig
|
||||
image: docker.io/mstruebing/editorconfig-checker:v3.0.3
|
||||
image: docker.io/woodpeckerci/plugin-editorconfig-checker:0.2.0
|
||||
depends_on: []
|
||||
when:
|
||||
- event: pull_request
|
||||
- event: push
|
||||
branch: renovate/*
|
||||
|
||||
- name: spellcheck
|
||||
image: docker.io/node:23-alpine
|
||||
@ -23,15 +19,7 @@ steps:
|
||||
- tree --gitignore -I 012_columns_rename_procs_to_steps.go -I versioned_docs -I '*opensource.svg'| pnpx cspell lint --no-progress stdin
|
||||
|
||||
- name: prettier
|
||||
image: docker.io/woodpeckerci/plugin-prettier:0.2.0
|
||||
image: docker.io/woodpeckerci/plugin-prettier:1.0.0
|
||||
depends_on: []
|
||||
settings:
|
||||
version: 3.3.3
|
||||
|
||||
- name: links
|
||||
image: docker.io/lycheeverse/lychee:0.15.1
|
||||
depends_on: []
|
||||
commands:
|
||||
- lychee pipeline/frontend/yaml/linter/schema/schema.json
|
||||
- lychee --user-agent "curl/8.4.0" --exclude localhost docs/docs/
|
||||
- lychee --user-agent "curl/8.4.0" --exclude localhost docs/src/pages/
|
||||
|
@ -10,14 +10,9 @@ variables:
|
||||
# schema changes
|
||||
- 'pipeline/schema/**'
|
||||
event: pull_request
|
||||
- event: push
|
||||
branch: renovate/*
|
||||
path: *when_path
|
||||
|
||||
when:
|
||||
- event: pull_request
|
||||
- event: push
|
||||
branch: renovate/*
|
||||
- event: push
|
||||
branch: ${CI_REPO_DEFAULT_BRANCH}
|
||||
path: *when_path
|
||||
@ -40,7 +35,8 @@ steps:
|
||||
- go run go.woodpecker-ci.org/woodpecker/v2/cmd/cli lint
|
||||
environment:
|
||||
WOODPECKER_DISABLE_UPDATE_CHECK: true
|
||||
WOODPECKER_PLUGINS_PRIVILEGED: 'docker.io/woodpeckerci/plugin-docker-buildx:5.0.0'
|
||||
WOODPECKER_LINT_STRICT: true
|
||||
WOODPECKER_PLUGINS_PRIVILEGED: 'docker.io/woodpeckerci/plugin-docker-buildx'
|
||||
when:
|
||||
- event: pull_request
|
||||
path:
|
||||
@ -62,14 +58,14 @@ steps:
|
||||
- make lint
|
||||
when: *when
|
||||
|
||||
check-swagger:
|
||||
check-openapi:
|
||||
depends_on:
|
||||
- vendor
|
||||
image: *golang_image
|
||||
commands:
|
||||
- 'make generate-swagger'
|
||||
- 'make generate-openapi'
|
||||
- 'DIFF=$(git diff | head)'
|
||||
- '[ -n "$DIFF" ] && { echo "swagger not up to date, exec `make generate-swagger` and commit"; exit 1; } || true'
|
||||
- '[ -n "$DIFF" ] && { echo "openapi not up to date, exec `make generate-openapi` and commit"; exit 1; } || true'
|
||||
when: *when
|
||||
|
||||
lint-license-header:
|
||||
|
@ -3,7 +3,6 @@ when:
|
||||
- event: push
|
||||
branch:
|
||||
- release/*
|
||||
- renovate/*
|
||||
|
||||
variables:
|
||||
- &node_image 'docker.io/node:23-alpine'
|
||||
|
19
Makefile
19
Makefile
@ -40,7 +40,7 @@ CGO_ENABLED ?= 1 # only used to compile server
|
||||
HAS_GO = $(shell hash go > /dev/null 2>&1 && echo "GO" || echo "NOGO" )
|
||||
ifeq ($(HAS_GO),GO)
|
||||
# renovate: datasource=docker depName=docker.io/techknowlogick/xgo
|
||||
XGO_VERSION ?= go-1.22.x
|
||||
XGO_VERSION ?= go-1.23.x
|
||||
CGO_CFLAGS ?= $(shell go env CGO_CFLAGS)
|
||||
endif
|
||||
CGO_CFLAGS ?=
|
||||
@ -109,15 +109,15 @@ clean: ## Clean build artifacts
|
||||
clean-all: clean ## Clean all artifacts
|
||||
rm -rf ${DIST_DIR} web/dist docs/build docs/node_modules web/node_modules
|
||||
# delete generated
|
||||
rm -rf docs/docs/40-cli.md docs/swagger.json
|
||||
rm -rf docs/docs/40-cli.md docs/openapi.json
|
||||
|
||||
.PHONY: generate
|
||||
generate: install-tools generate-swagger ## Run all code generations
|
||||
generate: install-tools generate-openapi ## Run all code generations
|
||||
CGO_ENABLED=0 go generate ./...
|
||||
|
||||
generate-swagger: install-tools ## Run swagger code generation
|
||||
swag init -g server/api/ -g cmd/server/swagger.go --outputTypes go -output cmd/server/docs
|
||||
CGO_ENABLED=0 go generate cmd/server/swagger.go
|
||||
generate-openapi: install-tools ## Run openapi code generation and format it
|
||||
go run github.com/swaggo/swag/cmd/swag fmt
|
||||
CGO_ENABLED=0 go generate cmd/server/openapi.go
|
||||
|
||||
generate-license-header: install-tools
|
||||
addlicense -c "Woodpecker Authors" -ignore "vendor/**" **/*.go
|
||||
@ -134,9 +134,6 @@ install-tools: ## Install development tools
|
||||
hash gofumpt > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
go install mvdan.cc/gofumpt@latest; \
|
||||
fi ; \
|
||||
hash swag > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
go install github.com/swaggo/swag/cmd/swag@latest; \
|
||||
fi ; \
|
||||
hash addlicense > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
go install github.com/google/addlicense@latest; \
|
||||
fi ; \
|
||||
@ -196,7 +193,7 @@ test: test-agent test-server test-server-datastore test-cli test-lib ## Run all
|
||||
build-ui: ## Build UI
|
||||
(cd web/; pnpm install --frozen-lockfile; pnpm build)
|
||||
|
||||
build-server: build-ui generate-swagger ## Build server
|
||||
build-server: build-ui generate-openapi ## Build server
|
||||
CGO_ENABLED=${CGO_ENABLED} GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -tags '$(TAGS)' -ldflags '${LDFLAGS}' -o ${DIST_DIR}/woodpecker-server${BIN_SUFFIX} go.woodpecker-ci.org/woodpecker/v2/cmd/server
|
||||
|
||||
build-agent: ## Build agent
|
||||
@ -343,6 +340,6 @@ spellcheck:
|
||||
.PHONY: docs
|
||||
docs: ## Generate docs (currently only for the cli)
|
||||
CGO_ENABLED=0 go generate cmd/cli/app.go
|
||||
CGO_ENABLED=0 go generate cmd/server/swagger.go
|
||||
CGO_ENABLED=0 go generate cmd/server/openapi.go
|
||||
|
||||
endif
|
||||
|
@ -17,14 +17,20 @@ package admin
|
||||
import (
|
||||
"github.com/urfave/cli/v3"
|
||||
|
||||
"go.woodpecker-ci.org/woodpecker/v2/cli/admin/loglevel"
|
||||
"go.woodpecker-ci.org/woodpecker/v2/cli/admin/registry"
|
||||
"go.woodpecker-ci.org/woodpecker/v2/cli/admin/secret"
|
||||
"go.woodpecker-ci.org/woodpecker/v2/cli/admin/user"
|
||||
)
|
||||
|
||||
// Command exports the admin command set.
|
||||
var Command = &cli.Command{
|
||||
Name: "admin",
|
||||
Usage: "administer server settings",
|
||||
Usage: "manage server settings",
|
||||
Commands: []*cli.Command{
|
||||
loglevel.Command,
|
||||
registry.Command,
|
||||
secret.Command,
|
||||
user.Command,
|
||||
},
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ import (
|
||||
var Command = &cli.Command{
|
||||
Name: "log-level",
|
||||
ArgsUsage: "[level]",
|
||||
Usage: "get the logging level of the server, or set it with [level]",
|
||||
Usage: "retrieve log level from server, or set it with [level]",
|
||||
Action: logLevel,
|
||||
}
|
||||
|
||||
@ -59,6 +59,6 @@ func logLevel(ctx context.Context, c *cli.Command) error {
|
||||
}
|
||||
}
|
||||
|
||||
log.Info().Msgf("logging level: %s", ll.Level)
|
||||
log.Info().Msgf("log level: %s", ll.Level)
|
||||
return nil
|
||||
}
|
@ -25,8 +25,8 @@ var Command = &cli.Command{
|
||||
Commands: []*cli.Command{
|
||||
registryCreateCmd,
|
||||
registryDeleteCmd,
|
||||
registryUpdateCmd,
|
||||
registryInfoCmd,
|
||||
registryListCmd,
|
||||
registryShowCmd,
|
||||
registryUpdateCmd,
|
||||
},
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ import (
|
||||
|
||||
var registryCreateCmd = &cli.Command{
|
||||
Name: "add",
|
||||
Usage: "adds a registry",
|
||||
Usage: "add a registry",
|
||||
Action: registryCreate,
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
|
@ -23,6 +23,7 @@ import (
|
||||
|
||||
"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 registryListCmd = &cli.Command{
|
||||
@ -42,7 +43,9 @@ func registryList(ctx context.Context, c *cli.Command) error {
|
||||
return err
|
||||
}
|
||||
|
||||
list, err := client.GlobalRegistryList()
|
||||
opt := woodpecker.RegistryListOptions{}
|
||||
|
||||
list, err := client.GlobalRegistryList(opt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -25,10 +25,10 @@ import (
|
||||
"go.woodpecker-ci.org/woodpecker/v2/cli/internal"
|
||||
)
|
||||
|
||||
var registryInfoCmd = &cli.Command{
|
||||
Name: "info",
|
||||
Usage: "display registry info",
|
||||
Action: registryInfo,
|
||||
var registryShowCmd = &cli.Command{
|
||||
Name: "show",
|
||||
Usage: "show registry information",
|
||||
Action: registryShow,
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "hostname",
|
||||
@ -39,7 +39,7 @@ var registryInfoCmd = &cli.Command{
|
||||
},
|
||||
}
|
||||
|
||||
func registryInfo(ctx context.Context, c *cli.Command) error {
|
||||
func registryShow(ctx context.Context, c *cli.Command) error {
|
||||
var (
|
||||
hostname = c.String("hostname")
|
||||
format = c.String("format") + "\n"
|
32
cli/admin/secret/secret.go
Normal file
32
cli/admin/secret/secret.go
Normal file
@ -0,0 +1,32 @@
|
||||
// Copyright 2023 Woodpecker Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package secret
|
||||
|
||||
import (
|
||||
"github.com/urfave/cli/v3"
|
||||
)
|
||||
|
||||
// Command exports the secret command.
|
||||
var Command = &cli.Command{
|
||||
Name: "secret",
|
||||
Usage: "manage global secrets",
|
||||
Commands: []*cli.Command{
|
||||
secretCreateCmd,
|
||||
secretDeleteCmd,
|
||||
secretListCmd,
|
||||
secretShowCmd,
|
||||
secretUpdateCmd,
|
||||
},
|
||||
}
|
82
cli/admin/secret/secret_add.go
Normal file
82
cli/admin/secret/secret_add.go
Normal file
@ -0,0 +1,82 @@
|
||||
// Copyright 2023 Woodpecker Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package secret
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/urfave/cli/v3"
|
||||
|
||||
"go.woodpecker-ci.org/woodpecker/v2/cli/internal"
|
||||
"go.woodpecker-ci.org/woodpecker/v2/woodpecker-go/woodpecker"
|
||||
)
|
||||
|
||||
var secretCreateCmd = &cli.Command{
|
||||
Name: "add",
|
||||
Usage: "add a secret",
|
||||
ArgsUsage: "[repo-id|repo-full-name]",
|
||||
Action: secretCreate,
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "value",
|
||||
Usage: "secret value",
|
||||
},
|
||||
&cli.StringSliceFlag{
|
||||
Name: "event",
|
||||
Usage: "secret limited to these events",
|
||||
},
|
||||
&cli.StringSliceFlag{
|
||||
Name: "image",
|
||||
Usage: "secret limited to these images",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func secretCreate(ctx context.Context, c *cli.Command) error {
|
||||
client, err := internal.NewClient(ctx, c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
secret := &woodpecker.Secret{
|
||||
Name: strings.ToLower(c.String("name")),
|
||||
Value: c.String("value"),
|
||||
Images: c.StringSlice("image"),
|
||||
Events: c.StringSlice("event"),
|
||||
}
|
||||
if len(secret.Events) == 0 {
|
||||
secret.Events = defaultSecretEvents
|
||||
}
|
||||
if strings.HasPrefix(secret.Value, "@") {
|
||||
path := strings.TrimPrefix(secret.Value, "@")
|
||||
out, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
secret.Value = string(out)
|
||||
}
|
||||
|
||||
_, err = client.GlobalSecretCreate(secret)
|
||||
return err
|
||||
}
|
||||
|
||||
var defaultSecretEvents = []string{
|
||||
woodpecker.EventPush,
|
||||
woodpecker.EventTag,
|
||||
woodpecker.EventRelease,
|
||||
woodpecker.EventDeploy,
|
||||
}
|
@ -33,12 +33,6 @@ var secretListCmd = &cli.Command{
|
||||
ArgsUsage: "[repo-id|repo-full-name]",
|
||||
Action: secretList,
|
||||
Flags: []cli.Flag{
|
||||
&cli.BoolFlag{
|
||||
Name: "global",
|
||||
Usage: "global secret",
|
||||
},
|
||||
common.OrgFlag,
|
||||
common.RepoFlag,
|
||||
common.FormatFlag(tmplSecretList, true),
|
||||
},
|
||||
}
|
||||
@ -51,30 +45,13 @@ func secretList(ctx context.Context, c *cli.Command) error {
|
||||
return err
|
||||
}
|
||||
|
||||
global, orgID, repoID, err := parseTargetArgs(client, c)
|
||||
opt := woodpecker.SecretListOptions{}
|
||||
|
||||
list, err := client.GlobalSecretList(opt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var list []*woodpecker.Secret
|
||||
switch {
|
||||
case global:
|
||||
list, err = client.GlobalSecretList()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case orgID != -1:
|
||||
list, err = client.OrgSecretList(orgID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
list, err = client.SecretList(repoID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
tmpl, err := template.New("_").Funcs(secretFuncMap).Parse(format)
|
||||
if err != nil {
|
||||
return err
|
47
cli/admin/secret/secret_rm.go
Normal file
47
cli/admin/secret/secret_rm.go
Normal file
@ -0,0 +1,47 @@
|
||||
// Copyright 2023 Woodpecker Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package secret
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/urfave/cli/v3"
|
||||
|
||||
"go.woodpecker-ci.org/woodpecker/v2/cli/internal"
|
||||
)
|
||||
|
||||
var secretDeleteCmd = &cli.Command{
|
||||
Name: "rm",
|
||||
Usage: "remove a secret",
|
||||
ArgsUsage: "[repo-id|repo-full-name]",
|
||||
Action: secretDelete,
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "name",
|
||||
Usage: "secret name",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func secretDelete(ctx context.Context, c *cli.Command) error {
|
||||
secretName := c.String("name")
|
||||
|
||||
client, err := internal.NewClient(ctx, c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return client.GlobalSecretDelete(secretName)
|
||||
}
|
76
cli/admin/secret/secret_set.go
Normal file
76
cli/admin/secret/secret_set.go
Normal file
@ -0,0 +1,76 @@
|
||||
// Copyright 2023 Woodpecker Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package secret
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/urfave/cli/v3"
|
||||
|
||||
"go.woodpecker-ci.org/woodpecker/v2/cli/internal"
|
||||
"go.woodpecker-ci.org/woodpecker/v2/woodpecker-go/woodpecker"
|
||||
)
|
||||
|
||||
var secretUpdateCmd = &cli.Command{
|
||||
Name: "update",
|
||||
Usage: "update a secret",
|
||||
ArgsUsage: "[repo-id|repo-full-name]",
|
||||
Action: secretUpdate,
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "name",
|
||||
Usage: "secret name",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "value",
|
||||
Usage: "secret value",
|
||||
},
|
||||
&cli.StringSliceFlag{
|
||||
Name: "event",
|
||||
Usage: "secret limited to these events",
|
||||
},
|
||||
&cli.StringSliceFlag{
|
||||
Name: "image",
|
||||
Usage: "secret limited to these images",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func secretUpdate(ctx context.Context, c *cli.Command) error {
|
||||
client, err := internal.NewClient(ctx, c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
secret := &woodpecker.Secret{
|
||||
Name: strings.ToLower(c.String("name")),
|
||||
Value: c.String("value"),
|
||||
Images: c.StringSlice("image"),
|
||||
Events: c.StringSlice("event"),
|
||||
}
|
||||
if strings.HasPrefix(secret.Value, "@") {
|
||||
path := strings.TrimPrefix(secret.Value, "@")
|
||||
out, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
secret.Value = string(out)
|
||||
}
|
||||
|
||||
_, err = client.GlobalSecretUpdate(secret)
|
||||
return err
|
||||
}
|
68
cli/admin/secret/secret_show.go
Normal file
68
cli/admin/secret/secret_show.go
Normal file
@ -0,0 +1,68 @@
|
||||
// Copyright 2023 Woodpecker Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package secret
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"os"
|
||||
|
||||
"github.com/urfave/cli/v3"
|
||||
|
||||
"go.woodpecker-ci.org/woodpecker/v2/cli/common"
|
||||
"go.woodpecker-ci.org/woodpecker/v2/cli/internal"
|
||||
)
|
||||
|
||||
var secretShowCmd = &cli.Command{
|
||||
Name: "show",
|
||||
Usage: "show secret information",
|
||||
ArgsUsage: "[repo-id|repo-full-name]",
|
||||
Action: secretShow,
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "name",
|
||||
Usage: "secret name",
|
||||
},
|
||||
common.FormatFlag(tmplSecretList, true),
|
||||
},
|
||||
}
|
||||
|
||||
func secretShow(ctx context.Context, c *cli.Command) error {
|
||||
var (
|
||||
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
|
||||
}
|
||||
|
||||
secret, err := client.GlobalSecret(secretName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tmpl, err := template.New("_").Funcs(secretFuncMap).Parse(format)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return tmpl.Execute(os.Stdout, secret)
|
||||
}
|
@ -23,9 +23,9 @@ var Command = &cli.Command{
|
||||
Name: "user",
|
||||
Usage: "manage users",
|
||||
Commands: []*cli.Command{
|
||||
userListCmd,
|
||||
userInfoCmd,
|
||||
userAddCmd,
|
||||
userListCmd,
|
||||
userRemoveCmd,
|
||||
userShowCmd,
|
||||
},
|
||||
}
|
@ -26,7 +26,7 @@ import (
|
||||
|
||||
var userAddCmd = &cli.Command{
|
||||
Name: "add",
|
||||
Usage: "adds a user",
|
||||
Usage: "add a user",
|
||||
ArgsUsage: "<username>",
|
||||
Action: userAdd,
|
||||
}
|
@ -23,6 +23,7 @@ import (
|
||||
|
||||
"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 userListCmd = &cli.Command{
|
||||
@ -39,7 +40,9 @@ func userList(ctx context.Context, c *cli.Command) error {
|
||||
return err
|
||||
}
|
||||
|
||||
users, err := client.UserList()
|
||||
opt := woodpecker.UserListOptions{}
|
||||
|
||||
users, err := client.UserList(opt)
|
||||
if err != nil || len(users) == 0 {
|
||||
return err
|
||||
}
|
@ -26,15 +26,15 @@ import (
|
||||
"go.woodpecker-ci.org/woodpecker/v2/cli/internal"
|
||||
)
|
||||
|
||||
var userInfoCmd = &cli.Command{
|
||||
Name: "info",
|
||||
Usage: "show user details",
|
||||
var userShowCmd = &cli.Command{
|
||||
Name: "show",
|
||||
Usage: "show user information",
|
||||
ArgsUsage: "<username>",
|
||||
Action: userInfo,
|
||||
Action: userShow,
|
||||
Flags: []cli.Flag{common.FormatFlag(tmplUserInfo)},
|
||||
}
|
||||
|
||||
func userInfo(ctx context.Context, c *cli.Command) error {
|
||||
func userShow(ctx context.Context, c *cli.Command) error {
|
||||
client, err := internal.NewClient(ctx, c)
|
||||
if err != nil {
|
||||
return err
|
@ -48,19 +48,16 @@ var GlobalFlags = append([]cli.Flag{
|
||||
Sources: cli.EnvVars("WOODPECKER_SKIP_VERIFY"),
|
||||
Name: "skip-verify",
|
||||
Usage: "skip ssl verification",
|
||||
Hidden: true,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Sources: cli.EnvVars("SOCKS_PROXY"),
|
||||
Name: "socks-proxy",
|
||||
Usage: "socks proxy address",
|
||||
Hidden: true,
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Sources: cli.EnvVars("SOCKS_PROXY_OFF"),
|
||||
Name: "socks-proxy-off",
|
||||
Usage: "socks proxy ignored",
|
||||
Hidden: true,
|
||||
},
|
||||
}, logger.GlobalLoggerFlags...)
|
||||
|
||||
|
@ -17,9 +17,9 @@ var (
|
||||
cancelWaitForUpdate context.CancelCauseFunc
|
||||
)
|
||||
|
||||
func Before(ctx context.Context, c *cli.Command) error {
|
||||
func Before(ctx context.Context, c *cli.Command) (context.Context, error) {
|
||||
if err := setupGlobalLogger(ctx, c); err != nil {
|
||||
return err
|
||||
return ctx, err
|
||||
}
|
||||
|
||||
go func(context.Context) {
|
||||
@ -35,22 +35,22 @@ func Before(ctx context.Context, c *cli.Command) error {
|
||||
waitForUpdateCheck, cancelWaitForUpdate = context.WithCancelCause(context.Background())
|
||||
defer cancelWaitForUpdate(errors.New("update check finished"))
|
||||
|
||||
log.Debug().Msg("Checking for updates ...")
|
||||
log.Debug().Msg("checking for updates ...")
|
||||
|
||||
newVersion, err := update.CheckForUpdate(waitForUpdateCheck, false) //nolint:contextcheck
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msgf("Failed to check for updates")
|
||||
log.Error().Err(err).Msgf("failed to check for updates")
|
||||
return
|
||||
}
|
||||
|
||||
if newVersion != nil {
|
||||
log.Warn().Msgf("A new version of woodpecker-cli is available: %s. Update by running: %s update", newVersion.Version, c.Root().Name)
|
||||
log.Warn().Msgf("new version of woodpecker-cli is available: %s, update with: %s update", newVersion.Version, c.Root().Name)
|
||||
} else {
|
||||
log.Debug().Msgf("No update required")
|
||||
log.Debug().Msgf("no update required")
|
||||
}
|
||||
}(ctx)
|
||||
|
||||
return config.Load(ctx, c)
|
||||
return ctx, config.Load(ctx, c)
|
||||
}
|
||||
|
||||
func After(_ context.Context, _ *cli.Command) error {
|
||||
@ -59,7 +59,7 @@ func After(_ context.Context, _ *cli.Command) error {
|
||||
case <-waitForUpdateCheck.Done():
|
||||
// When the actual command already finished, we still wait 500ms for the update check to finish
|
||||
case <-time.After(time.Millisecond * 500):
|
||||
log.Debug().Msg("Update check stopped due to timeout")
|
||||
log.Debug().Msg("update check stopped due to timeout")
|
||||
cancelWaitForUpdate(errors.New("update check timeout"))
|
||||
}
|
||||
}
|
||||
|
@ -227,7 +227,7 @@ func execWithAxis(ctx context.Context, c *cli.Command, file, repoPath string, ax
|
||||
Workflow: conf,
|
||||
}})
|
||||
if err != nil {
|
||||
str, err := lint.FormatLintError(file, err)
|
||||
str, err := lint.FormatLintError(file, err, false)
|
||||
fmt.Print(str)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -44,7 +44,7 @@ func Load(ctx context.Context, c *cli.Command) error {
|
||||
}
|
||||
|
||||
if config.ServerURL == "" || config.Token == "" {
|
||||
log.Info().Msg("The woodpecker-cli is not yet set up. Please run `woodpecker-cli setup` or provide the required environment variables / flags.")
|
||||
log.Info().Msg("woodpecker-cli is not set up, run `woodpecker-cli setup` or provide required environment variables/flags")
|
||||
return errors.New("woodpecker-cli is not configured")
|
||||
}
|
||||
|
||||
@ -63,7 +63,7 @@ func Load(ctx context.Context, c *cli.Command) error {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Debug().Any("config", config).Msg("Loaded config")
|
||||
log.Debug().Any("config", config).Msg("loaded config")
|
||||
|
||||
return nil
|
||||
}
|
||||
@ -93,16 +93,16 @@ func Get(_ context.Context, c *cli.Command, _configPath string) (*Config, error)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Debug().Str("configPath", configPath).Msg("Checking for config file")
|
||||
log.Debug().Str("configPath", configPath).Msg("checking for config file")
|
||||
|
||||
content, err := os.ReadFile(configPath)
|
||||
switch {
|
||||
case err != nil && !os.IsNotExist(err):
|
||||
log.Debug().Err(err).Msg("Failed to read the config file")
|
||||
log.Debug().Err(err).Msg("failed to read the config file")
|
||||
return nil, err
|
||||
|
||||
case err != nil && os.IsNotExist(err):
|
||||
log.Debug().Msg("The config file does not exist")
|
||||
log.Debug().Msg("config file does not exist")
|
||||
|
||||
default:
|
||||
configFromFile := &Config{}
|
||||
@ -111,7 +111,7 @@ func Get(_ context.Context, c *cli.Command, _configPath string) (*Config, error)
|
||||
return nil, err
|
||||
}
|
||||
conf.MergeIfNotSet(configFromFile)
|
||||
log.Debug().Msg("Loaded config from file")
|
||||
log.Debug().Msg("loaded config from file")
|
||||
}
|
||||
|
||||
// if server or token are explicitly set, use them
|
||||
@ -123,11 +123,11 @@ func Get(_ context.Context, c *cli.Command, _configPath string) (*Config, error)
|
||||
service := c.Root().Name
|
||||
secret, err := keyring.Get(service, conf.ServerURL)
|
||||
if errors.Is(err, keyring.ErrUnsupportedPlatform) {
|
||||
log.Warn().Msg("Keyring is not supported on this platform")
|
||||
log.Warn().Msg("keyring is not supported on this platform")
|
||||
return conf, nil
|
||||
}
|
||||
if errors.Is(err, keyring.ErrNotFound) {
|
||||
log.Warn().Msg("Token not found in keyring")
|
||||
log.Warn().Msg("token not found in keyring")
|
||||
return conf, nil
|
||||
}
|
||||
conf.Token = secret
|
||||
|
@ -40,14 +40,19 @@ var Command = &cli.Command{
|
||||
&cli.StringSliceFlag{
|
||||
Sources: cli.EnvVars("WOODPECKER_PLUGINS_PRIVILEGED"),
|
||||
Name: "plugins-privileged",
|
||||
Usage: "Allow plugins to run in privileged mode, if environment variable is defined but empty there will be none",
|
||||
Usage: "allow plugins to run in privileged mode, if set empty, there is no",
|
||||
},
|
||||
&cli.StringSliceFlag{
|
||||
Sources: cli.EnvVars("WOODPECKER_PLUGINS_TRUSTED_CLONE"),
|
||||
Name: "plugins-trusted-clone",
|
||||
Usage: "Plugins which are trusted to handle the netrc info in clone steps",
|
||||
Usage: "plugins that are trusted to handle Git credentials in cloning steps",
|
||||
Value: constant.TrustedClonePlugins,
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Sources: cli.EnvVars("WOODPECKER_LINT_STRICT"),
|
||||
Name: "strict",
|
||||
Usage: "treat warnings as errors",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@ -119,7 +124,7 @@ func lintFile(_ context.Context, c *cli.Command, file string) error {
|
||||
linter.WithTrustedClonePlugins(c.StringSlice("plugins-trusted-clone")),
|
||||
).Lint([]*linter.WorkflowConfig{config})
|
||||
if err != nil {
|
||||
str, err := FormatLintError(config.File, err)
|
||||
str, err := FormatLintError(config.File, err, c.Bool("strict"))
|
||||
|
||||
if str != "" {
|
||||
fmt.Print(str)
|
||||
|
@ -10,7 +10,7 @@ import (
|
||||
pipeline_errors "go.woodpecker-ci.org/woodpecker/v2/pipeline/errors"
|
||||
)
|
||||
|
||||
func FormatLintError(file string, err error) (string, error) {
|
||||
func FormatLintError(file string, err error, strict bool) (string, error) {
|
||||
if err == nil {
|
||||
return "", nil
|
||||
}
|
||||
@ -24,7 +24,7 @@ func FormatLintError(file string, err error) (string, error) {
|
||||
for _, err := range linterErrors {
|
||||
line := " "
|
||||
|
||||
if err.IsWarning {
|
||||
if !strict && err.IsWarning {
|
||||
line = fmt.Sprintf("%s ⚠️ ", line)
|
||||
amountWarnings++
|
||||
} else {
|
||||
|
@ -18,6 +18,7 @@ import (
|
||||
"github.com/urfave/cli/v3"
|
||||
|
||||
"go.woodpecker-ci.org/woodpecker/v2/cli/org/registry"
|
||||
"go.woodpecker-ci.org/woodpecker/v2/cli/org/secret"
|
||||
)
|
||||
|
||||
// Command exports the org command set.
|
||||
@ -26,5 +27,6 @@ var Command = &cli.Command{
|
||||
Usage: "manage organizations",
|
||||
Commands: []*cli.Command{
|
||||
registry.Command,
|
||||
secret.Command,
|
||||
},
|
||||
}
|
||||
|
@ -29,9 +29,9 @@ var Command = &cli.Command{
|
||||
Commands: []*cli.Command{
|
||||
registryCreateCmd,
|
||||
registryDeleteCmd,
|
||||
registryUpdateCmd,
|
||||
registryInfoCmd,
|
||||
registryListCmd,
|
||||
registryShowCmd,
|
||||
registryUpdateCmd,
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -28,7 +28,7 @@ import (
|
||||
|
||||
var registryCreateCmd = &cli.Command{
|
||||
Name: "add",
|
||||
Usage: "adds a registry",
|
||||
Usage: "add a registry",
|
||||
ArgsUsage: "[org-id|org-full-name]",
|
||||
Action: registryCreate,
|
||||
Flags: []cli.Flag{
|
||||
|
@ -23,6 +23,7 @@ import (
|
||||
|
||||
"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 registryListCmd = &cli.Command{
|
||||
@ -49,7 +50,9 @@ func registryList(ctx context.Context, c *cli.Command) error {
|
||||
return err
|
||||
}
|
||||
|
||||
list, err := client.OrgRegistryList(orgID)
|
||||
opt := woodpecker.RegistryListOptions{}
|
||||
|
||||
list, err := client.OrgRegistryList(orgID, opt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -25,11 +25,11 @@ import (
|
||||
"go.woodpecker-ci.org/woodpecker/v2/cli/internal"
|
||||
)
|
||||
|
||||
var registryInfoCmd = &cli.Command{
|
||||
Name: "info",
|
||||
Usage: "display registry info",
|
||||
var registryShowCmd = &cli.Command{
|
||||
Name: "show",
|
||||
Usage: "show registry information",
|
||||
ArgsUsage: "[org-id|org-full-name]",
|
||||
Action: registryInfo,
|
||||
Action: registryShow,
|
||||
Flags: []cli.Flag{
|
||||
common.OrgFlag,
|
||||
&cli.StringFlag{
|
||||
@ -41,7 +41,7 @@ var registryInfoCmd = &cli.Command{
|
||||
},
|
||||
}
|
||||
|
||||
func registryInfo(ctx context.Context, c *cli.Command) error {
|
||||
func registryShow(ctx context.Context, c *cli.Command) error {
|
||||
var (
|
||||
hostname = c.String("hostname")
|
||||
format = c.String("format") + "\n"
|
60
cli/org/secret/secret.go
Normal file
60
cli/org/secret/secret.go
Normal file
@ -0,0 +1,60 @@
|
||||
// Copyright 2023 Woodpecker Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package secret
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/urfave/cli/v3"
|
||||
|
||||
"go.woodpecker-ci.org/woodpecker/v2/woodpecker-go/woodpecker"
|
||||
)
|
||||
|
||||
// Command exports the secret command.
|
||||
var Command = &cli.Command{
|
||||
Name: "secret",
|
||||
Usage: "manage secrets",
|
||||
Commands: []*cli.Command{
|
||||
secretCreateCmd,
|
||||
secretDeleteCmd,
|
||||
secretListCmd,
|
||||
secretShowCmd,
|
||||
secretUpdateCmd,
|
||||
},
|
||||
}
|
||||
|
||||
func parseTargetArgs(client woodpecker.Client, c *cli.Command) (orgID int64, err error) {
|
||||
orgIDOrName := c.String("organization")
|
||||
if orgIDOrName == "" {
|
||||
orgIDOrName = c.Args().First()
|
||||
}
|
||||
|
||||
if orgIDOrName == "" {
|
||||
if err := cli.ShowSubcommandHelp(c); err != nil {
|
||||
return -1, err
|
||||
}
|
||||
}
|
||||
|
||||
if orgID, err := strconv.ParseInt(orgIDOrName, 10, 64); err == nil {
|
||||
return orgID, nil
|
||||
}
|
||||
|
||||
org, err := client.OrgLookup(orgIDOrName)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
|
||||
return org.ID, nil
|
||||
}
|
@ -28,16 +28,11 @@ import (
|
||||
|
||||
var secretCreateCmd = &cli.Command{
|
||||
Name: "add",
|
||||
Usage: "adds a secret",
|
||||
Usage: "add a secret",
|
||||
ArgsUsage: "[repo-id|repo-full-name]",
|
||||
Action: secretCreate,
|
||||
Flags: []cli.Flag{
|
||||
&cli.BoolFlag{
|
||||
Name: "global",
|
||||
Usage: "global secret",
|
||||
},
|
||||
common.OrgFlag,
|
||||
common.RepoFlag,
|
||||
&cli.StringFlag{
|
||||
Name: "name",
|
||||
Usage: "secret name",
|
||||
@ -81,22 +76,12 @@ func secretCreate(ctx context.Context, c *cli.Command) error {
|
||||
secret.Value = string(out)
|
||||
}
|
||||
|
||||
global, orgID, repoID, err := parseTargetArgs(client, c)
|
||||
orgID, err := parseTargetArgs(client, c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if global {
|
||||
_, err = client.GlobalSecretCreate(secret)
|
||||
return err
|
||||
}
|
||||
|
||||
if orgID != -1 {
|
||||
_, err = client.OrgSecretCreate(orgID, secret)
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = client.SecretCreate(repoID, secret)
|
||||
_, err = client.OrgSecretCreate(orgID, secret)
|
||||
return err
|
||||
}
|
||||
|
87
cli/org/secret/secret_list.go
Normal file
87
cli/org/secret/secret_list.go
Normal file
@ -0,0 +1,87 @@
|
||||
// Copyright 2023 Woodpecker Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package secret
|
||||
|
||||
import (
|
||||
"context"
|
||||
"html/template"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/urfave/cli/v3"
|
||||
|
||||
"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 secretListCmd = &cli.Command{
|
||||
Name: "ls",
|
||||
Usage: "list secrets",
|
||||
ArgsUsage: "[repo-id|repo-full-name]",
|
||||
Action: secretList,
|
||||
Flags: []cli.Flag{
|
||||
common.OrgFlag,
|
||||
common.FormatFlag(tmplSecretList, true),
|
||||
},
|
||||
}
|
||||
|
||||
func secretList(ctx context.Context, c *cli.Command) error {
|
||||
format := c.String("format") + "\n"
|
||||
|
||||
client, err := internal.NewClient(ctx, c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
orgID, err := parseTargetArgs(client, c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
opt := woodpecker.SecretListOptions{}
|
||||
|
||||
list, err := client.OrgSecretList(orgID, opt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tmpl, err := template.New("_").Funcs(secretFuncMap).Parse(format)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, secret := range list {
|
||||
if err := tmpl.Execute(os.Stdout, secret); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Template for secret list items.
|
||||
var tmplSecretList = "\x1b[33m{{ .Name }} \x1b[0m" + `
|
||||
Events: {{ list .Events }}
|
||||
{{- if .Images }}
|
||||
Images: {{ list .Images }}
|
||||
{{- else }}
|
||||
Images: <any>
|
||||
{{- end }}
|
||||
`
|
||||
|
||||
var secretFuncMap = template.FuncMap{
|
||||
"list": func(s []string) string {
|
||||
return strings.Join(s, ", ")
|
||||
},
|
||||
}
|
54
cli/org/secret/secret_rm.go
Normal file
54
cli/org/secret/secret_rm.go
Normal file
@ -0,0 +1,54 @@
|
||||
// Copyright 2023 Woodpecker Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package secret
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/urfave/cli/v3"
|
||||
|
||||
"go.woodpecker-ci.org/woodpecker/v2/cli/common"
|
||||
"go.woodpecker-ci.org/woodpecker/v2/cli/internal"
|
||||
)
|
||||
|
||||
var secretDeleteCmd = &cli.Command{
|
||||
Name: "rm",
|
||||
Usage: "remove a secret",
|
||||
ArgsUsage: "[repo-id|repo-full-name]",
|
||||
Action: secretDelete,
|
||||
Flags: []cli.Flag{
|
||||
common.OrgFlag,
|
||||
&cli.StringFlag{
|
||||
Name: "name",
|
||||
Usage: "secret name",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func secretDelete(ctx context.Context, c *cli.Command) error {
|
||||
secretName := c.String("name")
|
||||
|
||||
client, err := internal.NewClient(ctx, c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
orgID, err := parseTargetArgs(client, c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return client.OrgSecretDelete(orgID, secretName)
|
||||
}
|
83
cli/org/secret/secret_set.go
Normal file
83
cli/org/secret/secret_set.go
Normal file
@ -0,0 +1,83 @@
|
||||
// Copyright 2023 Woodpecker Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package secret
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/urfave/cli/v3"
|
||||
|
||||
"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 secretUpdateCmd = &cli.Command{
|
||||
Name: "update",
|
||||
Usage: "update a secret",
|
||||
ArgsUsage: "[repo-id|repo-full-name]",
|
||||
Action: secretUpdate,
|
||||
Flags: []cli.Flag{
|
||||
common.OrgFlag,
|
||||
&cli.StringFlag{
|
||||
Name: "name",
|
||||
Usage: "secret name",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "value",
|
||||
Usage: "secret value",
|
||||
},
|
||||
&cli.StringSliceFlag{
|
||||
Name: "event",
|
||||
Usage: "limit secret to these event",
|
||||
},
|
||||
&cli.StringSliceFlag{
|
||||
Name: "image",
|
||||
Usage: "limit secret to these image",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func secretUpdate(ctx context.Context, c *cli.Command) error {
|
||||
client, err := internal.NewClient(ctx, c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
secret := &woodpecker.Secret{
|
||||
Name: strings.ToLower(c.String("name")),
|
||||
Value: c.String("value"),
|
||||
Images: c.StringSlice("image"),
|
||||
Events: c.StringSlice("event"),
|
||||
}
|
||||
if strings.HasPrefix(secret.Value, "@") {
|
||||
path := strings.TrimPrefix(secret.Value, "@")
|
||||
out, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
secret.Value = string(out)
|
||||
}
|
||||
|
||||
orgID, err := parseTargetArgs(client, c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = client.OrgSecretUpdate(orgID, secret)
|
||||
return err
|
||||
}
|
74
cli/org/secret/secret_show.go
Normal file
74
cli/org/secret/secret_show.go
Normal file
@ -0,0 +1,74 @@
|
||||
// Copyright 2023 Woodpecker Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package secret
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"os"
|
||||
|
||||
"github.com/urfave/cli/v3"
|
||||
|
||||
"go.woodpecker-ci.org/woodpecker/v2/cli/common"
|
||||
"go.woodpecker-ci.org/woodpecker/v2/cli/internal"
|
||||
)
|
||||
|
||||
var secretShowCmd = &cli.Command{
|
||||
Name: "show",
|
||||
Usage: "show secret information",
|
||||
ArgsUsage: "[repo-id|repo-full-name]",
|
||||
Action: secretShow,
|
||||
Flags: []cli.Flag{
|
||||
common.OrgFlag,
|
||||
&cli.StringFlag{
|
||||
Name: "name",
|
||||
Usage: "secret name",
|
||||
},
|
||||
common.FormatFlag(tmplSecretList, true),
|
||||
},
|
||||
}
|
||||
|
||||
func secretShow(ctx context.Context, c *cli.Command) error {
|
||||
var (
|
||||
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
|
||||
}
|
||||
|
||||
orgID, err := parseTargetArgs(client, c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
secret, err := client.OrgSecret(orgID, secretName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tmpl, err := template.New("_").Funcs(secretFuncMap).Parse(format)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return tmpl.Execute(os.Stdout, secret)
|
||||
}
|
@ -147,12 +147,12 @@ func (o *Table) Write(columns []string, obj any) error {
|
||||
colName := strings.ToLower(col)
|
||||
if alias, ok := o.fieldAlias[colName]; ok {
|
||||
if fn, ok := o.fieldMapping[alias]; ok {
|
||||
out = append(out, fn(obj))
|
||||
out = append(out, sanitizeString(fn(obj)))
|
||||
continue
|
||||
}
|
||||
}
|
||||
if fn, ok := o.fieldMapping[colName]; ok {
|
||||
out = append(out, fn(obj))
|
||||
out = append(out, sanitizeString(fn(obj)))
|
||||
continue
|
||||
}
|
||||
if value, ok := dataL[strings.ReplaceAll(colName, "_", "")]; ok {
|
||||
@ -165,10 +165,10 @@ func (o *Table) Write(columns []string, obj any) error {
|
||||
continue
|
||||
}
|
||||
if s, ok := value.(string); ok {
|
||||
out = append(out, NA(s))
|
||||
out = append(out, NA(sanitizeString(s)))
|
||||
continue
|
||||
}
|
||||
out = append(out, fmt.Sprintf("%v", value))
|
||||
out = append(out, sanitizeString(value))
|
||||
}
|
||||
}
|
||||
_, _ = fmt.Fprintln(o.w, strings.Join(out, "\t"))
|
||||
@ -201,3 +201,9 @@ func fieldName(name string) string {
|
||||
}
|
||||
return string(out)
|
||||
}
|
||||
|
||||
func sanitizeString(value any) string {
|
||||
str := fmt.Sprintf("%v", value)
|
||||
replacer := strings.NewReplacer("\n", " ", "\r", " ")
|
||||
return strings.TrimSpace(replacer.Replace(str))
|
||||
}
|
||||
|
@ -74,5 +74,5 @@ func pipelineCreate(ctx context.Context, c *cli.Command) error {
|
||||
return err
|
||||
}
|
||||
|
||||
return pipelineOutput(c, []woodpecker.Pipeline{*pipeline})
|
||||
return pipelineOutput(c, []*woodpecker.Pipeline{pipeline})
|
||||
}
|
||||
|
@ -53,7 +53,7 @@ var Command = &cli.Command{
|
||||
&cli.StringSliceFlag{
|
||||
Name: "param",
|
||||
Aliases: []string{"p"},
|
||||
Usage: "custom parameters to be injected into the step environment. Format: KEY=value",
|
||||
Usage: "custom parameters to inject into the step environment. Format: KEY=value",
|
||||
},
|
||||
},
|
||||
}
|
||||
@ -80,14 +80,14 @@ func deploy(ctx context.Context, c *cli.Command) error {
|
||||
return err
|
||||
}
|
||||
|
||||
branch = repo.DefaultBranch
|
||||
branch = repo.Branch
|
||||
}
|
||||
|
||||
pipelineArg := c.Args().Get(1)
|
||||
var number int64
|
||||
if pipelineArg == "last" {
|
||||
// Fetch the pipeline number from the last pipeline
|
||||
pipelines, err := client.PipelineList(repoID)
|
||||
pipelines, err := client.PipelineList(repoID, woodpecker.PipelineListOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -121,9 +121,12 @@ func deploy(ctx context.Context, c *cli.Command) error {
|
||||
return fmt.Errorf("please specify the target environment (i.e. production)")
|
||||
}
|
||||
|
||||
params := internal.ParseKeyPair(c.StringSlice("param"))
|
||||
opt := woodpecker.DeployOptions{
|
||||
DeployTo: env,
|
||||
Params: internal.ParseKeyPair(c.StringSlice("param")),
|
||||
}
|
||||
|
||||
deploy, err := client.Deploy(repoID, number, env, params)
|
||||
deploy, err := client.Deploy(repoID, number, opt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
@ -48,7 +48,7 @@ func pipelineKill(ctx context.Context, c *cli.Command) (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
err = client.PipelineKill(repoID, number)
|
||||
err = client.PipelineDelete(repoID, number)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -26,7 +26,7 @@ import (
|
||||
|
||||
var pipelineLastCmd = &cli.Command{
|
||||
Name: "last",
|
||||
Usage: "show latest pipeline details",
|
||||
Usage: "show latest pipeline information",
|
||||
ArgsUsage: "<repo-id|repo-full-name>",
|
||||
Action: pipelineLast,
|
||||
Flags: append(common.OutputFlags("table"), []cli.Flag{
|
||||
@ -49,10 +49,14 @@ func pipelineLast(ctx context.Context, c *cli.Command) error {
|
||||
return err
|
||||
}
|
||||
|
||||
pipeline, err := client.PipelineLast(repoID, c.String("branch"))
|
||||
opt := woodpecker.PipelineLastOptions{
|
||||
Branch: c.String("branch"),
|
||||
}
|
||||
|
||||
pipeline, err := client.PipelineLast(repoID, opt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return pipelineOutput(c, []woodpecker.Pipeline{*pipeline})
|
||||
return pipelineOutput(c, []*woodpecker.Pipeline{pipeline})
|
||||
}
|
||||
|
@ -16,39 +16,61 @@ package pipeline
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/urfave/cli/v3"
|
||||
|
||||
"go.woodpecker-ci.org/woodpecker/v2/cli/common"
|
||||
"go.woodpecker-ci.org/woodpecker/v2/cli/internal"
|
||||
shared_utils "go.woodpecker-ci.org/woodpecker/v2/shared/utils"
|
||||
"go.woodpecker-ci.org/woodpecker/v2/woodpecker-go/woodpecker"
|
||||
)
|
||||
|
||||
//nolint:mnd
|
||||
var pipelineListCmd = &cli.Command{
|
||||
Name: "ls",
|
||||
Usage: "show pipeline history",
|
||||
ArgsUsage: "<repo-id|repo-full-name>",
|
||||
Action: List,
|
||||
Flags: append(common.OutputFlags("table"), []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "branch",
|
||||
Usage: "branch filter",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "event",
|
||||
Usage: "event filter",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "status",
|
||||
Usage: "status filter",
|
||||
},
|
||||
&cli.IntFlag{
|
||||
Name: "limit",
|
||||
Usage: "limit the list size",
|
||||
Value: 25,
|
||||
},
|
||||
}...),
|
||||
func buildPipelineListCmd() *cli.Command {
|
||||
return &cli.Command{
|
||||
Name: "ls",
|
||||
Usage: "show pipeline history",
|
||||
ArgsUsage: "<repo-id|repo-full-name>",
|
||||
Action: List,
|
||||
Flags: append(common.OutputFlags("table"), []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "branch",
|
||||
Usage: "branch filter",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "event",
|
||||
Usage: "event filter",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "status",
|
||||
Usage: "status filter",
|
||||
},
|
||||
&cli.IntFlag{
|
||||
Name: "limit",
|
||||
Usage: "limit the list size",
|
||||
Value: 25,
|
||||
},
|
||||
&cli.TimestampFlag{
|
||||
Name: "before",
|
||||
Usage: "only return pipelines before this date (RFC3339)",
|
||||
Config: cli.TimestampConfig{
|
||||
Layouts: []string{
|
||||
time.RFC3339,
|
||||
},
|
||||
},
|
||||
},
|
||||
&cli.TimestampFlag{
|
||||
Name: "after",
|
||||
Usage: "only return pipelines after this date (RFC3339)",
|
||||
Config: cli.TimestampConfig{
|
||||
Layouts: []string{
|
||||
time.RFC3339,
|
||||
},
|
||||
},
|
||||
},
|
||||
}...),
|
||||
}
|
||||
}
|
||||
|
||||
func List(ctx context.Context, c *cli.Command) error {
|
||||
@ -56,25 +78,27 @@ func List(ctx context.Context, c *cli.Command) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
resources, err := pipelineList(ctx, c, client)
|
||||
pipelines, err := pipelineList(c, client)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return pipelineOutput(c, resources)
|
||||
return pipelineOutput(c, pipelines)
|
||||
}
|
||||
|
||||
func pipelineList(_ context.Context, c *cli.Command, client woodpecker.Client) ([]woodpecker.Pipeline, error) {
|
||||
resources := make([]woodpecker.Pipeline, 0)
|
||||
|
||||
func pipelineList(c *cli.Command, client woodpecker.Client) ([]*woodpecker.Pipeline, error) {
|
||||
repoIDOrFullName := c.Args().First()
|
||||
repoID, err := internal.ParseRepo(client, repoIDOrFullName)
|
||||
if err != nil {
|
||||
return resources, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pipelines, err := client.PipelineList(repoID)
|
||||
if err != nil {
|
||||
return resources, err
|
||||
opt := woodpecker.PipelineListOptions{}
|
||||
|
||||
if before := c.Timestamp("before"); !before.IsZero() {
|
||||
opt.Before = before
|
||||
}
|
||||
if after := c.Timestamp("after"); !after.IsZero() {
|
||||
opt.After = after
|
||||
}
|
||||
|
||||
branch := c.String("branch")
|
||||
@ -82,23 +106,23 @@ func pipelineList(_ context.Context, c *cli.Command, client woodpecker.Client) (
|
||||
status := c.String("status")
|
||||
limit := int(c.Int("limit"))
|
||||
|
||||
var count int
|
||||
for _, pipeline := range pipelines {
|
||||
if count >= limit {
|
||||
break
|
||||
}
|
||||
if branch != "" && pipeline.Branch != branch {
|
||||
continue
|
||||
}
|
||||
if event != "" && pipeline.Event != event {
|
||||
continue
|
||||
}
|
||||
if status != "" && pipeline.Status != status {
|
||||
continue
|
||||
}
|
||||
resources = append(resources, *pipeline)
|
||||
count++
|
||||
pipelines, err := shared_utils.Paginate(func(page int) ([]*woodpecker.Pipeline, error) {
|
||||
return client.PipelineList(repoID,
|
||||
woodpecker.PipelineListOptions{
|
||||
ListOptions: woodpecker.ListOptions{
|
||||
Page: page,
|
||||
},
|
||||
Before: opt.Before,
|
||||
After: opt.After,
|
||||
Branch: branch,
|
||||
Events: []string{event},
|
||||
Status: status,
|
||||
},
|
||||
)
|
||||
}, limit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resources, nil
|
||||
return pipelines, nil
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ func TestPipelineList(t *testing.T) {
|
||||
pipelines []*woodpecker.Pipeline
|
||||
pipelineErr error
|
||||
args []string
|
||||
expected []woodpecker.Pipeline
|
||||
expected []*woodpecker.Pipeline
|
||||
wantErr error
|
||||
}{
|
||||
{
|
||||
@ -34,53 +34,12 @@ func TestPipelineList(t *testing.T) {
|
||||
{ID: 3, Branch: "main", Event: "push", Status: "failure"},
|
||||
},
|
||||
args: []string{"ls", "repo/name"},
|
||||
expected: []woodpecker.Pipeline{
|
||||
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,
|
||||
@ -90,7 +49,7 @@ func TestPipelineList(t *testing.T) {
|
||||
{ID: 3, Branch: "main", Event: "push", Status: "failure"},
|
||||
},
|
||||
args: []string{"ls", "--limit", "2", "repo/name"},
|
||||
expected: []woodpecker.Pipeline{
|
||||
expected: []*woodpecker.Pipeline{
|
||||
{ID: 1, Branch: "main", Event: "push", Status: "success"},
|
||||
{ID: 2, Branch: "develop", Event: "pull_request", Status: "running"},
|
||||
},
|
||||
@ -107,13 +66,21 @@ func TestPipelineList(t *testing.T) {
|
||||
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("PipelineList", mock.Anything, mock.Anything).Return(func(_ int64, opt woodpecker.PipelineListOptions) ([]*woodpecker.Pipeline, error) {
|
||||
if tt.pipelineErr != nil {
|
||||
return nil, tt.pipelineErr
|
||||
}
|
||||
if opt.Page == 1 {
|
||||
return tt.pipelines, nil
|
||||
}
|
||||
return []*woodpecker.Pipeline{}, nil
|
||||
}).Maybe()
|
||||
mockClient.On("RepoLookup", mock.Anything).Return(&woodpecker.Repo{ID: tt.repoID}, nil)
|
||||
|
||||
command := pipelineListCmd
|
||||
command := buildPipelineListCmd()
|
||||
command.Writer = io.Discard
|
||||
command.Action = func(ctx context.Context, c *cli.Command) error {
|
||||
pipelines, err := pipelineList(ctx, c, mockClient)
|
||||
command.Action = func(_ context.Context, c *cli.Command) error {
|
||||
pipelines, err := pipelineList(c, mockClient)
|
||||
if tt.wantErr != nil {
|
||||
assert.EqualError(t, err, tt.wantErr.Error())
|
||||
return nil
|
||||
|
@ -24,5 +24,6 @@ var Command = &cli.Command{
|
||||
Usage: "manage logs",
|
||||
Commands: []*cli.Command{
|
||||
logPurgeCmd,
|
||||
logShowCmd,
|
||||
},
|
||||
}
|
@ -12,7 +12,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package pipeline
|
||||
package log
|
||||
|
||||
import (
|
||||
"context"
|
||||
@ -27,14 +27,14 @@ import (
|
||||
"go.woodpecker-ci.org/woodpecker/v2/woodpecker-go/woodpecker"
|
||||
)
|
||||
|
||||
var pipelineLogsCmd = &cli.Command{
|
||||
Name: "logs",
|
||||
var logShowCmd = &cli.Command{
|
||||
Name: "show",
|
||||
Usage: "show pipeline logs",
|
||||
ArgsUsage: "<repo-id|repo-full-name> <pipeline> [step-number|step-name]",
|
||||
Action: pipelineLogs,
|
||||
Action: logShow,
|
||||
}
|
||||
|
||||
func pipelineLogs(ctx context.Context, c *cli.Command) error {
|
||||
func logShow(ctx context.Context, c *cli.Command) error {
|
||||
repoIDOrFullName := c.Args().First()
|
||||
client, err := internal.NewClient(ctx, c)
|
||||
if err != nil {
|
||||
@ -59,17 +59,17 @@ func pipelineLogs(ctx context.Context, c *cli.Command) error {
|
||||
|
||||
stepArg := c.Args().Get(2) //nolint:mnd
|
||||
if len(stepArg) == 0 {
|
||||
return showPipelineLog(client, repoID, number)
|
||||
return pipelineLog(client, repoID, number)
|
||||
}
|
||||
|
||||
step, err := internal.ParseStep(client, repoID, number, stepArg)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid step '%s': %w", stepArg, err)
|
||||
}
|
||||
return showStepLog(client, repoID, number, step)
|
||||
return stepLog(client, repoID, number, step)
|
||||
}
|
||||
|
||||
func showPipelineLog(client woodpecker.Client, repoID, number int64) error {
|
||||
func pipelineLog(client woodpecker.Client, repoID, number int64) error {
|
||||
pipeline, err := client.Pipeline(repoID, number)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -85,7 +85,7 @@ func showPipelineLog(client woodpecker.Client, repoID, number int64) error {
|
||||
if err := tmpl.Execute(os.Stdout, map[string]any{"workflow": workflow, "step": step}); err != nil {
|
||||
return err
|
||||
}
|
||||
err := showStepLog(client, repoID, number, step.ID)
|
||||
err := stepLog(client, repoID, number, step.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -95,7 +95,7 @@ func showPipelineLog(client woodpecker.Client, repoID, number int64) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func showStepLog(client woodpecker.Client, repoID, number, step int64) error {
|
||||
func stepLog(client woodpecker.Client, repoID, number, step int64) error {
|
||||
logs, err := client.StepLogEntries(repoID, number, step)
|
||||
if err != nil {
|
||||
return err
|
@ -23,6 +23,8 @@ import (
|
||||
"github.com/urfave/cli/v3"
|
||||
|
||||
"go.woodpecker-ci.org/woodpecker/v2/cli/output"
|
||||
"go.woodpecker-ci.org/woodpecker/v2/cli/pipeline/deploy"
|
||||
"go.woodpecker-ci.org/woodpecker/v2/cli/pipeline/log"
|
||||
"go.woodpecker-ci.org/woodpecker/v2/woodpecker-go/woodpecker"
|
||||
)
|
||||
|
||||
@ -31,22 +33,24 @@ var Command = &cli.Command{
|
||||
Name: "pipeline",
|
||||
Usage: "manage pipelines",
|
||||
Commands: []*cli.Command{
|
||||
pipelineListCmd,
|
||||
pipelineLastCmd,
|
||||
pipelineLogsCmd,
|
||||
pipelineInfoCmd,
|
||||
pipelineStopCmd,
|
||||
pipelineStartCmd,
|
||||
pipelineApproveCmd,
|
||||
pipelineDeclineCmd,
|
||||
pipelineQueueCmd,
|
||||
pipelineKillCmd,
|
||||
pipelinePsCmd,
|
||||
pipelineCreateCmd,
|
||||
pipelineDeclineCmd,
|
||||
deploy.Command,
|
||||
pipelineKillCmd,
|
||||
pipelineLastCmd,
|
||||
buildPipelineListCmd(),
|
||||
log.Command,
|
||||
pipelinePsCmd,
|
||||
pipelinePurgeCmd,
|
||||
pipelineQueueCmd,
|
||||
pipelineShowCmd,
|
||||
pipelineStartCmd,
|
||||
pipelineStopCmd,
|
||||
},
|
||||
}
|
||||
|
||||
func pipelineOutput(c *cli.Command, resources []woodpecker.Pipeline, fd ...io.Writer) error {
|
||||
func pipelineOutput(c *cli.Command, pipelines []*woodpecker.Pipeline, fd ...io.Writer) error {
|
||||
outFmt, outOpt := output.ParseOutputOptions(c.String("output"))
|
||||
noHeader := c.Bool("output-no-headers")
|
||||
|
||||
@ -70,7 +74,7 @@ func pipelineOutput(c *cli.Command, resources []woodpecker.Pipeline, fd ...io.Wr
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tmpl.Execute(out, resources); err != nil {
|
||||
if err := tmpl.Execute(out, pipelines); err != nil {
|
||||
return err
|
||||
}
|
||||
case "table":
|
||||
@ -85,7 +89,7 @@ func pipelineOutput(c *cli.Command, resources []woodpecker.Pipeline, fd ...io.Wr
|
||||
if !noHeader {
|
||||
table.WriteHeader(cols)
|
||||
}
|
||||
for _, resource := range resources {
|
||||
for _, resource := range pipelines {
|
||||
if err := table.Write(cols, resource); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ func TestPipelineOutput(t *testing.T) {
|
||||
{
|
||||
name: "table output with default columns",
|
||||
args: []string{},
|
||||
expected: "NUMBER STATUS EVENT BRANCH MESSAGE AUTHOR\n1 success push main message John Doe\n",
|
||||
expected: "NUMBER STATUS EVENT BRANCH MESSAGE AUTHOR\n1 success push main message multiline John Doe\n",
|
||||
},
|
||||
{
|
||||
name: "table output with custom columns",
|
||||
@ -33,7 +33,7 @@ func TestPipelineOutput(t *testing.T) {
|
||||
{
|
||||
name: "table output with no header",
|
||||
args: []string{"output", "--output-no-headers"},
|
||||
expected: "1 success push main message John Doe\n",
|
||||
expected: "1 success push main message multiline John Doe\n",
|
||||
},
|
||||
{
|
||||
name: "go-template output",
|
||||
@ -47,14 +47,14 @@ func TestPipelineOutput(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
pipelines := []woodpecker.Pipeline{
|
||||
pipelines := []*woodpecker.Pipeline{
|
||||
{
|
||||
Number: 1,
|
||||
Status: "success",
|
||||
Event: "push",
|
||||
Branch: "main",
|
||||
Message: "message",
|
||||
Author: "John Doe",
|
||||
Message: "message\nmultiline",
|
||||
Author: "John Doe\n",
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -25,6 +25,7 @@ import (
|
||||
|
||||
"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 pipelinePsCmd = &cli.Command{
|
||||
@ -51,7 +52,7 @@ func pipelinePs(ctx context.Context, c *cli.Command) error {
|
||||
|
||||
if pipelineArg == "last" || len(pipelineArg) == 0 {
|
||||
// Fetch the pipeline number from the last pipeline
|
||||
pipeline, err := client.PipelineLast(repoID, "")
|
||||
pipeline, err := client.PipelineLast(repoID, woodpecker.PipelineLastOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
157
cli/pipeline/purge.go
Normal file
157
cli/pipeline/purge.go
Normal file
@ -0,0 +1,157 @@
|
||||
// Copyright 2022 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 pipeline
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/urfave/cli/v3"
|
||||
|
||||
"go.woodpecker-ci.org/woodpecker/v2/cli/internal"
|
||||
shared_utils "go.woodpecker-ci.org/woodpecker/v2/shared/utils"
|
||||
"go.woodpecker-ci.org/woodpecker/v2/woodpecker-go/woodpecker"
|
||||
)
|
||||
|
||||
//nolint:mnd
|
||||
var pipelinePurgeCmd = &cli.Command{
|
||||
Name: "purge",
|
||||
Usage: "purge pipelines",
|
||||
ArgsUsage: "<repo-id|repo-full-name>",
|
||||
Action: Purge,
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "older-than",
|
||||
Usage: "remove pipelines older than the specified time limit",
|
||||
Required: true,
|
||||
},
|
||||
&cli.IntFlag{
|
||||
Name: "keep-min",
|
||||
Usage: "minimum number of pipelines to keep",
|
||||
Value: 10,
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "dry-run",
|
||||
Usage: "disable non-read api calls",
|
||||
Value: false,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func Purge(ctx context.Context, c *cli.Command) error {
|
||||
client, err := internal.NewClient(ctx, c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return pipelinePurge(c, client)
|
||||
}
|
||||
|
||||
func pipelinePurge(c *cli.Command, client woodpecker.Client) (err error) {
|
||||
repoIDOrFullName := c.Args().First()
|
||||
if len(repoIDOrFullName) == 0 {
|
||||
return fmt.Errorf("missing required argument repo-id / repo-full-name")
|
||||
}
|
||||
repoID, err := internal.ParseRepo(client, repoIDOrFullName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid repo '%s': %w", repoIDOrFullName, err)
|
||||
}
|
||||
|
||||
olderThan := c.String("older-than")
|
||||
keepMin := c.Int("keep-min")
|
||||
dryRun := c.Bool("dry-run")
|
||||
|
||||
duration, err := time.ParseDuration(olderThan)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var pipelinesKeep []*woodpecker.Pipeline
|
||||
|
||||
if keepMin > 0 {
|
||||
pipelinesKeep, err = fetchPipelinesToKeep(client, repoID, int(keepMin))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
pipelines, err := fetchPipelines(client, repoID, duration)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Create a map of pipeline IDs to keep
|
||||
keepMap := make(map[int64]struct{})
|
||||
for _, p := range pipelinesKeep {
|
||||
keepMap[p.ID] = struct{}{}
|
||||
}
|
||||
|
||||
// Filter pipelines to only include those not in keepMap
|
||||
var pipelinesToPurge []*woodpecker.Pipeline
|
||||
for _, p := range pipelines {
|
||||
if _, exists := keepMap[p.ID]; !exists {
|
||||
pipelinesToPurge = append(pipelinesToPurge, p)
|
||||
}
|
||||
}
|
||||
|
||||
msgPrefix := ""
|
||||
if dryRun {
|
||||
msgPrefix = "DRY-RUN: "
|
||||
}
|
||||
|
||||
for i, p := range pipelinesToPurge {
|
||||
// cspell:words spurge
|
||||
log.Debug().Msgf("%spurge %v/%v pipelines from repo '%v'", msgPrefix, i+1, len(pipelinesToPurge), repoIDOrFullName)
|
||||
if dryRun {
|
||||
continue
|
||||
}
|
||||
|
||||
err := client.PipelineDelete(repoID, p.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func fetchPipelinesToKeep(client woodpecker.Client, repoID int64, keepMin int) ([]*woodpecker.Pipeline, error) {
|
||||
if keepMin <= 0 {
|
||||
return nil, nil
|
||||
}
|
||||
return shared_utils.Paginate(func(page int) ([]*woodpecker.Pipeline, error) {
|
||||
return client.PipelineList(repoID,
|
||||
woodpecker.PipelineListOptions{
|
||||
ListOptions: woodpecker.ListOptions{
|
||||
Page: page,
|
||||
},
|
||||
},
|
||||
)
|
||||
}, keepMin)
|
||||
}
|
||||
|
||||
func fetchPipelines(client woodpecker.Client, repoID int64, duration time.Duration) ([]*woodpecker.Pipeline, error) {
|
||||
return shared_utils.Paginate(func(page int) ([]*woodpecker.Pipeline, error) {
|
||||
return client.PipelineList(repoID,
|
||||
woodpecker.PipelineListOptions{
|
||||
ListOptions: woodpecker.ListOptions{
|
||||
Page: page,
|
||||
},
|
||||
After: time.Now().Add(-duration),
|
||||
},
|
||||
)
|
||||
}, -1)
|
||||
}
|
105
cli/pipeline/purge_test.go
Normal file
105
cli/pipeline/purge_test.go
Normal file
@ -0,0 +1,105 @@
|
||||
package pipeline
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/urfave/cli/v3"
|
||||
|
||||
"go.woodpecker-ci.org/woodpecker/v2/woodpecker-go/woodpecker"
|
||||
"go.woodpecker-ci.org/woodpecker/v2/woodpecker-go/woodpecker/mocks"
|
||||
)
|
||||
|
||||
func TestPipelinePurge(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
repoID int64
|
||||
args []string
|
||||
pipelinesKeep []*woodpecker.Pipeline
|
||||
pipelines []*woodpecker.Pipeline
|
||||
wantDelete int
|
||||
wantErr error
|
||||
}{
|
||||
{
|
||||
name: "success with no pipelines to purge",
|
||||
repoID: 1,
|
||||
args: []string{"purge", "--older-than", "1h", "repo/name"},
|
||||
pipelinesKeep: []*woodpecker.Pipeline{
|
||||
{ID: 1},
|
||||
},
|
||||
pipelines: []*woodpecker.Pipeline{},
|
||||
},
|
||||
{
|
||||
name: "success with pipelines to purge",
|
||||
repoID: 1,
|
||||
args: []string{"purge", "--older-than", "1h", "repo/name"},
|
||||
pipelinesKeep: []*woodpecker.Pipeline{
|
||||
{ID: 1},
|
||||
},
|
||||
pipelines: []*woodpecker.Pipeline{
|
||||
{ID: 1},
|
||||
{ID: 2},
|
||||
{ID: 3},
|
||||
},
|
||||
wantDelete: 2,
|
||||
},
|
||||
{
|
||||
name: "error on invalid duration",
|
||||
repoID: 1,
|
||||
args: []string{"purge", "--older-than", "invalid", "repo/name"},
|
||||
wantErr: errors.New("time: invalid duration \"invalid\""),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
mockClient := mocks.NewClient(t)
|
||||
mockClient.On("RepoLookup", mock.Anything).Maybe().Return(&woodpecker.Repo{ID: tt.repoID}, nil)
|
||||
|
||||
mockClient.On("PipelineList", mock.Anything, mock.Anything).Return(func(_ int64, opt woodpecker.PipelineListOptions) ([]*woodpecker.Pipeline, error) {
|
||||
// Return keep pipelines for first call
|
||||
if opt.After.IsZero() {
|
||||
if opt.Page == 1 {
|
||||
return tt.pipelinesKeep, nil
|
||||
}
|
||||
return []*woodpecker.Pipeline{}, nil
|
||||
}
|
||||
|
||||
// Return pipelines to purge for calls with After filter
|
||||
if !opt.After.IsZero() {
|
||||
if opt.Page == 1 {
|
||||
return tt.pipelines, nil
|
||||
}
|
||||
return []*woodpecker.Pipeline{}, nil
|
||||
}
|
||||
|
||||
return []*woodpecker.Pipeline{}, nil
|
||||
}).Maybe()
|
||||
|
||||
if tt.wantDelete > 0 {
|
||||
mockClient.On("PipelineDelete", tt.repoID, mock.Anything).Return(nil).Times(tt.wantDelete)
|
||||
}
|
||||
|
||||
command := pipelinePurgeCmd
|
||||
command.Writer = io.Discard
|
||||
command.Action = func(_ context.Context, c *cli.Command) error {
|
||||
err := pipelinePurge(c, mockClient)
|
||||
|
||||
if tt.wantErr != nil {
|
||||
assert.EqualError(t, err, tt.wantErr.Error())
|
||||
return nil
|
||||
}
|
||||
|
||||
assert.NoError(t, err)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
_ = command.Run(context.Background(), tt.args)
|
||||
})
|
||||
}
|
||||
}
|
@ -25,15 +25,15 @@ import (
|
||||
"go.woodpecker-ci.org/woodpecker/v2/woodpecker-go/woodpecker"
|
||||
)
|
||||
|
||||
var pipelineInfoCmd = &cli.Command{
|
||||
Name: "info",
|
||||
Usage: "show pipeline details",
|
||||
var pipelineShowCmd = &cli.Command{
|
||||
Name: "show",
|
||||
Usage: "show pipeline information",
|
||||
ArgsUsage: "<repo-id|repo-full-name> [pipeline]",
|
||||
Action: pipelineInfo,
|
||||
Action: pipelineShow,
|
||||
Flags: common.OutputFlags("table"),
|
||||
}
|
||||
|
||||
func pipelineInfo(ctx context.Context, c *cli.Command) error {
|
||||
func pipelineShow(ctx context.Context, c *cli.Command) error {
|
||||
repoIDOrFullName := c.Args().First()
|
||||
client, err := internal.NewClient(ctx, c)
|
||||
if err != nil {
|
||||
@ -48,7 +48,7 @@ func pipelineInfo(ctx context.Context, c *cli.Command) error {
|
||||
var number int64
|
||||
if pipelineArg == "last" || len(pipelineArg) == 0 {
|
||||
// Fetch the pipeline number from the last pipeline
|
||||
pipeline, err := client.PipelineLast(repoID, "")
|
||||
pipeline, err := client.PipelineLast(repoID, woodpecker.PipelineLastOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -65,5 +65,5 @@ func pipelineInfo(ctx context.Context, c *cli.Command) error {
|
||||
return err
|
||||
}
|
||||
|
||||
return pipelineOutput(c, []woodpecker.Pipeline{*pipeline})
|
||||
return pipelineOutput(c, []*woodpecker.Pipeline{pipeline})
|
||||
}
|
@ -23,6 +23,7 @@ import (
|
||||
"github.com/urfave/cli/v3"
|
||||
|
||||
"go.woodpecker-ci.org/woodpecker/v2/cli/internal"
|
||||
"go.woodpecker-ci.org/woodpecker/v2/woodpecker-go/woodpecker"
|
||||
)
|
||||
|
||||
var pipelineStartCmd = &cli.Command{
|
||||
@ -34,7 +35,7 @@ var pipelineStartCmd = &cli.Command{
|
||||
&cli.StringSliceFlag{
|
||||
Name: "param",
|
||||
Aliases: []string{"p"},
|
||||
Usage: "custom parameters to be injected into the step environment. Format: KEY=value",
|
||||
Usage: "custom parameters to inject into the step environment. Format: KEY=value",
|
||||
},
|
||||
},
|
||||
}
|
||||
@ -54,7 +55,7 @@ func pipelineStart(ctx context.Context, c *cli.Command) (err error) {
|
||||
var number int64
|
||||
if pipelineArg == "last" {
|
||||
// Fetch the pipeline number from the last pipeline
|
||||
pipeline, err := client.PipelineLast(repoID, "")
|
||||
pipeline, err := client.PipelineLast(repoID, woodpecker.PipelineLastOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -69,9 +70,11 @@ func pipelineStart(ctx context.Context, c *cli.Command) (err error) {
|
||||
}
|
||||
}
|
||||
|
||||
params := internal.ParseKeyPair(c.StringSlice("param"))
|
||||
opt := woodpecker.PipelineStartOptions{
|
||||
Params: internal.ParseKeyPair(c.StringSlice("param")),
|
||||
}
|
||||
|
||||
pipeline, err := client.PipelineStart(repoID, number, params)
|
||||
pipeline, err := client.PipelineStart(repoID, number, opt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -25,8 +25,8 @@ var Command = &cli.Command{
|
||||
Commands: []*cli.Command{
|
||||
cronCreateCmd,
|
||||
cronDeleteCmd,
|
||||
cronUpdateCmd,
|
||||
cronInfoCmd,
|
||||
cronListCmd,
|
||||
cronShowCmd,
|
||||
cronUpdateCmd,
|
||||
},
|
||||
}
|
@ -23,6 +23,7 @@ import (
|
||||
|
||||
"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 cronListCmd = &cli.Command{
|
||||
@ -52,7 +53,8 @@ func cronList(ctx context.Context, c *cli.Command) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
list, err := client.CronList(repoID)
|
||||
opt := woodpecker.CronListOptions{}
|
||||
list, err := client.CronList(repoID, opt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
@ -25,11 +25,11 @@ import (
|
||||
"go.woodpecker-ci.org/woodpecker/v2/cli/internal"
|
||||
)
|
||||
|
||||
var cronInfoCmd = &cli.Command{
|
||||
Name: "info",
|
||||
Usage: "display info about a cron job",
|
||||
var cronShowCmd = &cli.Command{
|
||||
Name: "show",
|
||||
Usage: "show cron job information",
|
||||
ArgsUsage: "[repo-id|repo-full-name]",
|
||||
Action: cronInfo,
|
||||
Action: cronShow,
|
||||
Flags: []cli.Flag{
|
||||
common.RepoFlag,
|
||||
&cli.StringFlag{
|
||||
@ -41,7 +41,7 @@ var cronInfoCmd = &cli.Command{
|
||||
},
|
||||
}
|
||||
|
||||
func cronInfo(ctx context.Context, c *cli.Command) error {
|
||||
func cronShow(ctx context.Context, c *cli.Command) error {
|
||||
var (
|
||||
cronID = c.Int("id")
|
||||
repoIDOrFullName = c.String("repository")
|
@ -28,9 +28,9 @@ var Command = &cli.Command{
|
||||
Commands: []*cli.Command{
|
||||
registryCreateCmd,
|
||||
registryDeleteCmd,
|
||||
registryUpdateCmd,
|
||||
registryInfoCmd,
|
||||
registryListCmd,
|
||||
registryShowCmd,
|
||||
registryUpdateCmd,
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -28,7 +28,7 @@ import (
|
||||
|
||||
var registryCreateCmd = &cli.Command{
|
||||
Name: "add",
|
||||
Usage: "adds a registry",
|
||||
Usage: "add a registry",
|
||||
ArgsUsage: "[repo-id|repo-full-name]",
|
||||
Action: registryCreate,
|
||||
Flags: []cli.Flag{
|
||||
|
@ -23,6 +23,7 @@ import (
|
||||
|
||||
"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 registryListCmd = &cli.Command{
|
||||
@ -49,7 +50,9 @@ func registryList(ctx context.Context, c *cli.Command) error {
|
||||
return err
|
||||
}
|
||||
|
||||
list, err := client.RegistryList(repoID)
|
||||
opt := woodpecker.RegistryListOptions{}
|
||||
|
||||
list, err := client.RegistryList(repoID, opt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -25,11 +25,11 @@ import (
|
||||
"go.woodpecker-ci.org/woodpecker/v2/cli/internal"
|
||||
)
|
||||
|
||||
var registryInfoCmd = &cli.Command{
|
||||
Name: "info",
|
||||
Usage: "display registry info",
|
||||
var registryShowCmd = &cli.Command{
|
||||
Name: "show",
|
||||
Usage: "show registry information",
|
||||
ArgsUsage: "[repo-id|repo-full-name]",
|
||||
Action: registryInfo,
|
||||
Action: registryShow,
|
||||
Flags: []cli.Flag{
|
||||
common.RepoFlag,
|
||||
&cli.StringFlag{
|
||||
@ -41,7 +41,7 @@ var registryInfoCmd = &cli.Command{
|
||||
},
|
||||
}
|
||||
|
||||
func registryInfo(ctx context.Context, c *cli.Command) error {
|
||||
func registryShow(ctx context.Context, c *cli.Command) error {
|
||||
var (
|
||||
hostname = c.String("hostname")
|
||||
format = c.String("format") + "\n"
|
@ -17,7 +17,9 @@ package repo
|
||||
import (
|
||||
"github.com/urfave/cli/v3"
|
||||
|
||||
"go.woodpecker-ci.org/woodpecker/v2/cli/repo/cron"
|
||||
"go.woodpecker-ci.org/woodpecker/v2/cli/repo/registry"
|
||||
"go.woodpecker-ci.org/woodpecker/v2/cli/repo/secret"
|
||||
)
|
||||
|
||||
// Command exports the repository command.
|
||||
@ -25,14 +27,16 @@ var Command = &cli.Command{
|
||||
Name: "repo",
|
||||
Usage: "manage repositories",
|
||||
Commands: []*cli.Command{
|
||||
repoListCmd,
|
||||
repoInfoCmd,
|
||||
repoAddCmd,
|
||||
repoUpdateCmd,
|
||||
repoChownCmd,
|
||||
cron.Command,
|
||||
repoListCmd,
|
||||
registry.Command,
|
||||
repoRemoveCmd,
|
||||
repoRepairCmd,
|
||||
repoChownCmd,
|
||||
secret.Command,
|
||||
repoShowCmd,
|
||||
repoSyncCmd,
|
||||
registry.Command,
|
||||
repoUpdateCmd,
|
||||
},
|
||||
}
|
||||
|
@ -22,6 +22,7 @@ import (
|
||||
"github.com/urfave/cli/v3"
|
||||
|
||||
"go.woodpecker-ci.org/woodpecker/v2/cli/internal"
|
||||
"go.woodpecker-ci.org/woodpecker/v2/woodpecker-go/woodpecker"
|
||||
)
|
||||
|
||||
var repoAddCmd = &cli.Command{
|
||||
@ -43,7 +44,11 @@ func repoAdd(ctx context.Context, c *cli.Command) error {
|
||||
return err
|
||||
}
|
||||
|
||||
repo, err := client.RepoPost(int64(forgeRemoteID))
|
||||
opt := woodpecker.RepoPostOptions{
|
||||
ForgeRemoteID: int64(forgeRemoteID),
|
||||
}
|
||||
|
||||
repo, err := client.RepoPost(opt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ import (
|
||||
|
||||
"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 repoListCmd = &cli.Command{
|
||||
@ -36,6 +37,10 @@ var repoListCmd = &cli.Command{
|
||||
Name: "org",
|
||||
Usage: "filter by organization",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "all",
|
||||
Usage: "query all repos, including inactive ones",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@ -45,7 +50,11 @@ func repoList(ctx context.Context, c *cli.Command) error {
|
||||
return err
|
||||
}
|
||||
|
||||
repos, err := client.RepoList()
|
||||
opt := woodpecker.RepoListOptions{
|
||||
All: c.Bool("all"),
|
||||
}
|
||||
|
||||
repos, err := client.RepoList(opt)
|
||||
if err != nil || len(repos) == 0 {
|
||||
return err
|
||||
}
|
||||
@ -68,4 +77,4 @@ func repoList(ctx context.Context, c *cli.Command) error {
|
||||
}
|
||||
|
||||
// Template for repository list items.
|
||||
var tmplRepoList = "\x1b[33m{{ .FullName }}\x1b[0m (id: {{ .ID }}, forgeRemoteID: {{ .ForgeRemoteID }})"
|
||||
var tmplRepoList = "\x1b[33m{{ .FullName }}\x1b[0m (id: {{ .ID }}, forgeRemoteID: {{ .ForgeRemoteID }}, isActive: {{ .IsActive }})"
|
||||
|
@ -25,15 +25,15 @@ import (
|
||||
"go.woodpecker-ci.org/woodpecker/v2/cli/internal"
|
||||
)
|
||||
|
||||
var repoInfoCmd = &cli.Command{
|
||||
Name: "info",
|
||||
Usage: "show repository details",
|
||||
var repoShowCmd = &cli.Command{
|
||||
Name: "show",
|
||||
Usage: "show repository information",
|
||||
ArgsUsage: "<repo-id|repo-full-name>",
|
||||
Action: repoInfo,
|
||||
Action: repoShow,
|
||||
Flags: []cli.Flag{common.FormatFlag(tmplRepoInfo)},
|
||||
}
|
||||
|
||||
func repoInfo(ctx context.Context, c *cli.Command) error {
|
||||
func repoShow(ctx context.Context, c *cli.Command) error {
|
||||
repoIDOrFullName := c.Args().First()
|
||||
client, err := internal.NewClient(ctx, c)
|
||||
if err != nil {
|
||||
@ -65,6 +65,7 @@ Visibility: {{ .Visibility }}
|
||||
Private: {{ .IsSCMPrivate }}
|
||||
Trusted: {{ .IsTrusted }}
|
||||
Gated: {{ .IsGated }}
|
||||
Require approval for: {{ .RequireApproval }}
|
||||
Clone url: {{ .Clone }}
|
||||
Allow pull-requests: {{ .AllowPullRequests }}
|
||||
`
|
@ -23,6 +23,7 @@ import (
|
||||
|
||||
"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 repoSyncCmd = &cli.Command{
|
||||
@ -40,7 +41,11 @@ func repoSync(ctx context.Context, c *cli.Command) error {
|
||||
return err
|
||||
}
|
||||
|
||||
repos, err := client.RepoListOpts(true)
|
||||
opt := woodpecker.RepoListOptions{
|
||||
All: true,
|
||||
}
|
||||
|
||||
repos, err := client.RepoList(opt)
|
||||
if err != nil || len(repos) == 0 {
|
||||
return err
|
||||
}
|
||||
|
@ -36,8 +36,12 @@ var repoUpdateCmd = &cli.Command{
|
||||
Usage: "repository is trusted",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "gated",
|
||||
Usage: "repository is gated",
|
||||
Name: "gated", // TODO: remove in next release
|
||||
Hidden: true,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "require-approval",
|
||||
Usage: "repository requires approval for",
|
||||
},
|
||||
&cli.DurationFlag{
|
||||
Name: "timeout",
|
||||
@ -49,7 +53,7 @@ var repoUpdateCmd = &cli.Command{
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "config",
|
||||
Usage: "repository configuration path (e.g. .woodpecker.yml)",
|
||||
Usage: "repository configuration path. Example: .woodpecker.yml",
|
||||
},
|
||||
&cli.IntFlag{
|
||||
Name: "pipeline-counter",
|
||||
@ -57,7 +61,7 @@ var repoUpdateCmd = &cli.Command{
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "unsafe",
|
||||
Usage: "validate updating the pipeline-counter is unsafe",
|
||||
Usage: "allow unsafe operations",
|
||||
},
|
||||
},
|
||||
}
|
||||
@ -78,7 +82,7 @@ func repoUpdate(ctx context.Context, c *cli.Command) error {
|
||||
config = c.String("config")
|
||||
timeout = c.Duration("timeout")
|
||||
trusted = c.Bool("trusted")
|
||||
gated = c.Bool("gated")
|
||||
requireApproval = c.String("require-approval")
|
||||
pipelineCounter = int(c.Int("pipeline-counter"))
|
||||
unsafe = c.Bool("unsafe")
|
||||
)
|
||||
@ -87,8 +91,18 @@ func repoUpdate(ctx context.Context, c *cli.Command) error {
|
||||
if c.IsSet("trusted") {
|
||||
patch.IsTrusted = &trusted
|
||||
}
|
||||
|
||||
// TODO: remove in next release
|
||||
if c.IsSet("gated") {
|
||||
patch.IsGated = &gated
|
||||
return fmt.Errorf("'gated' option has been set in version 2.8, use 'require-approval' in >= 3.0")
|
||||
}
|
||||
|
||||
if c.IsSet("require-approval") {
|
||||
if mode := woodpecker.ApprovalMode(requireApproval); mode.Valid() {
|
||||
patch.RequireApproval = &mode
|
||||
} else {
|
||||
return fmt.Errorf("update approval mode failed: '%s' is no valid mode", mode)
|
||||
}
|
||||
}
|
||||
if c.IsSet("timeout") {
|
||||
v := int64(timeout / time.Minute)
|
||||
|
@ -15,10 +15,6 @@
|
||||
package secret
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/urfave/cli/v3"
|
||||
|
||||
"go.woodpecker-ci.org/woodpecker/v2/cli/internal"
|
||||
@ -32,52 +28,17 @@ var Command = &cli.Command{
|
||||
Commands: []*cli.Command{
|
||||
secretCreateCmd,
|
||||
secretDeleteCmd,
|
||||
secretUpdateCmd,
|
||||
secretInfoCmd,
|
||||
secretListCmd,
|
||||
secretShowCmd,
|
||||
secretUpdateCmd,
|
||||
},
|
||||
}
|
||||
|
||||
func parseTargetArgs(client woodpecker.Client, c *cli.Command) (global bool, orgID, repoID int64, err error) {
|
||||
if c.Bool("global") {
|
||||
return true, -1, -1, nil
|
||||
}
|
||||
|
||||
func parseTargetArgs(client woodpecker.Client, c *cli.Command) (repoID int64, err error) {
|
||||
repoIDOrFullName := c.String("repository")
|
||||
if repoIDOrFullName == "" {
|
||||
repoIDOrFullName = c.Args().First()
|
||||
}
|
||||
|
||||
orgIDOrName := c.String("organization")
|
||||
if orgIDOrName == "" && repoIDOrFullName == "" {
|
||||
if err := cli.ShowSubcommandHelp(c); err != nil {
|
||||
return false, -1, -1, err
|
||||
}
|
||||
|
||||
return false, -1, -1, fmt.Errorf("missing arguments")
|
||||
}
|
||||
|
||||
if orgIDOrName != "" && repoIDOrFullName == "" {
|
||||
if orgID, err := strconv.ParseInt(orgIDOrName, 10, 64); err == nil {
|
||||
return false, orgID, -1, nil
|
||||
}
|
||||
|
||||
org, err := client.OrgLookup(orgIDOrName)
|
||||
if err != nil {
|
||||
return false, -1, -1, err
|
||||
}
|
||||
|
||||
return false, org.ID, -1, nil
|
||||
}
|
||||
|
||||
if orgIDOrName != "" && !strings.Contains(repoIDOrFullName, "/") {
|
||||
repoIDOrFullName = orgIDOrName + "/" + repoIDOrFullName
|
||||
}
|
||||
|
||||
repoID, err = internal.ParseRepo(client, repoIDOrFullName)
|
||||
if err != nil {
|
||||
return false, -1, -1, err
|
||||
}
|
||||
|
||||
return false, -1, repoID, nil
|
||||
return internal.ParseRepo(client, repoIDOrFullName)
|
||||
}
|
93
cli/repo/secret/secret_add.go
Normal file
93
cli/repo/secret/secret_add.go
Normal file
@ -0,0 +1,93 @@
|
||||
// Copyright 2023 Woodpecker Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package secret
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/urfave/cli/v3"
|
||||
|
||||
"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 secretCreateCmd = &cli.Command{
|
||||
Name: "add",
|
||||
Usage: "add a secret",
|
||||
ArgsUsage: "[repo-id|repo-full-name]",
|
||||
Action: secretCreate,
|
||||
Flags: []cli.Flag{
|
||||
common.RepoFlag,
|
||||
&cli.StringFlag{
|
||||
Name: "name",
|
||||
Usage: "secret name",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "value",
|
||||
Usage: "secret value",
|
||||
},
|
||||
&cli.StringSliceFlag{
|
||||
Name: "event",
|
||||
Usage: "limit secret to these events",
|
||||
},
|
||||
&cli.StringSliceFlag{
|
||||
Name: "image",
|
||||
Usage: "limit secret to these images",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func secretCreate(ctx context.Context, c *cli.Command) error {
|
||||
client, err := internal.NewClient(ctx, c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
secret := &woodpecker.Secret{
|
||||
Name: strings.ToLower(c.String("name")),
|
||||
Value: c.String("value"),
|
||||
Images: c.StringSlice("image"),
|
||||
Events: c.StringSlice("event"),
|
||||
}
|
||||
if len(secret.Events) == 0 {
|
||||
secret.Events = defaultSecretEvents
|
||||
}
|
||||
if strings.HasPrefix(secret.Value, "@") {
|
||||
path := strings.TrimPrefix(secret.Value, "@")
|
||||
out, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
secret.Value = string(out)
|
||||
}
|
||||
|
||||
repoID, err := parseTargetArgs(client, c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = client.SecretCreate(repoID, secret)
|
||||
return err
|
||||
}
|
||||
|
||||
var defaultSecretEvents = []string{
|
||||
woodpecker.EventPush,
|
||||
woodpecker.EventTag,
|
||||
woodpecker.EventRelease,
|
||||
woodpecker.EventDeploy,
|
||||
}
|
87
cli/repo/secret/secret_list.go
Normal file
87
cli/repo/secret/secret_list.go
Normal file
@ -0,0 +1,87 @@
|
||||
// Copyright 2023 Woodpecker Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package secret
|
||||
|
||||
import (
|
||||
"context"
|
||||
"html/template"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/urfave/cli/v3"
|
||||
|
||||
"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 secretListCmd = &cli.Command{
|
||||
Name: "ls",
|
||||
Usage: "list secrets",
|
||||
ArgsUsage: "[repo-id|repo-full-name]",
|
||||
Action: secretList,
|
||||
Flags: []cli.Flag{
|
||||
common.RepoFlag,
|
||||
common.FormatFlag(tmplSecretList, true),
|
||||
},
|
||||
}
|
||||
|
||||
func secretList(ctx context.Context, c *cli.Command) error {
|
||||
format := c.String("format") + "\n"
|
||||
|
||||
client, err := internal.NewClient(ctx, c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
repoID, err := parseTargetArgs(client, c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
opt := woodpecker.SecretListOptions{}
|
||||
|
||||
list, err := client.SecretList(repoID, opt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tmpl, err := template.New("_").Funcs(secretFuncMap).Parse(format)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, secret := range list {
|
||||
if err := tmpl.Execute(os.Stdout, secret); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Template for secret list items.
|
||||
var tmplSecretList = "\x1b[33m{{ .Name }} \x1b[0m" + `
|
||||
Events: {{ list .Events }}
|
||||
{{- if .Images }}
|
||||
Images: {{ list .Images }}
|
||||
{{- else }}
|
||||
Images: <any>
|
||||
{{- end }}
|
||||
`
|
||||
|
||||
var secretFuncMap = template.FuncMap{
|
||||
"list": func(s []string) string {
|
||||
return strings.Join(s, ", ")
|
||||
},
|
||||
}
|
@ -29,11 +29,6 @@ var secretDeleteCmd = &cli.Command{
|
||||
ArgsUsage: "[repo-id|repo-full-name]",
|
||||
Action: secretDelete,
|
||||
Flags: []cli.Flag{
|
||||
&cli.BoolFlag{
|
||||
Name: "global",
|
||||
Usage: "global secret",
|
||||
},
|
||||
common.OrgFlag,
|
||||
common.RepoFlag,
|
||||
&cli.StringFlag{
|
||||
Name: "name",
|
||||
@ -50,16 +45,10 @@ func secretDelete(ctx context.Context, c *cli.Command) error {
|
||||
return err
|
||||
}
|
||||
|
||||
global, orgID, repoID, err := parseTargetArgs(client, c)
|
||||
repoID, err := parseTargetArgs(client, c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if global {
|
||||
return client.GlobalSecretDelete(secretName)
|
||||
}
|
||||
if orgID != -1 {
|
||||
return client.OrgSecretDelete(orgID, secretName)
|
||||
}
|
||||
return client.SecretDelete(repoID, secretName)
|
||||
}
|
@ -32,11 +32,6 @@ var secretUpdateCmd = &cli.Command{
|
||||
ArgsUsage: "[repo-id|repo-full-name]",
|
||||
Action: secretUpdate,
|
||||
Flags: []cli.Flag{
|
||||
&cli.BoolFlag{
|
||||
Name: "global",
|
||||
Usage: "global secret",
|
||||
},
|
||||
common.OrgFlag,
|
||||
common.RepoFlag,
|
||||
&cli.StringFlag{
|
||||
Name: "name",
|
||||
@ -48,11 +43,11 @@ var secretUpdateCmd = &cli.Command{
|
||||
},
|
||||
&cli.StringSliceFlag{
|
||||
Name: "event",
|
||||
Usage: "secret limited to these events",
|
||||
Usage: "limit secret to these events",
|
||||
},
|
||||
&cli.StringSliceFlag{
|
||||
Name: "image",
|
||||
Usage: "secret limited to these images",
|
||||
Usage: "limit secret to these images",
|
||||
},
|
||||
},
|
||||
}
|
||||
@ -78,19 +73,11 @@ func secretUpdate(ctx context.Context, c *cli.Command) error {
|
||||
secret.Value = string(out)
|
||||
}
|
||||
|
||||
global, orgID, repoID, err := parseTargetArgs(client, c)
|
||||
repoID, err := parseTargetArgs(client, c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if global {
|
||||
_, err = client.GlobalSecretUpdate(secret)
|
||||
return err
|
||||
}
|
||||
if orgID != -1 {
|
||||
_, err = client.OrgSecretUpdate(orgID, secret)
|
||||
return err
|
||||
}
|
||||
_, err = client.SecretUpdate(repoID, secret)
|
||||
return err
|
||||
}
|
@ -24,20 +24,14 @@ import (
|
||||
|
||||
"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 secretInfoCmd = &cli.Command{
|
||||
Name: "info",
|
||||
Usage: "display secret info",
|
||||
var secretShowCmd = &cli.Command{
|
||||
Name: "show",
|
||||
Usage: "show secret information",
|
||||
ArgsUsage: "[repo-id|repo-full-name]",
|
||||
Action: secretInfo,
|
||||
Action: secretShow,
|
||||
Flags: []cli.Flag{
|
||||
&cli.BoolFlag{
|
||||
Name: "global",
|
||||
Usage: "global secret",
|
||||
},
|
||||
common.OrgFlag,
|
||||
common.RepoFlag,
|
||||
&cli.StringFlag{
|
||||
Name: "name",
|
||||
@ -47,7 +41,7 @@ var secretInfoCmd = &cli.Command{
|
||||
},
|
||||
}
|
||||
|
||||
func secretInfo(ctx context.Context, c *cli.Command) error {
|
||||
func secretShow(ctx context.Context, c *cli.Command) error {
|
||||
var (
|
||||
secretName = c.String("name")
|
||||
format = c.String("format") + "\n"
|
||||
@ -62,28 +56,14 @@ func secretInfo(ctx context.Context, c *cli.Command) error {
|
||||
return err
|
||||
}
|
||||
|
||||
global, orgID, repoID, err := parseTargetArgs(client, c)
|
||||
repoID, err := parseTargetArgs(client, c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var secret *woodpecker.Secret
|
||||
switch {
|
||||
case global:
|
||||
secret, err = client.GlobalSecret(secretName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case orgID != -1:
|
||||
secret, err = client.OrgSecret(orgID, secretName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
secret, err = client.Secret(repoID, secretName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
secret, err := client.Secret(repoID, secretName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tmpl, err := template.New("_").Funcs(secretFuncMap).Parse(format)
|
@ -20,11 +20,11 @@ var Command = &cli.Command{
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "server",
|
||||
Usage: "The URL of the woodpecker server",
|
||||
Usage: "URL of the woodpecker server",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "token",
|
||||
Usage: "The token to authenticate with the woodpecker server",
|
||||
Usage: "token to authenticate with the woodpecker server",
|
||||
},
|
||||
},
|
||||
Action: setup,
|
||||
@ -41,7 +41,7 @@ func setup(ctx context.Context, c *cli.Command) error {
|
||||
}
|
||||
|
||||
if !setupAgain {
|
||||
log.Info().Msg("Configuration skipped")
|
||||
log.Info().Msg("configuration skipped")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@ -87,7 +87,7 @@ func setup(ctx context.Context, c *cli.Command) error {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Info().Msg("The woodpecker-cli has been successfully setup")
|
||||
log.Info().Msg("woodpecker-cli has been successfully setup")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -24,12 +24,12 @@ func receiveTokenFromUI(c context.Context, serverURL string) (string, error) {
|
||||
srv.Handler = setupRouter(tokenReceived)
|
||||
|
||||
go func() {
|
||||
log.Debug().Msgf("Listening for token response on :%d", port)
|
||||
log.Debug().Msgf("listening for token response on :%d", port)
|
||||
_ = srv.ListenAndServe()
|
||||
}()
|
||||
|
||||
defer func() {
|
||||
log.Debug().Msg("Shutting down server")
|
||||
log.Debug().Msg("shutting down server")
|
||||
_ = srv.Shutdown(c)
|
||||
}()
|
||||
|
||||
@ -90,7 +90,7 @@ func setupRouter(tokenReceived chan string) *gin.Engine {
|
||||
|
||||
err := c.BindJSON(&data)
|
||||
if err != nil {
|
||||
log.Debug().Err(err).Msg("Failed to bind JSON")
|
||||
log.Debug().Err(err).Msg("failed to bind JSON")
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": "invalid request",
|
||||
})
|
||||
@ -110,7 +110,7 @@ func setupRouter(tokenReceived chan string) *gin.Engine {
|
||||
func openBrowser(url string) error {
|
||||
var err error
|
||||
|
||||
log.Debug().Msgf("Opening browser with URL: %s", url)
|
||||
log.Debug().Msgf("opening browser with URL: %s", url)
|
||||
|
||||
switch runtime.GOOS {
|
||||
case "linux":
|
||||
|
@ -24,7 +24,7 @@ var Command = &cli.Command{
|
||||
}
|
||||
|
||||
func update(ctx context.Context, c *cli.Command) error {
|
||||
log.Info().Msg("Checking for updates ...")
|
||||
log.Info().Msg("checking for updates ...")
|
||||
|
||||
newVersion, err := CheckForUpdate(ctx, c.Bool("force"))
|
||||
if err != nil {
|
||||
@ -32,11 +32,11 @@ func update(ctx context.Context, c *cli.Command) error {
|
||||
}
|
||||
|
||||
if newVersion == nil {
|
||||
fmt.Println("You are using the latest version of woodpecker-cli")
|
||||
fmt.Println("you are using the latest version of woodpecker-cli")
|
||||
return nil
|
||||
}
|
||||
|
||||
log.Info().Msgf("New version %s is available! Updating ...", newVersion.Version)
|
||||
log.Info().Msgf("new version %s is available! Updating ...", newVersion.Version)
|
||||
|
||||
var tarFilePath string
|
||||
tarFilePath, err = downloadNewVersion(ctx, newVersion.AssetURL)
|
||||
@ -44,14 +44,14 @@ func update(ctx context.Context, c *cli.Command) error {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Debug().Msgf("New version %s has been downloaded successfully! Installing ...", newVersion.Version)
|
||||
log.Debug().Msgf("new version %s has been downloaded successfully! Installing ...", newVersion.Version)
|
||||
|
||||
binFile, err := extractNewVersion(tarFilePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Debug().Msgf("New version %s has been extracted to %s", newVersion.Version, binFile)
|
||||
log.Debug().Msgf("new version %s has been extracted to %s", newVersion.Version, binFile)
|
||||
|
||||
executablePathOrSymlink, err := os.Executable()
|
||||
if err != nil {
|
||||
|
@ -22,10 +22,10 @@ func CheckForUpdate(ctx context.Context, force bool) (*NewVersion, error) {
|
||||
}
|
||||
|
||||
func checkForUpdate(ctx context.Context, versionURL string, force bool) (*NewVersion, error) {
|
||||
log.Debug().Msgf("Current version: %s", version.String())
|
||||
log.Debug().Msgf("current version: %s", version.String())
|
||||
|
||||
if (version.String() == "dev" || strings.HasPrefix(version.String(), "next-")) && !force {
|
||||
log.Debug().Msgf("Skipping update check for development & next versions")
|
||||
log.Debug().Msgf("skipping update check for development/next versions")
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
@ -61,11 +61,11 @@ func checkForUpdate(ctx context.Context, versionURL string, force bool) (*NewVer
|
||||
|
||||
// using the latest release
|
||||
if installedVersion == upstreamVersion && !force {
|
||||
log.Debug().Msgf("No new version available")
|
||||
log.Debug().Msgf("no new version available")
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
log.Debug().Msgf("New version available: %s", upstreamVersion)
|
||||
log.Debug().Msgf("new version available: %s", upstreamVersion)
|
||||
|
||||
assetURL := fmt.Sprintf(githubBinaryURL, upstreamVersion, runtime.GOOS, runtime.GOARCH)
|
||||
return &NewVersion{
|
||||
@ -75,7 +75,7 @@ func checkForUpdate(ctx context.Context, versionURL string, force bool) (*NewVer
|
||||
}
|
||||
|
||||
func downloadNewVersion(ctx context.Context, downloadURL string) (string, error) {
|
||||
log.Debug().Msgf("Downloading new version from %s ...", downloadURL)
|
||||
log.Debug().Msgf("downloading new version from %s ...", downloadURL)
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, downloadURL, nil)
|
||||
if err != nil {
|
||||
@ -102,13 +102,13 @@ func downloadNewVersion(ctx context.Context, downloadURL string) (string, error)
|
||||
return "", err
|
||||
}
|
||||
|
||||
log.Debug().Msgf("New version downloaded to %s", file.Name())
|
||||
log.Debug().Msgf("new version downloaded to %s", file.Name())
|
||||
|
||||
return file.Name(), nil
|
||||
}
|
||||
|
||||
func extractNewVersion(tarFilePath string) (string, error) {
|
||||
log.Debug().Msgf("Extracting new version from %s ...", tarFilePath)
|
||||
log.Debug().Msgf("extracting new version from %s ...", tarFilePath)
|
||||
|
||||
tarFile, err := os.Open(tarFilePath)
|
||||
if err != nil {
|
||||
@ -132,7 +132,7 @@ func extractNewVersion(tarFilePath string) (string, error) {
|
||||
return "", err
|
||||
}
|
||||
|
||||
log.Debug().Msgf("New version extracted to %s", tmpDir)
|
||||
log.Debug().Msgf("new version extracted to %s", tmpDir)
|
||||
|
||||
return path.Join(tmpDir, "woodpecker-cli"), nil
|
||||
}
|
||||
|
@ -36,6 +36,9 @@ var flags = []cli.Flag{
|
||||
Sources: cli.NewValueSourceChain(
|
||||
cli.File(os.Getenv("WOODPECKER_AGENT_SECRET_FILE")),
|
||||
cli.EnvVar("WOODPECKER_AGENT_SECRET")),
|
||||
Config: cli.StringConfig{
|
||||
TrimSpace: true,
|
||||
},
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Sources: cli.EnvVars("WOODPECKER_GRPC_SECURE"),
|
||||
|
@ -19,20 +19,14 @@ import (
|
||||
|
||||
"go.woodpecker-ci.org/woodpecker/v2/cli/admin"
|
||||
"go.woodpecker-ci.org/woodpecker/v2/cli/common"
|
||||
"go.woodpecker-ci.org/woodpecker/v2/cli/cron"
|
||||
"go.woodpecker-ci.org/woodpecker/v2/cli/deploy"
|
||||
"go.woodpecker-ci.org/woodpecker/v2/cli/exec"
|
||||
"go.woodpecker-ci.org/woodpecker/v2/cli/info"
|
||||
"go.woodpecker-ci.org/woodpecker/v2/cli/lint"
|
||||
"go.woodpecker-ci.org/woodpecker/v2/cli/log"
|
||||
"go.woodpecker-ci.org/woodpecker/v2/cli/loglevel"
|
||||
"go.woodpecker-ci.org/woodpecker/v2/cli/org"
|
||||
"go.woodpecker-ci.org/woodpecker/v2/cli/pipeline"
|
||||
"go.woodpecker-ci.org/woodpecker/v2/cli/repo"
|
||||
"go.woodpecker-ci.org/woodpecker/v2/cli/secret"
|
||||
"go.woodpecker-ci.org/woodpecker/v2/cli/setup"
|
||||
"go.woodpecker-ci.org/woodpecker/v2/cli/update"
|
||||
"go.woodpecker-ci.org/woodpecker/v2/cli/user"
|
||||
"go.woodpecker-ci.org/woodpecker/v2/version"
|
||||
)
|
||||
|
||||
@ -49,18 +43,12 @@ func newApp() *cli.Command {
|
||||
app.Suggest = true
|
||||
app.Commands = []*cli.Command{
|
||||
admin.Command,
|
||||
org.Command,
|
||||
repo.Command,
|
||||
pipeline.Command,
|
||||
log.Command,
|
||||
deploy.Command,
|
||||
exec.Command,
|
||||
info.Command,
|
||||
secret.Command,
|
||||
user.Command,
|
||||
lint.Command,
|
||||
loglevel.Command,
|
||||
cron.Command,
|
||||
org.Command,
|
||||
pipeline.Command,
|
||||
repo.Command,
|
||||
setup.Command,
|
||||
update.Command,
|
||||
}
|
||||
|
@ -93,16 +93,6 @@ var flags = append([]cli.Flag{
|
||||
Name: "custom-js-file",
|
||||
Usage: "file path for the server to serve a custom .JS file, used for customizing the UI",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Sources: cli.EnvVars("WOODPECKER_LETS_ENCRYPT_EMAIL"),
|
||||
Name: "lets-encrypt-email",
|
||||
Usage: "let's encrypt email",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Sources: cli.EnvVars("WOODPECKER_LETS_ENCRYPT"),
|
||||
Name: "lets-encrypt",
|
||||
Usage: "enable let's encrypt",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Sources: cli.EnvVars("WOODPECKER_GRPC_ADDR"),
|
||||
Name: "grpc-addr",
|
||||
@ -116,6 +106,9 @@ var flags = append([]cli.Flag{
|
||||
Name: "grpc-secret",
|
||||
Usage: "grpc jwt secret",
|
||||
Value: "secret",
|
||||
Config: cli.StringConfig{
|
||||
TrimSpace: true,
|
||||
},
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Sources: cli.EnvVars("WOODPECKER_METRICS_SERVER_ADDR"),
|
||||
@ -173,6 +166,11 @@ var flags = append([]cli.Flag{
|
||||
Usage: "The maximum time in minutes you can set in the repo settings before a pipeline gets killed",
|
||||
Value: 120,
|
||||
},
|
||||
&cli.StringSliceFlag{
|
||||
Sources: cli.EnvVars("WOODPECKER_DEFAULT_WORKFLOW_LABELS"),
|
||||
Name: "default-workflow-labels",
|
||||
Usage: "The default label filter to set for workflows that has no label filter set. By default workflows will be allowed to run on any agent, if not specified in the workflow.",
|
||||
},
|
||||
&cli.DurationFlag{
|
||||
Sources: cli.EnvVars("WOODPECKER_SESSION_EXPIRES"),
|
||||
Name: "session-expires",
|
||||
@ -187,7 +185,7 @@ var flags = append([]cli.Flag{
|
||||
&cli.StringSliceFlag{
|
||||
Sources: cli.EnvVars("WOODPECKER_PLUGINS_TRUSTED_CLONE"),
|
||||
Name: "plugins-trusted-clone",
|
||||
Usage: "Plugins which are trusted to handle the netrc info in clone steps",
|
||||
Usage: "Plugins which are trusted to handle Git credentials in clone steps",
|
||||
Value: constant.TrustedClonePlugins,
|
||||
},
|
||||
&cli.StringSliceFlag{
|
||||
@ -212,6 +210,14 @@ var flags = append([]cli.Flag{
|
||||
cli.EnvVar("WOODPECKER_AGENT_SECRET")),
|
||||
Name: "agent-secret",
|
||||
Usage: "server-agent shared password",
|
||||
Config: cli.StringConfig{
|
||||
TrimSpace: true,
|
||||
},
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Sources: cli.EnvVars("WOODPECKER_DISABLE_USER_AGENT_REGISTRATION"),
|
||||
Name: "disable-user-agent-registration",
|
||||
Usage: "Disable user registered agents",
|
||||
},
|
||||
&cli.DurationFlag{
|
||||
Sources: cli.EnvVars("WOODPECKER_KEEPALIVE_MIN_TIME"),
|
||||
@ -238,6 +244,9 @@ var flags = append([]cli.Flag{
|
||||
Aliases: []string{"datasource"}, // TODO: remove in v4.0.0
|
||||
Usage: "database driver configuration string",
|
||||
Value: datasourceDefaultValue(),
|
||||
Config: cli.StringConfig{
|
||||
TrimSpace: true,
|
||||
},
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Sources: cli.NewValueSourceChain(
|
||||
@ -245,6 +254,9 @@ var flags = append([]cli.Flag{
|
||||
cli.EnvVar("WOODPECKER_PROMETHEUS_AUTH_TOKEN")),
|
||||
Name: "prometheus-auth-token",
|
||||
Usage: "token to secure prometheus metrics endpoint",
|
||||
Config: cli.StringConfig{
|
||||
TrimSpace: true,
|
||||
},
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Sources: cli.EnvVars("WOODPECKER_STATUS_CONTEXT", "WOODPECKER_GITHUB_CONTEXT", "WOODPECKER_GITEA_CONTEXT"),
|
||||
@ -344,6 +356,9 @@ var flags = append([]cli.Flag{
|
||||
cli.EnvVar("WOODPECKER_BITBUCKET_DC_CLIENT_ID")),
|
||||
Name: "forge-oauth-client",
|
||||
Usage: "oauth2 client id",
|
||||
Config: cli.StringConfig{
|
||||
TrimSpace: true,
|
||||
},
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Sources: cli.NewValueSourceChain(
|
||||
@ -365,6 +380,9 @@ var flags = append([]cli.Flag{
|
||||
cli.EnvVar("WOODPECKER_BITBUCKET_DC_CLIENT_SECRET")),
|
||||
Name: "forge-oauth-secret",
|
||||
Usage: "oauth2 client secret",
|
||||
Config: cli.StringConfig{
|
||||
TrimSpace: true,
|
||||
},
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "forge-skip-verify",
|
||||
@ -456,6 +474,9 @@ var flags = append([]cli.Flag{
|
||||
cli.EnvVar("WOODPECKER_BITBUCKET_DC_GIT_USERNAME")),
|
||||
Name: "bitbucket-dc-git-username",
|
||||
Usage: "Bitbucket DataCenter/Server service account username",
|
||||
Config: cli.StringConfig{
|
||||
TrimSpace: true,
|
||||
},
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Sources: cli.NewValueSourceChain(
|
||||
@ -463,6 +484,9 @@ var flags = append([]cli.Flag{
|
||||
cli.EnvVar("WOODPECKER_BITBUCKET_DC_GIT_PASSWORD")),
|
||||
Name: "bitbucket-dc-git-password",
|
||||
Usage: "Bitbucket DataCenter/Server service account password",
|
||||
Config: cli.StringConfig{
|
||||
TrimSpace: true,
|
||||
},
|
||||
},
|
||||
//
|
||||
// development flags
|
||||
@ -490,6 +514,9 @@ var flags = append([]cli.Flag{
|
||||
cli.EnvVar("WOODPECKER_ENCRYPTION_KEY")),
|
||||
Name: "encryption-raw-key",
|
||||
Usage: "Raw encryption key",
|
||||
Config: cli.StringConfig{
|
||||
TrimSpace: true,
|
||||
},
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Sources: cli.EnvVars("WOODPECKER_ENCRYPTION_TINK_KEYSET_FILE"),
|
||||
|
@ -39,7 +39,7 @@ func pinger(_ context.Context, c *cli.Command) error {
|
||||
}
|
||||
|
||||
// if woodpecker do ssl on it's own
|
||||
if c.String("server-cert") != "" || c.Bool("lets-encrypt") {
|
||||
if c.String("server-cert") != "" {
|
||||
scheme = "https"
|
||||
}
|
||||
|
||||
|
@ -12,6 +12,9 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build !generate
|
||||
// +build !generate
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
@ -22,7 +25,7 @@ import (
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/urfave/cli/v3"
|
||||
|
||||
_ "go.woodpecker-ci.org/woodpecker/v2/cmd/server/docs"
|
||||
_ "go.woodpecker-ci.org/woodpecker/v2/cmd/server/openapi"
|
||||
"go.woodpecker-ci.org/woodpecker/v2/shared/utils"
|
||||
"go.woodpecker-ci.org/woodpecker/v2/version"
|
||||
)
|
||||
@ -46,7 +49,7 @@ func main() {
|
||||
}
|
||||
app.Flags = flags
|
||||
|
||||
setupSwaggerStaticConfig()
|
||||
setupOpenAPIStaticConfig()
|
||||
|
||||
if err := app.Run(ctx, os.Args); err != nil {
|
||||
log.Error().Err(err).Msgf("error running server")
|
||||
|
38
cmd/server/openapi.go
Normal file
38
cmd/server/openapi.go
Normal file
@ -0,0 +1,38 @@
|
||||
// Copyright 2023 Woodpecker Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"go.woodpecker-ci.org/woodpecker/v2/cmd/server/openapi"
|
||||
"go.woodpecker-ci.org/woodpecker/v2/version"
|
||||
)
|
||||
|
||||
// Generate docs/openapi.json via:
|
||||
//go:generate go run github.com/swaggo/swag/cmd/swag init -g cmd/server/openapi.go --outputTypes go -output openapi -d ../../
|
||||
//go:generate go run openapi_json_gen.go openapi.go
|
||||
//go:generate go run github.com/getkin/kin-openapi/cmd/validate@latest ../../docs/openapi.json
|
||||
|
||||
// setupOpenAPIStaticConfig initializes static content (version) for the OpenAPI config.
|
||||
//
|
||||
// @title Woodpecker CI API
|
||||
// @description Woodpecker is a simple, yet powerful CI/CD engine with great extensibility.
|
||||
// @description To get a personal access token (PAT) for authentication, please log in your Woodpecker server,
|
||||
// @description and go to you personal profile page, by clicking the user icon at the top right.
|
||||
// @BasePath /api
|
||||
// @contact.name Woodpecker CI
|
||||
// @contact.url https://woodpecker-ci.org/
|
||||
func setupOpenAPIStaticConfig() {
|
||||
openapi.SwaggerInfo.Version = version.String()
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
// Package docs Code generated by swaggo/swag. DO NOT EDIT
|
||||
package docs
|
||||
// Package openapi Code generated by swaggo/swag. DO NOT EDIT
|
||||
package openapi
|
||||
|
||||
import "github.com/swaggo/swag"
|
||||
|
||||
@ -10,7 +10,7 @@ const docTemplate = `{
|
||||
"description": "{{escape .Description}}",
|
||||
"title": "{{.Title}}",
|
||||
"contact": {
|
||||
"name": "Woodpecker CI Community",
|
||||
"name": "Woodpecker CI",
|
||||
"url": "https://woodpecker-ci.org/"
|
||||
},
|
||||
"version": "{{.Version}}"
|
||||
@ -122,7 +122,7 @@ const docTemplate = `{
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "the agent's id",
|
||||
"name": "agent",
|
||||
"name": "agent_id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
@ -156,7 +156,7 @@ const docTemplate = `{
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "the agent's id",
|
||||
"name": "agent",
|
||||
"name": "agent_id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
@ -187,7 +187,7 @@ const docTemplate = `{
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "the agent's id",
|
||||
"name": "agent",
|
||||
"name": "agent_id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
@ -232,7 +232,7 @@ const docTemplate = `{
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "the agent's id",
|
||||
"name": "agent",
|
||||
"name": "agent_id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
@ -2966,6 +2966,30 @@ const docTemplate = `{
|
||||
"description": "only return pipelines after this RFC3339 date",
|
||||
"name": "after",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "filter pipelines by branch",
|
||||
"name": "branch",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "filter pipelines by webhook events (comma separated)",
|
||||
"name": "event",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "filter pipelines by strings contained in ref",
|
||||
"name": "ref",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "filter pipelines by status",
|
||||
"name": "status",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
@ -4554,22 +4578,15 @@ const docTemplate = `{
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"allOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"source": {
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"source": {
|
||||
"type": "string"
|
||||
},
|
||||
"version": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
"version": {
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -4905,6 +4922,9 @@ const docTemplate = `{
|
||||
"forge_url": {
|
||||
"type": "string"
|
||||
},
|
||||
"from_fork": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"id": {
|
||||
"type": "integer"
|
||||
},
|
||||
@ -5068,17 +5088,17 @@ const docTemplate = `{
|
||||
"full_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"gated": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"id": {
|
||||
"type": "integer"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"netrc_only_trusted": {
|
||||
"type": "boolean"
|
||||
"netrc_trusted": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"org_id": {
|
||||
"type": "integer"
|
||||
@ -5092,6 +5112,9 @@ const docTemplate = `{
|
||||
"private": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"require_approval": {
|
||||
"$ref": "#/definitions/model.ApprovalMode"
|
||||
},
|
||||
"scm": {
|
||||
"$ref": "#/definitions/SCMKind"
|
||||
},
|
||||
@ -5125,10 +5148,17 @@ const docTemplate = `{
|
||||
"type": "string"
|
||||
},
|
||||
"gated": {
|
||||
"description": "TODO: deprecated in favor of RequireApproval =\u003e Remove in next major release",
|
||||
"type": "boolean"
|
||||
},
|
||||
"netrc_only_trusted": {
|
||||
"type": "boolean"
|
||||
"netrc_trusted": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"require_approval": {
|
||||
"type": "string"
|
||||
},
|
||||
"timeout": {
|
||||
"type": "integer"
|
||||
@ -5621,6 +5651,27 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"model.ApprovalMode": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"none",
|
||||
"forks",
|
||||
"pull_requests",
|
||||
"all_events"
|
||||
],
|
||||
"x-enum-comments": {
|
||||
"RequireApprovalAllEvents": "require approval for all external events",
|
||||
"RequireApprovalForks": "require approval for PRs from forks (default)",
|
||||
"RequireApprovalNone": "require approval for no events",
|
||||
"RequireApprovalPullRequests": "require approval for all PRs"
|
||||
},
|
||||
"x-enum-varnames": [
|
||||
"RequireApprovalNone",
|
||||
"RequireApprovalForks",
|
||||
"RequireApprovalPullRequests",
|
||||
"RequireApprovalAllEvents"
|
||||
]
|
||||
},
|
||||
"model.ForgeType": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
@ -5763,10 +5814,10 @@ const docTemplate = `{
|
||||
var SwaggerInfo = &swag.Spec{
|
||||
Version: "",
|
||||
Host: "",
|
||||
BasePath: "",
|
||||
BasePath: "/api",
|
||||
Schemes: []string{},
|
||||
Title: "",
|
||||
Description: "",
|
||||
Title: "Woodpecker CI API",
|
||||
Description: "Woodpecker is a simple, yet powerful CI/CD engine with great extensibility.\nTo get a personal access token (PAT) for authentication, please log in your Woodpecker server,\nand go to you personal profile page, by clicking the user icon at the top right.",
|
||||
InfoInstanceName: "swagger",
|
||||
SwaggerTemplate: docTemplate,
|
||||
LeftDelim: "{{",
|
@ -12,9 +12,9 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// ************************************************************************************************
|
||||
// This is a generator tool, to update the Markdown documentation for the woodpecker-ci.org website
|
||||
// ************************************************************************************************
|
||||
// *********************************************************
|
||||
// This is a generator tool, to update the openapi.json file
|
||||
// *********************************************************
|
||||
|
||||
//go:build generate
|
||||
// +build generate
|
||||
@ -30,23 +30,24 @@ import (
|
||||
|
||||
"github.com/getkin/kin-openapi/openapi2"
|
||||
"github.com/getkin/kin-openapi/openapi2conv"
|
||||
"go.woodpecker-ci.org/woodpecker/v2/cmd/server/docs"
|
||||
|
||||
"go.woodpecker-ci.org/woodpecker/v2/cmd/server/openapi"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// set swagger infos
|
||||
setupSwaggerStaticConfig()
|
||||
// set openapi infos
|
||||
setupOpenAPIStaticConfig()
|
||||
|
||||
basePath := path.Join("..", "..")
|
||||
filePath := path.Join(basePath, "docs", "swagger.json")
|
||||
filePath := path.Join(basePath, "docs", "openapi.json")
|
||||
|
||||
// generate swagger file
|
||||
// generate openapi file
|
||||
f, err := os.Create(filePath)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer f.Close()
|
||||
doc := docs.SwaggerInfo.ReadDoc()
|
||||
doc := openapi.SwaggerInfo.ReadDoc()
|
||||
doc, err = removeHost(doc)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
@ -56,6 +57,8 @@ func main() {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fmt.Println("generated openapi.json")
|
||||
|
||||
// convert to OpenApi3
|
||||
if err := toOpenApi3(filePath, filePath); err != nil {
|
||||
fmt.Printf("converting '%s' from openapi v2 to v3 failed\n", filePath)
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user