fork: initial commit

This commit is contained in:
Robert Kaussow 2021-01-17 13:06:23 +01:00
commit f9e5a49b33
No known key found for this signature in database
GPG Key ID: 65362AE74AF98B61
23 changed files with 2277 additions and 0 deletions

322
.drone.jsonnet Normal file
View File

@ -0,0 +1,322 @@
local PipelineTest = {
kind: 'pipeline',
image_pull_secrets: ['docker_config'],
name: 'test',
platform: {
os: 'linux',
arch: 'amd64',
},
steps: [
{
name: 'staticcheck',
image: 'golang:1.15',
commands: [
'go run honnef.co/go/tools/cmd/staticcheck ./...',
],
volumes: [
{
name: 'gopath',
path: '/go',
},
],
},
{
name: 'lint',
image: 'golang:1.15',
commands: [
'go run golang.org/x/lint/golint -set_exit_status ./...',
],
volumes: [
{
name: 'gopath',
path: '/go',
},
],
},
{
name: 'vet',
image: 'golang:1.15',
commands: [
'go vet ./...',
],
volumes: [
{
name: 'gopath',
path: '/go',
},
],
},
{
name: 'test',
image: 'golang:1.15',
commands: [
'go test -cover ./...',
],
volumes: [
{
name: 'gopath',
path: '/go',
},
],
},
],
volumes: [
{
name: 'gopath',
temp: {},
},
],
trigger: {
ref: ['refs/heads/main', 'refs/tags/**', 'refs/pull/**'],
},
};
local PipelineBuildBinaries = {
kind: 'pipeline',
image_pull_secrets: ['docker_config'],
name: 'build-binaries',
platform: {
os: 'linux',
arch: 'amd64',
},
steps: [
{
name: 'build',
image: 'techknowlogick/xgo:go-1.15.x',
commands: [
'[ -z "${DRONE_TAG}" ] && BUILD_VERSION=${DRONE_COMMIT_SHA:0:8} || BUILD_VERSION=${DRONE_TAG##v}',
'mkdir -p release/',
"cd cmd/drone-docker && xgo -ldflags \"-s -w -X main.version=$BUILD_VERSION\" -tags netgo -targets 'linux/amd64,linux/arm-6,linux/arm64' -out drone-docker .",
'mv /build/* /drone/src/release/',
],
},
{
name: 'executable',
image: 'alpine',
commands: [
'$(find release/ -executable -type f | grep drone-docker-linux-amd64) --help',
],
},
{
name: 'compress',
image: 'alpine',
commands: [
'apk add upx',
'find release/ -maxdepth 1 -executable -type f -exec upx {} \\;',
'ls -lh release/',
],
},
{
name: 'checksum',
image: 'alpine',
commands: [
'cd release/ && sha256sum * > sha256sum.txt',
],
},
{
name: 'publish',
image: 'plugins/github-release',
settings: {
overwrite: true,
api_key: {
from_secret: 'github_token',
},
files: ['release/*'],
title: '${DRONE_TAG}',
note: 'CHANGELOG.md',
},
when: {
ref: [
'refs/tags/**',
],
},
},
],
depends_on: [
'test',
],
trigger: {
ref: ['refs/heads/main', 'refs/tags/**', 'refs/pull/**'],
},
};
local PipelineBuildContainer(arch='amd64') = {
kind: 'pipeline',
image_pull_secrets: ['docker_config'],
name: 'build-container-' + arch,
platform: {
os: 'linux',
arch: arch,
},
steps: [
{
name: 'build',
image: 'golang:1.15',
commands: [
'[ -z "${DRONE_TAG}" ] && BUILD_VERSION=${DRONE_COMMIT_SHA:0:8} || BUILD_VERSION=${DRONE_TAG##v}',
'go build -v -ldflags "-X main.version=$BUILD_VERSION" -a -tags netgo -o release/' + arch + '/drone-docker ./cmd/drone-docker',
],
},
{
name: 'dryrun',
image: 'plugins/docker:19',
settings: {
config: { from_secret: 'docker_config' },
dry_run: true,
dockerfile: 'docker/Dockerfile.' + arch,
repo: 'thegeeklab/${DRONE_REPO_NAME}',
username: { from_secret: 'docker_username' },
password: { from_secret: 'docker_password' },
},
depends_on: ['build'],
when: {
ref: ['refs/pull/**'],
},
},
{
name: 'publish-dockerhub',
image: 'plugins/docker:19',
settings: {
config: { from_secret: 'docker_config' },
auto_tag: true,
auto_tag_suffix: arch,
dockerfile: 'docker/Dockerfile.' + arch,
repo: 'thegeeklab/${DRONE_REPO_NAME}',
username: { from_secret: 'docker_username' },
password: { from_secret: 'docker_password' },
},
when: {
ref: ['refs/heads/main', 'refs/tags/**'],
},
depends_on: ['dryrun'],
},
{
name: 'publish-quay',
image: 'plugins/docker:19',
settings: {
config: { from_secret: 'docker_config' },
auto_tag: true,
auto_tag_suffix: arch,
dockerfile: 'docker/Dockerfile.' + arch,
registry: 'quay.io',
repo: 'quay.io/thegeeklab/${DRONE_REPO_NAME}',
username: { from_secret: 'quay_username' },
password: { from_secret: 'quay_password' },
},
when: {
ref: ['refs/heads/main', 'refs/tags/**'],
},
depends_on: ['dryrun'],
},
],
depends_on: [
'test',
],
trigger: {
ref: ['refs/heads/main', 'refs/tags/**', 'refs/pull/**'],
},
};
local PipelineNotifications = {
kind: 'pipeline',
image_pull_secrets: ['docker_config'],
name: 'notifications',
platform: {
os: 'linux',
arch: 'amd64',
},
steps: [
{
image: 'plugins/manifest',
name: 'manifest-dockerhub',
settings: {
ignore_missing: true,
auto_tag: true,
username: { from_secret: 'docker_username' },
password: { from_secret: 'docker_password' },
spec: 'docker/manifest.tmpl',
},
when: {
status: ['success'],
},
},
{
image: 'plugins/manifest',
name: 'manifest-quay',
settings: {
ignore_missing: true,
auto_tag: true,
username: { from_secret: 'quay_username' },
password: { from_secret: 'quay_password' },
spec: 'docker/manifest-quay.tmpl',
},
when: {
status: ['success'],
},
},
{
name: 'pushrm-dockerhub',
image: 'chko/docker-pushrm:1',
environment: {
DOCKER_PASS: {
from_secret: 'docker_password',
},
DOCKER_USER: {
from_secret: 'docker_username',
},
PUSHRM_FILE: 'README.md',
PUSHRM_SHORT: 'Drone plugin to build multiarch Docker images',
PUSHRM_TARGET: 'thegeeklab/${DRONE_REPO_NAME}',
},
when: {
status: ['success'],
},
},
{
name: 'pushrm-quay',
image: 'chko/docker-pushrm:1',
environment: {
APIKEY__QUAY_IO: {
from_secret: 'quay_token',
},
PUSHRM_FILE: 'README.md',
PUSHRM_TARGET: 'quay.io/thegeeklab/${DRONE_REPO_NAME}',
},
when: {
status: ['success'],
},
},
{
name: 'matrix',
image: 'plugins/matrix',
settings: {
homeserver: { from_secret: 'matrix_homeserver' },
roomid: { from_secret: 'matrix_roomid' },
template: 'Status: **{{ build.status }}**<br/> Build: [{{ repo.Owner }}/{{ repo.Name }}]({{ build.link }}) ({{ build.branch }}) by {{ build.author }}<br/> Message: {{ build.message }}',
username: { from_secret: 'matrix_username' },
password: { from_secret: 'matrix_password' },
},
when: {
status: ['success', 'failure'],
},
},
],
depends_on: [
'build-binaries',
'build-container-amd64',
'build-container-arm64',
],
trigger: {
ref: ['refs/heads/main', 'refs/tags/**'],
status: ['success', 'failure'],
},
};
[
PipelineTest,
PipelineBuildBinaries,
PipelineBuildContainer(arch='amd64'),
PipelineBuildContainer(arch='arm64'),
PipelineNotifications,
]

