Compare commits
90 commits
Author | SHA1 | Date | |
---|---|---|---|
f61c066940 | |||
4b8b7aaf73 | |||
699844458c | |||
89771161a0 | |||
8a80dd4ef6 | |||
ef56bac838 | |||
4d1a4cbe2b | |||
7499423e30 | |||
27d19d77cd | |||
bd45f56155 | |||
8c928fc303 | |||
5d98a5da22 | |||
8367365643 | |||
b9250cc7af | |||
fef922e92a | |||
55cc3b1d21 | |||
452d5e2277 | |||
8596b8b771 | |||
689534359c | |||
f5ac6ccb8e | |||
fa58979fa6 | |||
00037ec982 | |||
46a8845883 | |||
fd51808d6e | |||
5be066ff28 | |||
f7ad298bfa | |||
ff8ada357e | |||
5dd0b39678 | |||
b39f5dc372 | |||
0d7dfb2566 | |||
2b05d0a014 | |||
5f5b2d4161 | |||
e93554e4bc | |||
cb851be7df | |||
a5864fb416 | |||
75f4ccee62 | |||
aaad385133 | |||
dd7bec809c | |||
d771a60d94 | |||
43d3ea541b | |||
da8571c718 | |||
21518998d8 | |||
4c119d46fc | |||
5116f6263f | |||
b71ce15f17 | |||
3d64e92383 | |||
e29bb19c9d | |||
a0371f8850 | |||
c8fa946f16 | |||
b518543505 | |||
204732bf49 | |||
db0f4f02d8 | |||
4bc7de4d63 | |||
24efab297a | |||
44a3791a87 | |||
b6077f0500 | |||
6e50b742e6 | |||
aea2a7d740 | |||
6e08c19e98 | |||
c539c9e8a9 | |||
cfb24b5c90 | |||
9da535bc03 | |||
0f0aba7f82 | |||
c35b2e1ae9 | |||
051ea11a5c | |||
37718ded77 | |||
a5ea4573b0 | |||
2a8de61424 | |||
33b6eb4faa | |||
ee3adee4d4 | |||
38b9f475bb | |||
8243640a5d | |||
fc4f0ea0a5 | |||
ccd5834d4c | |||
186b87cf88 | |||
a187589df9 | |||
a2d26878e7 | |||
8a70e48182 | |||
ca6adcf472 | |||
37b5e2ed2d | |||
d17ff57fda | |||
57b0ae23c2 | |||
fce3289743 | |||
f10f7b4c7f | |||
466dfce406 | |||
2ad93d24fe | |||
fca44ab7ae | |||
5868a6e6a8 | |||
97917ac4b9 | |||
f9c1b35f59 |
48
.changelog.yml
Executable file
48
.changelog.yml
Executable file
|
@ -0,0 +1,48 @@
|
|||
service: gitea
|
||||
base-url: https://codeberg.org
|
||||
repo: woodpecker-plugins/plugin-docker-buildx
|
||||
|
||||
# Changelog groups and which labeled PRs to add to each group
|
||||
groups:
|
||||
-
|
||||
name: BREAKING
|
||||
labels:
|
||||
- breaking
|
||||
-
|
||||
name: FEATURES
|
||||
labels:
|
||||
- feature
|
||||
-
|
||||
name: BUGFIXES
|
||||
labels:
|
||||
- bug
|
||||
-
|
||||
name: ENHANCEMENTS
|
||||
labels:
|
||||
- enhancement
|
||||
-
|
||||
name: SECURITY
|
||||
labels:
|
||||
- security
|
||||
-
|
||||
name: TESTING
|
||||
labels:
|
||||
- testing
|
||||
-
|
||||
name: TRANSLATION
|
||||
labels:
|
||||
- translation
|
||||
-
|
||||
name: BUILD
|
||||
labels:
|
||||
- build
|
||||
-
|
||||
name: DOCS
|
||||
labels:
|
||||
- documentation
|
||||
-
|
||||
name: MISC
|
||||
default: true
|
||||
|
||||
# regex indicating which labels to skip for the changelog
|
||||
skip-labels: skip-changelog|backport*
|
11
.ecrc
Normal file
11
.ecrc
Normal file
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"Exclude": [
|
||||
".git",
|
||||
"go.mod", "go.sum",
|
||||
"vendor",
|
||||
"LICENSE",
|
||||
"node_modules",
|
||||
"_test.go",
|
||||
"Makefile"
|
||||
]
|
||||
}
|
19
.editorconfig
Normal file
19
.editorconfig
Normal file
|
@ -0,0 +1,19 @@
|
|||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
tab_width = 2
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[*.go]
|
||||
indent_style = tab
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[Makefile]
|
||||
indent_style = tab
|
16
.gitea/ISSUE_TEMPLATE.yml
Normal file
16
.gitea/ISSUE_TEMPLATE.yml
Normal file
|
@ -0,0 +1,16 @@
|
|||
name: Default
|
||||
about: Use this template if nothing seems to work.
|
||||
title: ''
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
> **Note**: Thanks for taking the time to fill out this bug report!
|
||||
- type: textarea
|
||||
id: content
|
||||
attributes:
|
||||
label: ❔ What happened
|
||||
description: Simply ask your question here.
|
||||
placeholder: Tell us what you want to know...
|
||||
validations:
|
||||
required: true
|
11
.gitea/ISSUE_TEMPLATE/Question.yml
Normal file
11
.gitea/ISSUE_TEMPLATE/Question.yml
Normal file
|
@ -0,0 +1,11 @@
|
|||
name: ❓ Question
|
||||
about: Ask a question
|
||||
title: ""
|
||||
labels:
|
||||
- question
|
||||
body:
|
||||
- type: textarea
|
||||
id: content
|
||||
attributes:
|
||||
label: Question content
|
||||
|
71
.gitea/ISSUE_TEMPLATE/bug_report.yml
Normal file
71
.gitea/ISSUE_TEMPLATE/bug_report.yml
Normal file
|
@ -0,0 +1,71 @@
|
|||
name: "\U0001F41E Bug report"
|
||||
about: Report an issue with the plugin
|
||||
title: ""
|
||||
labels:
|
||||
- bug
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thanks for taking the time to fill out this bug report!
|
||||
- type: dropdown
|
||||
id: component
|
||||
attributes:
|
||||
label: Component
|
||||
description: Which component of Woodpecker is affected by the issue?
|
||||
multiple: true
|
||||
options:
|
||||
- server
|
||||
- agent
|
||||
- cli
|
||||
- web-ui
|
||||
- other
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: bug-description
|
||||
attributes:
|
||||
label: Describe the bug
|
||||
description: A clear and concise description of what the bug is. If you intend to submit a PR for this issue, tell us in the description. Thanks!
|
||||
placeholder: Bug description
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: system-info
|
||||
attributes:
|
||||
label: System Info
|
||||
description: Output of `https://<your-woodpecker-instance>/version`
|
||||
render: shell
|
||||
placeholder: Version info, docker-compose config, Kubernetes manifests
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: additional-context
|
||||
attributes:
|
||||
label: Additional context
|
||||
description: |
|
||||
Logs? Screenshots? Anything that will give us more context about the issue you are encountering!
|
||||
Sometimes a picture is worth a thousand words, but please try not to insert an image of logs / text
|
||||
and copy paste the text instead.
|
||||
|
||||
Tip: You can attach images by clicking this area to highlight it and then dragging files in.
|
||||
validations:
|
||||
required: false
|
||||
- type: checkboxes
|
||||
id: checkboxes
|
||||
attributes:
|
||||
label: Validations
|
||||
description: Before submitting the issue, please make sure you do the following
|
||||
options:
|
||||
# - label: Follow our [Code of Conduct](https://github.com/woodpecker-ci/woodpecker/blob/main/CODE_OF_CONDUCT.md)
|
||||
# required: true
|
||||
- label: Read the [Contributing Guidelines](https://github.com/woodpecker-ci/woodpecker/blob/main/CONTRIBUTING.md).
|
||||
required: true
|
||||
- label: Read the [docs](https://woodpecker-ci.org/docs/intro).
|
||||
required: true
|
||||
- label: Check that there isn't [already an issue](https://github.com/woodpecker-ci/woodpecker/issues) that reports the same bug to avoid creating a duplicate.
|
||||
required: true
|
||||
- label: Checked that the bug isn't fixed in the `next` version already [https://woodpecker-ci.org/faq#which-version-of-woodpecker-should-i-use]
|
||||
required: true
|
||||
- label: Check that this is a concrete bug. For Q&A join our [Discord Chat Server](https://discord.gg/fcMQqSMXJy) or the [Matrix room](https://matrix.to/#/#woodpecker:matrix.org).
|
||||
required: true
|
8
.gitea/ISSUE_TEMPLATE/config.yml
Normal file
8
.gitea/ISSUE_TEMPLATE/config.yml
Normal file
|
@ -0,0 +1,8 @@
|
|||
blank_issues_enabled: true
|
||||
contact_links:
|
||||
- name: Ask a question in our Matrix room
|
||||
about: If you prefer a chat-like conversation or in need for quick help, this might be an alternative to opening an issue.
|
||||
url: https://matrix.to/#/#woodpecker:matrix.org
|
||||
- name: Frequently Asked Questions
|
||||
url: https://woodpecker-ci.org/faq
|
||||
about: Check the FAQs for common questions.
|
33
.gitea/ISSUE_TEMPLATE/feature_request.yml
Normal file
33
.gitea/ISSUE_TEMPLATE/feature_request.yml
Normal file
|
@ -0,0 +1,33 @@
|
|||
name: "\U0001F680 New feature proposal"
|
||||
description: Propose a new feature to be added
|
||||
title: ""
|
||||
labels: ["feature"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thanks for your interest in the project and taking the time to fill out this feature report!
|
||||
- type: textarea
|
||||
id: feature-description
|
||||
attributes:
|
||||
label: Clear and concise description of the problem
|
||||
description: "As a user of Woodpecker I want [goal / wish] so that [benefit]. If you intend to submit a PR for this issue, tell us in the description."
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: suggested-solution
|
||||
attributes:
|
||||
label: Suggested solution
|
||||
description: "In web-ui / config we could provide following functionality..."
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: alternative
|
||||
attributes:
|
||||
label: Alternative
|
||||
description: Clear and concise description of any alternative solutions or features you've considered.
|
||||
- type: textarea
|
||||
id: additional-context
|
||||
attributes:
|
||||
label: Additional context
|
||||
description: Any other context or screenshots about the feature request here.
|
30
.gitea/PULL_REQUEST_TEMPLATE.yml
Normal file
30
.gitea/PULL_REQUEST_TEMPLATE.yml
Normal file
|
@ -0,0 +1,30 @@
|
|||
name: Pull Request
|
||||
about: General pull request
|
||||
title: ""
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thanks for contributing to this project with your pull request!
|
||||
- type: textarea
|
||||
id: summary
|
||||
attributes:
|
||||
label: 📖 Summary
|
||||
description: Provide a concise summary of the changes. Ideally broken down to multiple bullet points. Please add details and longer text blocks to "Details" below.
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: build_pr_images
|
||||
attributes:
|
||||
label: 📑 Build PR Images?
|
||||
description: Should docker images be built for this PR and uploaded to Dockerhub? If so, a maintainer will add the `build_pr_images` label to this PR if this option is selected.
|
||||
options:
|
||||
- PR images are not needed
|
||||
- ✅ Yes, please build PR images
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: details
|
||||
attributes:
|
||||
label: 💬 Details
|
||||
description: Add additional information here.
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -5,3 +5,4 @@ plugin-docker-buildx
|
|||
|
||||
coverage.out
|
||||
CHANGELOG.md
|
||||
debug.test*
|
||||
|
|
|
@ -4,3 +4,7 @@ MD013: False
|
|||
MD041: False
|
||||
MD004:
|
||||
style: dash
|
||||
|
||||
MD033:
|
||||
# Allowed elements
|
||||
allowed_elements: [details, summary, img, a, br, p]
|
||||
|
|
106
.woodpecker.yml
106
.woodpecker.yml
|
@ -1,38 +1,100 @@
|
|||
variables:
|
||||
- &platforms 'linux/amd64,linux/arm64'
|
||||
- &golang 'golang:1.18'
|
||||
when:
|
||||
- event: [pull_request, tag, cron]
|
||||
- event: push
|
||||
branch:
|
||||
- ${CI_REPO_DEFAULT_BRANCH}
|
||||
- renovate/*
|
||||
|
||||
pipeline:
|
||||
variables:
|
||||
- &golang "golang:1.22"
|
||||
- &build_plugin "woodpeckerci/plugin-docker-buildx:3.0.1"
|
||||
- base_settings: &base_buildx_settings
|
||||
platforms: "linux/amd64,linux/arm64"
|
||||
dockerfile: Dockerfile.multiarch
|
||||
auto_tag: true
|
||||
repo: woodpeckerci/plugin-docker-buildx,codeberg.org/woodpecker-plugins/docker-buildx
|
||||
- &login_setting
|
||||
# Default DockerHub login
|
||||
- registry: https://index.docker.io/v1/
|
||||
username:
|
||||
from_secret: docker_username
|
||||
password:
|
||||
from_secret: docker_password
|
||||
# Additional Codeberg login
|
||||
- registry: https://codeberg.org
|
||||
username:
|
||||
from_secret: cb_username
|
||||
password:
|
||||
from_secret: cb_password
|
||||
|
||||
steps:
|
||||
vendor:
|
||||
image: *golang
|
||||
commands: go mod vendor
|
||||
|
||||
test:
|
||||
image: *golang
|
||||
depends_on: vendor
|
||||
commands: go test -cover ./...
|
||||
|
||||
publish-dryrun:
|
||||
image: woodpeckerci/plugin-docker-buildx:v20.10.18
|
||||
settings:
|
||||
platforms: *platforms
|
||||
repo: woodpeckerci/plugin-docker-buildx
|
||||
dockerfile: Dockerfile.multiarch
|
||||
dry_run: true
|
||||
lint-editorconfig:
|
||||
image: docker.io/mstruebing/editorconfig-checker:2.7.2
|
||||
when:
|
||||
event: pull_request
|
||||
branch: main
|
||||
|
||||
lint-format:
|
||||
image: *golang
|
||||
depends_on: vendor
|
||||
commands: make formatcheck
|
||||
when:
|
||||
event: pull_request
|
||||
|
||||
publish-dryrun:
|
||||
image: *build_plugin
|
||||
depends_on: test
|
||||
pull: true
|
||||
settings:
|
||||
<<: *base_buildx_settings
|
||||
repo: test
|
||||
dry-run: true
|
||||
when:
|
||||
evaluate: 'not (CI_COMMIT_PULL_REQUEST_LABELS contains "build_pr_images")'
|
||||
event: pull_request
|
||||
branch:
|
||||
- ${CI_REPO_DEFAULT_BRANCH}
|
||||
- renovate/*
|
||||
|
||||
publish:
|
||||
image: woodpeckerci/plugin-docker-buildx:v20.10.18
|
||||
image: *build_plugin
|
||||
depends_on: test
|
||||
settings:
|
||||
platforms: *platforms
|
||||
repo: woodpeckerci/plugin-docker-buildx
|
||||
auto_tag: true
|
||||
dockerfile: Dockerfile.multiarch
|
||||
username:
|
||||
from_secret: docker_username
|
||||
password:
|
||||
from_secret: docker_password
|
||||
<<: *base_buildx_settings
|
||||
logins: *login_setting
|
||||
when:
|
||||
event: [push, tag, cron]
|
||||
branch: main
|
||||
branch: ${CI_REPO_DEFAULT_BRANCH}
|
||||
|
||||
publish_pr_image:
|
||||
image: *build_plugin
|
||||
depends_on: test
|
||||
settings:
|
||||
<<: *base_buildx_settings
|
||||
tag: pull_${CI_COMMIT_PULL_REQUEST}
|
||||
logins: *login_setting
|
||||
when:
|
||||
evaluate: 'CI_COMMIT_PULL_REQUEST_LABELS contains "build_pr_images"'
|
||||
event: pull_request
|
||||
|
||||
# TODO: replace by plugin-ready-release-go once it supports gitea
|
||||
gitea-release:
|
||||
image: "woodpeckerci/plugin-gitea-release:0.3.1"
|
||||
depends_on: test
|
||||
settings:
|
||||
base_url: https://codeberg.org
|
||||
title: ${CI_COMMIT_TAG}
|
||||
api_key:
|
||||
from_secret: gitea_token
|
||||
target: main
|
||||
when:
|
||||
event: [tag]
|
||||
branch: ${CI_REPO_DEFAULT_BRANCH}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
ARG BUILDX_VERSION=0.9.1
|
||||
ARG DOCKER_VERSION=20.10-dind
|
||||
ARG GOLANG_VERSION=1.18
|
||||
ARG BUILDX_VERSION=0.12.1
|
||||
ARG DOCKER_VERSION=25.0.3-dind
|
||||
ARG GOLANG_VERSION=1.22
|
||||
|
||||
FROM --platform=$BUILDPLATFORM golang:${GOLANG_VERSION} as build
|
||||
|
||||
|
@ -15,6 +15,9 @@ RUN --mount=type=cache,target=/root/.cache/go-build \
|
|||
FROM docker/buildx-bin:${BUILDX_VERSION} as buildx-bin
|
||||
FROM docker:${DOCKER_VERSION}
|
||||
|
||||
RUN apk --update --no-cache add coredns git
|
||||
|
||||
COPY --from=build /src/Corefile /etc/coredns/Corefile
|
||||
COPY --from=buildx-bin /buildx /usr/libexec/docker/cli-plugins/docker-buildx
|
||||
COPY --from=build /src/plugin-docker-buildx /bin/plugin-docker-buildx
|
||||
|
||||
|
|
12
Makefile
12
Makefile
|
@ -2,5 +2,17 @@ TARGETOS ?= linux
|
|||
TARGETARCH ?= amd64
|
||||
LDFLAGS := -s -w -extldflags "-static"
|
||||
|
||||
.PHONY: build
|
||||
build:
|
||||
CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -ldflags '${LDFLAGS}' -v -a -tags netgo -o plugin-docker-buildx ./cmd/docker-buildx
|
||||
|
||||
format: install-tools
|
||||
gofumpt -extra -w .
|
||||
|
||||
formatcheck: install-tools
|
||||
@([ -z "$(shell gofumpt -d . | head)" ]) || (echo "Source is unformatted"; exit 1)
|
||||
|
||||
install-tools: ## Install development tools
|
||||
@hash gofumpt > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
go install mvdan.cc/gofumpt@latest; \
|
||||
fi
|
||||
|
|
33
README.md
33
README.md
|
@ -1,17 +1,34 @@
|
|||
# plugin-docker-buildx
|
||||
|
||||
<a href="https://codeberg.org/woodpecker-plugins/plugin-docker-buildx">
|
||||
<img alt="Get it on Codeberg" src="https://codeberg.org/Codeberg/GetItOnCodeberg/media/branch/main/get-it-on-neon-blue.png" height="60">
|
||||
<br/>
|
||||
<p align="center">
|
||||
<a href="https://ci.codeberg.org/repos/3265" target="_blank">
|
||||
<img src="https://ci.codeberg.org/api/badges/3265/status.svg" alt="status-badge" />
|
||||
</a>
|
||||
<a href="https://codeberg.org/woodpecker-plugins/docker-buildx/releases" title="Latest release">
|
||||
<img src="https://img.shields.io/gitea/v/release/woodpecker-plugins/docker-buildx?gitea_url=https%3A%2F%2Fcodeberg.org
|
||||
" alt="Latest release">
|
||||
</a>
|
||||
<a href="https://matrix.to/#/#woodpecker:matrix.org" title="Join the Matrix space at https://matrix.to/#/#woodpecker:matrix.org">
|
||||
<img src="https://img.shields.io/matrix/woodpecker:matrix.org?label=matrix" alt="Matrix space">
|
||||
</a>
|
||||
<a href="https://hub.docker.com/r/woodpeckerci/plugin-docker-buildx" title="Docker pulls">
|
||||
<img src="https://img.shields.io/docker/pulls/woodpeckerci/plugin-docker-buildx" alt="Docker pulls">
|
||||
</a>
|
||||
<a href="https://opensource.org/licenses/Apache-2.0" title="License: Apache-2.0">
|
||||
<img src="https://img.shields.io/badge/License-Apache%202.0-blue.svg" alt="License: Apache-2.0">
|
||||
</a>
|
||||
</p>
|
||||
<br/>
|
||||
|
||||
Woodpecker CI plugin to build multiarch Docker images with buildx
|
||||
Woodpecker CI plugin to build multiarch Docker images with [buildx](https://duckduckgo.com/?q=docker+buildx&ia=web).
|
||||
This plugin was initially a fork of [thegeeklab/drone-docker-buildx](https://github.com/thegeeklab/drone-docker-buildx/) (now archived in favor of this plugin) which itself was a fork of [drone-plugins/drone-docker](https://github.com/drone-plugins/drone-docker).
|
||||
I also contains the ability to publish to AWS ECR which was previously provided by [drone-plugins/drone-ecr](https://github.com/drone-plugins/drone-docker/tree/master/cmd/drone-ecr).
|
||||
You can find the full documentation at [woodpecker-ci.org](https://woodpecker-ci.org/plugins/Docker%20Buildx) ([docs.md](./docs.md)).
|
||||
|
||||
Woodpecker CI plugin to build multiarch Docker images with buildx. This plugin is a fork of [thegeeklab/drone-docker-buildx](https://github.com/thegeeklab/drone-docker-buildx/) which itself is a fork of [drone-plugins/drone-docker](https://github.com/drone-plugins/drone-docker). You can find the full documentation at You can find the full documentation at https://woodpecker-ci.org/plugins/plugin-docker-buildx.
|
||||
## Images
|
||||
|
||||
## Contributors
|
||||
|
||||
Special thanks goes to all [contributors](https://codeberg.org/woodpecker-plugins/plugin-docker-buildx/activity). If you would like to contribute,
|
||||
please see the [instructions](https://codeberg.org/woodpecker-plugins/plugin-docker-buildx/src/branch/main/CONTRIBUTING.md).
|
||||
Images are available on [Dockerhub](https://hub.docker.com/r/woodpeckerci/plugin-docker-buildx) and in the [Codeberg registry](https://codeberg.org/woodpecker-plugins/-/packages/container/docker-buildx/latest).
|
||||
|
||||
## License
|
||||
|
||||
|
|
|
@ -99,6 +99,18 @@ func settingsFlags(settings *plugin.Settings) []cli.Flag {
|
|||
Usage: "sets content of the docker buildkit json config",
|
||||
Destination: &settings.Daemon.BuildkitConfig,
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "daemon.buildkit-debug",
|
||||
EnvVars: []string{"PLUGIN_BUILDKIT_DEBUG"},
|
||||
Usage: "enables buildkit debug",
|
||||
Destination: &settings.Daemon.BuildkitDebug,
|
||||
},
|
||||
&cli.StringSliceFlag{
|
||||
Name: "daemon.buildkit-driveropt",
|
||||
EnvVars: []string{"PLUGIN_BUILDKIT_DRIVEROPT"},
|
||||
Usage: "adds optional driver-ops args like 'env.http_proxy'",
|
||||
Destination: &settings.Daemon.BuildkitDriverOpt,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "dockerfile",
|
||||
EnvVars: []string{"PLUGIN_DOCKERFILE"},
|
||||
|
@ -117,16 +129,29 @@ func settingsFlags(settings *plugin.Settings) []cli.Flag {
|
|||
Name: "tags",
|
||||
EnvVars: []string{"PLUGIN_TAG", "PLUGIN_TAGS"},
|
||||
Usage: "sets repository tags to use for the image",
|
||||
Value: cli.NewStringSlice([]string{"latest"}...),
|
||||
Value: cli.NewStringSlice("latest"),
|
||||
FilePath: ".tags",
|
||||
Destination: &settings.Build.Tags,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "tags.file",
|
||||
EnvVars: []string{"PLUGIN_TAGS_FILE", "PLUGIN_TAG_FILE"},
|
||||
Usage: "overwrites tags flag with values find in set file",
|
||||
Destination: &settings.Build.TagsFile,
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "tags.auto",
|
||||
EnvVars: []string{"PLUGIN_DEFAULT_TAGS", "PLUGIN_AUTO_TAG"},
|
||||
EnvVars: []string{"PLUGIN_AUTO_TAG"},
|
||||
Usage: "generates tag names automatically based on git branch and git tag",
|
||||
Destination: &settings.Build.TagsAuto,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "tags.defaultName",
|
||||
EnvVars: []string{"PLUGIN_DEFAULT_TAG"},
|
||||
Usage: "allows setting an alternative to `latest` for the auto tag",
|
||||
Destination: &settings.Build.TagsDefaultName,
|
||||
Value: "latest",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "tags.suffix",
|
||||
EnvVars: []string{"PLUGIN_DEFAULT_SUFFIX", "PLUGIN_AUTO_TAG_SUFFIX"},
|
||||
|
@ -170,12 +195,24 @@ func settingsFlags(settings *plugin.Settings) []cli.Flag {
|
|||
Usage: "sets the build target to use",
|
||||
Destination: &settings.Build.Target,
|
||||
},
|
||||
&cli.StringSliceFlag{
|
||||
&cli.StringFlag{
|
||||
Name: "cache-from",
|
||||
EnvVars: []string{"PLUGIN_CACHE_FROM"},
|
||||
Usage: "sets images to consider as cache sources",
|
||||
Destination: &settings.Build.CacheFrom,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "cache-to",
|
||||
EnvVars: []string{"PLUGIN_CACHE_TO"},
|
||||
Usage: "cache destination for the build cache",
|
||||
Destination: &settings.Build.CacheTo,
|
||||
},
|
||||
&cli.StringSliceFlag{
|
||||
Name: "cache-images",
|
||||
EnvVars: []string{"PLUGIN_CACHE_IMAGES"},
|
||||
Usage: "list of images to use for build cache. applies both to and from flags for each image",
|
||||
Destination: &settings.Build.CacheImages,
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "pull-image",
|
||||
EnvVars: []string{"PLUGIN_PULL_IMAGE"},
|
||||
|
@ -264,5 +301,47 @@ func settingsFlags(settings *plugin.Settings) []cli.Flag {
|
|||
Usage: "sets build output type and destination configuration",
|
||||
Destination: &settings.Build.Output,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "ecr.aws_access_key_id",
|
||||
EnvVars: []string{"PLUGIN_AWS_ACCESS_KEY_ID"},
|
||||
Usage: "Access Key ID for AWS",
|
||||
Destination: &settings.AwsAccessKeyId,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "ecr.aws_secret_access_key_id",
|
||||
EnvVars: []string{"PLUGIN_AWS_SECRET_ACCESS_KEY"},
|
||||
Usage: "Secret Access Key for AWS",
|
||||
Destination: &settings.AwsSecretAccessKey,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "ecr.aws_region",
|
||||
EnvVars: []string{"PLUGIN_AWS_REGION"},
|
||||
Usage: "AWS region to use",
|
||||
Destination: &settings.AwsRegion,
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "ecr.create_repository",
|
||||
EnvVars: []string{"PLUGIN_ECR_CREATE_REPOSITORY"},
|
||||
Usage: "creates the ECR repository if it does not exist",
|
||||
Destination: &settings.EcrCreateRepository,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "ecr.lifecycle_policy",
|
||||
EnvVars: []string{"PLUGIN_ECR_LIFECYCLE_POLICY"},
|
||||
Usage: "AWS ECR lifecycle policy",
|
||||
Destination: &settings.EcrLifecyclePolicy,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "ecr.repository_policy",
|
||||
EnvVars: []string{"PLUGIN_ECR_REPOSITORY_POLICY"},
|
||||
Usage: "AWS ECR repository policy",
|
||||
Destination: &settings.EcrRepositoryPolicy,
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "ecr.scan_on_push",
|
||||
EnvVars: []string{"PLUGIN_ECR_SCAN_ON_PUSH"},
|
||||
Usage: "AWS: whether to enable image scanning on push",
|
||||
Destination: &settings.EcrScanOnPush,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,12 +14,18 @@ import (
|
|||
var version = "unknown"
|
||||
|
||||
func main() {
|
||||
settings := &plugin.Settings{}
|
||||
settings := &plugin.Settings{
|
||||
CustomCertStore: "/etc/docker/certs.d/",
|
||||
}
|
||||
|
||||
if _, err := os.Stat("/run/drone/env"); err == nil {
|
||||
godotenv.Overload("/run/drone/env")
|
||||
}
|
||||
|
||||
if envFile, set := os.LookupEnv("PLUGIN_ENV_FILE"); set {
|
||||
godotenv.Overload(envFile)
|
||||
}
|
||||
|
||||
app := &cli.App{
|
||||
Name: "docker-buildx",
|
||||
Usage: "build docker container with DinD and buildx",
|
||||
|
|
14
docker.svg
Normal file
14
docker.svg
Normal file
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 27.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 439 309" style="enable-background:new 0 0 439 309;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#1D63ED;}
|
||||
</style>
|
||||
<path class="st0" d="M379.6,111.7c-2.3-16.7-11.5-31.2-28.1-44.3l-9.6-6.5l-6.4,9.7c-8.2,12.5-12.3,29.9-11,46.6
|
||||
c0.6,5.8,2.5,16.4,8.4,25.5c-5.9,3.3-17.6,7.7-33.2,7.4H1.7l-0.6,3.5c-2.8,16.7-2.8,69,30.7,109.1c25.5,30.5,63.6,46,113.4,46
|
||||
c108,0,187.8-50.3,225.3-141.9c14.7,0.3,46.4,0.1,62.7-31.4c0.4-0.7,1.4-2.6,4.2-8.6l1.6-3.3l-9.1-6.2
|
||||
C419.9,110.8,397.2,108.3,379.6,111.7L379.6,111.7z M240,0h-45.3v41.7H240V0z M240,50.1h-45.3v41.7H240V50.1z M186.4,50.1h-45.3
|
||||
v41.7h45.3V50.1z M132.9,50.1H87.6v41.7h45.3V50.1z M79.3,100.2H34v41.7h45.3V100.2z M132.9,100.2H87.6v41.7h45.3V100.2z
|
||||
M186.4,100.2h-45.3v41.7h45.3V100.2z M240,100.2h-45.3v41.7H240V100.2z M293.6,100.2h-45.3v41.7h45.3V100.2z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.1 KiB |
288
docs.md
288
docs.md
|
@ -1,22 +1,21 @@
|
|||
---
|
||||
name: Docker Buildx
|
||||
icon: https://woodpecker-ci.org/img/logo.svg
|
||||
icon: https://codeberg.org/woodpecker-plugins/docker-buildx/raw/branch/main/docker.svg
|
||||
description: plugin to build multiarch Docker images with buildx
|
||||
authors: Woodpecker Authors
|
||||
author: Woodpecker Authors
|
||||
tags: [docker, image, container, build]
|
||||
containerImage: woodpeckerci/plugin-docker-buildx
|
||||
containerImageUrl: https://hub.docker.com/r/woodpeckerci/plugin-docker-buildx
|
||||
url: https://codeberg.org/woodpecker-plugins/plugin-docker-buildx
|
||||
url: https://codeberg.org/woodpecker-plugins/docker-buildx
|
||||
---
|
||||
|
||||
Woodpecker CI plugin to build multiarch Docker images with buildx. This plugin is a fork of [thegeeklab/drone-docker-buildx](https://github.com/thegeeklab/drone-docker-buildx/) which itself is a fork of [drone-plugins/drone-docker](https://github.com/drone-plugins/drone-docker). You can find the full documentation at You can find the full documentation at [woodpecker-plugins.codeberg.page](https://woodpecker-plugins.codeberg.page/plugins/drone-docker-buildx).
|
||||
Woodpecker CI plugin to build multiarch Docker images with buildx. This plugin is a fork of [thegeeklab/drone-docker-buildx](https://github.com/thegeeklab/drone-docker-buildx/) which itself is a fork of [drone-plugins/drone-docker](https://github.com/drone-plugins/drone-docker).
|
||||
|
||||
## Features
|
||||
|
||||
- Build without push
|
||||
- Use custom registries
|
||||
- Build based on existing tags when needed
|
||||
- Push to multible registries/repos
|
||||
- Push to multiple registries/repos
|
||||
|
||||
It will automatically generate buildkit configuration to use custom CA certificate if following conditions are met:
|
||||
|
||||
|
@ -28,98 +27,121 @@ It will automatically generate buildkit configuration to use custom CA certifica
|
|||
|
||||
## Settings
|
||||
|
||||
| Settings Name | Default | Description
|
||||
| --------------------------| ----------------- | --------------------------------------------
|
||||
| `dry-run` | `false` | disables docker push
|
||||
| `repo` | *none* | sets repository name for the image (can be a list)
|
||||
| `username` | *none* | sets username to authenticates with
|
||||
| `password` | *none* | sets password / token to authenticates with
|
||||
| `email` | *none* | sets email address to authenticates with
|
||||
| `registry` | `https://index.docker.io/v1/` | sets docker registry to authenticate with
|
||||
| `dockerfile` | `Dockerfile` | sets dockerfile to use for the image build
|
||||
| `tag`/`tags` | @".tags" | sets repository tags to use for the image
|
||||
| `platforms` | *none* | sets target platform for build
|
||||
|
||||
| Settings Name | Default | Description |
|
||||
| ------------------------- | ------------------------------- | ---------------------------------------------------- |
|
||||
| `dry-run` | `false` | disables docker push |
|
||||
| `repo` | _none_ | sets repository name for the image (can be a list) |
|
||||
| `username` | _none_ | sets username to authenticates with |
|
||||
| `password` | _none_ | sets password / token to authenticates with |
|
||||
| `aws_access_key_id` | _none_ | sets AWS_ACCESS_KEY_ID for AWS ECR auth |
|
||||
| `aws_secret_access_key` | _none_ | sets AWS_SECRET_ACCESS_KEY for AWS ECR auth |
|
||||
| `aws_region` | `us-east-1` | sets AWS_DEFAULT_REGION for AWS ECR auth |
|
||||
| `password` | _none_ | sets password / token to authenticates with |
|
||||
| `email` | _none_ | sets email address to authenticates with |
|
||||
| `registry` | `https://index.docker.io/v1/` | sets docker registry to authenticate with |
|
||||
| `dockerfile` | `Dockerfile` | sets dockerfile to use for the image build |
|
||||
| `tag`/`tags` | _none_ | sets repository tags to use for the image |
|
||||
| `platforms` | _none_ | sets target platform for build |
|
||||
|
||||
## auto_tag
|
||||
|
||||
If set to true, it will use the `default_tag` ("latest") on tag event or default branch.
|
||||
If it's a tag event it will also assume sem versioning and add tags accordingly (`x`, `x.x` and `x.x.x`).
|
||||
If it's not a tag event, and no default branch, automated tags are skipped.
|
||||
|
||||
## Examples
|
||||
|
||||
```yml
|
||||
publish-next-agent:
|
||||
image: woodpeckerci/plugin-docker-buildx
|
||||
secrets: [docker_username, docker_password]
|
||||
settings:
|
||||
repo: woodpeckerci/woodpecker-agent
|
||||
dockerfile: docker/Dockerfile.agent.multiarch
|
||||
platforms: windows/amd64,darwin/amd64,darwin/arm64,freebsd/amd64,linux/amd64,linux/arm64/v8
|
||||
tag: next
|
||||
when:
|
||||
branch: ${CI_REPO_DEFAULT_BRANCH}
|
||||
event: push
|
||||
```yaml
|
||||
publish-next-agent:
|
||||
image: woodpeckerci/plugin-docker-buildx
|
||||
secrets: [docker_username, docker_password]
|
||||
settings:
|
||||
repo: woodpeckerci/woodpecker-agent
|
||||
dockerfile: docker/Dockerfile.agent.multiarch
|
||||
platforms: windows/amd64,darwin/amd64,darwin/arm64,freebsd/amd64,linux/amd64,linux/arm64/v8
|
||||
tag: next
|
||||
when:
|
||||
branch: ${CI_REPO_DEFAULT_BRANCH}
|
||||
event: push
|
||||
```
|
||||
|
||||
```yml
|
||||
publish:
|
||||
image: woodpeckerci/plugin-docker-buildx
|
||||
settings:
|
||||
platforms: linux/386,linux/amd64,linux/arm/v6,linux/arm64/v8,linux/ppc64le,linux/riscv64,linux/s390x
|
||||
repo: codeberg.org/${CI_REPO_OWNER}/hello
|
||||
registry: codeberg.org
|
||||
tags: latest
|
||||
username: ${CI_REPO_OWNER}
|
||||
password:
|
||||
from_secret: cb_token
|
||||
```yaml
|
||||
publish:
|
||||
image: woodpeckerci/plugin-docker-buildx
|
||||
settings:
|
||||
platforms: linux/386,linux/amd64,linux/arm/v6,linux/arm64/v8,linux/ppc64le,linux/riscv64,linux/s390x
|
||||
repo: codeberg.org/${CI_REPO_OWNER}/hello
|
||||
registry: codeberg.org
|
||||
tags: latest
|
||||
username: ${CI_REPO_OWNER}
|
||||
password:
|
||||
from_secret: cb_token
|
||||
```
|
||||
|
||||
```yml
|
||||
docker-build:
|
||||
image: woodpeckerci/plugin-docker-buildx
|
||||
settings:
|
||||
repo: codeberg.org/${CI_REPO_OWNER}/hello
|
||||
registry: codeberg.org
|
||||
dry_run: true
|
||||
output: type=oci,dest=${CI_REPO_OWNER}-hello.tar
|
||||
```yaml
|
||||
docker-build:
|
||||
image: woodpeckerci/plugin-docker-buildx
|
||||
settings:
|
||||
repo: codeberg.org/${CI_REPO_OWNER}/hello
|
||||
registry: codeberg.org
|
||||
dry-run: true
|
||||
output: type=oci,dest=${CI_REPO_OWNER}-hello.tar
|
||||
```
|
||||
|
||||
## Advanced Settings
|
||||
|
||||
| Settings Name | Default | Description
|
||||
| --------------------------| ----------------- | --------------------------------------------
|
||||
| `mirror` | *none* | sets a registry mirror to pull images
|
||||
| `storage_driver` | *none* | sets the docker daemon storage driver
|
||||
| `storage_path` | `/var/lib/docker` | sets the docker daemon storage path
|
||||
| `bip` | *none* | allows the docker daemon to bride ip address
|
||||
| `mtu` | *none* | sets docker daemon custom mtu setting
|
||||
| `custom_dns` | *none* | sets custom docker daemon dns server
|
||||
| `custom_dns_search` | *none* | sets custom docker daemon dns search domain
|
||||
| `insecure` | `false` | allows the docker daemon to use insecure registries
|
||||
| `ipv6` | `false` | enables docker daemon IPv6 support
|
||||
| `experimental` | `false` | enables docker daemon experimental mode
|
||||
| `debug` | `false` | enables verbose debug mode for the docker daemon
|
||||
| `daemon_off` | `false` | disables the startup of the docker daemon
|
||||
| `buildkit_config` | *none* | sets content of the docker [buildkit TOML config](https://github.com/moby/buildkit/blob/master/docs/buildkitd.toml.md)
|
||||
| `context` | `.` | sets the path of the build context to use
|
||||
| `default_tags`/`auto_tag` | `false` | generates tag names automatically based on git branch and git tag
|
||||
| `default_suffix"`/`auto_tag_suffix`| *none* | generates tag names with the given suffix
|
||||
| `label`/`labels` | *none* | sets labels to use for the image in format `<name>=<value>`
|
||||
| `default_labels`/`auto_labels` | `true` | sets docker image labels based on git information
|
||||
| `build_args` | *none* | sets custom build arguments for the build
|
||||
| `build_args_from_env` | *none* | forwards environment variables as custom arguments to the build
|
||||
| `quiet` | `false` | enables suppression of the build output
|
||||
| `target` | *none* | sets the build target to use
|
||||
| `cache_from` | *none* | sets images to consider as cache sources
|
||||
| `pull_image` | `true` | enforces to pull base image at build time
|
||||
| `compress` | `false` | enables compression of the build context using gzip
|
||||
| `config` | *none* | sets content of the docker daemon json config
|
||||
| `purge` | `true` | enables cleanup of the docker environment at the end of a build
|
||||
| `no_cache` | `false` | disables the usage of cached intermediate containers
|
||||
| `add_host` | *none* | sets additional host:ip mapping
|
||||
| `output` | *none* | sets build output in format `type=<type>[,<key>=<value>]`
|
||||
| `logins` | *none* | option to log into multible registrys
|
||||
|
||||
| Settings Name | Default | Description |
|
||||
| ------------------------------------- | ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `mirror` | _none_ | sets a registry mirror to pull images |
|
||||
| `storage_driver` | _none_ | sets the docker daemon storage driver |
|
||||
| `storage_path` | `/var/lib/docker` | sets the docker daemon storage path |
|
||||
| `bip` | _none_ | allows the docker daemon to bride ip address |
|
||||
| `mtu` | _none_ | sets docker daemon custom mtu setting |
|
||||
| `custom_dns` | _none_ | sets custom docker daemon dns server |
|
||||
| `custom_dns_search` | _none_ | sets custom docker daemon dns search domain |
|
||||
| `insecure` | `false` | allows the docker daemon to use insecure registries |
|
||||
| `ipv6` | `false` | enables docker daemon IPv6 support |
|
||||
| `experimental` | `false` | enables docker daemon experimental mode |
|
||||
| `debug` | `false` | enables verbose debug mode for the docker daemon |
|
||||
| `daemon_off` | `false` | disables the startup of the docker daemon |
|
||||
| `buildkit_debug` | `false` | enables debug output of buildkit |
|
||||
| `buildkit_config` | _none_ | sets content of the docker[buildkit TOML config](https://github.com/moby/buildkit/blob/master/docs/buildkitd.toml.md) |
|
||||
| `buildkit_driveropt` | _none_ | adds one or multiple`--driver-opt` buildx arguments for the default buildkit builder instance |
|
||||
| `tags_file` | _none_ | overrides the`tags` option with values in a file named `.tags`; multiple tags can be specified separated by a newline |
|
||||
| `context` | `.` | sets the path of the build context to use |
|
||||
| `auto_tag` | `false` | generates tag names automatically based on git branch and git tag, tags supplied via`tags` are additionally added to the auto_tags without suffix |
|
||||
| `default_suffix"`/`auto_tag_suffix` | _none_ | generates tag names with the given suffix |
|
||||
| `default_tag` | `latest` | overrides the default tag name used when generating with`auto_tag` enabled |
|
||||
| `label`/`labels` | _none_ | sets labels to use for the image in format`<name>=<value>` |
|
||||
| `default_labels`/`auto_labels` | `true` | sets docker image labels based on git information |
|
||||
| `build_args` | _none_ | sets custom build arguments for the build |
|
||||
| `build_args_from_env` | _none_ | forwards environment variables as custom arguments to the build |
|
||||
| `quiet` | `false` | enables suppression of the build output |
|
||||
| `target` | _none_ | sets the build target to use |
|
||||
| `cache_from` | _none_ | sets configuration for cache source |
|
||||
| `cache_to` | _none_ | sets configuration for cache export |
|
||||
| `cache_images` | _none_ | a list of images to use as cache. |
|
||||
| `pull_image` | `true` | enforces to pull base image at build time |
|
||||
| `compress` | `false` | enables compression of the build context using gzip |
|
||||
| `config` | _none_ | sets content of the docker daemon json config |
|
||||
| `purge` | `true` | enables cleanup of the docker environment at the end of a build |
|
||||
| `no_cache` | `false` | disables the usage of cached intermediate containers |
|
||||
| `add_host` | _none_ | sets additional host:ip mapping |
|
||||
| `output` | _none_ | sets build output in format`type=<type>[,<key>=<value>]` |
|
||||
| `logins` | _none_ | option to log into multiple registries |
|
||||
| `env_file` | _none_ | load env vars from specified file |
|
||||
| `ecr_create_repository` | `false` | creates the ECR repository if it does not exist |
|
||||
| `ecr_lifecycle_policy` | _none_ | AWS ECR lifecycle policy |
|
||||
| `ecr_repository_policy` | _none_ | AWS ECR repository policy |
|
||||
| `ecr_scan_on_push` | _none_ | AWS: whether to enable image scanning on push |
|
||||
|
||||
## Multi registry push example
|
||||
|
||||
Only supported with `woodpecker >= 1.0.0` (next-da997fa3).
|
||||
|
||||
```yml
|
||||
```yaml
|
||||
settings:
|
||||
repo: a6543/tmp,codeberg.org/6543/tmp
|
||||
tag: demo
|
||||
|
@ -128,8 +150,110 @@ settings:
|
|||
username: a6543
|
||||
password:
|
||||
from_secret: docker_token
|
||||
mirrors:
|
||||
- "my-docker-mirror-host.local"
|
||||
- registry: https://codeberg.org
|
||||
username: "6543"
|
||||
password:
|
||||
from_secret: cb_token
|
||||
- registry: https://<account-id>.dkr.ecr.<region>.amazonaws.com
|
||||
aws_region: <region>
|
||||
aws_access_key_id:
|
||||
from_secret: aws_access_key_id
|
||||
aws_secret_access_key:
|
||||
from_secret: aws_secret_access_key
|
||||
```
|
||||
|
||||
## Using `plugin-docker-buildx` behind a proxy
|
||||
|
||||
When performing a docker build behind a corporate proxy one needs to pass through the proxy settings to the plugin.
|
||||
|
||||
```yaml
|
||||
variables:
|
||||
# proxy config
|
||||
- proxy_conf: &proxy_conf
|
||||
- http_proxy: "http://X.Y.Z.Z:3128"
|
||||
- https_proxy: "http://X.Y.Z.Z:3128"
|
||||
- no_proxy: ".my-subdomain.com"
|
||||
# deployment targets
|
||||
- &publish_repos "codeberg.org/test"
|
||||
# logins for deployment targets
|
||||
- publish_logins: &publish_logins
|
||||
- registry: https://codeberg.org
|
||||
username:
|
||||
from_secret: CODEBERG_USER
|
||||
password:
|
||||
from_secret: CODEBERG_TOKEN
|
||||
|
||||
steps:
|
||||
test:
|
||||
image: woodpeckerci/plugin-docker-buildx:2
|
||||
environment:
|
||||
# adding proxy in env for the plugin runtime itself.
|
||||
- <<: *proxy_conf
|
||||
privileged: true
|
||||
settings:
|
||||
dry-run: true
|
||||
repo: *publish_repos
|
||||
dockerfile: Dockerfile.multi
|
||||
platforms: linux/amd64
|
||||
auto_tag: true
|
||||
logins: *publish_logins
|
||||
# Adding custom dns server to lookup internal Docker Hub mirror.
|
||||
# custom_dns:
|
||||
# - 192.168.55.31
|
||||
# - 192.168.55.32
|
||||
# Adding an optional Docker Hub mirror for the nested dockerd.
|
||||
# mirror: https://my-mirror.example.com
|
||||
build_args:
|
||||
# passthrough proxy config to the build process and Dockerfile CMDs itself.
|
||||
- <<: *proxy_conf
|
||||
# add driver-opt http config to tell buildkit + buildx to resolve external checksums through a proxy.
|
||||
buildkit_driveropt:
|
||||
- "env.http_proxy=http://X.Y.Z.Z:3128"
|
||||
- "env.https_proxy=http://X.Y.Z.Z:3128"
|
||||
- "env.no_proxy=.my-subdomain.com"
|
||||
```
|
||||
|
||||
## Using cache images
|
||||
|
||||
You can provide a list of images to use for cache.
|
||||
These cache images are built with mode=max, image-manifest=true, and oci-mediatypes=true.
|
||||
This is to provide better usage of cache and better compatibility with image stores like Harbor.
|
||||
|
||||
```yaml
|
||||
steps:
|
||||
build:
|
||||
image: woodpeckerci/plugin-docker-buildx
|
||||
settings:
|
||||
repo: hari/radiant
|
||||
cache_images:
|
||||
- hari/radiant:cache
|
||||
- harbor.example.com/hari/radiant:cache
|
||||
logins:
|
||||
- registry: https://index.docker.io/v1/
|
||||
username: hari
|
||||
password:
|
||||
from_secret: docker_password
|
||||
- registry: https://harbor.example.com
|
||||
username: hari
|
||||
password:
|
||||
from_secret: harbor_password
|
||||
```
|
||||
|
||||
## Using other cache types
|
||||
|
||||
You can specify cache_to and cache_from to use specific settings.
|
||||
For example you can configure an s3 object as cache.
|
||||
|
||||
More details can be found [in the docker docs](https://docs.docker.com/build/cache/backends/).
|
||||
|
||||
```yaml
|
||||
steps:
|
||||
build:
|
||||
image: woodpeckerci/plugin-docker-buildx
|
||||
settings:
|
||||
repo: hari/radiant
|
||||
cache_to: type=s3,region=east,bucket=mystuff,name=radiant-cache
|
||||
cache_from: type=s3,region=east,bucket=mystuff,name=radiant-cache
|
||||
```
|
||||
|
|
32
go.mod
32
go.mod
|
@ -1,24 +1,32 @@
|
|||
module codeberg.org/woodpecker-plugins/plugin-docker-buildx
|
||||
|
||||
go 1.18
|
||||
go 1.20
|
||||
|
||||
require (
|
||||
github.com/coreos/go-semver v0.3.0
|
||||
github.com/drone-plugins/drone-plugin-lib v0.4.0
|
||||
github.com/joho/godotenv v1.4.0
|
||||
github.com/sirupsen/logrus v1.9.0
|
||||
github.com/urfave/cli/v2 v2.16.3
|
||||
codeberg.org/6543/go-yaml2json v1.0.0
|
||||
github.com/6543/go-version v1.3.1
|
||||
github.com/aws/aws-sdk-go v1.50.13
|
||||
github.com/drone-plugins/drone-plugin-lib v0.4.2
|
||||
github.com/joho/godotenv v1.5.1
|
||||
github.com/pelletier/go-toml/v2 v2.1.1
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
github.com/stretchr/testify v1.8.4
|
||||
github.com/urfave/cli/v2 v2.27.1
|
||||
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616
|
||||
honnef.co/go/tools v0.3.3
|
||||
honnef.co/go/tools v0.4.6
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/BurntSushi/toml v1.1.0 // indirect
|
||||
github.com/BurntSushi/toml v1.3.2 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
|
||||
golang.org/x/exp/typeparams v0.0.0-20220218215828-6cf2b201936e // indirect
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // indirect
|
||||
golang.org/x/tools v0.1.11-0.20220513221640-090b14e8501f // indirect
|
||||
golang.org/x/exp/typeparams v0.0.0-20221208152030-732eee02a75a // indirect
|
||||
golang.org/x/mod v0.12.0 // indirect
|
||||
golang.org/x/sys v0.12.0 // indirect
|
||||
golang.org/x/tools v0.12.1-0.20230825192346-2191a27a6dc5 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
|
88
go.sum
88
go.sum
|
@ -1,69 +1,79 @@
|
|||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I=
|
||||
github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||
github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM=
|
||||
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
codeberg.org/6543/go-yaml2json v1.0.0 h1:heGqo9VEi7gY2yNqjj7X4ADs5nzlFIbGsJtgYDLrnig=
|
||||
codeberg.org/6543/go-yaml2json v1.0.0/go.mod h1:mz61q14LWF4ZABrgMEDMmk3t9dPi6zgR1uBh2VKV2RQ=
|
||||
github.com/6543/go-version v1.3.1 h1:HvOp+Telns7HWJ2Xo/05YXQSB2bE0WmVgbHqwMPZT4U=
|
||||
github.com/6543/go-version v1.3.1/go.mod h1:oqFAHCwtLVUTLdhQmVZWYvaHXTdsbB4SY85at64SQEo=
|
||||
github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
|
||||
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||
github.com/aws/aws-sdk-go v1.49.17 h1:Cc+7LgPjKeJkF2SdNo1IkpQ5Dfl9HCZEVw9OP3CPuEI=
|
||||
github.com/aws/aws-sdk-go v1.49.17/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk=
|
||||
github.com/aws/aws-sdk-go v1.49.22 h1:r01+cQJ3cORQI1PJxG8af0jzrZpUOL9L+/3kU2x1geU=
|
||||
github.com/aws/aws-sdk-go v1.49.22/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk=
|
||||
github.com/aws/aws-sdk-go v1.50.5 h1:H2Aadcgwr7a2aqS6ZwcE+l1mA6ZrTseYCvjw2QLmxIA=
|
||||
github.com/aws/aws-sdk-go v1.50.5/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk=
|
||||
github.com/aws/aws-sdk-go v1.50.13 h1:yeXram2g7q8uKkQkAEeZyk9FmPzxI4UpGwAZGZtEGmM=
|
||||
github.com/aws/aws-sdk-go v1.50.13/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/drone-plugins/drone-plugin-lib v0.4.0 h1:qywEYGhquUuid6zNLmKia8CWY1TUa8jPQQ/G9ozfAmc=
|
||||
github.com/drone-plugins/drone-plugin-lib v0.4.0/go.mod h1:EgqogX38GoJFtckeSQyhBJYX8P+KWBPhdprAVvyRxF8=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg=
|
||||
github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/drone-plugins/drone-plugin-lib v0.4.2 h1:EiJ3Kco6ypP5noBQqVt1bBbuO1eUAumtPvLTX/NVAYg=
|
||||
github.com/drone-plugins/drone-plugin-lib v0.4.2/go.mod h1:KwCu92jFjHV3xv2hu5Qg/8zBNvGwbhoJDQw/EwnTvoM=
|
||||
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
|
||||
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
|
||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
|
||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
|
||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||
github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI=
|
||||
github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
||||
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
|
||||
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/urfave/cli/v2 v2.2.0/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ=
|
||||
github.com/urfave/cli/v2 v2.16.3 h1:gHoFIwpPjoyIMbJp/VFd+/vuD0dAgFK4B6DpEMFJfQk=
|
||||
github.com/urfave/cli/v2 v2.16.3/go.mod h1:1CNUng3PtjQMtRzJO4FMXBQvkGtuYRxxiR9xMa7jMwI=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/urfave/cli/v2 v2.27.1 h1:8xSQ6szndafKVRmfyeUMxkNUJQMjL1F2zmsZ+qHpfho=
|
||||
github.com/urfave/cli/v2 v2.27.1/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ=
|
||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
|
||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/exp/typeparams v0.0.0-20220218215828-6cf2b201936e h1:qyrTQ++p1afMkO4DPEeLGq/3oTsdlvdH4vqZUBWzUKM=
|
||||
golang.org/x/exp/typeparams v0.0.0-20220218215828-6cf2b201936e/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
|
||||
golang.org/x/exp/typeparams v0.0.0-20221208152030-732eee02a75a h1:Jw5wfR+h9mnIYH+OtGT2im5wV1YGGDora5vTv/aa5bE=
|
||||
golang.org/x/exp/typeparams v0.0.0-20221208152030-732eee02a75a/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
|
||||
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug=
|
||||
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
|
||||
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.1.11-0.20220513221640-090b14e8501f h1:OKYpQQVE3DKSc3r3zHVzq46vq5YH7x8xpR3/k9ixmUg=
|
||||
golang.org/x/tools v0.1.11-0.20220513221640-090b14e8501f/go.mod h1:SgwaegtQh8clINPpECJMqnxLv9I09HLqnW3RMqW0CA4=
|
||||
golang.org/x/tools v0.12.1-0.20230825192346-2191a27a6dc5 h1:Vk4mysSz+GqQK2eqgWbo4zEO89wkeAjJiFIr9bpqa8k=
|
||||
golang.org/x/tools v0.12.1-0.20230825192346-2191a27a6dc5/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=
|
||||
honnef.co/go/tools v0.3.3 h1:oDx7VAwstgpYpb3wv0oxiZlxY+foCpRAwY7Vk6XpAgA=
|
||||
honnef.co/go/tools v0.3.3/go.mod h1:jzwdWgg7Jdq75wlfblQxO4neNaFFSvgc1tD5Wv8U0Yw=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.4.6 h1:oFEHCKeID7to/3autwsWfnuv69j3NsfcXbvJKuIcep8=
|
||||
honnef.co/go/tools v0.4.6/go.mod h1:+rnGS1THNh8zMwnd2oVOTL9QF6vmfyG6ZXBULae2uc0=
|
||||
|
|
48
plugin/coredns.go
Normal file
48
plugin/coredns.go
Normal file
|
@ -0,0 +1,48 @@
|
|||
package plugin
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
func (p Plugin) startCoredns() {
|
||||
cmd := exec.Command("coredns", "-conf", "/etc/coredns/Corefile")
|
||||
if p.settings.Daemon.Debug {
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
} else {
|
||||
cmd.Stdout = io.Discard
|
||||
cmd.Stderr = io.Discard
|
||||
}
|
||||
go func() {
|
||||
trace(cmd)
|
||||
cmd.Run()
|
||||
}()
|
||||
}
|
||||
|
||||
func getContainerIP() (string, error) {
|
||||
ifaces, err := net.Interfaces()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
for _, i := range ifaces {
|
||||
addrs, err := i.Addrs()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
for _, addr := range addrs {
|
||||
switch v := addr.(type) {
|
||||
case *net.IPNet:
|
||||
if v.IP.IsLoopback() {
|
||||
continue
|
||||
}
|
||||
if v.IP.To4() != nil {
|
||||
return v.IP.String(), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return "", nil
|
||||
}
|
|
@ -6,11 +6,10 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
dockerExe = "/usr/local/bin/docker"
|
||||
dockerdExe = "/usr/local/bin/dockerd"
|
||||
dockerHome = "/root/.docker/"
|
||||
buildkitConfig = "/tmp/buildkit.toml"
|
||||
buildkitConfigTemplate = "[registry.\"%s\"]\n ca=[\"%s\"]\n"
|
||||
dockerExe = "/usr/local/bin/docker"
|
||||
dockerdExe = "/usr/local/bin/dockerd"
|
||||
dockerHome = "/root/.docker/"
|
||||
buildkitConfig = "/tmp/buildkit.toml"
|
||||
)
|
||||
|
||||
func (p Plugin) startDaemon() {
|
||||
|
|
|
@ -10,15 +10,6 @@ import (
|
|||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
// helper to check if args match "docker pull <image>"
|
||||
func isCommandPull(args []string) bool {
|
||||
return len(args) > 2 && args[1] == "pull"
|
||||
}
|
||||
|
||||
func commandPull(repo string) *exec.Cmd {
|
||||
return exec.Command(dockerExe, "pull", repo)
|
||||
}
|
||||
|
||||
func commandLoginEmail(login Login) *exec.Cmd {
|
||||
return exec.Command(
|
||||
dockerExe, "login",
|
||||
|
@ -50,6 +41,10 @@ func commandBuilder(daemon Daemon) *exec.Cmd {
|
|||
args = append(args, "--config", buildkitConfig)
|
||||
}
|
||||
|
||||
for _, driveropt := range daemon.BuildkitDriverOpt.Value() {
|
||||
args = append(args, "--driver-opt", driveropt)
|
||||
}
|
||||
|
||||
return exec.Command(dockerExe, args...)
|
||||
}
|
||||
|
||||
|
@ -83,8 +78,15 @@ func commandBuild(build Build, dryrun bool) *exec.Cmd {
|
|||
if build.NoCache {
|
||||
args = append(args, "--no-cache")
|
||||
}
|
||||
for _, arg := range build.CacheFrom.Value() {
|
||||
if build.CacheFrom != "" {
|
||||
args = append(args, "--cache-from", build.CacheFrom)
|
||||
}
|
||||
if build.CacheTo != "" {
|
||||
args = append(args, "--cache-to", build.CacheTo)
|
||||
}
|
||||
for _, arg := range build.CacheImages.Value() {
|
||||
args = append(args, "--cache-from", arg)
|
||||
args = append(args, string("--cache-to=type=registry,ref="+arg+",mode=max,image-manifest=true,oci-mediatypes=true"))
|
||||
}
|
||||
for _, arg := range build.ArgsEnv.Value() {
|
||||
addProxyValue(&build, arg)
|
||||
|
@ -181,9 +183,6 @@ func commandDaemon(daemon Daemon) *exec.Cmd {
|
|||
if daemon.IPv6 {
|
||||
args = append(args, "--ipv6")
|
||||
}
|
||||
if len(daemon.Mirror) != 0 {
|
||||
args = append(args, "--registry-mirror", daemon.Mirror)
|
||||
}
|
||||
if len(daemon.Bip) != 0 {
|
||||
args = append(args, "--bip", daemon.Bip)
|
||||
}
|
||||
|
|
69
plugin/docker_test.go
Normal file
69
plugin/docker_test.go
Normal file
|
@ -0,0 +1,69 @@
|
|||
package plugin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
func TestCommandBuilder(t *testing.T) {
|
||||
tests := []struct {
|
||||
Name string
|
||||
Daemon Daemon
|
||||
Input string
|
||||
WantedLen int
|
||||
Skip bool
|
||||
Excuse string
|
||||
}{
|
||||
{
|
||||
Name: "Single driver-opt value",
|
||||
Daemon: Daemon{},
|
||||
Input: "no_proxy=*.mydomain",
|
||||
WantedLen: 1,
|
||||
},
|
||||
{
|
||||
Name: "Single driver-opt value with comma",
|
||||
Input: "no_proxy=.mydomain,.sub.domain.com",
|
||||
WantedLen: 1,
|
||||
Skip: true,
|
||||
Excuse: "Can be enabled whenever #94 is fixed.",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.Name, func(t *testing.T) {
|
||||
if test.Skip {
|
||||
t.Skip(fmt.Printf("%v skipped. %v", test.Name, test.Excuse))
|
||||
}
|
||||
// prepare test values to mock plugin call with settings
|
||||
os.Setenv("PLUGIN_BUILDKIT_DRIVEROPT", test.Input)
|
||||
|
||||
// create dummy cli app to reproduce the issue
|
||||
app := &cli.App{
|
||||
Name: "dummy App",
|
||||
Usage: "testing inputs",
|
||||
Version: "0.0.1",
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringSliceFlag{
|
||||
Name: "daemon.buildkit-driveropt",
|
||||
EnvVars: []string{"PLUGIN_BUILDKIT_DRIVEROPT"},
|
||||
Usage: "adds optional driver-ops args like 'env.http_proxy'",
|
||||
Destination: &test.Daemon.BuildkitDriverOpt,
|
||||
},
|
||||
},
|
||||
Action: nil,
|
||||
}
|
||||
|
||||
// need to run the app to resolve the flags
|
||||
_ = app.Run(nil)
|
||||
|
||||
// call the commandBuilder to prepare the cmd with its args
|
||||
_ = commandBuilder(test.Daemon)
|
||||
|
||||
assert.Len(t, test.Daemon.BuildkitDriverOpt.Value(), test.WantedLen)
|
||||
})
|
||||
}
|
||||
}
|
226
plugin/ecr.go
Normal file
226
plugin/ecr.go
Normal file
|
@ -0,0 +1,226 @@
|
|||
// Source: https://github.com/drone-plugins/drone-docker/tree/939591f01828eceae54f5768dc7ce08ad0ad0bba/cmd/drone-ecr
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||
"github.com/aws/aws-sdk-go/aws/credentials/stscreds"
|
||||
"github.com/aws/aws-sdk-go/aws/session"
|
||||
"github.com/aws/aws-sdk-go/service/ecr"
|
||||
)
|
||||
|
||||
const DefaultRegion = "us-east-1"
|
||||
|
||||
var (
|
||||
repo string
|
||||
assumeRole string
|
||||
externalID string
|
||||
ecr_login Login
|
||||
aws_region string
|
||||
)
|
||||
|
||||
func (p *Plugin) EcrInit() {
|
||||
// create a standalone Login object to account for single repo and multi-repo case
|
||||
if len(p.settings.Logins) >= 1 {
|
||||
for _, login := range p.settings.Logins {
|
||||
if strings.Contains(login.Registry, "amazonaws.com") {
|
||||
ecr_login = login
|
||||
aws_region = login.Aws_region
|
||||
|
||||
// filter repo containing ecr registry
|
||||
substrings := make([]string, 0)
|
||||
for _, repo := range p.settings.Build.Repo.Value() {
|
||||
substrings = append(substrings, strings.Split(repo, ",")...)
|
||||
}
|
||||
filtered := make([]string, 0)
|
||||
for _, s := range substrings {
|
||||
if strings.Contains(s, "amazonaws.com") {
|
||||
filtered = append(filtered, s)
|
||||
}
|
||||
}
|
||||
|
||||
// Join the filtered substrings into a comma-separated string
|
||||
repo = strings.Join(filtered, ",")
|
||||
|
||||
// set the region
|
||||
if aws_region == "" {
|
||||
aws_region = DefaultRegion
|
||||
}
|
||||
|
||||
os.Setenv("AWS_REGION", aws_region)
|
||||
os.Setenv("AWS_ACCESS_KEY_ID", ecr_login.Aws_access_key_id)
|
||||
os.Setenv("AWS_SECRET_ACCESS_KEY", ecr_login.Aws_secret_access_key)
|
||||
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ecr_login.Aws_access_key_id = p.settings.AwsAccessKeyId
|
||||
ecr_login.Aws_secret_access_key = p.settings.AwsSecretAccessKey
|
||||
aws_region = p.settings.AwsRegion
|
||||
repo = p.settings.Build.Repo.Value()[0]
|
||||
|
||||
// set the region
|
||||
if aws_region == "" {
|
||||
aws_region = DefaultRegion
|
||||
}
|
||||
|
||||
os.Setenv("AWS_REGION", p.settings.AwsRegion)
|
||||
os.Setenv("AWS_ACCESS_KEY_ID", p.settings.AwsAccessKeyId)
|
||||
os.Setenv("AWS_SECRET_ACCESS_KEY", p.settings.AwsSecretAccessKey)
|
||||
}
|
||||
// here the env vars are used for authentication
|
||||
sess, err := session.NewSession(&aws.Config{Region: &aws_region})
|
||||
if err != nil {
|
||||
log.Fatalf("error creating aws session: %v", err)
|
||||
}
|
||||
|
||||
svc := getECRClient(sess, assumeRole, externalID)
|
||||
username, password, registry, err := getAuthInfo(svc)
|
||||
if err != nil {
|
||||
log.Fatalf("error getting ECR auth: %v", err)
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(repo, registry) {
|
||||
repo = fmt.Sprintf("%s/%s", registry, repo)
|
||||
}
|
||||
|
||||
if p.settings.EcrCreateRepository {
|
||||
err = ensureRepoExists(svc, trimHostname(repo, registry), p.settings.EcrScanOnPush)
|
||||
if err != nil {
|
||||
log.Fatalf("error creating ECR repo: %v", err)
|
||||
}
|
||||
err = updateImageScannningConfig(svc, trimHostname(repo, registry), p.settings.EcrScanOnPush)
|
||||
if err != nil {
|
||||
log.Fatalf("error updating scan on push for ECR repo: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
if p.settings.EcrLifecyclePolicy != "" {
|
||||
p, err := os.ReadFile(p.settings.EcrLifecyclePolicy)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if err := uploadLifeCyclePolicy(svc, string(p), trimHostname(repo, registry)); err != nil {
|
||||
log.Fatalf("error uploading ECR lifecycle policy: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
if p.settings.EcrRepositoryPolicy != "" {
|
||||
p, err := os.ReadFile(p.settings.EcrRepositoryPolicy)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if err := uploadRepositoryPolicy(svc, string(p), trimHostname(repo, registry)); err != nil {
|
||||
log.Fatalf("error uploading ECR repository policy. %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// set Username and Password for all Login which contain an AWS key
|
||||
if len(p.settings.Logins) >= 1 {
|
||||
for i, login := range p.settings.Logins {
|
||||
if login.Aws_secret_access_key != "" && login.Aws_access_key_id != "" {
|
||||
p.settings.Logins[i].Username = username
|
||||
p.settings.Logins[i].Password = password
|
||||
p.settings.Logins[i].Registry = registry
|
||||
}
|
||||
}
|
||||
} else {
|
||||
p.settings.DefaultLogin.Username = username
|
||||
p.settings.DefaultLogin.Password = password
|
||||
p.settings.DefaultLogin.Registry = registry
|
||||
}
|
||||
}
|
||||
|
||||
func trimHostname(repo, registry string) string {
|
||||
repo = strings.TrimPrefix(repo, registry)
|
||||
repo = strings.TrimLeft(repo, "/")
|
||||
return repo
|
||||
}
|
||||
|
||||
func ensureRepoExists(svc *ecr.ECR, name string, scanOnPush bool) (err error) {
|
||||
input := &ecr.CreateRepositoryInput{}
|
||||
input.SetRepositoryName(name)
|
||||
input.SetImageScanningConfiguration(&ecr.ImageScanningConfiguration{ScanOnPush: &scanOnPush})
|
||||
_, err = svc.CreateRepository(input)
|
||||
if err != nil {
|
||||
if aerr, ok := err.(awserr.Error); ok && aerr.Code() == ecr.ErrCodeRepositoryAlreadyExistsException {
|
||||
// eat it, we skip checking for existing to save two requests
|
||||
err = nil
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func updateImageScannningConfig(svc *ecr.ECR, name string, scanOnPush bool) (err error) {
|
||||
input := &ecr.PutImageScanningConfigurationInput{}
|
||||
input.SetRepositoryName(name)
|
||||
input.SetImageScanningConfiguration(&ecr.ImageScanningConfiguration{ScanOnPush: &scanOnPush})
|
||||
_, err = svc.PutImageScanningConfiguration(input)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func uploadLifeCyclePolicy(svc *ecr.ECR, lifecyclePolicy, name string) (err error) {
|
||||
input := &ecr.PutLifecyclePolicyInput{}
|
||||
input.SetLifecyclePolicyText(lifecyclePolicy)
|
||||
input.SetRepositoryName(name)
|
||||
_, err = svc.PutLifecyclePolicy(input)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func uploadRepositoryPolicy(svc *ecr.ECR, repositoryPolicy, name string) (err error) {
|
||||
input := &ecr.SetRepositoryPolicyInput{}
|
||||
input.SetPolicyText(repositoryPolicy)
|
||||
input.SetRepositoryName(name)
|
||||
_, err = svc.SetRepositoryPolicy(input)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func getAuthInfo(svc *ecr.ECR) (username, password, registry string, err error) {
|
||||
var result *ecr.GetAuthorizationTokenOutput
|
||||
var decoded []byte
|
||||
|
||||
result, err = svc.GetAuthorizationToken(&ecr.GetAuthorizationTokenInput{})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
auth := result.AuthorizationData[0]
|
||||
token := *auth.AuthorizationToken
|
||||
decoded, err = base64.StdEncoding.DecodeString(token)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
registry = strings.TrimPrefix(*auth.ProxyEndpoint, "https://")
|
||||
creds := strings.Split(string(decoded), ":")
|
||||
username = creds[0]
|
||||
password = creds[1]
|
||||
return
|
||||
}
|
||||
|
||||
func getECRClient(sess *session.Session, role, externalId string) *ecr.ECR {
|
||||
if role == "" {
|
||||
return ecr.New(sess)
|
||||
}
|
||||
if externalId != "" {
|
||||
return ecr.New(sess, &aws.Config{
|
||||
Credentials: stscreds.NewCredentials(sess, role, func(p *stscreds.AssumeRoleProvider) {
|
||||
p.ExternalID = &externalId
|
||||
}),
|
||||
})
|
||||
} else {
|
||||
return ecr.New(sess, &aws.Config{
|
||||
Credentials: stscreds.NewCredentials(sess, role),
|
||||
})
|
||||
}
|
||||
}
|
314
plugin/impl.go
314
plugin/impl.go
|
@ -10,72 +10,98 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/pelletier/go-toml/v2"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/urfave/cli/v2"
|
||||
|
||||
"codeberg.org/woodpecker-plugins/plugin-docker-buildx/utils"
|
||||
)
|
||||
|
||||
// Daemon defines Docker daemon parameters.
|
||||
type Daemon struct {
|
||||
Registry string // Docker registry
|
||||
Mirror string // Docker registry mirror
|
||||
Insecure bool // Docker daemon enable insecure registries
|
||||
StorageDriver string // Docker daemon storage driver
|
||||
StoragePath string // Docker daemon storage path
|
||||
Disabled bool // DOcker daemon is disabled (already running)
|
||||
Debug bool // Docker daemon started in debug mode
|
||||
Bip string // Docker daemon network bridge IP address
|
||||
DNS cli.StringSlice // Docker daemon dns server
|
||||
DNSSearch cli.StringSlice // Docker daemon dns search domain
|
||||
MTU string // Docker daemon mtu setting
|
||||
IPv6 bool // Docker daemon IPv6 networking
|
||||
Experimental bool // Docker daemon enable experimental mode
|
||||
BuildkitConfig string // Docker buildkit config
|
||||
Registry string // Docker registry
|
||||
Mirror string // Docker registry mirror
|
||||
Insecure bool // Docker daemon enable insecure registries
|
||||
StorageDriver string // Docker daemon storage driver
|
||||
StoragePath string // Docker daemon storage path
|
||||
Disabled bool // Docker daemon is disabled (already running)
|
||||
Debug bool // Docker daemon started in debug mode
|
||||
Bip string // Docker daemon network bridge IP address
|
||||
DNS cli.StringSlice // Docker daemon dns server
|
||||
DNSSearch cli.StringSlice // Docker daemon dns search domain
|
||||
MTU string // Docker daemon mtu setting
|
||||
IPv6 bool // Docker daemon IPv6 networking
|
||||
Experimental bool // Docker daemon enable experimental mode
|
||||
BuildkitConfig string // Docker buildkit config
|
||||
BuildkitDriverOpt cli.StringSlice // Docker buildkit driveropt args
|
||||
BuildkitDebug bool // Docker buildkit debug setting
|
||||
}
|
||||
|
||||
// Login defines Docker login parameters.
|
||||
type Login struct {
|
||||
Registry string // Docker registry address
|
||||
Username string // Docker registry username
|
||||
Password string // Docker registry password
|
||||
Email string // Docker registry email
|
||||
Config string // Docker Auth Config
|
||||
// Generic
|
||||
Registry string // Docker registry address
|
||||
Username string // Docker registry username
|
||||
Password string // Docker registry password
|
||||
Email string // Docker registry email
|
||||
Config string // Docker Auth Config
|
||||
Mirrors []string // Docker registry mirrors
|
||||
// ECR
|
||||
Aws_access_key_id string `json:"aws_access_key_id"` // AWS access key id
|
||||
Aws_secret_access_key string `json:"aws_secret_access_key"` // AWS secret access key
|
||||
Aws_region string `json:"aws_region"` // AWS region
|
||||
}
|
||||
|
||||
// Build defines Docker build parameters.
|
||||
type Build struct {
|
||||
Remote string // Git remote URL
|
||||
Ref string // Git commit ref
|
||||
Branch string // Git repository branch
|
||||
Dockerfile string // Docker build Dockerfile
|
||||
Context string // Docker build context
|
||||
TagsAuto bool // Docker build auto tag
|
||||
TagsSuffix string // Docker build tags with suffix
|
||||
Tags cli.StringSlice // Docker build tags
|
||||
LabelsAuto bool // Docker build auto labels
|
||||
Labels cli.StringSlice // Docker build labels
|
||||
Platforms cli.StringSlice // Docker build target platforms
|
||||
Args cli.StringSlice // Docker build args
|
||||
ArgsEnv cli.StringSlice // Docker build args from env
|
||||
Target string // Docker build target
|
||||
Output string // Docker build output
|
||||
Pull bool // Docker build pull
|
||||
CacheFrom cli.StringSlice // Docker build cache-from
|
||||
Compress bool // Docker build compress
|
||||
Repo cli.StringSlice // Docker build repository
|
||||
NoCache bool // Docker build no-cache
|
||||
AddHost cli.StringSlice // Docker build add-host
|
||||
Quiet bool // Docker build quiet
|
||||
Remote string // Git remote URL
|
||||
Ref string // Git commit ref
|
||||
Branch string // Git repository branch
|
||||
Dockerfile string // Docker build Dockerfile
|
||||
Context string // Docker build context
|
||||
TagsAuto bool // Docker build auto tag
|
||||
TagsDefaultName string // Docker build auto tag name override
|
||||
TagsSuffix string // Docker build tags with suffix
|
||||
Tags cli.StringSlice // Docker build tags
|
||||
TagsFile string // Docker build tags read from an file
|
||||
LabelsAuto bool // Docker build auto labels
|
||||
Labels cli.StringSlice // Docker build labels
|
||||
Platforms cli.StringSlice // Docker build target platforms
|
||||
Args cli.StringSlice // Docker build args
|
||||
ArgsEnv cli.StringSlice // Docker build args from env
|
||||
Target string // Docker build target
|
||||
Output string // Docker build output
|
||||
Pull bool // Docker build pull
|
||||
CacheFrom string // Docker build cache-from
|
||||
CacheTo string // Docker build cache-to
|
||||
CacheImages cli.StringSlice // Docker build cache images
|
||||
Compress bool // Docker build compress
|
||||
Repo cli.StringSlice // Docker build repository
|
||||
NoCache bool // Docker build no-cache
|
||||
AddHost cli.StringSlice // Docker build add-host
|
||||
Quiet bool // Docker build quiet
|
||||
}
|
||||
|
||||
// Settings for the Plugin.
|
||||
type Settings struct {
|
||||
Daemon Daemon
|
||||
Logins []Login
|
||||
LoginsRaw string
|
||||
DefaultLogin Login
|
||||
Build Build
|
||||
Dryrun bool
|
||||
Cleanup bool
|
||||
// ECR
|
||||
AwsRegion string `json:"aws_region"` // AWS region
|
||||
EcrScanOnPush bool `json:"ecr_scan_on_push"` // ECR scan on push
|
||||
EcrRepositoryPolicy string `json:"ecr_repository_policy"` // ECR repository policy
|
||||
EcrLifecyclePolicy string `json:"ecr_lifecycle_policy"` // ECR lifecycle policy
|
||||
EcrCreateRepository bool `json:"ecr_create_repository"` // ECR create repository
|
||||
AwsAccessKeyId string `json:"aws_access_key_id"` // AWS access key id
|
||||
AwsSecretAccessKey string `json:"aws_secret_access_key"` // AWS secret access key
|
||||
|
||||
// Generic
|
||||
Daemon Daemon
|
||||
Logins []Login
|
||||
LoginsRaw string
|
||||
DefaultLogin Login
|
||||
Build Build
|
||||
Dryrun bool
|
||||
Cleanup bool
|
||||
CustomCertStore string // e.g. for "/etc/docker/certs.d/<registry>/ca.crt"
|
||||
}
|
||||
|
||||
func (l Login) anonymous() bool {
|
||||
|
@ -84,15 +110,33 @@ func (l Login) anonymous() bool {
|
|||
|
||||
// Init initialise plugin settings
|
||||
func (p *Plugin) InitSettings() error {
|
||||
if err := json.Unmarshal([]byte(p.settings.LoginsRaw), &p.settings.Logins); err != nil {
|
||||
return fmt.Errorf("Could not unmarshal logins: %v", err)
|
||||
if p.settings.LoginsRaw != "" {
|
||||
if err := json.Unmarshal([]byte(p.settings.LoginsRaw), &p.settings.Logins); err != nil {
|
||||
return fmt.Errorf("could not unmarshal logins: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
p.settings.Build.Branch = p.pipeline.Repo.Branch
|
||||
p.settings.Build.Ref = p.pipeline.Commit.Ref
|
||||
if p.settings.DefaultLogin.anonymous() {
|
||||
p.settings.Logins = append(p.settings.Logins, p.settings.DefaultLogin)
|
||||
} else {
|
||||
|
||||
// check if any Login struct contains AWS credentials
|
||||
for _, login := range p.settings.Logins {
|
||||
if strings.Contains(login.Registry, "amazonaws.com") {
|
||||
p.EcrInit()
|
||||
}
|
||||
}
|
||||
|
||||
if p.settings.AwsAccessKeyId != "" && p.settings.AwsSecretAccessKey != "" {
|
||||
p.EcrInit()
|
||||
}
|
||||
|
||||
if p.settings.DefaultLogin.Registry != "" && p.settings.Daemon.Mirror != "" {
|
||||
p.settings.DefaultLogin.Mirrors = []string{p.settings.Daemon.Mirror}
|
||||
}
|
||||
|
||||
if len(p.settings.Logins) == 0 {
|
||||
p.settings.Logins = []Login{p.settings.DefaultLogin}
|
||||
} else if !p.settings.DefaultLogin.anonymous() {
|
||||
p.settings.Logins = prepend(p.settings.Logins, p.settings.DefaultLogin)
|
||||
}
|
||||
|
||||
|
@ -107,6 +151,10 @@ func (p *Plugin) Validate() error {
|
|||
return err
|
||||
}
|
||||
|
||||
if !isSingleTag(p.settings.Build.TagsDefaultName) {
|
||||
return fmt.Errorf("'%s' is not a valid, single tag", p.settings.Build.TagsDefaultName)
|
||||
}
|
||||
|
||||
// beside the default login all other logins need to set a username and password
|
||||
for _, l := range p.settings.Logins[1:] {
|
||||
if l.anonymous() {
|
||||
|
@ -114,69 +162,150 @@ func (p *Plugin) Validate() error {
|
|||
}
|
||||
}
|
||||
|
||||
// overload tags flag with tags.file if set
|
||||
if p.settings.Build.TagsFile != "" {
|
||||
tagsFile, err := os.ReadFile(p.settings.Build.TagsFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not read tags file: %w", err)
|
||||
}
|
||||
|
||||
// split file content into slice of tags
|
||||
tagsFileList := strings.Split(strings.TrimSpace(string(tagsFile)), "\n")
|
||||
// trim space of each tag
|
||||
tagsFileList = utils.Map(tagsFileList, func(s string) string { return strings.TrimSpace(s) })
|
||||
|
||||
// finally overwrite
|
||||
p.settings.Build.Tags = *cli.NewStringSlice(tagsFileList...)
|
||||
}
|
||||
|
||||
if p.settings.Build.TagsAuto {
|
||||
// return true if tag event or default branch
|
||||
// we only generate tags on default branch or an tag event
|
||||
if UseDefaultTag(
|
||||
p.settings.Build.Ref,
|
||||
p.settings.Build.Branch,
|
||||
) {
|
||||
tag, err := DefaultTagSuffix(
|
||||
p.settings.Build.Ref,
|
||||
p.settings.Build.TagsDefaultName,
|
||||
p.settings.Build.TagsSuffix,
|
||||
)
|
||||
if err != nil {
|
||||
logrus.Printf("cannot build docker image for %s, invalid semantic version", p.settings.Build.Ref)
|
||||
return err
|
||||
}
|
||||
|
||||
// include user supplied tags
|
||||
tag = append(tag, p.sanitizedUserTags()...)
|
||||
|
||||
p.settings.Build.Tags = *cli.NewStringSlice(tag...)
|
||||
} else {
|
||||
logrus.Printf("skipping automated docker build for %s", p.settings.Build.Ref)
|
||||
return nil
|
||||
}
|
||||
} else {
|
||||
// ignore empty tags
|
||||
var tags []string
|
||||
for _, t := range p.settings.Build.Tags.Value() {
|
||||
t = strings.TrimSpace(t)
|
||||
if t != "" {
|
||||
tags = append(tags, t)
|
||||
}
|
||||
}
|
||||
p.settings.Build.Tags = *cli.NewStringSlice(tags...)
|
||||
p.settings.Build.Tags = *cli.NewStringSlice(p.sanitizedUserTags()...)
|
||||
}
|
||||
|
||||
if p.settings.Build.LabelsAuto {
|
||||
p.settings.Build.Labels = *cli.NewStringSlice(p.Labels()...)
|
||||
}
|
||||
|
||||
if err := p.generateBuildkitConfig(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Plugin) writeBuildkitConfig() error {
|
||||
// no buildkit config, automatically generate buildkit configuration to use a custom CA certificate for each registry
|
||||
if p.settings.Daemon.BuildkitConfig == "" && p.settings.Daemon.Registry != "" {
|
||||
for _, login := range p.settings.Logins {
|
||||
if registry := login.Registry; registry != "" {
|
||||
u, err := url.Parse(registry)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not parse registry address: %s: %v", registry, err)
|
||||
}
|
||||
if u.Host != "" {
|
||||
registry = u.Host
|
||||
}
|
||||
func (p *Plugin) sanitizedUserTags() []string {
|
||||
// ignore empty tags
|
||||
var tags []string
|
||||
for _, t := range p.settings.Build.Tags.Value() {
|
||||
t = strings.TrimSpace(t)
|
||||
if t != "" {
|
||||
tags = append(tags, t)
|
||||
}
|
||||
}
|
||||
return tags
|
||||
}
|
||||
|
||||
caPath := fmt.Sprintf("/etc/docker/certs.d/%s/ca.crt", registry)
|
||||
ca, err := os.Open(caPath)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
logrus.Warnf("error reading %s: %v", caPath, err)
|
||||
} else if err == nil {
|
||||
ca.Close()
|
||||
p.settings.Daemon.BuildkitConfig += fmt.Sprintf(buildkitConfigTemplate, registry, caPath)
|
||||
type BuildkitConfigTOML struct {
|
||||
Debug bool `toml:"debug,omitempty"` // needs to be public for toml lib to use
|
||||
Registry map[string]*RegistryInfo `toml:"registry,omitempty"`
|
||||
}
|
||||
|
||||
type RegistryInfo struct {
|
||||
Mirrors []string `toml:"mirrors,omitempty"`
|
||||
CA []string `toml:"ca,omitempty"`
|
||||
}
|
||||
|
||||
func (p *Plugin) generateBuildkitConfig() error {
|
||||
// no buildkit config, automatically generate buildkit configuration
|
||||
if p.settings.Daemon.BuildkitConfig == "" {
|
||||
|
||||
cfg := BuildkitConfigTOML{}
|
||||
cfg.Registry = make(map[string]*RegistryInfo)
|
||||
|
||||
if p.settings.Daemon.BuildkitDebug {
|
||||
cfg.Debug = p.settings.Daemon.BuildkitDebug
|
||||
logrus.Println("buildkit debug enabled")
|
||||
}
|
||||
|
||||
// use a custom CA certificate for each registry
|
||||
if p.settings.Daemon.Registry != "" {
|
||||
for _, login := range p.settings.Logins {
|
||||
if registry := login.Registry; registry != "" {
|
||||
u, err := url.Parse(registry)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not parse registry address: %s: %v", registry, err)
|
||||
}
|
||||
if u.Host != "" {
|
||||
registry = u.Host
|
||||
}
|
||||
|
||||
// docker hub fix
|
||||
if registry == "index.docker.io" {
|
||||
registry = "docker.io"
|
||||
}
|
||||
|
||||
caPath := fmt.Sprintf("%s/%s/ca.crt", p.settings.CustomCertStore, registry)
|
||||
ca, err := os.Open(caPath)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
logrus.Warnf("error reading %s: %v", caPath, err)
|
||||
} else if err == nil {
|
||||
ca.Close()
|
||||
logrus.Infof("found ca file for '%s' registry", registry)
|
||||
// add registry and ca path to buildkit.toml
|
||||
if cfg.Registry[registry] == nil {
|
||||
cfg.Registry[registry] = new(RegistryInfo)
|
||||
}
|
||||
cfg.Registry[registry].CA = []string{caPath}
|
||||
}
|
||||
|
||||
if len(login.Mirrors) != 0 {
|
||||
if cfg.Registry[registry] == nil {
|
||||
cfg.Registry[registry] = new(RegistryInfo)
|
||||
}
|
||||
cfg.Registry[registry].Mirrors = login.Mirrors
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if cfg.Debug || len(cfg.Registry) > 0 {
|
||||
tomlData, err := toml.Marshal(cfg)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error marshaling buildkit.toml: %s", err)
|
||||
} else {
|
||||
p.settings.Daemon.BuildkitConfig = string(tomlData)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Plugin) writeBuildkitConfig() error {
|
||||
// save buildkit config as described
|
||||
if p.settings.Daemon.BuildkitConfig != "" {
|
||||
err := os.WriteFile(buildkitConfig, []byte(p.settings.Daemon.BuildkitConfig), 0o600)
|
||||
|
@ -192,6 +321,17 @@ func (p *Plugin) writeBuildkitConfig() error {
|
|||
func (p *Plugin) Execute() error {
|
||||
// start the Docker daemon server
|
||||
if !p.settings.Daemon.Disabled {
|
||||
// If no custom DNS value set start internal DNS server
|
||||
if len(p.settings.Daemon.DNS.Value()) == 0 {
|
||||
ip, err := getContainerIP()
|
||||
if err != nil {
|
||||
logrus.Warnf("error detecting IP address: %v", err)
|
||||
} else if ip != "" {
|
||||
p.startCoredns()
|
||||
p.settings.Daemon.DNS.Set(ip)
|
||||
}
|
||||
}
|
||||
|
||||
p.startDaemon()
|
||||
}
|
||||
|
||||
|
@ -243,12 +383,6 @@ func (p *Plugin) Execute() error {
|
|||
cmds = append(cmds, commandInfo()) // docker info
|
||||
cmds = append(cmds, commandBuilder(p.settings.Daemon))
|
||||
cmds = append(cmds, commandBuildx())
|
||||
|
||||
// pre-pull cache images
|
||||
for _, img := range p.settings.Build.CacheFrom.Value() {
|
||||
cmds = append(cmds, commandPull(img))
|
||||
}
|
||||
|
||||
cmds = append(cmds, commandBuild(p.settings.Build, p.settings.Dryrun)) // docker build
|
||||
|
||||
// execute all commands in batch mode.
|
||||
|
@ -258,9 +392,7 @@ func (p *Plugin) Execute() error {
|
|||
trace(cmd)
|
||||
|
||||
err := cmd.Run()
|
||||
if err != nil && isCommandPull(cmd.Args) {
|
||||
fmt.Printf("Could not pull cache-from image %s. Ignoring...\n", cmd.Args[2])
|
||||
} else if err != nil {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
|
111
plugin/impl_test.go
Normal file
111
plugin/impl_test.go
Normal file
|
@ -0,0 +1,111 @@
|
|||
package plugin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"codeberg.org/6543/go-yaml2json"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
var defaultTestSettings = Settings{
|
||||
Daemon: Daemon{
|
||||
StoragePath: "/var/lib/docker",
|
||||
},
|
||||
Build: Build{
|
||||
Context: ".",
|
||||
Tags: *cli.NewStringSlice("latest"),
|
||||
TagsDefaultName: "latest",
|
||||
LabelsAuto: true,
|
||||
Pull: true,
|
||||
},
|
||||
DefaultLogin: Login{
|
||||
Registry: "https://index.docker.io/v1/",
|
||||
},
|
||||
LoginsRaw: "[]",
|
||||
Cleanup: true,
|
||||
CustomCertStore: "/etc/docker/certs.d/",
|
||||
}
|
||||
|
||||
func TestDefaultLogin(t *testing.T) {
|
||||
s := defaultTestSettings
|
||||
assert.NoError(t, newSettingsOnly(&s).Validate())
|
||||
if assert.Len(t, s.Logins, 1) {
|
||||
assert.EqualValues(t, defaultTestSettings.DefaultLogin.Registry, s.Logins[0].Registry)
|
||||
}
|
||||
|
||||
// only use login to auth to registrys
|
||||
loginsRaw, err := yaml2json.Convert([]byte(`
|
||||
- registry: https://index.docker.io/v1/
|
||||
username: docker_username
|
||||
password: docker_password
|
||||
- registry: https://codeberg.org
|
||||
username: cb_username
|
||||
password: cb_password`))
|
||||
assert.NoError(t, err)
|
||||
s.LoginsRaw = string(loginsRaw)
|
||||
assert.NoError(t, newSettingsOnly(&s).Validate())
|
||||
if assert.Len(t, s.Logins, 2) {
|
||||
assert.EqualValues(t, defaultTestSettings.DefaultLogin.Registry, s.Logins[0].Registry)
|
||||
}
|
||||
|
||||
// mixed login settings ('logins' and 'username', 'password' are used)
|
||||
s = defaultTestSettings
|
||||
loginsRaw, err = yaml2json.Convert([]byte(`
|
||||
- registry: https://codeberg.org
|
||||
username: cb_username
|
||||
password: cb_password`))
|
||||
assert.NoError(t, err)
|
||||
s.LoginsRaw = string(loginsRaw)
|
||||
s.DefaultLogin.Username = "docker_username"
|
||||
s.DefaultLogin.Password = "docker_password"
|
||||
assert.NoError(t, newSettingsOnly(&s).Validate())
|
||||
if assert.Len(t, s.Logins, 2) {
|
||||
assert.EqualValues(t, defaultTestSettings.DefaultLogin.Registry, s.Logins[0].Registry)
|
||||
}
|
||||
|
||||
// ignore default registry
|
||||
s = defaultTestSettings
|
||||
loginsRaw, err = yaml2json.Convert([]byte(`
|
||||
- registry: https://codeberg.org
|
||||
username: cb_username
|
||||
password: cb_password`))
|
||||
assert.NoError(t, err)
|
||||
s.LoginsRaw = string(loginsRaw)
|
||||
assert.NoError(t, newSettingsOnly(&s).Validate())
|
||||
if assert.Len(t, s.Logins, 1) {
|
||||
assert.EqualValues(t, "https://codeberg.org", s.Logins[0].Registry)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriteBuildkitConfig(t *testing.T) {
|
||||
settings := defaultTestSettings
|
||||
assert.NoError(t, newSettingsOnly(&settings).Validate())
|
||||
assert.EqualValues(t, "", settings.Daemon.BuildkitConfig)
|
||||
|
||||
settings = defaultTestSettings
|
||||
settings.Daemon.BuildkitDebug = true
|
||||
assert.NoError(t, newSettingsOnly(&settings).Validate())
|
||||
assert.EqualValues(t, "debug = true\n", settings.Daemon.BuildkitConfig)
|
||||
|
||||
settings = defaultTestSettings
|
||||
settings.Daemon.Mirror = "mirror.example.com"
|
||||
assert.NoError(t, newSettingsOnly(&settings).Validate())
|
||||
assert.EqualValues(t, "[registry]\n[registry.'docker.io']\nmirrors = ['mirror.example.com']\n", settings.Daemon.BuildkitConfig)
|
||||
|
||||
settings = defaultTestSettings
|
||||
settings.DefaultLogin.Registry = "codeberg.org"
|
||||
tmpDir, err := os.MkdirTemp("", "go-test-*")
|
||||
assert.NoError(t, err)
|
||||
settings.CustomCertStore = tmpDir
|
||||
defer os.RemoveAll(tmpDir)
|
||||
assert.NoError(t, os.Mkdir(tmpDir+"/codeberg.org", os.ModePerm))
|
||||
caFile, err := os.Create(tmpDir + "/codeberg.org/" + "ca.crt")
|
||||
assert.NoError(t, err)
|
||||
assert.NoError(t, caFile.Close())
|
||||
|
||||
assert.NoError(t, newSettingsOnly(&settings).Validate())
|
||||
assert.EqualValues(t, fmt.Sprintf("[registry]\n[registry.'codeberg.org']\nca = ['%s/codeberg.org/ca.crt']\n", tmpDir), settings.Daemon.BuildkitConfig)
|
||||
}
|
|
@ -5,7 +5,7 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/coreos/go-semver/semver"
|
||||
"github.com/6543/go-version"
|
||||
)
|
||||
|
||||
// Labels returns list of labels to use for image
|
||||
|
@ -22,8 +22,8 @@ func (p *Plugin) Labels() []string {
|
|||
if p.pipeline.Commit.SHA != "" {
|
||||
l = append(l, fmt.Sprintf("org.opencontainers.image.revision=%s", p.pipeline.Commit.SHA))
|
||||
}
|
||||
if p.settings.Build.Ref != "" && strings.HasPrefix(p.settings.Build.Ref, "refs/tags/") {
|
||||
v, err := semver.NewVersion(strings.TrimPrefix(p.settings.Build.Ref[10:], "v"))
|
||||
if p.settings.Build.Ref != "" && strings.HasPrefix(p.settings.Build.Ref, tagRefPrefix) {
|
||||
v, err := version.NewSemver(stripTagPrefix(p.settings.Build.Ref))
|
||||
if err == nil && v != nil {
|
||||
l = append(l, fmt.Sprintf("org.opencontainers.image.version=%s", v.String()))
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ import (
|
|||
|
||||
// Plugin implements drone.Plugin to provide the plugin implementation.
|
||||
type Plugin struct {
|
||||
settings Settings
|
||||
settings *Settings
|
||||
pipeline drone.Pipeline
|
||||
network drone.Network
|
||||
}
|
||||
|
@ -14,8 +14,15 @@ type Plugin struct {
|
|||
// New initializes a plugin from the given Settings, Pipeline, and Network.
|
||||
func New(settings Settings, pipeline drone.Pipeline, network drone.Network) drone.Plugin {
|
||||
return &Plugin{
|
||||
settings: settings,
|
||||
settings: &settings,
|
||||
pipeline: pipeline,
|
||||
network: network,
|
||||
}
|
||||
}
|
||||
|
||||
// newSettingsOnly initializes a plugin from the given Settings only.
|
||||
func newSettingsOnly(settings *Settings) drone.Plugin {
|
||||
return &Plugin{
|
||||
settings: settings,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,15 +2,20 @@ package plugin
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/coreos/go-semver/semver"
|
||||
"github.com/6543/go-version"
|
||||
)
|
||||
|
||||
const tagRefPrefix = "refs/tags/"
|
||||
|
||||
var dateRegex = regexp.MustCompile(`^(\d{8}|\d{4}-\d{2}-\d{2})$`)
|
||||
|
||||
// DefaultTagSuffix returns a set of default suggested tags
|
||||
// based on the commit ref with an attached suffix.
|
||||
func DefaultTagSuffix(ref, suffix string) ([]string, error) {
|
||||
tags, err := DefaultTags(ref)
|
||||
func DefaultTagSuffix(ref, defaultTag, suffix string) ([]string, error) {
|
||||
tags, err := DefaultTags(ref, defaultTag)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -18,7 +23,7 @@ func DefaultTagSuffix(ref, suffix string) ([]string, error) {
|
|||
return tags, nil
|
||||
}
|
||||
for i, tag := range tags {
|
||||
if tag == "latest" {
|
||||
if tag == defaultTag {
|
||||
tags[i] = suffix
|
||||
} else {
|
||||
tags[i] = fmt.Sprintf("%s-%s", tag, suffix)
|
||||
|
@ -39,47 +44,61 @@ func splitOff(input, delim string) string {
|
|||
|
||||
// DefaultTags returns a set of default suggested tags based on
|
||||
// the commit ref.
|
||||
func DefaultTags(ref string) ([]string, error) {
|
||||
if !strings.HasPrefix(ref, "refs/tags/") {
|
||||
return []string{"latest"}, nil
|
||||
func DefaultTags(ref, defaultTag string) ([]string, error) {
|
||||
// check if no tag event
|
||||
if !strings.HasPrefix(ref, tagRefPrefix) {
|
||||
return []string{defaultTag}, nil
|
||||
}
|
||||
v := stripTagPrefix(ref)
|
||||
version, err := semver.NewVersion(v)
|
||||
|
||||
// else it's an tag event
|
||||
tagString := stripTagPrefix(ref)
|
||||
|
||||
// check if date
|
||||
if dateRegex.MatchString(tagString) {
|
||||
return []string{tagString}, nil
|
||||
}
|
||||
|
||||
version, err := version.NewSemver(tagString)
|
||||
// if no semversion return default tag and error
|
||||
if err != nil {
|
||||
return []string{"latest"}, err
|
||||
return []string{defaultTag}, err
|
||||
}
|
||||
if version.PreRelease != "" || version.Metadata != "" {
|
||||
|
||||
vParts := version.Segments()
|
||||
major, minor, patch := vParts[0], vParts[1], vParts[2]
|
||||
|
||||
// if prerelease or version with metadata, only use this strict version
|
||||
if version.Prerelease() != "" || version.Metadata() != "" {
|
||||
return []string{
|
||||
version.String(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
v = stripTagPrefix(ref)
|
||||
v = splitOff(splitOff(v, "+"), "-")
|
||||
dotParts := strings.SplitN(v, ".", 3)
|
||||
|
||||
if version.Major == 0 {
|
||||
// check if version is acutaly a date (%Y%m%d) ... and only return that if so
|
||||
if major > 999 && major < 10000 && minor == 0 && patch == 0 {
|
||||
return []string{
|
||||
fmt.Sprintf("%0*d.%0*d", len(dotParts[0]), version.Major, len(dotParts[1]), version.Minor),
|
||||
fmt.Sprintf("%0*d.%0*d.%0*d", len(dotParts[0]), version.Major, len(dotParts[1]), version.Minor, len(dotParts[2]), version.Patch),
|
||||
fmt.Sprintf("%d", major),
|
||||
}, nil
|
||||
}
|
||||
|
||||
if major == 0 {
|
||||
return []string{
|
||||
fmt.Sprintf("%d.%d", major, minor),
|
||||
fmt.Sprintf("%d.%d.%d", major, minor, patch),
|
||||
}, nil
|
||||
}
|
||||
return []string{
|
||||
fmt.Sprintf("%0*d", len(dotParts[0]), version.Major),
|
||||
fmt.Sprintf("%0*d.%0*d", len(dotParts[0]), version.Major, len(dotParts[1]), version.Minor),
|
||||
fmt.Sprintf("%0*d.%0*d.%0*d", len(dotParts[0]), version.Major, len(dotParts[1]), version.Minor, len(dotParts[2]), version.Patch),
|
||||
fmt.Sprintf("%d", major),
|
||||
fmt.Sprintf("%d.%d", major, minor),
|
||||
fmt.Sprintf("%d.%d.%d", major, minor, patch),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// UseDefaultTag for keep only default branch for latest tag
|
||||
// return true if tag event or default branch
|
||||
func UseDefaultTag(ref, defaultBranch string) bool {
|
||||
if strings.HasPrefix(ref, "refs/tags/") {
|
||||
return true
|
||||
}
|
||||
if stripHeadPrefix(ref) == defaultBranch {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
return strings.HasPrefix(ref, tagRefPrefix) ||
|
||||
stripHeadPrefix(ref) == defaultBranch
|
||||
}
|
||||
|
||||
func stripHeadPrefix(ref string) string {
|
||||
|
@ -87,7 +106,10 @@ func stripHeadPrefix(ref string) string {
|
|||
}
|
||||
|
||||
func stripTagPrefix(ref string) string {
|
||||
ref = strings.TrimPrefix(ref, "refs/tags/")
|
||||
ref = strings.TrimPrefix(ref, "v")
|
||||
return ref
|
||||
return strings.TrimPrefix(ref, tagRefPrefix)
|
||||
}
|
||||
|
||||
func isSingleTag(tag string) bool {
|
||||
// currently only filtering for seperators, this could be improoved...
|
||||
return !regexp.MustCompile(`[,\s]+`).MatchString(tag) && len(tag) > 0 && len(tag) <= 128
|
||||
}
|
||||
|
|
|
@ -3,6 +3,8 @@ package plugin
|
|||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_stripTagPrefix(t *testing.T) {
|
||||
|
@ -11,8 +13,8 @@ func Test_stripTagPrefix(t *testing.T) {
|
|||
After string
|
||||
}{
|
||||
{"refs/tags/1.0.0", "1.0.0"},
|
||||
{"refs/tags/v1.0.0", "1.0.0"},
|
||||
{"v1.0.0", "1.0.0"},
|
||||
{"refs/tags/v1.0.0", "v1.0.0"},
|
||||
{"v1.0.0", "v1.0.0"},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
|
@ -25,19 +27,28 @@ func Test_stripTagPrefix(t *testing.T) {
|
|||
|
||||
func TestDefaultTags(t *testing.T) {
|
||||
tests := []struct {
|
||||
Before string
|
||||
After []string
|
||||
DefaultTag string
|
||||
Before string
|
||||
After []string
|
||||
}{
|
||||
{"", []string{"latest"}},
|
||||
{"refs/heads/master", []string{"latest"}},
|
||||
{"refs/tags/0.9.0", []string{"0.9", "0.9.0"}},
|
||||
{"refs/tags/1.0.0", []string{"1", "1.0", "1.0.0"}},
|
||||
{"refs/tags/v1.0.0", []string{"1", "1.0", "1.0.0"}},
|
||||
{"refs/tags/v1.0.0-alpha.1", []string{"1.0.0-alpha.1"}},
|
||||
// no tag event
|
||||
{"latest", "", []string{"latest"}},
|
||||
{"latest", "refs/heads/master", []string{"latest"}},
|
||||
// tag event with semver
|
||||
{"latest", "refs/tags/0.9.0", []string{"0.9", "0.9.0"}},
|
||||
{"latest", "refs/tags/1.0.0", []string{"1", "1.0", "1.0.0"}},
|
||||
{"latest", "refs/tags/v1.0.0", []string{"1", "1.0", "1.0.0"}},
|
||||
{"latest", "refs/tags/v1.2.3-rc1", []string{"1.2.3-rc1"}},
|
||||
{"latest", "refs/tags/v1.0.0-alpha.1", []string{"1.0.0-alpha.1"}},
|
||||
{"latest", "refs/tags/v20221221", []string{"20221221", "20221221.0", "20221221.0.0"}},
|
||||
{"latest", "refs/tags/v2022-12-21", []string{"2022.0.0-12-21"}},
|
||||
// tag event with date
|
||||
{"latest", "refs/tags/20221221", []string{"20221221"}},
|
||||
{"latest", "refs/tags/2022-12-21", []string{"2022-12-21"}},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
tags, err := DefaultTags(test.Before)
|
||||
tags, err := DefaultTags(test.Before, test.DefaultTag)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
continue
|
||||
|
@ -50,45 +61,83 @@ func TestDefaultTags(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestDefaultTagsError(t *testing.T) {
|
||||
tests := []string{
|
||||
"refs/tags/x1.0.0",
|
||||
"refs/tags/20190203",
|
||||
tests := []struct {
|
||||
DefaultTag string
|
||||
Before string
|
||||
}{
|
||||
{
|
||||
DefaultTag: "latest",
|
||||
Before: "refs/tags/x1.0.0",
|
||||
},
|
||||
{
|
||||
DefaultTag: "latest",
|
||||
Before: "refs/tags/2a",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
_, err := DefaultTags(test)
|
||||
tags, err := DefaultTags(test.Before, test.DefaultTag)
|
||||
if err == nil {
|
||||
t.Errorf("Expect tag error for %s", test)
|
||||
t.Errorf("Expect tag error for %s, got tags %v", test, tags)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDefaultTagSuffix(t *testing.T) {
|
||||
tests := []struct {
|
||||
Before string
|
||||
Suffix string
|
||||
After []string
|
||||
Name string
|
||||
Before string
|
||||
Suffix string
|
||||
After []string
|
||||
DefaultTag string
|
||||
}{
|
||||
// without suffix
|
||||
{
|
||||
After: []string{"latest"},
|
||||
Name: "Default tag without suffix",
|
||||
DefaultTag: "latest",
|
||||
After: []string{"latest"},
|
||||
},
|
||||
{
|
||||
Before: "refs/tags/v1.0.0",
|
||||
Name: "Overridden default tag without suffix",
|
||||
DefaultTag: "next",
|
||||
After: []string{"next"},
|
||||
},
|
||||
{
|
||||
Name: "Generate version",
|
||||
DefaultTag: "latest",
|
||||
Before: "refs/tags/v1.0.0",
|
||||
After: []string{
|
||||
"1",
|
||||
"1.0",
|
||||
"1.0.0",
|
||||
},
|
||||
},
|
||||
// with suffix
|
||||
{
|
||||
Suffix: "linux-amd64",
|
||||
After: []string{"linux-amd64"},
|
||||
Name: "Generate version with overridden default tag",
|
||||
DefaultTag: "next",
|
||||
Before: "refs/tags/v1.0.0",
|
||||
After: []string{
|
||||
"1",
|
||||
"1.0",
|
||||
"1.0.0",
|
||||
},
|
||||
},
|
||||
{
|
||||
Before: "refs/tags/v1.0.0",
|
||||
Suffix: "linux-amd64",
|
||||
Name: "Default tag with suffix (linux-amd64)",
|
||||
DefaultTag: "latest",
|
||||
Suffix: "linux-amd64",
|
||||
After: []string{"linux-amd64"},
|
||||
},
|
||||
{
|
||||
Name: "Overridden default tag with suffix (linux-amd64)",
|
||||
DefaultTag: "next",
|
||||
Suffix: "linux-amd64",
|
||||
After: []string{"linux-amd64"},
|
||||
},
|
||||
{
|
||||
Name: "Generate version with suffix (linux-amd64)",
|
||||
DefaultTag: "latest",
|
||||
Before: "refs/tags/v1.0.0",
|
||||
Suffix: "linux-amd64",
|
||||
After: []string{
|
||||
"1-linux-amd64",
|
||||
"1.0-linux-amd64",
|
||||
|
@ -96,12 +145,33 @@ func TestDefaultTagSuffix(t *testing.T) {
|
|||
},
|
||||
},
|
||||
{
|
||||
Suffix: "nanoserver",
|
||||
After: []string{"nanoserver"},
|
||||
Name: "Generate version with suffix (linux-amd64) and overridden default tag (next)",
|
||||
DefaultTag: "next",
|
||||
Before: "refs/tags/v1.0.0",
|
||||
Suffix: "linux-amd64",
|
||||
After: []string{
|
||||
"1-linux-amd64",
|
||||
"1.0-linux-amd64",
|
||||
"1.0.0-linux-amd64",
|
||||
},
|
||||
},
|
||||
{
|
||||
Before: "refs/tags/v1.9.2",
|
||||
Suffix: "nanoserver",
|
||||
Name: "Default tag with suffix (nanoserver)",
|
||||
DefaultTag: "latest",
|
||||
Suffix: "nanoserver",
|
||||
After: []string{"nanoserver"},
|
||||
},
|
||||
{
|
||||
Name: "Overridden default tag with suffix (nanoserver)",
|
||||
DefaultTag: "next",
|
||||
Suffix: "nanoserver",
|
||||
After: []string{"nanoserver"},
|
||||
},
|
||||
{
|
||||
Name: "Generate version with suffix (nanoserver)",
|
||||
DefaultTag: "latest",
|
||||
Before: "refs/tags/v1.9.2",
|
||||
Suffix: "nanoserver",
|
||||
After: []string{
|
||||
"1-nanoserver",
|
||||
"1.9-nanoserver",
|
||||
|
@ -109,26 +179,47 @@ func TestDefaultTagSuffix(t *testing.T) {
|
|||
},
|
||||
},
|
||||
{
|
||||
Before: "refs/tags/v18.06.0",
|
||||
Suffix: "nanoserver",
|
||||
Name: "Generate version with suffix (nanoserver) and overridden default tag (next)",
|
||||
DefaultTag: "latest",
|
||||
Before: "refs/tags/v1.9.2",
|
||||
Suffix: "nanoserver",
|
||||
After: []string{
|
||||
"1-nanoserver",
|
||||
"1.9-nanoserver",
|
||||
"1.9.2-nanoserver",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "Generate version with suffix (zero-padded version)",
|
||||
DefaultTag: "latest",
|
||||
Before: "refs/tags/v18.06.0",
|
||||
Suffix: "nanoserver",
|
||||
After: []string{
|
||||
"18-nanoserver",
|
||||
"18.06-nanoserver",
|
||||
"18.06.0-nanoserver",
|
||||
"18.6-nanoserver",
|
||||
"18.6.0-nanoserver",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "Generate version with suffix (zero-padded version) with overridden default tag (next)",
|
||||
DefaultTag: "next",
|
||||
Before: "refs/tags/v18.06.0",
|
||||
Suffix: "nanoserver",
|
||||
After: []string{
|
||||
"18-nanoserver",
|
||||
"18.6-nanoserver",
|
||||
"18.6.0-nanoserver",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
tag, err := DefaultTagSuffix(test.Before, test.Suffix)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
continue
|
||||
}
|
||||
got, want := tag, test.After
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("Got tag %v, want %v", got, want)
|
||||
}
|
||||
t.Run(test.Name, func(t *testing.T) {
|
||||
tags, err := DefaultTagSuffix(test.Before, test.DefaultTag, test.Suffix)
|
||||
if assert.NoError(t, err) {
|
||||
assert.EqualValues(t, test.After, tags)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -195,3 +286,26 @@ func TestUseDefaultTag(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Test_isSingleTag(t *testing.T) {
|
||||
tests := []struct {
|
||||
Tag string
|
||||
IsValid bool
|
||||
}{
|
||||
{"latest", true},
|
||||
{" latest", false},
|
||||
{"LaTest__Hi", true},
|
||||
{"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ__.-0123456789", true},
|
||||
{"_wierd.but-ok1", true},
|
||||
{"latest ", false},
|
||||
{"latest,next", false},
|
||||
// more tests to be added, once the validation is more powerful
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
valid := isSingleTag(test.Tag)
|
||||
if valid != test.IsValid {
|
||||
t.Errorf("Tag verification '%s' tag %v, want %v", test.Tag, valid, test.IsValid)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
{
|
||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||
"extends": ["github>thegeeklab/renovate-presets"]
|
||||
"extends": ["local>woodpecker-plugins/renovate-config"]
|
||||
}
|
||||
|
|
9
utils/lamda.go
Normal file
9
utils/lamda.go
Normal file
|
@ -0,0 +1,9 @@
|
|||
package utils
|
||||
|
||||
func Map[T any](in []T, fn func(T) T) []T {
|
||||
out := in
|
||||
for i := range in {
|
||||
out[i] = fn(out[i])
|
||||
}
|
||||
return out
|
||||
}
|
18
utils/lamda_test.go
Normal file
18
utils/lamda_test.go
Normal file
|
@ -0,0 +1,18 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestMap(t *testing.T) {
|
||||
ints := []int{1, 2, 3, 4}
|
||||
ints = Map(ints, func(i int) int { return i * 10 })
|
||||
assert.EqualValues(t, []int{10, 20, 30, 40}, ints)
|
||||
|
||||
sl := []string{"a ", "b", " c"}
|
||||
sl = Map(sl, func(s string) string { return "#" + strings.TrimSpace(s) })
|
||||
assert.EqualValues(t, []string{"#a", "#b", "#c"}, sl)
|
||||
}
|
Loading…
Reference in a new issue