382
.drone.yml Normal file
View File

@ -0,0 +1,382 @@
---
kind: pipeline
name: test
platform:
os: linux
arch: amd64
steps:
- name: staticcheck
image: golang:1.15
commands:
- go run honnef.co/go/tools/cmd/staticcheck ./...
volumes:
- name: gopath
path: /go
- name: lint
image: golang:1.15
commands:
- go run golang.org/x/lint/golint -set_exit_status ./...
volumes:
- name: gopath
path: /go
- name: vet
image: golang:1.15
commands:
- go vet ./...
volumes:
- name: gopath
path: /go
- name: test
image: golang:1.15
commands:
- go test -cover ./...
volumes:
- name: gopath
path: /go
volumes:
- name: gopath
temp: {}
image_pull_secrets:
- docker_config
trigger:
ref:
- refs/heads/main
- refs/tags/**
- refs/pull/**
---
kind: pipeline
name: build-binaries
platform:
os: linux
arch: amd64
steps:
- name: build
image: techknowlogick/xgo:go-1.15.x
commands:
- "[ -z \"${DRONE_TAG}\" ] && BUILD_VERSION=${DRONE_COMMIT_SHA:0:8} || BUILD_VERSION=${DRONE_TAG##v}"
- mkdir -p release/
- cd cmd/drone-docker && xgo -ldflags "-s -w -X main.version=$BUILD_VERSION" -tags netgo -targets 'linux/amd64,linux/arm-6,linux/arm64' -out drone-docker .
- mv /build/* /drone/src/release/
- name: executable
image: alpine
commands:
- $(find release/ -executable -type f | grep drone-docker-linux-amd64) --help
- name: compress
image: alpine
commands:
- apk add upx
- find release/ -maxdepth 1 -executable -type f -exec upx {} \;
- ls -lh release/
- name: checksum
image: alpine
commands:
- cd release/ && sha256sum * > sha256sum.txt
- name: publish
image: plugins/github-release
settings:
api_key:
from_secret: github_token
files:
- release/*
note: CHANGELOG.md
overwrite: true
title: ${DRONE_TAG}
when:
ref:
- refs/tags/**
image_pull_secrets:
- docker_config
trigger:
ref:
- refs/heads/main
- refs/tags/**
- refs/pull/**
depends_on:
- test
---
kind: pipeline
name: build-container-amd64
platform:
os: linux
arch: amd64
steps:
- name: build
image: golang:1.15
commands:
- "[ -z \"${DRONE_TAG}\" ] && BUILD_VERSION=${DRONE_COMMIT_SHA:0:8} || BUILD_VERSION=${DRONE_TAG##v}"
- go build -v -ldflags "-X main.version=$BUILD_VERSION" -a -tags netgo -o release/amd64/drone-docker ./cmd/drone-docker
- name: dryrun
image: plugins/docker:19
settings:
config:
from_secret: docker_config
dockerfile: docker/Dockerfile.amd64
dry_run: true
password:
from_secret: docker_password
repo: thegeeklab/${DRONE_REPO_NAME}
username:
from_secret: docker_username
when:
ref:
- refs/pull/**
depends_on:
- build
- name: publish-dockerhub
image: plugins/docker:19
settings:
auto_tag: true
auto_tag_suffix: amd64
config:
from_secret: docker_config
dockerfile: docker/Dockerfile.amd64
password:
from_secret: docker_password
repo: thegeeklab/${DRONE_REPO_NAME}
username:
from_secret: docker_username
when:
ref:
- refs/heads/main
- refs/tags/**
depends_on:
- dryrun
- name: publish-quay
image: plugins/docker:19
settings:
auto_tag: true
auto_tag_suffix: amd64
config:
from_secret: docker_config
dockerfile: docker/Dockerfile.amd64
password:
from_secret: quay_password
registry: quay.io
repo: quay.io/thegeeklab/${DRONE_REPO_NAME}
username:
from_secret: quay_username
when:
ref:
- refs/heads/main
- refs/tags/**
depends_on:
- dryrun
image_pull_secrets:
- docker_config
trigger:
ref:
- refs/heads/main
- refs/tags/**
- refs/pull/**
depends_on:
- test
---
kind: pipeline
name: build-container-arm64
platform:
os: linux
arch: arm64
steps:
- name: build
image: golang:1.15
commands:
- "[ -z \"${DRONE_TAG}\" ] && BUILD_VERSION=${DRONE_COMMIT_SHA:0:8} || BUILD_VERSION=${DRONE_TAG##v}"
- go build -v -ldflags "-X main.version=$BUILD_VERSION" -a -tags netgo -o release/arm64/drone-docker ./cmd/drone-docker
- name: dryrun
image: plugins/docker:19
settings:
config:
from_secret: docker_config
dockerfile: docker/Dockerfile.arm64
dry_run: true
password:
from_secret: docker_password
repo: thegeeklab/${DRONE_REPO_NAME}
username:
from_secret: docker_username
when:
ref:
- refs/pull/**
depends_on:
- build
- name: publish-dockerhub
image: plugins/docker:19
settings:
auto_tag: true
auto_tag_suffix: arm64
config:
from_secret: docker_config
dockerfile: docker/Dockerfile.arm64
password:
from_secret: docker_password
repo: thegeeklab/${DRONE_REPO_NAME}
username:
from_secret: docker_username
when:
ref:
- refs/heads/main
- refs/tags/**
depends_on:
- dryrun
- name: publish-quay
image: plugins/docker:19
settings:
auto_tag: true
auto_tag_suffix: arm64
config:
from_secret: docker_config
dockerfile: docker/Dockerfile.arm64
password:
from_secret: quay_password
registry: quay.io
repo: quay.io/thegeeklab/${DRONE_REPO_NAME}
username:
from_secret: quay_username
when:
ref:
- refs/heads/main
- refs/tags/**
depends_on:
- dryrun
image_pull_secrets:
- docker_config
trigger:
ref:
- refs/heads/main
- refs/tags/**
- refs/pull/**
depends_on:
- test
---
kind: pipeline
name: notifications
platform:
os: linux
arch: amd64
steps:
- name: manifest-dockerhub
image: plugins/manifest
settings:
auto_tag: true
ignore_missing: true
password:
from_secret: docker_password
spec: docker/manifest.tmpl
username:
from_secret: docker_username
when:
status:
- success
- name: manifest-quay
image: plugins/manifest
settings:
auto_tag: true
ignore_missing: true
password:
from_secret: quay_password
spec: docker/manifest-quay.tmpl
username:
from_secret: quay_username
when:
status:
- success
- name: pushrm-dockerhub
image: chko/docker-pushrm:1
environment:
DOCKER_PASS:
from_secret: docker_password
DOCKER_USER:
from_secret: docker_username
PUSHRM_FILE: README.md
PUSHRM_SHORT: Drone plugin to build multiarch Docker images
PUSHRM_TARGET: thegeeklab/${DRONE_REPO_NAME}
when:
status:
- success
- name: pushrm-quay
image: chko/docker-pushrm:1
environment:
APIKEY__QUAY_IO:
from_secret: quay_token
PUSHRM_FILE: README.md
PUSHRM_TARGET: quay.io/thegeeklab/${DRONE_REPO_NAME}
when:
status:
- success
- name: matrix
image: plugins/matrix
settings:
homeserver:
from_secret: matrix_homeserver
password:
from_secret: matrix_password
roomid:
from_secret: matrix_roomid
template: "Status: **{{ build.status }}**<br/> Build: [{{ repo.Owner }}/{{ repo.Name }}]({{ build.link }}) ({{ build.branch }}) by {{ build.author }}<br/> Message: {{ build.message }}"
username:
from_secret: matrix_username
when:
status:
- success
- failure
image_pull_secrets:
- docker_config
trigger:
ref:
- refs/heads/main
- refs/tags/**
status:
- success
- failure
depends_on:
- build-binaries
- build-container-amd64
- build-container-arm64
...

55
.github/settings.yml vendored Normal file
View File

@ -0,0 +1,55 @@
repository:
name: drone-docker
description: Drone plugin to build multiarch Docker images
topics: drone, drone-plugin
private: false
has_issues: true
has_wiki: false
has_downloads: true
default_branch: master
allow_squash_merge: true
allow_merge_commit: true
allow_rebase_merge: true
labels:
- name: bug
color: d73a4a
description: Something isn't working
- name: documentation
color: 0075ca
description: Improvements or additions to documentation
- name: duplicate
color: cfd3d7
description: This issue or pull request already exists
- name: enhancement
color: a2eeef
description: New feature or request
- name: good first issue
color: 7057ff
description: Good for newcomers
- name: help wanted
color: 008672
description: Extra attention is needed
- name: invalid
color: e4e669
description: This doesn't seem right
- name: question
color: d876e3
description: Further information is requested
- name: wontfix
color: ffffff
description: This will not be worked on
branches:
- name: master
protection:
required_pull_request_reviews: null
required_status_checks:
strict: true
contexts:
- continuous-integration/drone/pr
enforce_admins: null
restrictions: null

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
/release/
/drone-docker*
coverage.out

202
LICENSE Normal file
View File

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2021 Robert Kaussow <mail@thegeeklab.de>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

56
README.md Normal file
View File

@ -0,0 +1,56 @@
# drone-docker
Drone plugin to build multiarch Docker images
[![Build Status](https://img.shields.io/drone/build/thegeeklab/drone-docker?logo=drone)](https://cloud.drone.io/thegeeklab/drone-docker)
[![Docker Hub](https://img.shields.io/badge/dockerhub-latest-blue.svg?logo=docker&logoColor=white)](https://hub.docker.com/r/thegeeklab/drone-docker)
[![Quay.io](https://img.shields.io/badge/quay-latest-blue.svg?logo=docker&logoColor=white)](https://quay.io/repository/thegeeklab/drone-docker)
[![Go Report Card](https://goreportcard.com/badge/github.com/thegeeklab/drone-docker)](https://goreportcard.com/report/github.com/thegeeklab/drone-docker)
[![GitHub contributors](https://img.shields.io/github/contributors/thegeeklab/drone-docker)](https://github.com/thegeeklab/drone-docker/graphs/contributors)
[![Source: GitHub](https://img.shields.io/badge/source-github-blue.svg?logo=github&logoColor=white)](https://github.com/thegeeklab/drone-docker)
[![License: MIT](https://img.shields.io/github/license/thegeeklab/drone-docker)](https://github.com/thegeeklab/drone-docker/blob/main/LICENSE)
Drone plugin to build multiarch Docker images.
## Build
Build the binary with the following command:
```Shell
export GOOS=linux
export GOARCH=amd64
export CGO_ENABLED=0
export GO111MODULE=on
go build -v -a -tags netgo -o release/drone-docker
```
Build the Docker image with the following command:
```Shell
docker build --file docker/Dockerfile.amd64 --tag thegeeklab/drone-docker .
```
## Usage
> Notice: Be aware that the tis plugin requires privileged capabilities, otherwise the integrated Docker daemon is not able to start.
```console
docker run --rm \
-e PLUGIN_TAG=latest \
-e PLUGIN_REPO=octocat/hello-world \
-e DRONE_COMMIT_SHA=00000000 \
-v $(pwd):$(pwd) \
-w $(pwd) \
--privileged \
thegeeklab/drone-docker --dry-run
```
## Contributors
Special thanks goes to all [contributors](https://github.com/thegeeklab/drone-docker/graphs/contributors). If you would like to contribute,
please see the [instructions](https://github.com/thegeeklab/drone-docker/blob/main/CONTRIBUTING.md).
## License
This project is licensed under the MIT License - see the [LICENSE](https://github.com/thegeeklab/drone-docker/blob/main/LICENSE) file for details.

236
cmd/drone-docker/config.go Normal file
View File

@ -0,0 +1,236 @@
package main
import (
"github.com/thegeeklab/drone-docker/plugin"
"github.com/urfave/cli/v2"
)
// settingsFlags has the cli.Flags for the plugin.Settings.
func settingsFlags(settings *plugin.Settings) []cli.Flag {
return []cli.Flag{
&cli.BoolFlag{
Name: "dry-run",
Usage: "dry run disables docker push",
EnvVars: []string{"PLUGIN_DRY_RUN"},
Destination: &settings.Dryrun,
},
&cli.StringFlag{
Name: "remote.url",
Usage: "git remote url",
EnvVars: []string{"DRONE_REMOTE_URL"},
Destination: &settings.Build.Remote,
},
&cli.StringFlag{
Name: "daemon.mirror",
Usage: "docker daemon registry mirror",
EnvVars: []string{"PLUGIN_MIRROR", "DOCKER_PLUGIN_MIRROR"},
Destination: &settings.Daemon.Mirror,
},
&cli.StringFlag{
Name: "daemon.storage-driver",
Usage: "docker daemon storage driver",
EnvVars: []string{"PLUGIN_STORAGE_DRIVER"},
Destination: &settings.Daemon.StorageDriver,
},
&cli.StringFlag{
Name: "daemon.storage-path",
Usage: "docker daemon storage path",
Value: "/var/lib/docker",
EnvVars: []string{"PLUGIN_STORAGE_PATH"},
Destination: &settings.Daemon.StoragePath,
},
&cli.StringFlag{
Name: "daemon.bip",
Usage: "docker daemon bride ip address",
EnvVars: []string{"PLUGIN_BIP"},
Destination: &settings.Daemon.Bip,
},
&cli.StringFlag{
Name: "daemon.mtu",
Usage: "docker daemon custom mtu setting",
EnvVars: []string{"PLUGIN_MTU"},
Destination: &settings.Daemon.MTU,
},
&cli.StringSliceFlag{
Name: "daemon.dns",
Usage: "docker daemon dns server",
EnvVars: []string{"PLUGIN_CUSTOM_DNS"},
Destination: &settings.Daemon.DNS,
},
&cli.StringSliceFlag{
Name: "daemon.dns-search",
Usage: "docker daemon dns search domains",
EnvVars: []string{"PLUGIN_CUSTOM_DNS_SEARCH"},
Destination: &settings.Daemon.DNSSearch,
},
&cli.BoolFlag{
Name: "daemon.insecure",
Usage: "docker daemon allows insecure registries",
EnvVars: []string{"PLUGIN_INSECURE"},
Destination: &settings.Daemon.Insecure,
},
&cli.BoolFlag{
Name: "daemon.ipv6",
Usage: "docker daemon IPv6 networking",
EnvVars: []string{"PLUGIN_IPV6"},
Destination: &settings.Daemon.IPv6,
},
&cli.BoolFlag{
Name: "daemon.experimental",
Usage: "docker daemon Experimental mode",
EnvVars: []string{"PLUGIN_EXPERIMENTAL"},
Destination: &settings.Daemon.Experimental,
},
&cli.BoolFlag{
Name: "daemon.debug",
Usage: "docker daemon executes in debug mode",
EnvVars: []string{"PLUGIN_DEBUG", "DOCKER_LAUNCH_DEBUG"},
Destination: &settings.Daemon.Debug,
},
&cli.BoolFlag{
Name: "daemon.off",
Usage: "don't start the docker daemon",
EnvVars: []string{"PLUGIN_DAEMON_OFF"},
Destination: &settings.Daemon.Disabled,
},
&cli.StringFlag{
Name: "dockerfile",
Usage: "build dockerfile",
Value: "Dockerfile",
EnvVars: []string{"PLUGIN_DOCKERFILE"},
Destination: &settings.Build.Dockerfile,
},
&cli.StringFlag{
Name: "context",
Usage: "build context",
Value: ".",
EnvVars: []string{"PLUGIN_CONTEXT"},
Destination: &settings.Build.Context,
},
&cli.StringSliceFlag{
Name: "tags",
Usage: "build tags",
Value: cli.NewStringSlice([]string{"latest"}...),
EnvVars: []string{"PLUGIN_TAG", "PLUGIN_TAGS"},
FilePath: ".tags",
Destination: &settings.Build.Tags,
},
&cli.BoolFlag{
Name: "tags.auto",
Usage: "default build tags",
EnvVars: []string{"PLUGIN_DEFAULT_TAGS", "PLUGIN_AUTO_TAG"},
Destination: &settings.Build.TagsAuto,
},
&cli.StringFlag{
Name: "tags.suffix",
Usage: "default build tags with suffix",
EnvVars: []string{"PLUGIN_DEFAULT_SUFFIX", "PLUGIN_AUTO_TAG_SUFFIX"},
Destination: &settings.Build.TagsSuffix,
},
&cli.StringSliceFlag{
Name: "args",
Usage: "build args",
EnvVars: []string{"PLUGIN_BUILD_ARGS"},
Destination: &settings.Build.Args,
},
&cli.StringSliceFlag{
Name: "args-from-env",
Usage: "build args",
EnvVars: []string{"PLUGIN_BUILD_ARGS_FROM_ENV"},
Destination: &settings.Build.ArgsEnv,
},
&cli.BoolFlag{
Name: "quiet",
Usage: "quiet docker build",
EnvVars: []string{"PLUGIN_QUIET"},
Destination: &settings.Build.Quiet,
},
&cli.StringFlag{
Name: "target",
Usage: "build target",
EnvVars: []string{"PLUGIN_TARGET"},
Destination: &settings.Build.Target,
},
&cli.StringSliceFlag{
Name: "cache-from",
Usage: "images to consider as cache sources",
EnvVars: []string{"PLUGIN_CACHE_FROM"},
Destination: &settings.Build.CacheFrom,
},
&cli.BoolFlag{
Name: "squash",
Usage: "squash the layers at build time",
EnvVars: []string{"PLUGIN_SQUASH"},
Destination: &settings.Build.Squash,
},
&cli.BoolFlag{
Name: "pull-image",
Usage: "force pull base image at build time",
EnvVars: []string{"PLUGIN_PULL_IMAGE"},
Value: true,
Destination: &settings.Build.Pull,
},
&cli.BoolFlag{
Name: "compress",
Usage: "compress the build context using gzip",
EnvVars: []string{"PLUGIN_COMPRESS"},
Destination: &settings.Build.Compress,
},
&cli.StringFlag{
Name: "repo",
Usage: "docker repository",
EnvVars: []string{"PLUGIN_REPO"},
Destination: &settings.Build.Repo,
},
&cli.StringFlag{
Name: "docker.registry",
Usage: "docker registry",
Value: "https://index.docker.io/v1/",
EnvVars: []string{"PLUGIN_REGISTRY", "DOCKER_REGISTRY"},
Destination: &settings.Login.Registry,
},
&cli.StringFlag{
Name: "docker.username",
Usage: "docker username",
EnvVars: []string{"PLUGIN_USERNAME", "DOCKER_USERNAME"},
Destination: &settings.Login.Username,
},
&cli.StringFlag{
Name: "docker.password",
Usage: "docker password",
EnvVars: []string{"PLUGIN_PASSWORD", "DOCKER_PASSWORD"},
Destination: &settings.Login.Password,
},
&cli.StringFlag{
Name: "docker.email",
Usage: "docker email",
EnvVars: []string{"PLUGIN_EMAIL", "DOCKER_EMAIL"},
Destination: &settings.Login.Email,
},
&cli.StringFlag{
Name: "docker.config",
Usage: "docker json dockerconfig content",
EnvVars: []string{"PLUGIN_CONFIG", "DOCKER_PLUGIN_CONFIG"},
Destination: &settings.Login.Config,
},
&cli.BoolFlag{
Name: "docker.purge",
Usage: "docker should cleanup images",
EnvVars: []string{"PLUGIN_PURGE"},
Value: true,
Destination: &settings.Cleanup,
},
&cli.BoolFlag{
Name: "no-cache",
Usage: "do not use cached intermediate containers",
EnvVars: []string{"PLUGIN_NO_CACHE"},
Destination: &settings.Build.NoCache,
},
&cli.StringSliceFlag{
Name: "add-host",
Usage: "additional host:IP mapping",
EnvVars: []string{"PLUGIN_ADD_HOST"},
Destination: &settings.Build.AddHost,
},
}
}

64
cmd/drone-docker/main.go Normal file
View File

@ -0,0 +1,64 @@
package main
import (
"os"
"github.com/joho/godotenv"
"github.com/thegeeklab/drone-docker/plugin"
"github.com/urfave/cli/v2"
"github.com/drone-plugins/drone-plugin-lib/errors"
"github.com/drone-plugins/drone-plugin-lib/urfave"
)
var version = "unknown"
func main() {
settings := &plugin.Settings{}
if _, err := os.Stat("/run/drone/env"); err == nil {
godotenv.Overload("/run/drone/env")
}
app := &cli.App{
Name: "drone-docker",
Usage: "build docker container with DinD",
Version: version,
Flags: append(settingsFlags(settings), urfave.Flags()...),
Action: run(settings),
}
if err := app.Run(os.Args); err != nil {
errors.HandleExit(err)
}
}
func run(settings *plugin.Settings) cli.ActionFunc {
return func(ctx *cli.Context) error {
urfave.LoggingFromContext(ctx)
plugin := plugin.New(
*settings,
urfave.PipelineFromContext(ctx),
urfave.NetworkFromContext(ctx),
)
if err := plugin.Validate(); err != nil {
if e, ok := err.(errors.ExitCoder); ok {
return e
}
return errors.ExitMessagef("validation failed: %w", err)
}
if err := plugin.Execute(); err != nil {
if e, ok := err.(errors.ExitCoder); ok {
return e
}
return errors.ExitMessagef("execution failed: %w", err)
}
return nil
}
}

23
docker/Dockerfile.amd64 Normal file
View File

@ -0,0 +1,23 @@
FROM amd64/docker:19.03-dind
LABEL maintainer="Robert Kaussow <mail@thegeeklab.de>"
LABEL org.opencontainers.image.authors="Robert Kaussow <mail@thegeeklab.de>"
LABEL org.opencontainers.image.title="drone-docker"
LABEL org.opencontainers.image.url="https://github.com/thegeeklab/drone-docker"
LABEL org.opencontainers.image.source="https://github.com/thegeeklab/drone-docker"
LABEL org.opencontainers.image.documentation="https://github.com/thegeeklab/drone-docker"
ENV DOCKER_HOST=unix:///var/run/docker.sock
RUN apk --update add --virtual .build-deps curl && \
mkdir -p /etc/docker/ && \
curl -SsL -o /etc/docker/default.json https://github.com/moby/moby/blob/19.03/profiles/seccomp/default.json && \
sed -i 's/SCMP_ACT_ERRNO/SCMP_ACT_TRACE/g' /etc/docker/default.json && \
chmod 600 /etc/docker/default.json && \
apk del .build-deps && \
rm -rf /var/cache/apk/* && \
rm -rf /tmp/*
ADD release/amd64/drone-docker /bin/
ENTRYPOINT ["/usr/local/bin/dockerd-entrypoint.sh", "drone-docker"]

23
docker/Dockerfile.arm Normal file
View File

@ -0,0 +1,23 @@
FROM arm32v7/docker:19.03-dind
LABEL maintainer="Robert Kaussow <mail@thegeeklab.de>"
LABEL org.opencontainers.image.authors="Robert Kaussow <mail@thegeeklab.de>"
LABEL org.opencontainers.image.title="drone-docker"
LABEL org.opencontainers.image.url="https://github.com/thegeeklab/drone-docker"
LABEL org.opencontainers.image.source="https://github.com/thegeeklab/drone-docker"
LABEL org.opencontainers.image.documentation="https://github.com/thegeeklab/drone-docker"
ENV DOCKER_HOST=unix:///var/run/docker.sock
RUN apk --update add --virtual .build-deps curl && \
mkdir -p /etc/docker/ && \
curl -SsL -o /etc/docker/default.json https://github.com/moby/moby/blob/19.03/profiles/seccomp/default.json && \
sed -i 's/SCMP_ACT_ERRNO/SCMP_ACT_TRACE/g' /etc/docker/default.json && \
chmod 600 /etc/docker/default.json && \
apk del .build-deps && \
rm -rf /var/cache/apk/* && \
rm -rf /tmp/*
ADD release/arm/drone-docker /bin/
ENTRYPOINT ["/usr/local/bin/dockerd-entrypoint.sh", "drone-docker"]

23
docker/Dockerfile.arm64 Normal file
View File

@ -0,0 +1,23 @@
FROM arm64v8/docker:19.03-dind
LABEL maintainer="Robert Kaussow <mail@thegeeklab.de>"
LABEL org.opencontainers.image.authors="Robert Kaussow <mail@thegeeklab.de>"
LABEL org.opencontainers.image.title="drone-docker"
LABEL org.opencontainers.image.url="https://github.com/thegeeklab/drone-docker"
LABEL org.opencontainers.image.source="https://github.com/thegeeklab/drone-docker"
LABEL org.opencontainers.image.documentation="https://github.com/thegeeklab/drone-docker"
ENV DOCKER_HOST=unix:///var/run/docker.sock
RUN apk --update add --virtual .build-deps curl && \
mkdir -p /etc/docker/ && \
curl -SsL -o /etc/docker/default.json https://github.com/moby/moby/blob/19.03/profiles/seccomp/default.json && \
sed -i 's/SCMP_ACT_ERRNO/SCMP_ACT_TRACE/g' /etc/docker/default.json && \
chmod 600 /etc/docker/default.json && \
apk del .build-deps && \
rm -rf /var/cache/apk/* && \
rm -rf /tmp/*
ADD release/arm64/drone-docker /bin/
ENTRYPOINT ["/usr/local/bin/dockerd-entrypoint.sh", "drone-docker"]

24
docker/manifest-quay.tmpl Normal file
View File

@ -0,0 +1,24 @@
image: quay.io/thegeeklab/drone-docker:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}latest{{/if}}
{{#if build.tags}}
tags:
{{#each build.tags}}
- {{this}}
{{/each}}
{{/if}}
manifests:
- image: quay.io/thegeeklab/drone-docker:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}amd64
platform:
architecture: amd64
os: linux
- image: quay.io/thegeeklab/drone-docker:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}arm64
platform:
architecture: arm64
os: linux
variant: v8
- image: quay.io/thegeeklab/drone-docker:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}arm
platform:
architecture: arm
os: linux
variant: v7

24
docker/manifest.tmpl Normal file
View File

@ -0,0 +1,24 @@
image: thegeeklab/drone-docker:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}latest{{/if}}
{{#if build.tags}}
tags:
{{#each build.tags}}
- {{this}}
{{/each}}
{{/if}}
manifests:
- image: thegeeklab/drone-docker:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}amd64
platform:
architecture: amd64
os: linux
- image: thegeeklab/drone-docker:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}arm64
platform:
architecture: arm64
os: linux
variant: v8
- image: thegeeklab/drone-docker:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}arm
platform:
architecture: arm
os: linux
variant: v7

16
go.mod Normal file
View File

@ -0,0 +1,16 @@
module github.com/thegeeklab/drone-docker
require (
github.com/coreos/go-semver v0.3.0
github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect
github.com/drone-plugins/drone-plugin-lib v0.4.0
github.com/joho/godotenv v1.3.0
github.com/kr/pretty v0.1.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/sirupsen/logrus v1.7.0
github.com/urfave/cli/v2 v2.3.0
golang.org/x/sys v0.0.0-20210110051926-789bb1bd4061 // indirect
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
)
go 1.13

58
go.sum Normal file
View File

@ -0,0 +1,58 @@
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
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 h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
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.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
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 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
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 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
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.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/urfave/cli/v2 v2.2.0/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ=
github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M=
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210110051926-789bb1bd4061 h1:DQmQoKxQWtyybCtX/3dIuDBcAhFszqq8YiNeS6sNu1c=
golang.org/x/sys v0.0.0-20210110051926-789bb1bd4061/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
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=
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/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/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.3 h1:fvjTMHxHEw/mxHbtzPi3JCcKXQRAnQTBRo6YCJSVHKI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=

25
plugin/daemon.go Normal file
View File

@ -0,0 +1,25 @@
package plugin
import (
"io/ioutil"
"os"
)
const dockerExe = "/usr/local/bin/docker"
const dockerdExe = "/usr/local/bin/dockerd"
const dockerHome = "/root/.docker/"
func (p Plugin) startDaemon() {
cmd := commandDaemon(p.settings.Daemon)
if p.settings.Daemon.Debug {
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
} else {
cmd.Stdout = ioutil.Discard
cmd.Stderr = ioutil.Discard
}
go func() {
trace(cmd)
cmd.Run()
}()
}

217
plugin/docker.go Normal file
View File

@ -0,0 +1,217 @@
package plugin
import (
"fmt"
"os"
"os/exec"
"strings"
"github.com/urfave/cli/v2"
)
// helper function to create the docker login command.
func commandLogin(login Login) *exec.Cmd {
if login.Email != "" {
return commandLoginEmail(login)
}
return exec.Command(
dockerExe, "login",
"-u", login.Username,
"-p", login.Password,
login.Registry,
)
}
// 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",
"-u", login.Username,
"-p", login.Password,
"-e", login.Email,
login.Registry,
)
}
// helper function to create the docker info command.
func commandVersion() *exec.Cmd {
return exec.Command(dockerExe, "version")
}
// helper function to create the docker info command.
func commandInfo() *exec.Cmd {
return exec.Command(dockerExe, "info")
}
// helper function to create the docker build command.
func commandBuild(build Build) *exec.Cmd {
args := []string{
"build",
"--rm=true",
"-f", build.Dockerfile,
"-t", build.Name,
}
args = append(args, build.Context)
if build.Squash {
args = append(args, "--squash")
}
if build.Compress {
args = append(args, "--compress")
}
if build.Pull {
args = append(args, "--pull=true")
}
if build.NoCache {
args = append(args, "--no-cache")
}
for _, arg := range build.CacheFrom.Value() {
args = append(args, "--cache-from", arg)
}
for _, arg := range build.ArgsEnv.Value() {
addProxyValue(&build, arg)
}
for _, arg := range build.Args.Value() {
args = append(args, "--build-arg", arg)
}
for _, host := range build.AddHost.Value() {
args = append(args, "--add-host", host)
}
if build.Target != "" {
args = append(args, "--target", build.Target)
}
if build.Quiet {
args = append(args, "--quiet")
}
return exec.Command(dockerExe, args...)
}
// helper function to add proxy values from the environment
func addProxyBuildArgs(build *Build) {
addProxyValue(build, "http_proxy")
addProxyValue(build, "https_proxy")
addProxyValue(build, "no_proxy")
}
// helper function to add the upper and lower case version of a proxy value.
func addProxyValue(build *Build, key string) {
value := getProxyValue(key)
if len(value) > 0 && !hasProxyBuildArg(build, key) {
build.Args = *cli.NewStringSlice(append(build.Args.Value(), fmt.Sprintf("%s=%s", key, value))...)
build.Args = *cli.NewStringSlice(append(build.Args.Value(), fmt.Sprintf("%s=%s", strings.ToUpper(key), value))...)
}
}
// helper function to get a proxy value from the environment.
//
// assumes that the upper and lower case versions of are the same.
func getProxyValue(key string) string {
value := os.Getenv(key)
if len(value) > 0 {
return value
}
return os.Getenv(strings.ToUpper(key))
}
// helper function that looks to see if a proxy value was set in the build args.
func hasProxyBuildArg(build *Build, key string) bool {
keyUpper := strings.ToUpper(key)
for _, s := range build.Args.Value() {
if strings.HasPrefix(s, key) || strings.HasPrefix(s, keyUpper) {
return true
}
}
return false
}
// helper function to create the docker tag command.
func commandTag(build Build, tag string) *exec.Cmd {
var (
source = build.Name
target = fmt.Sprintf("%s:%s", build.Repo, tag)
)
return exec.Command(
dockerExe, "tag", source, target,
)
}
// helper function to create the docker push command.
func commandPush(build Build, tag string) *exec.Cmd {
target := fmt.Sprintf("%s:%s", build.Repo, tag)
return exec.Command(dockerExe, "push", target)
}
// helper function to create the docker daemon command.
func commandDaemon(daemon Daemon) *exec.Cmd {
args := []string{
"--data-root", daemon.StoragePath,
"--host=unix:///var/run/docker.sock",
}
if daemon.StorageDriver != "" {
args = append(args, "-s", daemon.StorageDriver)
}
if daemon.Insecure && daemon.Registry != "" {
args = append(args, "--insecure-registry", daemon.Registry)
}
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)
}
for _, dns := range daemon.DNS.Value() {
args = append(args, "--dns", dns)
}
for _, dnsSearch := range daemon.DNSSearch.Value() {
args = append(args, "--dns-search", dnsSearch)
}
if len(daemon.MTU) != 0 {
args = append(args, "--mtu", daemon.MTU)
}
if daemon.Experimental {
args = append(args, "--experimental")
}
return exec.Command(dockerdExe, args...)
}
// helper to check if args match "docker prune"
func isCommandPrune(args []string) bool {
return len(args) > 3 && args[2] == "prune"
}
func commandPrune() *exec.Cmd {
return exec.Command(dockerExe, "system", "prune", "-f")
}
// helper to check if args match "docker rmi"
func isCommandRmi(args []string) bool {
return len(args) > 2 && args[1] == "rmi"
}
func commandRmi(tag string) *exec.Cmd {
return exec.Command(dockerExe, "rmi", tag)
}
// trace writes each command to stdout with the command wrapped in an xml
// tag so that it can be extracted and displayed in the logs.
func trace(cmd *exec.Cmd) {
fmt.Fprintf(os.Stdout, "+ %s\n", strings.Join(cmd.Args, " "))
}

1
plugin/docker_test.go Normal file
View File

@ -0,0 +1 @@
package plugin

207
plugin/impl.go Normal file
View File

@ -0,0 +1,207 @@
package plugin
import (
"fmt"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"time"
"github.com/sirupsen/logrus"
"github.com/urfave/cli/v2"
)
// 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
}
// 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
}
// Build defines Docker build parameters.
type Build struct {
Remote string // Git remote URL
Name string // Git commit sha used as docker default named tag
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
Args cli.StringSlice // Docker build args
ArgsEnv cli.StringSlice // Docker build args from env
Target string // Docker build target
Squash bool // Docker build squash
Pull bool // Docker build pull
CacheFrom cli.StringSlice // Docker build cache-from
Compress bool // Docker build compress
Repo string // 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
Login Login
Build Build
Dryrun bool
Cleanup bool
}
// Validate handles the settings validation of the plugin.
func (p *Plugin) Validate() error {
p.settings.Build.Name = "00000000"
if p.pipeline.Commit.SHA != "" {
p.settings.Build.Name = p.pipeline.Commit.SHA
}
p.settings.Build.Branch = p.pipeline.Repo.Branch
p.settings.Build.Ref = p.pipeline.Commit.Ref
p.settings.Daemon.Registry = p.settings.Login.Registry
if p.settings.Build.TagsAuto {
// return true if tag event or default branch
if UseDefaultTag(
p.settings.Build.Ref,
p.settings.Build.Branch,
) {
tag, err := DefaultTagSuffix(
p.settings.Build.Ref,
p.settings.Build.TagsSuffix,
)
if err != nil {
logrus.Printf("cannot build docker image for %s, invalid semantic version", p.settings.Build.Ref)
return err
}
p.settings.Build.Tags = *cli.NewStringSlice(tag...)
} else {
logrus.Printf("skipping automated docker build for %s", p.settings.Build.Ref)
return nil
}
}
return nil
}
// Execute provides the implementation of the plugin.
func (p *Plugin) Execute() error {
// start the Docker daemon server
if !p.settings.Daemon.Disabled {
p.startDaemon()
}
// poll the docker daemon until it is started. This ensures the daemon is
// ready to accept connections before we proceed.
for i := 0; i < 15; i++ {
cmd := commandInfo()
err := cmd.Run()
if err == nil {
break
}
time.Sleep(time.Second * 1)
}
// Create Auth Config File
if p.settings.Login.Config != "" {
os.MkdirAll(dockerHome, 0600)
path := filepath.Join(dockerHome, "config.json")
err := ioutil.WriteFile(path, []byte(p.settings.Login.Config), 0600)
if err != nil {
return fmt.Errorf("error writing config.json: %s", err)
}
}
// login to the Docker registry
if p.settings.Login.Password != "" {
cmd := commandLogin(p.settings.Login)
err := cmd.Run()
if err != nil {
return fmt.Errorf("error authenticating: %s", err)
}
}
switch {
case p.settings.Login.Password != "":
fmt.Println("Detected registry credentials")
case p.settings.Login.Config != "":
fmt.Println("Detected registry credentials file")
default:
fmt.Println("Registry credentials or Docker config not provided. Guest mode enabled.")
}
if p.settings.Build.Squash && !p.settings.Daemon.Experimental {
fmt.Println("Squash build flag is only available when Docker deamon is started with experimental flag. Ignoring...")
p.settings.Build.Squash = false
}
// add proxy build args
addProxyBuildArgs(&p.settings.Build)
var cmds []*exec.Cmd
cmds = append(cmds, commandVersion()) // docker version
cmds = append(cmds, commandInfo()) // docker info
// pre-pull cache images
for _, img := range p.settings.Build.CacheFrom.Value() {
cmds = append(cmds, commandPull(img))
}
cmds = append(cmds, commandBuild(p.settings.Build)) // docker build
for _, tag := range p.settings.Build.Tags.Value() {
cmds = append(cmds, commandTag(p.settings.Build, tag)) // docker tag
if !p.settings.Dryrun {
cmds = append(cmds, commandPush(p.settings.Build, tag)) // docker push
}
}
if p.settings.Cleanup {
cmds = append(cmds, commandRmi(p.settings.Build.Name)) // docker rmi
cmds = append(cmds, commandPrune()) // docker system prune -f
}
// execute all commands in batch mode.
for _, cmd := range cmds {
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
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 && isCommandPrune(cmd.Args) {
fmt.Printf("Could not prune system containers. Ignoring...\n")
} else if err != nil && isCommandRmi(cmd.Args) {
fmt.Printf("Could not remove image %s. Ignoring...\n", cmd.Args[2])
} else if err != nil {
return err
}
}
return nil
}

21
plugin/plugin.go Normal file
View File

@ -0,0 +1,21 @@
package plugin
import (
"github.com/drone-plugins/drone-plugin-lib/drone"
)
// Plugin implements drone.Plugin to provide the plugin implementation.
type Plugin struct {
settings Settings
pipeline drone.Pipeline
network drone.Network
}
// 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,
pipeline: pipeline,
network: network,
}
}

93
plugin/tags.go Normal file
View File

@ -0,0 +1,93 @@
package plugin
import (
"fmt"
"strings"
"github.com/coreos/go-semver/semver"
)
// 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)
if err != nil {
return nil, err
}
if len(suffix) == 0 {
return tags, nil
}
for i, tag := range tags {
if tag == "latest" {
tags[i] = suffix
} else {
tags[i] = fmt.Sprintf("%s-%s", tag, suffix)
}
}
return tags, nil
}
func splitOff(input string, delim string) string {
parts := strings.SplitN(input, delim, 2)
if len(parts) == 2 {
return parts[0]
}
return input
}
// 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
}
v := stripTagPrefix(ref)
version, err := semver.NewVersion(v)
if err != nil {
return []string{"latest"}, err
}
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 {
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),
}, 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),
}, nil
}
// UseDefaultTag for keep only default branch for latest tag
func UseDefaultTag(ref, defaultBranch string) bool {
if strings.HasPrefix(ref, "refs/tags/") {
return true
}
if stripHeadPrefix(ref) == defaultBranch {
return true
}
return false
}
func stripHeadPrefix(ref string) string {
return strings.TrimPrefix(ref, "refs/heads/")
}
func stripTagPrefix(ref string) string {
ref = strings.TrimPrefix(ref, "refs/tags/")
ref = strings.TrimPrefix(ref, "v")
return ref
}

197
plugin/tags_test.go Normal file
View File

@ -0,0 +1,197 @@
package plugin
import (
"reflect"
"testing"
)
func Test_stripTagPrefix(t *testing.T) {
var tests = []struct {
Before string
After string
}{
{"refs/tags/1.0.0", "1.0.0"},
{"refs/tags/v1.0.0", "1.0.0"},
{"v1.0.0", "1.0.0"},
}
for _, test := range tests {
got, want := stripTagPrefix(test.Before), test.After
if got != want {
t.Errorf("Got tag %s, want %s", got, want)
}
}
}
func TestDefaultTags(t *testing.T) {
var tests = []struct {
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"}},
}
for _, test := range tests {
tags, err := DefaultTags(test.Before)
if err != nil {
t.Error(err)
continue
}
got, want := tags, test.After
if !reflect.DeepEqual(got, want) {
t.Errorf("Got tag %v, want %v", got, want)
}
}
}
func TestDefaultTagsError(t *testing.T) {
var tests = []string{
"refs/tags/x1.0.0",
"refs/tags/20190203",
}
for _, test := range tests {
_, err := DefaultTags(test)
if err == nil {
t.Errorf("Expect tag error for %s", test)
}
}
}
func TestDefaultTagSuffix(t *testing.T) {
var tests = []struct {
Before string
Suffix string
After []string
}{
// without suffix
{
After: []string{"latest"},
},
{
Before: "refs/tags/v1.0.0",
After: []string{
"1",
"1.0",
"1.0.0",
},
},
// with suffix
{
Suffix: "linux-amd64",
After: []string{"linux-amd64"},
},
{
Before: "refs/tags/v1.0.0",
Suffix: "linux-amd64",
After: []string{
"1-linux-amd64",
"1.0-linux-amd64",
"1.0.0-linux-amd64",
},
},
{
Suffix: "nanoserver",
After: []string{"nanoserver"},
},
{
Before: "refs/tags/v1.9.2",
Suffix: "nanoserver",
After: []string{
"1-nanoserver",
"1.9-nanoserver",
"1.9.2-nanoserver",
},
},
{
Before: "refs/tags/v18.06.0",
Suffix: "nanoserver",
After: []string{
"18-nanoserver",
"18.06-nanoserver",
"18.06.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)
}
}
}
func Test_stripHeadPrefix(t *testing.T) {
type args struct {
ref string
}
tests := []struct {
args args
want string
}{
{
args: args{
ref: "refs/heads/master",
},
want: "master",
},
}
for _, tt := range tests {
if got := stripHeadPrefix(tt.args.ref); got != tt.want {
t.Errorf("stripHeadPrefix() = %v, want %v", got, tt.want)
}
}
}
func TestUseDefaultTag(t *testing.T) {
type args struct {
ref string
defaultBranch string
}
tests := []struct {
name string
args args
want bool
}{
{
name: "latest tag for default branch",
args: args{
ref: "refs/heads/master",
defaultBranch: "master",
},
want: true,
},
{
name: "build from tags",
args: args{
ref: "refs/tags/v1.0.0",
defaultBranch: "master",
},
want: true,
},
{
name: "skip build for not default branch",
args: args{
ref: "refs/heads/develop",
defaultBranch: "master",
},
want: false,
},
}
for _, tt := range tests {
if got := UseDefaultTag(tt.args.ref, tt.args.defaultBranch); got != tt.want {
t.Errorf("%q. UseDefaultTag() = %v, want %v", tt.name, got, tt.want)
}
}
}

4
renovate.json Normal file
View File

@ -0,0 +1,4 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": ["github>thegeeklab/renovate-presets"]
}