Initial Commit
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
Markus Pesch 2022-06-27 21:42:59 +02:00
commit daf58bb4ca
Signed by: volker.raschek
GPG Key ID: 852BCC170D81A982
30 changed files with 3040 additions and 0 deletions

1
.dockerignore Normal file
View File

@ -0,0 +1 @@
drone-email

697
.drone.yml Normal file
View File

@ -0,0 +1,697 @@
---
kind: pipeline
type: kubernetes
name: linter
platform:
os: linux
steps:
- name: markdown lint
commands:
- markdownlint *.md
image: docker.io/volkerraschek/markdownlint:0.32.0
resources:
limits:
cpu: 150
memory: 150M
- name: email-notification
environment:
SMTP_FROM_ADDRESS:
from_secret: smtp_from_address
SMTP_FROM_NAME:
from_secret: smtp_from_name
SMTP_HOST:
from_secret: smtp_host
SMTP_USERNAME:
from_secret: smtp_username
SMTP_PASSWORD:
from_secret: smtp_password
image: docker.io/volkerraschek/drone-email:0.1.0
resources:
limits:
cpu: 150
memory: 150M
when:
status:
- changed
- failure
trigger:
event:
exclude:
- tag
---
kind: pipeline
type: docker
name: dry-run-amd64
depends_on:
- linter
platform:
os: linux
arch: amd64
steps:
- name: build
image: docker.io/plugins/docker:latest
settings:
auto_tag: false
dockerfile: Dockerfile
dry_run: true
force_tag: true
no_cache: true
purge: true
repo: volkerraschek/drone-email
tags: latest-amd64
username:
from_secret: container_image_registry_user
password:
from_secret: container_image_registry_password
volumes:
- name: docker_socket
path: /var/run/docker.sock
- name: notify
image: docker.io/drillster/drone-email:latest
environment:
PLUGIN_HOST:
from_secret: smtp_host
PLUGIN_USERNAME:
from_secret: smtp_username
PLUGIN_PASSWORD:
from_secret: smtp_password
PLUGIN_FROM:
from_secret: smtp_mail_address
when:
status:
- changed
- failure
trigger:
branch:
exclude:
- master
event:
- pull_request
- push
repo:
- volker.raschek/docker-email
volumes:
- name: docker_socket
host:
path: /var/run/docker.sock
---
kind: pipeline
type: docker
name: dry-run-arm-v7
depends_on:
- linter
platform:
os: linux
arch: arm
steps:
- name: build
image: docker.io/plugins/docker:latest
settings:
auto_tag: false
dockerfile: Dockerfile
dry_run: true
force_tag: true
no_cache: true
purge: true
repo: volkerraschek/drone-email
tags: latest-arm-v7
username:
from_secret: container_image_registry_user
password:
from_secret: container_image_registry_password
- name: notify
image: docker.io/drillster/drone-email:latest
environment:
PLUGIN_HOST:
from_secret: smtp_host
PLUGIN_USERNAME:
from_secret: smtp_username
PLUGIN_PASSWORD:
from_secret: smtp_password
PLUGIN_FROM:
from_secret: smtp_mail_address
volumes:
- name: docker_socket
path: /var/run/docker.sock
when:
status:
- changed
- failure
trigger:
branch:
exclude:
- master
event:
- pull_request
- push
repo:
- volker.raschek/docker-email
volumes:
- name: docker_socket
host:
path: /var/run/docker.sock
---
kind: pipeline
type: docker
name: dry-run-arm64-v8
depends_on:
- linter
platform:
os: linux
arch: arm64
steps:
- name: build
image: docker.io/plugins/docker:latest
settings:
auto_tag: false
dockerfile: Dockerfile
dry_run: true
force_tag: true
no_cache: true
purge: true
repo: volkerraschek/drone-email
tags: latest-arm64-v8
username:
from_secret: container_image_registry_user
password:
from_secret: container_image_registry_password
- name: notify
image: docker.io/drillster/drone-email:latest
environment:
PLUGIN_HOST:
from_secret: smtp_host
PLUGIN_USERNAME:
from_secret: smtp_username
PLUGIN_PASSWORD:
from_secret: smtp_password
PLUGIN_FROM:
from_secret: smtp_mail_address
volumes:
- name: docker_socket
path: /var/run/docker.sock
when:
status:
- changed
- failure
trigger:
branch:
exclude:
- master
event:
- pull_request
- push
repo:
- volker.raschek/docker-email
volumes:
- name: docker_socket
host:
path: /var/run/docker.sock
---
kind: pipeline
type: docker
name: latest-amd64
depends_on:
- linter
platform:
os: linux
arch: amd64
steps:
- name: build
image: docker.io/plugins/docker:latest
settings:
auto_tag: false
dockerfile: Dockerfile
force_tag: true
no_cache: true
purge: true
repo: volkerraschek/drone-email
tags: latest-amd64
username:
from_secret: container_image_registry_user
password:
from_secret: container_image_registry_password
volumes:
- name: docker_socket
path: /var/run/docker.sock
- name: email-notification
environment:
SMTP_FROM_ADDRESS:
from_secret: smtp_from_address
SMTP_FROM_NAME:
from_secret: smtp_from_name
SMTP_HOST:
from_secret: smtp_host
SMTP_USERNAME:
from_secret: smtp_username
SMTP_PASSWORD:
from_secret: smtp_password
image: docker.io/volkerraschek/drone-email:0.1.0
when:
status:
- changed
- failure
trigger:
branch:
- master
event:
- cron
- push
repo:
- volker.raschek/drone-email
volumes:
- name: docker_socket
host:
path: /var/run/docker.sock
---
kind: pipeline
type: docker
name: latest-arm-v7
depends_on:
- linter
platform:
os: linux
arch: arm
steps:
- name: build
image: docker.io/plugins/docker:latest
settings:
auto_tag: false
dockerfile: Dockerfile
force_tag: true
no_cache: true
purge: true
repo: volkerraschek/drone-email
tags: latest-arm-v7
username:
from_secret: container_image_registry_user
password:
from_secret: container_image_registry_password
- name: email-notification
environment:
SMTP_FROM_ADDRESS:
from_secret: smtp_from_address
SMTP_FROM_NAME:
from_secret: smtp_from_name
SMTP_HOST:
from_secret: smtp_host
SMTP_USERNAME:
from_secret: smtp_username
SMTP_PASSWORD:
from_secret: smtp_password
image: docker.io/volkerraschek/drone-email:0.1.0
when:
status:
- changed
- failure
trigger:
branch:
- master
event:
- cron
- push
repo:
- volker.raschek/drone-email
volumes:
- name: docker_socket
host:
path: /var/run/docker.sock
---
kind: pipeline
type: docker
name: latest-arm64-v8
depends_on:
- linter
platform:
os: linux
arch: arm64
steps:
- name: build
image: docker.io/plugins/docker:latest
settings:
auto_tag: false
dockerfile: Dockerfile
force_tag: true
no_cache: true
purge: true
repo: volkerraschek/drone-email
tags: latest-arm64-v8
username:
from_secret: container_image_registry_user
password:
from_secret: container_image_registry_password
- name: email-notification
environment:
SMTP_FROM_ADDRESS:
from_secret: smtp_from_address
SMTP_FROM_NAME:
from_secret: smtp_from_name
SMTP_HOST:
from_secret: smtp_host
SMTP_USERNAME:
from_secret: smtp_username
SMTP_PASSWORD:
from_secret: smtp_password
image: docker.io/volkerraschek/drone-email:0.1.0
when:
status:
- changed
- failure
trigger:
branch:
- master
event:
- cron
- push
repo:
- volker.raschek/drone-email
volumes:
- name: docker_socket
host:
path: /var/run/docker.sock
---
kind: pipeline
type: kubernetes
name: latest-manifest
depends_on:
- latest-amd64
- latest-arm-v7
- latest-arm64-v8
steps:
- name: build-manifest
image: docker.io/plugins/manifest:latest
settings:
auto_tag: false
ignore_missing: true
spec: manifest.tmpl
username:
from_secret: container_image_registry_user
password:
from_secret: container_image_registry_password
- name: email-notification
environment:
SMTP_FROM_ADDRESS:
from_secret: smtp_from_address
SMTP_FROM_NAME:
from_secret: smtp_from_name
SMTP_HOST:
from_secret: smtp_host
SMTP_USERNAME:
from_secret: smtp_username
SMTP_PASSWORD:
from_secret: smtp_password
image: docker.io/volkerraschek/drone-email:0.1.0
resources:
limits:
cpu: 150
memory: 150M
when:
status:
- changed
- failure
trigger:
branch:
- master
event:
- cron
- push
repo:
- volker.raschek/drone-email
---
kind: pipeline
type: docker
name: tagged-amd64
platform:
os: linux
arch: amd64
steps:
- name: build
image: docker.io/plugins/docker:latest
settings:
auto_tag_suffix: amd64
auto_tag: true
dockerfile: Dockerfile
force_tag: true
no_cache: true
purge: true
repo: volkerraschek/drone-email
username:
from_secret: container_image_registry_user
password:
from_secret: container_image_registry_password
build_args:
- VERSION=${DRONE_TAG}
volumes:
- name: docker_socket
path: /var/run/docker.sock
- name: email-notification
environment:
SMTP_FROM_ADDRESS:
from_secret: smtp_from_address
SMTP_FROM_NAME:
from_secret: smtp_from_name
SMTP_HOST:
from_secret: smtp_host
SMTP_USERNAME:
from_secret: smtp_username
SMTP_PASSWORD:
from_secret: smtp_password
image: docker.io/volkerraschek/drone-email:0.1.0
when:
status:
- changed
- failure
trigger:
event:
- tag
repo:
- volker.raschek/drone-email
volumes:
- name: docker_socket
host:
path: /var/run/docker.sock
---
kind: pipeline
type: docker
name: tagged-arm-v7
platform:
os: linux
arch: arm
steps:
- name: build
image: docker.io/plugins/docker:latest
settings:
auto_tag_suffix: arm-v7
auto_tag: true
dockerfile: Dockerfile
force_tag: true
no_cache: true
purge: true
repo: volkerraschek/drone-email
username:
from_secret: container_image_registry_user
password:
from_secret: container_image_registry_password
build_args:
- VERSION=${DRONE_TAG}
volumes:
- name: docker_socket
path: /var/run/docker.sock
- name: email-notification
environment:
SMTP_FROM_ADDRESS:
from_secret: smtp_from_address
SMTP_FROM_NAME:
from_secret: smtp_from_name
SMTP_HOST:
from_secret: smtp_host
SMTP_USERNAME:
from_secret: smtp_username
SMTP_PASSWORD:
from_secret: smtp_password
image: docker.io/volkerraschek/drone-email:0.1.0
when:
status:
- changed
- failure
trigger:
event:
- tag
repo:
- volker.raschek/drone-email
volumes:
- name: docker_socket
host:
path: /var/run/docker.sock
---
kind: pipeline
type: docker
name: tagged-arm64-v8
platform:
os: linux
arch: arm64
steps:
- name: build
image: docker.io/plugins/docker:latest
settings:
auto_tag_suffix: arm64-v8
auto_tag: true
dockerfile: Dockerfile
force_tag: true
no_cache: true
purge: true
repo: volkerraschek/drone-email
username:
from_secret: container_image_registry_user
password:
from_secret: container_image_registry_password
build_args:
- VERSION=${DRONE_TAG}
volumes:
- name: docker_socket
path: /var/run/docker.sock
- name: email-notification
environment:
SMTP_FROM_ADDRESS:
from_secret: smtp_from_address
SMTP_FROM_NAME:
from_secret: smtp_from_name
SMTP_HOST:
from_secret: smtp_host
SMTP_USERNAME:
from_secret: smtp_username
SMTP_PASSWORD:
from_secret: smtp_password
image: docker.io/volkerraschek/drone-email:0.1.0
when:
status:
- changed
- failure
trigger:
event:
- tag
repo:
- volker.raschek/drone-email
volumes:
- name: docker_socket
host:
path: /var/run/docker.sock
---
kind: pipeline
type: kubernetes
name: tagged-manifest
depends_on:
- tagged-amd64
- tagged-arm-v7
- tagged-arm64-v8
steps:
- name: build-manifest
image: docker.io/plugins/manifest:latest
settings:
auto_tag: true
ignore_missing: true
spec: manifest.tmpl
username:
from_secret: container_image_registry_user
password:
from_secret: container_image_registry_password
- name: email-notification
environment:
SMTP_FROM_ADDRESS:
from_secret: smtp_from_address
SMTP_FROM_NAME:
from_secret: smtp_from_name
SMTP_HOST:
from_secret: smtp_host
SMTP_USERNAME:
from_secret: smtp_username
SMTP_PASSWORD:
from_secret: smtp_password
image: docker.io/volkerraschek/drone-email:0.1.0
resources:
limits:
cpu: 150
memory: 150M
when:
status:
- changed
- failure
trigger:
event:
- tag
repo:
- volker.raschek/drone-email

5
.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
drone-email*
config.yaml
container-test.sh
coverage.txt
variables.yaml

42
.golangci.yml Normal file
View File

@ -0,0 +1,42 @@
run:
skip-dirs:
- it
timeout: 10m
tests: true
linters:
disable-all: true
enable:
# Default
- deadcode
- errcheck
- gosimple
- govet
- ineffassign
- staticcheck
- structcheck
- typecheck
- unused
- varcheck
# Additionally linters
- containedctx
- decorder
- errname
- gosec
- ireturn
- makezero
- misspell
- unparam
- whitespace
- wrapcheck
linters-settings:
# https://golangci-lint.run/usage/linters/#ireturn
ireturn:
allow:
- anon
- error
- empty
- stdlib
- github.com\/dedalus-cis4u\/proxy\/t1k\/test\/scheme\.Database

144
.markdownlint.yaml Normal file
View File

@ -0,0 +1,144 @@
# markdownlint YAML configuration
# https://github.com/DavidAnson/markdownlint/blob/main/schema/.markdownlint.yaml
# Default state for all rules
default: true
# Path to configuration file to extend
extends: null
# MD003/heading-style/header-style - Heading style
MD003:
# Heading style
style: "atx"
# MD004/ul-style - Unordered list style
MD004:
style: "dash"
# MD007/ul-indent - Unordered list indentation
MD007:
# Spaces for indent
indent: 2
# Whether to indent the first level of the list
start_indented: false
# MD009/no-trailing-spaces - Trailing spaces
MD009:
# Spaces for line break
br_spaces: 2
# Allow spaces for empty lines in list items
list_item_empty_lines: false
# Include unnecessary breaks
strict: false
# MD010/no-hard-tabs - Hard tabs
MD010:
# Include code blocks
code_blocks: true
# MD012/no-multiple-blanks - Multiple consecutive blank lines
MD012:
# Consecutive blank lines
maximum: 1
# MD013/line-length - Line length
MD013:
# Number of characters
line_length: 80
# Number of characters for headings
heading_line_length: 80
# Number of characters for code blocks
code_block_line_length: 80
# Include code blocks
code_blocks: false
# Include tables
tables: false
# Include headings
headings: true
# Include headings
headers: true
# Strict length checking
strict: false
# Stern length checking
stern: false
# MD022/blanks-around-headings/blanks-around-headers - Headings should be surrounded by blank lines
MD022:
# Blank lines above heading
lines_above: 1
# Blank lines below heading
lines_below: 1
# MD024/no-duplicate-heading/no-duplicate-header - Multiple headings with the same content
MD024:
# Only check sibling headings
allow_different_nesting: true
# MD025/single-title/single-h1 - Multiple top-level headings in the same document
MD025:
# Heading level
level: 1
# RegExp for matching title in front matter
front_matter_title: "^\\s*title\\s*[:=]"
# MD026/no-trailing-punctuation - Trailing punctuation in heading
MD026:
# Punctuation characters
punctuation: ".,;:!。,;:!"
# MD029/ol-prefix - Ordered list item prefix
MD029:
# List style
style: "one_or_ordered"
# MD030/list-marker-space - Spaces after list markers
MD030:
# Spaces for single-line unordered list items
ul_single: 1
# Spaces for single-line ordered list items
ol_single: 1
# Spaces for multi-line unordered list items
ul_multi: 1
# Spaces for multi-line ordered list items
ol_multi: 1
# MD033/no-inline-html - Inline HTML
MD033:
# Allowed elements
allowed_elements: []
# MD035/hr-style - Horizontal rule style
MD035:
# Horizontal rule style
style: "---"
# MD036/no-emphasis-as-heading/no-emphasis-as-header - Emphasis used instead of a heading
MD036:
# Punctuation characters
punctuation: ".,;:!?。,;:!?"
# MD041/first-line-heading/first-line-h1 - First line in a file should be a top-level heading
MD041:
# Heading level
level: 1
# RegExp for matching title in front matter
front_matter_title: "^\\s*title\\s*[:=]"
# MD044/proper-names - Proper names should have the correct capitalization
MD044:
# List of proper names
names:
- Gitea
# Include code blocks
code_blocks: false
# MD046/code-block-style - Code block style
MD046:
# Block style
style: "fenced"
# MD048/code-fence-style - Code fence style
MD048:
# Code fence syle
style: "backtick"

26
Dockerfile Normal file
View File

@ -0,0 +1,26 @@
FROM docker.io/library/golang:1.18.3-alpine3.16 AS build
ARG GONOPROXY
ARG GONOSUMDB
ARG GOPRIVATE
ARG GOPROXY
ARG VERSION
COPY . /workspace
WORKDIR /workspace
RUN set -ex && \
apk update && \
apk add git make && \
make install DESTDIR=/drone-email PREFIX=/usr VERSION=${VERSION}
###############################################################################
FROM docker.io/library/alpine:3.16
RUN apk add --no-cache bash bash-completion ca-certificates tzdata
COPY --from=build /drone-email /
ENTRYPOINT [ "/usr/bin/drone-email" ]

20
LICENSE Normal file
View File

@ -0,0 +1,20 @@
Copyright (c) 2022 Markus Pesch
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

118
Makefile Normal file
View File

@ -0,0 +1,118 @@
VERSION?=$(shell git describe --abbrev=0)+$(shell date +'%Y%m%d_%H%M%S')
EXECUTABLE:=drone-email
DESTDIR?=
PREFIX?=/usr/local
# CONTAINER_RUNTIME
CONTAINER_RUNTIME?=$(shell which podman)
# DRONEEMAIL_IMAGE
DRONEEMAIL_IMAGE_REGISTRY_HOST?=docker.io
DRONEEMAIL_IMAGE_REPOSITORY=volkerraschek/${EXECUTABLE}
DRONEEMAIL_IMAGE_VERSION?=latest
DRONEEMAIL_IMAGE_FULLY_QUALIFIED=${DRONEEMAIL_IMAGE_REGISTRY_HOST}/${DRONEEMAIL_IMAGE_REPOSITORY}:${DRONEEMAIL_IMAGE_VERSION}
DRONEEMAIL_IMAGE_UNQUALIFIED=${DRONEEMAIL_IMAGE_REPOSITORY}:${DRONEEMAIL_IMAGE_VERSION}
# BINARIES
# ==============================================================================
EXECUTABLES := ${EXECUTABLE}
EXECUTABLES += $(addsuffix .sh, ${EXECUTABLE})
EXECUTABLES += $(addsuffix .fish, ${EXECUTABLE})
EXECUTABLES += $(addsuffix .zsh, ${EXECUTABLE})
all: ${EXECUTABLES}
${EXECUTABLE}:
CGO_ENABLED=0 \
GONOPROXY=$(shell go env GONOPROXY) \
GONOSUMDB=$(shell go env GONOSUMDB) \
GOPRIVATE=$(shell go env GOPRIVATE) \
GOPROXY=$(shell go env GOPROXY) \
go build -ldflags "-X main.version=${VERSION:v%=%}" -o ${@}
${EXECUTABLE}.sh: ${EXECUTABLE}
./${EXECUTABLE} completion bash > ${EXECUTABLE}.sh
${EXECUTABLE}.fish: ${EXECUTABLE}
./${EXECUTABLE} completion fish > ${EXECUTABLE}.fish
${EXECUTABLE}.zsh: ${EXECUTABLE}
./${EXECUTABLE} completion zsh > ${EXECUTABLE}.zsh
# UN/INSTALL
# ==============================================================================
PHONY+=install
install: all
install --directory ${DESTDIR}${PREFIX}/bin
install --mode 755 ${EXECUTABLE} ${DESTDIR}${PREFIX}/bin/${EXECUTABLE}
install --directory ${DESTDIR}/etc/bash_completion.d
install --mode 644 ${EXECUTABLE}.sh ${DESTDIR}/etc/bash_completion.d/${EXECUTABLE}.sh
install --directory ${DESTDIR}${PREFIX}/share/fish/vendor_completions.d
install --mode 644 ${EXECUTABLE}.fish ${DESTDIR}${PREFIX}/share/fish/vendor_completions.d/${EXECUTABLE}.fish
install --directory ${DESTDIR}${PREFIX}/share/zsh/site-functions
install --mode 644 ${EXECUTABLE}.zsh ${DESTDIR}${PREFIX}/share/zsh/site-functions/_${EXECUTABLE}.zsh
install --directory ${DESTDIR}${PREFIX}/licenses/${EXECUTABLE}
install --mode 644 LICENSE ${DESTDIR}${PREFIX}/licenses/${EXECUTABLE}/LICENSE
PHONY+=uninstall
uninstall:
-rm --recursive --force \
${DESTDIR}${PREFIX}/bin/${EXECUTABLE} \
${DESTDIR}/etc/bash_completion.d/${EXECUTABLE}.sh \
${DESTDIR}${PREFIX}/share/fish/vendor_completions.d/${EXECUTABLE}.fish \
${DESTDIR}${PREFIX}/share/zsh/site-functions/_${EXECUTABLE}.zsh \
${DESTDIR}${PREFIX}/licenses/${EXECUTABLE}/LICENSE
# CLEAN
# ==============================================================================
PHONY+=clean
clean:
-rm -rf ${EXECUTABLE}*
# TEST
# ==============================================================================
PHONY+=test/unit
test/unit:
go test -v -race -coverprofile=coverage.txt -covermode=atomic -timeout 600s -count=1 ./...
PHONY+=test/coverage
test/coverage: test/unit
go tool cover -html=coverage.txt
# GOLANGCI-LINT
# ==============================================================================
PHONY+=golangci-lint
golangci-lint:
golangci-lint run --concurrency=$(shell nproc)
# CONTAINER-IMAGE
# ==============================================================================
PHONY+=container-image/build
container-image/build:
${CONTAINER_RUNTIME} build \
--build-arg GONOPROXY=${GOPROXY} \
--build-arg GONOSUMDB=${GONOSUMDB} \
--build-arg GOPRIVATE=${GOPRIVATE} \
--build-arg GOPROXY=${GOPROXY} \
--build-arg VERSION=${VERSION} \
--file ./Dockerfile \
--no-cache \
--tag ${DRONEEMAIL_IMAGE_UNQUALIFIED} \
--tag ${DRONEEMAIL_IMAGE_FULLY_QUALIFIED} \
.
PHONY+=container-image/push
container-image/push: container-image/build
${CONTAINER_RUNTIME} push ${DRONEEMAIL_IMAGE_FULLY_QUALIFIED}
# PHONY
# ==============================================================================
# Declare the contents of the PHONY variable as phony. We keep that information
# in a variable so we can use it in if_changed.
.PHONY: ${PHONY}

126
README.md Normal file
View File

@ -0,0 +1,126 @@
# drone-email
[![Build Status](https://drone.cryptic.systems/api/badges/volker.raschek/drone-email/status.svg)](https://drone.cryptic.systems/volker.raschek/drone-email)
A Drone CI/CD plugin to send build status notifications via email. The plugin is
currently available for the following architectures:
- x86_64 / amd64
- aarch64 / arm64
- armv7 / arm
## Compile or install the binary locally
Checkout the source code of the project and use `make` to compile or install the
binary locally.
```bash
make all # compile all targets, including shell completions
make drone-email # compile only the binary
make install # install the binary with completions locally
```
## Usage
All params can be defined via cli flags. A list of all provided cli-flags will
be written to `stdout` via `drone-email --help`.
Alternatively can be the flags defined via environment variables or a config file.
### Environment variables
| name | description |
| ------------------------------- | ----------------------------------------------- |
| `DRONE_BUILD_CREATED` | Unix timestamp when the build has been created |
| `DRONE_BUILD_EVENT` | Drone event which triggered the build |
| `DRONE_BUILD_FINISHED` | Unix timestamp when the build has been finished |
| `DRONE_BUILD_LINK` | URL to the build pipeline |
| `DRONE_BUILD_NUMBER` | Build number |
| `DRONE_BUILD_STARTED` | Unix timestamp when the build has been started |
| `DRONE_BUILD_STATUS` | Build status |
| `DRONE_COMMIT_AUTHOR_NAME` | Name of the commit author |
| `DRONE_COMMIT_AUTHOR_AVATAR` | Avatar of the commit author |
| `DRONE_COMMIT_AUTHOR_EMAIL` | EMail of the commit author |
| `DRONE_COMMIT_BRANCH` | Commit branch |
| `DRONE_COMMIT_LINK` | Link to the commit |
| `DRONE_COMMIT_MESSAGE` | Commit message |
| `DRONE_COMMIT_REF` | Commit reference |
| `DRONE_COMMIT_SHA` | Commit sha sum |
| `DRONE_DEPLOY_TO` | Deploy target |
| `DRONE_JOB_EXIT_CODE` | Job exit code |
| `DRONE_JOB_FINISHED` | Unix timestamp when the job has been created |
| `DRONE_JOB_NUMBER` | Job number |
| `DRONE_JOB_STARTED` | Unix timestamp when the job has been started |
| `DRONE_JOB_STATUS` | Job status |
| `DRONE_PREV_BUILD_NUMBER` | Previous build number |
| `DRONE_PREV_BUILD_STATUS` | Previous build status |
| `DRONE_PREV_COMMIT_SHA` | Previous commit sha sum |
| `DRONE_PULL_REQUEST` | Number of pull-requests |
| `DRONE_REMOTE_URL` | Clone URL of the repository |
| `DRONE_REPO` | Name of the repository, including org/owner |
| `DRONE_REPO_AVATAR` | Avatar of the repository |
| `DRONE_REPO_BRANCH` | Branch of the repository |
| `DRONE_REPO_LINK` | URL of the repository |
| `DRONE_REPO_NAME` | Name of the repository, without org/owner |
| `DRONE_REPO_OWNER` | Org/Owner of the repository |
| `DRONE_REPO_PRIVATE` | Private repository |
| `DRONE_REPO_SCM` | SCM of the repository |
| `DRONE_REPO_TRUSTED` | Trusted repository |
| `DRONE_TAG` | Tag |
| `DRONE_YAML_SIGNED` | Yaml is singed |
| `DRONE_YAML_VERIFIED` | Yaml is trusted |
| `SMTP_FROM_ADDRESS` | SMTP-From Address |
| `SMTP_FROM_NAME` | SMTP-From Name |
| `SMTP_HELO` | SMTP-HELO\EHLO |
| `SMTP_HOST` | SMTP-Host |
| `SMTP_MAIL_SUBJECT` | Overwrite default mail subject template |
| `SMTP_PASSWORD` | SMTP-Password |
| `SMTP_PORT` | SMTP-Port |
| `SMTP_START_TLS` | SMTP-Start-TLS |
| `SMTP_TLS_INSECURE_SKIP_VERIFY` | Trust insecure TLS certificate |
| `SMTP_TO_ADDRESSES` | SMTP-To Addresses |
| `SMTP_USERNAME` | SMTP-Username |
### Config file
Instead of environment variables, a `config.yaml` can be places in
`/etc/drone-email` or next to the binary.
The yaml should contain the same parameters as the cli flags. For example:
```yaml
drone-build-link: https://drone.example.local/max.mustermann/drone-email/1
drone-build-number: 1
drone-build-status: success
drone-build-started: 1656354006
drone-commit-author-email: max@example.local
drone-commit-author-name: Max Mustermann
drone-commit-branch: master
drone-commit-sha: 06b44cbfa054f146881e7234f1773008f006a756
drone-repo: max.mustermann/drone-email
drone-repo-link: https://git.example.local/max.mustermann/drone-email
smtp-from-address: noreply@example.local
smtp-from-name: noreply
smtp-helo: hostname.example.local
smtp-host: smtp1.example.local
smtp-password: my-password
smtp-username: noreply@example.local
```
## Known issues
### Multiple success emails despite failed ci step
The [drone-runner-kube](https://github.com/drone-runners/drone-runner-kube) does
not define the environment variable `DRONE_PREV_BUILD_STATUS` like the
[drone-runner-docker](https://github.com/drone-runners/drone-runner-docker).
This make it impossible to use the correct email template based on the build
state of the previous step.
Furthermore, the environment variable `DRONE_BUILD_STATUS` is always defined as
`success`, even if the build has failed.
Related issues:
- [Drillster/drone-email](https://github.com/Drillster/drone-email/issues/69)
- [stackoverflow - drone-ci: get status of previous step](https://stackoverflow.com/questions/73096709/drone-ci-get-status-of-previous-step)

583
cmd/cmd.go Normal file
View File

@ -0,0 +1,583 @@
package cmd
import (
"fmt"
"os"
"strings"
"git.cryptic.systems/volker.raschek/drone-email-docker/pkg/domain"
"git.cryptic.systems/volker.raschek/drone-email-docker/pkg/flags"
"git.cryptic.systems/volker.raschek/drone-email-docker/pkg/mail"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"github.com/spf13/viper"
)
const (
// The name of our config file, without the file extension because viper
// supports many different config file languages.
defaultConfigFilename = "config"
defaultConfigExtension = "yaml"
// The environment variable prefix of all environment variables bound to our command line flags.
// For example, --number is bound to STING_NUMBER.
envPrefix = ""
)
func Execute(version string) error {
rootCmd := &cobra.Command{
Use: "drone-email",
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
return initializeConfig(cmd)
},
RunE: func(cmd *cobra.Command, args []string) error {
vars, err := newHTMLTemplateVarsByCommand(cmd)
if err != nil {
return fmt.Errorf("failed to initialize new html template vars: %w", err)
}
smtpSettings, err := newSMTPSettingsByCommand(cmd)
if err != nil {
return fmt.Errorf("failed to initialize new config vars: %w", err)
}
recipients, err := cmd.Flags().GetStringArray(flags.SMTP_TO_ADDRESSES)
if err != nil {
return fmt.Errorf("failed to detect value of %s: %w", flags.SMTP_TO_ADDRESSES, err)
}
err = mail.NewPlugin(smtpSettings).Exec(cmd.Context(), recipients, vars)
if err != nil {
return fmt.Errorf("failed to execute mail plugin: %w", err)
}
_, err = fmt.Fprint(os.Stdout, "E-Mails successfully sent")
if err != nil {
return fmt.Errorf("failed to write message on stdout: %w", err)
}
return nil
},
SilenceUsage: true,
Version: version,
}
hostname, err := os.Hostname()
if err != nil {
return fmt.Errorf("failed to detect hostname: %w", err)
}
// Drone environment variables/flags
// Flags which receive their values from environment variables of the drone
// CI/CD.
//
// The names of the FLags must match the environment variables, otherwise the
// environment variables will not be bound correctly to the flags.
rootCmd.Flags().Int64(flags.DRONE_BUILD_CREATED, 0, "Build created")
rootCmd.Flags().Int64(flags.DRONE_BUILD_FINISHED, 0, "Build finished")
rootCmd.Flags().Int64(flags.DRONE_BUILD_STARTED, 0, "Build stared")
rootCmd.Flags().String(flags.DRONE_BUILD_EVENT, "push", "Build event")
rootCmd.Flags().String(flags.DRONE_BUILD_LINK, "", "Build link")
rootCmd.Flags().Int(flags.DRONE_BUILD_NUMBER, 0, "Build number")
rootCmd.Flags().String(flags.DRONE_BUILD_STATUS, "success", "Build status")
rootCmd.Flags().String(flags.DRONE_COMMIT_SHA, "", "SHA sum of the commit")
rootCmd.Flags().String(flags.DRONE_COMMIT_REF, "refs/heads/master", "Commit reference")
rootCmd.Flags().String(flags.DRONE_COMMIT_BRANCH, "master", "Commit branch")
rootCmd.Flags().String(flags.DRONE_COMMIT_LINK, "", "Link to the commit")
rootCmd.Flags().String(flags.DRONE_COMMIT_MESSAGE, "", "Commit message")
rootCmd.Flags().String(flags.DRONE_COMMIT_AUTHOR_NAME, "", "Name of the commit author")
rootCmd.Flags().String(flags.DRONE_COMMIT_AUTHOR_EMAIL, "", "E-Mail of the commit author")
rootCmd.Flags().String(flags.DRONE_COMMIT_AUTHOR_AVATAR, "", "Avatar of the commit author")
rootCmd.Flags().String(flags.DRONE_DEPLOY_TO, "", "Deploy target")
rootCmd.Flags().String(flags.DRONE_JOB_NUMBER, "", "Job number")
rootCmd.Flags().String(flags.DRONE_JOB_STATUS, "", "Job status")
rootCmd.Flags().Int(flags.DRONE_JOB_EXIT_CODE, 0, "Job exit code")
rootCmd.Flags().Int(flags.DRONE_JOB_STARTED, 0, "Job started")
rootCmd.Flags().Int(flags.DRONE_JOB_FINISHED, 0, "Job finished")
rootCmd.Flags().String(flags.DRONE_PREV_BUILD_STATUS, "", "Previous build status")
rootCmd.Flags().Int(flags.DRONE_PREV_BUILD_NUMBER, 0, "Previous build number")
rootCmd.Flags().String(flags.DRONE_PREV_COMMIT_SHA, "", "Previous commit sha sum")
rootCmd.Flags().Int(flags.DRONE_PULL_REQUEST, 0, "Number of pull-request")
rootCmd.Flags().String(flags.DRONE_REMOTE_URL, "", "Clone URL of the repository")
rootCmd.Flags().Bool(flags.DRONE_REPO_PRIVATE, true, "Repository is private")
rootCmd.Flags().Bool(flags.DRONE_REPO_TRUSTED, false, "Repository is trusted")
rootCmd.Flags().String(flags.DRONE_REPO_AVATAR, "", "Avatar URL of the repository")
rootCmd.Flags().String(flags.DRONE_REPO_BRANCH, "master", "Branch of the repository")
rootCmd.Flags().String(flags.DRONE_REPO_LINK, "", "URL to the repository")
rootCmd.Flags().String(flags.DRONE_REPO_NAME, "", "Name of the repository")
rootCmd.Flags().String(flags.DRONE_REPO_OWNER, "", "Name of the repository owner")
rootCmd.Flags().String(flags.DRONE_REPO_SCM, "git", "Source code management provider")
rootCmd.Flags().String(flags.DRONE_REPO, "", "Full name of the repository")
rootCmd.Flags().String(flags.DRONE_TAG, "", "Tag")
rootCmd.Flags().Bool(flags.DRONE_YAML_SIGNED, false, "YAML is signed")
rootCmd.Flags().Bool(flags.DRONE_YAML_VERIFIED, false, "YAML is verified")
// MAIL SETTINGS
rootCmd.Flags().Bool(flags.SMTP_START_TLS, mail.DefaultSMTPStartTLS, "Use StartTLS instead of SSL")
rootCmd.Flags().Bool(flags.SMTP_TLS_INSECURE_SKIP_VERIFY, mail.DefaultSMTPTLSInsecureSkipVerify, "Trust insecure TLS certificates")
rootCmd.Flags().Int(flags.SMTP_PORT, mail.DefaultSMTPPort, "SMTP-Port")
rootCmd.Flags().String(flags.SMTP_FROM_ADDRESS, mail.DefaultSMTPFromAddress, "SMTP-From Address")
rootCmd.Flags().String(flags.SMTP_FROM_NAME, mail.DefaultSMTPFromName, "SMTP-From Name")
rootCmd.Flags().String(flags.SMTP_HELO, hostname, "SMTP-HELO/EHLO")
rootCmd.Flags().String(flags.SMTP_HOST, mail.DefaultSMTPHost, "SMTP-Host")
rootCmd.Flags().String(flags.SMTP_PASSWORD, "", "SMTP-Password")
rootCmd.Flags().String(flags.SMTP_USERNAME, "", "SMTP-User")
rootCmd.Flags().StringArray(flags.SMTP_TO_ADDRESSES, []string{}, "List of recipients")
rootCmd.AddCommand(completionCmd)
err = rootCmd.Execute()
if err != nil {
return fmt.Errorf("failed to execute root cmd: %w", err)
}
return nil
}
func initializeConfig(cmd *cobra.Command) error {
v := viper.New()
// Set the base name of the config file, without the file extension.
v.SetConfigName(defaultConfigFilename)
v.SetConfigType(defaultConfigExtension)
// Set as many paths as you like where viper should look for the
// config file. We are only looking in the current working directory.
v.AddConfigPath(".")
v.AddConfigPath("$HOME/.config/drone-email/")
v.AddConfigPath("/etc/drone-email/")
// Attempt to read the config file, gracefully ignoring errors
// caused by a config file not being found. Return an error
// if we cannot parse the config file.
if err := v.ReadInConfig(); err != nil {
// It's okay if there isn't a config file
if _, ok := err.(viper.ConfigFileNotFoundError); !ok {
return fmt.Errorf("failed to read config: %w", err)
}
}
// Bind to environment variables
// Works great for simple config names, but needs help for names
// like --favorite-color which we fix in the bindFlags function
v.AutomaticEnv()
// Bind the current command's flags to viper
bindFlags(cmd, v)
return nil
}
// Bind each cobra flag to its associated viper configuration (config file and environment variable)
func bindFlags(cmd *cobra.Command, v *viper.Viper) {
cmd.Flags().VisitAll(func(f *pflag.Flag) {
// Environment variables can't have dashes in them, so bind them to their equivalent
// keys with underscores, e.g. --favorite-color to STING_FAVORITE_COLOR
if strings.Contains(f.Name, "-") {
envVarSuffix := strings.ToUpper(strings.ReplaceAll(f.Name, "-", "_"))
if len(envPrefix) <= 0 {
_ = v.BindEnv(f.Name, envVarSuffix)
} else {
_ = v.BindEnv(f.Name, fmt.Sprintf("%s_%s", envPrefix, envVarSuffix))
}
}
// Apply the viper config value to the flag when the flag is not set and viper has a value
if !f.Changed && v.IsSet(f.Name) {
val := v.Get(f.Name)
_ = cmd.Flags().Set(f.Name, fmt.Sprintf("%v", val))
}
})
}
func newHTMLTemplateVarsByCommand(cmd *cobra.Command) (*mail.CIVars, error) {
build, err := newBuildByCommand(cmd)
if err != nil {
return nil, fmt.Errorf("failed to initialize new build struct: %w", err)
}
commit, err := newCommitByCommand(cmd)
if err != nil {
return nil, fmt.Errorf("failed to initialize new commit struct: %w", err)
}
deployTo, err := cmd.Flags().GetString(flags.DRONE_DEPLOY_TO)
if err != nil {
return nil, fmt.Errorf("failed to detect value of %s: %w", flags.DRONE_DEPLOY_TO, err)
}
job, err := newJobByCommand(cmd)
if err != nil {
return nil, fmt.Errorf("failed to initialize new job struct: %w", err)
}
prev, err := newPrevByCommand(cmd)
if err != nil {
return nil, fmt.Errorf("failed to initialize new prev struct: %w", err)
}
pullRequests, err := cmd.Flags().GetInt(flags.DRONE_PULL_REQUEST)
if err != nil {
return nil, fmt.Errorf("failed to detect value of %s: %w", flags.DRONE_PULL_REQUEST, err)
}
remote, err := newRemoteByCommand(cmd)
if err != nil {
return nil, fmt.Errorf("failed to initialize new remote struct: %w", err)
}
repo, err := newRepoByCommand(cmd)
if err != nil {
return nil, fmt.Errorf("failed to initialize new repo struct: %w", err)
}
tag, err := cmd.Flags().GetString(flags.DRONE_TAG)
if err != nil {
return nil, fmt.Errorf("failed to detect value of %s: %w", flags.DRONE_TAG, err)
}
yaml, err := newYAMLByCommand(cmd)
if err != nil {
return nil, fmt.Errorf("failed to initialize new yaml struct: %w", err)
}
return &mail.CIVars{
Build: build,
Commit: commit,
DeployTo: deployTo,
Job: job,
Prev: prev,
PullRequest: pullRequests,
Remote: remote,
Repo: repo,
Tag: tag,
Yaml: yaml,
}, nil
}
func newBuildByCommand(cmd *cobra.Command) (*domain.Build, error) {
buildCreated, err := cmd.Flags().GetInt64(flags.DRONE_BUILD_CREATED)
if err != nil {
return nil, fmt.Errorf("failed to detect value of %s: %w", flags.DRONE_BUILD_CREATED, err)
}
buildEvent, err := cmd.Flags().GetString(flags.DRONE_BUILD_EVENT)
if err != nil {
return nil, fmt.Errorf("failed to detect value of %s: %w", flags.DRONE_BUILD_EVENT, err)
}
buildFinished, err := cmd.Flags().GetInt64(flags.DRONE_BUILD_FINISHED)
if err != nil {
return nil, fmt.Errorf("failed to detect value of %s: %w", flags.DRONE_BUILD_FINISHED, err)
}
buildLink, err := cmd.Flags().GetString(flags.DRONE_BUILD_LINK)
if err != nil {
return nil, fmt.Errorf("failed to detect value of %s: %w", flags.DRONE_BUILD_LINK, err)
}
buildNumber, err := cmd.Flags().GetInt(flags.DRONE_BUILD_NUMBER)
if err != nil {
return nil, fmt.Errorf("failed to detect value of %s: %w", flags.DRONE_BUILD_NUMBER, err)
}
buildStared, err := cmd.Flags().GetInt64(flags.DRONE_BUILD_STARTED)
if err != nil {
return nil, fmt.Errorf("failed to detect value of %s: %w", flags.DRONE_BUILD_STARTED, err)
}
buildStatus, err := cmd.Flags().GetString(flags.DRONE_BUILD_STATUS)
if err != nil {
return nil, fmt.Errorf("failed to detect value of %s: %w", flags.DRONE_BUILD_STATUS, err)
}
build := &domain.Build{
Created: buildCreated,
Event: buildEvent,
Finished: buildFinished,
Link: buildLink,
Number: buildNumber,
Started: buildStared,
Status: buildStatus,
}
return build, nil
}
func newCommitByCommand(cmd *cobra.Command) (*domain.Commit, error) {
authorAvatar, err := cmd.Flags().GetString(flags.DRONE_COMMIT_AUTHOR_AVATAR)
if err != nil {
return nil, fmt.Errorf("failed to detect value of %s: %w", flags.DRONE_COMMIT_AUTHOR_AVATAR, err)
}
authorEmail, err := cmd.Flags().GetString(flags.DRONE_COMMIT_AUTHOR_EMAIL)
if err != nil {
return nil, fmt.Errorf("failed to detect value of %s: %w", flags.DRONE_COMMIT_AUTHOR_EMAIL, err)
}
authorName, err := cmd.Flags().GetString(flags.DRONE_COMMIT_AUTHOR_NAME)
if err != nil {
return nil, fmt.Errorf("failed to detect value of %s: %w", flags.DRONE_COMMIT_AUTHOR_NAME, err)
}
branch, err := cmd.Flags().GetString(flags.DRONE_COMMIT_BRANCH)
if err != nil {
return nil, fmt.Errorf("failed to detect value of %s: %w", flags.DRONE_COMMIT_BRANCH, err)
}
link, err := cmd.Flags().GetString(flags.DRONE_COMMIT_LINK)
if err != nil {
return nil, fmt.Errorf("failed to detect value of %s: %w", flags.DRONE_COMMIT_LINK, err)
}
message, err := cmd.Flags().GetString(flags.DRONE_COMMIT_MESSAGE)
if err != nil {
return nil, fmt.Errorf("failed to detect value of %s: %w", flags.DRONE_COMMIT_MESSAGE, err)
}
ref, err := cmd.Flags().GetString(flags.DRONE_COMMIT_REF)
if err != nil {
return nil, fmt.Errorf("failed to detect value of %s: %w", flags.DRONE_COMMIT_REF, err)
}
sha, err := cmd.Flags().GetString(flags.DRONE_COMMIT_SHA)
if err != nil {
return nil, fmt.Errorf("failed to detect value of %s: %w", flags.DRONE_COMMIT_SHA, err)
}
commit := &domain.Commit{
Author: &domain.Author{
Avatar: authorAvatar,
Email: authorEmail,
Name: authorName,
},
Branch: branch,
Link: link,
Message: message,
Ref: ref,
Sha: sha,
}
return commit, nil
}
func newJobByCommand(cmd *cobra.Command) (*domain.Job, error) {
exitCode, err := cmd.Flags().GetInt(flags.DRONE_JOB_EXIT_CODE)
if err != nil {
return nil, fmt.Errorf("failed to detect value of %s: %w", flags.DRONE_JOB_EXIT_CODE, err)
}
finished, err := cmd.Flags().GetInt64(flags.DRONE_BUILD_FINISHED)
if err != nil {
return nil, fmt.Errorf("failed to detect value of %s: %w", flags.DRONE_BUILD_FINISHED, err)
}
started, err := cmd.Flags().GetInt64(flags.DRONE_BUILD_STARTED)
if err != nil {
return nil, fmt.Errorf("failed to detect value of %s: %w", flags.DRONE_BUILD_STARTED, err)
}
status, err := cmd.Flags().GetString(flags.DRONE_JOB_STATUS)
if err != nil {
return nil, fmt.Errorf("failed to detect value of %s: %w", flags.DRONE_JOB_STATUS, err)
}
job := &domain.Job{
ExitCode: exitCode,
Finished: finished,
Started: started,
Status: status,
}
return job, nil
}
func newPrevByCommand(cmd *cobra.Command) (*domain.Prev, error) {
prevBuildNumber, err := cmd.Flags().GetInt(flags.DRONE_PREV_BUILD_NUMBER)
if err != nil {
return nil, fmt.Errorf("failed to detect value of %s: %w", flags.DRONE_PREV_BUILD_NUMBER, err)
}
prevBuildStatus, err := cmd.Flags().GetString(flags.DRONE_PREV_BUILD_STATUS)
if err != nil {
return nil, fmt.Errorf("failed to detect value of %s: %w", flags.DRONE_PREV_BUILD_STATUS, err)
}
prevCommitSha, err := cmd.Flags().GetString(flags.DRONE_PREV_COMMIT_SHA)
if err != nil {
return nil, fmt.Errorf("failed to detect value of %s: %w", flags.DRONE_PREV_COMMIT_SHA, err)
}
prev := &domain.Prev{
Build: &domain.PrevBuild{
Number: prevBuildNumber,
Status: prevBuildStatus,
},
Commit: &domain.PrevCommit{
Sha: prevCommitSha,
},
}
return prev, nil
}
func newRemoteByCommand(cmd *cobra.Command) (*domain.Remote, error) {
remoteURL, err := cmd.Flags().GetString(flags.DRONE_REMOTE_URL)
if err != nil {
return nil, fmt.Errorf("failed to detect value of %s: %w", flags.DRONE_REMOTE_URL, err)
}
remote := &domain.Remote{
URL: remoteURL,
}
return remote, nil
}
func newRepoByCommand(cmd *cobra.Command) (*domain.Repo, error) {
avatar, err := cmd.Flags().GetString(flags.DRONE_REPO_AVATAR)
if err != nil {
return nil, fmt.Errorf("failed to detect value of %s: %w", flags.DRONE_REPO_AVATAR, err)
}
branch, err := cmd.Flags().GetString(flags.DRONE_REPO_BRANCH)
if err != nil {
return nil, fmt.Errorf("failed to detect value of %s: %w", flags.DRONE_REPO_BRANCH, err)
}
fullName, err := cmd.Flags().GetString(flags.DRONE_REPO_NAME)
if err != nil {
return nil, fmt.Errorf("failed to detect value of %s: %w", flags.DRONE_REPO_NAME, err)
}
link, err := cmd.Flags().GetString(flags.DRONE_REPO_LINK)
if err != nil {
return nil, fmt.Errorf("failed to detect value of %s: %w", flags.DRONE_REPO_LINK, err)
}
name, err := cmd.Flags().GetString(flags.DRONE_REPO)
if err != nil {
return nil, fmt.Errorf("failed to detect value of %s: %w", flags.DRONE_REPO, err)
}
owner, err := cmd.Flags().GetString(flags.DRONE_REPO_OWNER)
if err != nil {
return nil, fmt.Errorf("failed to detect value of %s: %w", flags.DRONE_REPO_OWNER, err)
}
private, err := cmd.Flags().GetBool(flags.DRONE_REPO_PRIVATE)
if err != nil {
return nil, fmt.Errorf("failed to detect value of %s: %w", flags.DRONE_REPO_PRIVATE, err)
}
scm, err := cmd.Flags().GetString(flags.DRONE_REPO_SCM)
if err != nil {
return nil, fmt.Errorf("failed to detect value of %s: %w", flags.DRONE_REPO_SCM, err)
}
trusted, err := cmd.Flags().GetBool(flags.DRONE_REPO_TRUSTED)
if err != nil {
return nil, fmt.Errorf("failed to detect value of %s: %w", flags.DRONE_REPO_TRUSTED, err)
}
remote := &domain.Repo{
Avatar: avatar,
Branch: branch,
FullName: fullName,
Link: link,
Name: name,
Owner: owner,
Private: private,
SCM: scm,
Trusted: trusted,
}
return remote, nil
}
func newYAMLByCommand(cmd *cobra.Command) (*domain.Yaml, error) {
signed, err := cmd.Flags().GetBool(flags.DRONE_YAML_SIGNED)
if err != nil {
return nil, fmt.Errorf("failed to detect value of %s: %w", flags.DRONE_YAML_SIGNED, err)
}
verified, err := cmd.Flags().GetBool(flags.DRONE_YAML_VERIFIED)
if err != nil {
return nil, fmt.Errorf("failed to detect value of %s: %w", flags.DRONE_YAML_VERIFIED, err)
}
yaml := &domain.Yaml{
Signed: signed,
Verified: verified,
}
return yaml, nil
}
func newSMTPSettingsByCommand(cmd *cobra.Command) (*domain.SMTPSettings, error) {
smtpStartTLS, err := cmd.Flags().GetBool(flags.SMTP_START_TLS)
if err != nil {
return nil, fmt.Errorf("failed to detect value of %s: %w", flags.SMTP_START_TLS, err)
}
smtpFromAddress, err := cmd.Flags().GetString(flags.SMTP_FROM_ADDRESS)
if err != nil {
return nil, fmt.Errorf("failed to detect value of %s: %w", flags.SMTP_FROM_ADDRESS, err)
}
smtpFromName, err := cmd.Flags().GetString(flags.SMTP_FROM_NAME)
if err != nil {
return nil, fmt.Errorf("failed to detect value of %s: %w", flags.SMTP_FROM_NAME, err)
}
smtpHELOName, err := cmd.Flags().GetString(flags.SMTP_HELO)
if err != nil {
return nil, fmt.Errorf("failed to detect value of %s: %w", flags.SMTP_HELO, err)
}
smtpHost, err := cmd.Flags().GetString(flags.SMTP_HOST)
if err != nil {
return nil, fmt.Errorf("failed to detect value of %s: %w", flags.SMTP_HOST, err)
}
smtpPassword, err := cmd.Flags().GetString(flags.SMTP_PASSWORD)
if err != nil {
return nil, fmt.Errorf("failed to detect value of %s: %w", flags.SMTP_PASSWORD, err)
}
smtpPort, err := cmd.Flags().GetInt(flags.SMTP_PORT)
if err != nil {
return nil, fmt.Errorf("failed to detect value of %s: %w", flags.SMTP_PORT, err)
}
smtpTLSInsecureSkipVerify, err := cmd.Flags().GetBool(flags.SMTP_TLS_INSECURE_SKIP_VERIFY)
if err != nil {
return nil, fmt.Errorf("failed to detect value of %s: %w", flags.SMTP_TLS_INSECURE_SKIP_VERIFY, err)
}
smtpUsername, err := cmd.Flags().GetString(flags.SMTP_USERNAME)
if err != nil {
return nil, fmt.Errorf("failed to detect value of %s: %w", flags.SMTP_USERNAME, err)
}
return &domain.SMTPSettings{
FromAddress: smtpFromAddress,
FromName: smtpFromName,
HELOName: smtpHELOName,
Host: smtpHost,
Password: smtpPassword,
Port: smtpPort,
StartTLS: smtpStartTLS,
TLSInsecureSkipVerify: smtpTLSInsecureSkipVerify,
Username: smtpUsername,
}, nil
}

67
cmd/completion.go Normal file
View File

@ -0,0 +1,67 @@
package cmd
import (
"os"
"github.com/spf13/cobra"
)
var completionCmd = &cobra.Command{
Use: "completion [bash|zsh|fish|powershell]",
Short: "Generate completion script",
Long: `To load completions:
Bash:
$ source <(drone-email completion bash)
# To load completions for each session, execute once:
# Linux:
$ drone-email completion bash > /etc/bash_completion.d/drone-email
# macOS:
$ drone-email completion bash > $(brew --prefix)/etc/bash_completion.d/drone-email
Zsh:
# If shell completion is not already enabled in your environment,
# you will need to enable it. You can execute the following once:
$ echo "autoload -U compinit; compinit" >> ~/.zshrc
# To load completions for each session, execute once:
$ drone-email completion zsh > "${fpath[1]}/_drone-email"
# You will need to start a new shell for this setup to take effect.
fish:
$ drone-email completion fish | source
# To load completions for each session, execute once:
$ drone-email completion fish > ~/.config/fish/completions/drone-email.fish
PowerShell:
PS> drone-email completion powershell | Out-String | Invoke-Expression
# To load completions for every new session, run:
PS> drone-email completion powershell > drone-email.ps1
# and source this file from your PowerShell profile.
`,
DisableFlagsInUseLine: true,
ValidArgs: []string{"bash", "zsh", "fish", "powershell"},
Args: cobra.ExactValidArgs(1),
Hidden: true,
Run: func(cmd *cobra.Command, args []string) {
switch args[0] {
case "bash":
_ = cmd.Root().GenBashCompletion(os.Stdout)
case "zsh":
_ = cmd.Root().GenZshCompletion(os.Stdout)
case "fish":
_ = cmd.Root().GenFishCompletion(os.Stdout, true)
case "powershell":
_ = cmd.Root().GenPowerShellCompletionWithDesc(os.Stdout)
}
},
}

28
go.mod Normal file
View File

@ -0,0 +1,28 @@
module git.cryptic.systems/volker.raschek/drone-email-docker
go 1.18
require (
github.com/spf13/cobra v1.5.0
github.com/spf13/pflag v1.0.5
github.com/spf13/viper v1.12.0
)
require (
github.com/fsnotify/fsnotify v1.5.4 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/magiconair/properties v1.8.6 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/pelletier/go-toml v1.9.5 // indirect
github.com/pelletier/go-toml/v2 v2.0.2 // indirect
github.com/spf13/afero v1.8.2 // indirect
github.com/spf13/cast v1.5.0 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/subosito/gotenv v1.4.0 // indirect
golang.org/x/sys v0.0.0-20220627191245-f75cf1eec38b // indirect
golang.org/x/text v0.3.7 // indirect
gopkg.in/ini.v1 v1.66.6 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

483
go.sum Normal file
View File

@ -0,0 +1,483 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
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/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI=
github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo=
github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/pelletier/go-toml/v2 v2.0.2 h1:+jQXlF3scKIcSEKkdHzXhCTDLPFi5r1wnK6yPS+49Gw=
github.com/pelletier/go-toml/v2 v2.0.2/go.mod h1:MovirKjgVRESsAvNZlAjtFwV867yGuwRkXbG66OzopI=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg=
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/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/spf13/afero v1.8.2 h1:xehSyVa0YnHWsJ49JFljMpg1HX19V6NDZ1fkm1Xznbo=
github.com/spf13/afero v1.8.2/go.mod h1:CtAatgMJh6bJEIs48Ay/FOnkljP3WeGUG0MC1RfAqwo=
github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w=
github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU=
github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU=
github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM=
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.12.0 h1:CZ7eSOd3kZoaYDLbXnmzgQI5RlciuXBMA+18HwHRfZQ=
github.com/spf13/viper v1.12.0/go.mod h1:b6COn30jlNxbm/V2IqWiNWkJ+vZNiMNksliPCiuKtSI=
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.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s=
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
github.com/subosito/gotenv v1.4.0 h1:yAzM1+SmVcz5R4tXGsNMu1jUl2aOJXoiWUCEwwnGrvs=
github.com/subosito/gotenv v1.4.0/go.mod h1:mZd6rFysKEcUhUHXJk0C/08wAgyDBFuwEYL7vWWGaGo=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220627191245-f75cf1eec38b h1:2n253B2r0pYSmEV+UNCQoPfU/FiaizQEK5Gu4Bq4JE8=
golang.org/x/sys v0.0.0-20220627191245-f75cf1eec38b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
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/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/ini.v1 v1.66.6 h1:LATuAqN/shcYAOkv3wl2L4rkaKqkcgTBQjOyYDvcPKI=
gopkg.in/ini.v1 v1.66.6/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
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=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=

11
main.go Normal file
View File

@ -0,0 +1,11 @@
package main
import (
"git.cryptic.systems/volker.raschek/drone-email-docker/cmd"
)
var version string
func main() {
_ = cmd.Execute(version)
}

26
manifest.tmpl Normal file
View File

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

7
pkg/domain/author.go Normal file
View File

@ -0,0 +1,7 @@
package domain
type Author struct {
Avatar string
Email string
Name string
}

33
pkg/domain/build.go Normal file
View File

@ -0,0 +1,33 @@
package domain
import "time"
type Build struct {
Created int64
Event string
Finished int64
Link string
Number int
Started int64
Status string
}
func (b *Build) CreatedToTimeFormat(format string) string {
return time.Unix(b.Created, 0).Format(format)
}
func (b *Build) FinishedToTimeFormat(format string) string {
return time.Unix(b.Finished, 0).Format(format)
}
func (b *Build) IsEvent(expectedEvent string) bool {
return expectedEvent == b.Event
}
func (b *Build) IsStatus(expectedStatus string) bool {
return expectedStatus == b.Status
}
func (b *Build) StartedToTimeFormat(format string) string {
return time.Unix(b.Started, 0).Format(format)
}

10
pkg/domain/commit.go Normal file
View File

@ -0,0 +1,10 @@
package domain
type Commit struct {
Author *Author
Branch string
Link string
Message string
Ref string
Sha string
}

8
pkg/domain/job.go Normal file
View File

@ -0,0 +1,8 @@
package domain
type Job struct {
ExitCode int
Finished int64
Started int64
Status string
}

6
pkg/domain/prev.go Normal file
View File

@ -0,0 +1,6 @@
package domain
type Prev struct {
Build *PrevBuild
Commit *PrevCommit
}

6
pkg/domain/prevBuild.go Normal file
View File

@ -0,0 +1,6 @@
package domain
type PrevBuild struct {
Number int
Status string
}

5
pkg/domain/prevCommit.go Normal file
View File

@ -0,0 +1,5 @@
package domain
type PrevCommit struct {
Sha string
}

5
pkg/domain/remote.go Normal file
View File

@ -0,0 +1,5 @@
package domain
type Remote struct {
URL string
}

13
pkg/domain/repo.go Normal file
View File

@ -0,0 +1,13 @@
package domain
type Repo struct {
Avatar string
Branch string
FullName string
Link string
Name string
Owner string
Private bool
SCM string
Trusted bool
}

13
pkg/domain/smtp.go Normal file
View File

@ -0,0 +1,13 @@
package domain
type SMTPSettings struct {
FromAddress string
FromName string
HELOName string
Host string
Password string
Port int
StartTLS bool
TLSInsecureSkipVerify bool
Username string
}

6
pkg/domain/yaml.go Normal file
View File

@ -0,0 +1,6 @@
package domain
type Yaml struct {
Signed bool
Verified bool
}

56
pkg/flags/flags.go Normal file
View File

@ -0,0 +1,56 @@
package flags
const (
DRONE_BUILD_CREATED string = "drone-build-created"
DRONE_BUILD_EVENT string = "drone-build-event"
DRONE_BUILD_FINISHED string = "drone-build-finished"
DRONE_BUILD_LINK string = "drone-build-link"
DRONE_BUILD_NUMBER string = "drone-build-number"
DRONE_BUILD_STARTED string = "drone-build-started"
DRONE_BUILD_STATUS string = "drone-build-status"
DRONE_COMMIT_AUTHOR_NAME string = "drone-commit-author-name"
DRONE_COMMIT_AUTHOR_AVATAR string = "drone-commit-author-avatar"
DRONE_COMMIT_AUTHOR_EMAIL string = "drone-commit-author-email"
DRONE_COMMIT_BRANCH string = "drone-commit-branch"
DRONE_COMMIT_LINK string = "drone-commit-link"
DRONE_COMMIT_MESSAGE string = "drone-commit-message"
DRONE_COMMIT_REF string = "drone-commit-ref"
DRONE_COMMIT_SHA string = "drone-commit-sha"
DRONE_DEPLOY_TO string = "drone-deploy-to"
DRONE_JOB_EXIT_CODE string = "drone-job-exit-code"
DRONE_JOB_FINISHED string = "drone-job-finished"
DRONE_JOB_NUMBER string = "drone-job-number"
DRONE_JOB_STARTED string = "drone-job-started"
DRONE_JOB_STATUS string = "drone-job-status"
DRONE_PREV_BUILD_NUMBER string = "drone-prev-build-number"
DRONE_PREV_BUILD_STATUS string = "drone-prev-build-status"
DRONE_PREV_COMMIT_SHA string = "drone-prev-commit-sha"
DRONE_PULL_REQUEST string = "drone-pull-request"
DRONE_REMOTE_URL string = "drone-remote-url"
DRONE_REPO string = "drone-repo"
DRONE_REPO_AVATAR string = "drone-repo-avatar"
DRONE_REPO_BRANCH string = "drone-repo-branch"
DRONE_REPO_LINK string = "drone-repo-link"
DRONE_REPO_NAME string = "drone-repo-name"
DRONE_REPO_OWNER string = "drone-repo-owner"
DRONE_REPO_PRIVATE string = "drone-repo-private"
DRONE_REPO_SCM string = "drone-repo-scm"
DRONE_REPO_TRUSTED string = "drone-repo-trusted"
DRONE_TAG string = "drone-tag"
DRONE_YAML_SIGNED string = "drone-yaml-signed"
DRONE_YAML_VERIFIED string = "drone-yaml-verified"
)
const (
SMTP_FROM_ADDRESS string = "smtp-from-address"
SMTP_FROM_NAME string = "smtp-from-name"
SMTP_HELO string = "smtp-helo"
SMTP_HOST string = "smtp-host"
SMTP_MAIL_SUBJECT string = "smtp-mail-subject"
SMTP_PASSWORD string = "smtp-password"
SMTP_PORT string = "smtp-port"
SMTP_START_TLS string = "smtp-no-start-tls"
SMTP_TLS_INSECURE_SKIP_VERIFY string = "smtp-tls-insecure"
SMTP_TO_ADDRESSES string = "smtp-to-addresses"
SMTP_USERNAME string = "smtp-username"
)

278
pkg/mail/assets/mail.txt Normal file
View File

@ -0,0 +1,278 @@
Date: {{ .TimeNowFormat "Mon, 02 Jan 2006 15:04:05" }}
From: {{ .SMTPSettings.FromName }} <{{ .SMTPSettings.FromAddress }}>
To: {{ .Recipient }}
Subject: [{{ .CIVars.Build.Status }}] {{ .CIVars.Repo.Name }} ({{ .CIVars.Commit.Branch }} - {{ .CIVars.Commit.Sha }})
Content-Type: multipart/alternative;
boundary=3399d59dca7fb53c0236f440e2a402d670fc0abe57faa6f0233e85338b03
--3399d59dca7fb53c0236f440e2a402d670fc0abe57faa6f0233e85338b03
Content-Transfer-Encoding: quoted-printable
Content-Type: text/plain; charset=UTF-8
{{- if .CIVars.Build.IsStatus "success" -}}
Success build #{{ .CIVars.Build.Number }}
{{- else -}}
Failed build #{{ .CIVars.Build.Number }}
{{- end }}
Name: {{ .CIVars.Repo.Name }}
Author: {{ .CIVars.Commit.Author.Name }} <{{ .CIVars.Commit.Author.Email }}>
Branch: {{ .CIVars.Repo.Branch }}
Commit: {{ .CIVars.Commit.Sha }}
Started At: {{ .CIVars.Build.StartedToTimeFormat "2006-02-01 15:04:05" }}
Link: {{ .CIVars.Build.Link }}
--3399d59dca7fb53c0236f440e2a402d670fc0abe57faa6f0233e85338b03
Content-Transfer-Encoding: quoted-printable
Content-Type: text/html; charset=UTF-8
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta name="viewport" content="width=device-width" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<style>
* {
margin: 0;
padding: 0;
font-family: "Helvetica Neue", "Helvetica", Helvetica, Arial, sans-serif;
box-sizing: border-box;
font-size: 14px;
}
body {
-webkit-font-smoothing: antialiased;
-webkit-text-size-adjust: none;
width: 100% !important;
height: 100%;
line-height: 1.6;
background-color: #f6f6f6;
}
table td {
vertical-align: top;
}
.body-wrap {
background-color: #f6f6f6;
width: 100%;
}
.container {
display: block !important;
max-width: 600px !important;
margin: 0 auto !important;
/* makes it centered */
clear: both !important;
}
.content {
max-width: 600px;
margin: 0 auto;
display: block;
padding: 20px;
}
.main {
background: #fff;
border: 1px solid #e9e9e9;
border-radius: 3px;
}
.content-wrap {
padding: 20px;
}
.content-block {
padding: 0 0 20px;
}
.header {
width: 100%;
margin-bottom: 20px;
}
h1, h2, h3 {
font-family: "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif;
color: #000;
margin: 40px 0 0;
line-height: 1.2;
font-weight: 400;
}
h1 {
font-size: 32px;
font-weight: 500;
}
h2 {
font-size: 24px;
}
h3 {
font-size: 18px;
}
hr {
border: 1px solid #e9e9e9;
margin: 20px 0;
height: 1px;
padding: 0;
}
p,
ul,
ol {
margin-bottom: 10px;
font-weight: normal;
}
p li,
ul li,
ol li {
margin-left: 5px;
list-style-position: inside;
}
a {
color: #348eda;
text-decoration: underline;
}
.last {
margin-bottom: 0;
}
.first {
margin-top: 0;
}
.padding {
padding: 10px 0;
}
.aligncenter {
text-align: center;
}
.alignright {
text-align: right;
}
.alignleft {
text-align: left;
}
.clear {
clear: both;
}
.alert {
font-size: 16px;
color: #fff;
font-weight: 500;
padding: 20px;
text-align: center;
border-radius: 3px 3px 0 0;
}
.alert a {
color: #fff;
text-decoration: none;
font-weight: 500;
font-size: 16px;
}
.alert.alert-warning {
background: #ff9f00;
}
.alert.alert-bad {
background: #d0021b;
}
.alert.alert-good {
background: #68b90f;
}
@media only screen and (max-width: 640px) {
h1,
h2,
h3 {
font-weight: 600 !important;
margin: 20px 0 5px !important;
}
h1 {
font-size: 22px !important;
}
h2 {
font-size: 18px !important;
}
h3 {
font-size: 16px !important;
}
.container {
width: 100% !important;
}
.content,
.content-wrapper {
padding: 10px !important;
}
}
</style>
</head>
<body>
<table class="body-wrap">
<tr>
<td></td>
<td class="container" width="600">
<div class="content">
<table class="main" width="100%" cellpadding="0" cellspacing="0">
<tr>
{{ if .CIVars.Build.IsStatus "success" }}
<td class="alert alert-good">
<a href="{{ .CIVars.Build.Link }}">
Successful build #{{ .CIVars.Build.Number }}
</a>
</td>
{{ else }}
<td class="alert alert-bad">
<a href="{{ .CIVars.Build.Link }}">
Failed build #{{ .CIVars.Build.Number }}
</a>
</td>
{{ end }}
</tr>
<tr>
<td class="content-wrap">
<table width="100%" cellpadding="0" cellspacing="0">
<tr>
<td>
Repo:
</td>
<td>
{{ .CIVars.Repo.Name }}
</td>
</tr>
<tr>
<td>
Author:
</td>
<td>
{{ .CIVars.Commit.Author.Name }} &lt{{ .CIVars.Commit.Author.Email }}&gt
</td>
</tr>
<tr>
<td>
Branch:
</td>
<td>
{{ .CIVars.Commit.Branch }}
</td>
</tr>
<tr>
<td>
Commit:
</td>
<td>
{{ .CIVars.Commit.Sha }}
</td>
</tr>
<tr>
<td>
Started at:
</td>
<td>
{{ .CIVars.Build.StartedToTimeFormat "2006-02-01 15:04:05" }}
</td>
</tr>
</table>
<hr>
<table width="100%" cellpadding="0" cellspacing="0">
<tr>
<td>
{{ .CIVars.Commit.Message }}
</td>
</tr>
</table>
</td>
</tr>
</table>
</div>
</td>
<td></td>
</tr>
</table>
</body>
</html>
--3399d59dca7fb53c0236f440e2a402d670fc0abe57faa6f0233e85338b03--

180
pkg/mail/mail.go Normal file
View File

@ -0,0 +1,180 @@
package mail
import (
"bytes"
"context"
"crypto/tls"
"fmt"
"io"
"net"
"net/smtp"
"text/template"
"time"
"git.cryptic.systems/volker.raschek/drone-email-docker/pkg/domain"
_ "embed"
)
const (
DefaultSMTPFromAddress = "root@localhost"
DefaultSMTPFromName = "root"
DefaultSMTPHost = "localhost"
DefaultSMTPPort = 587
DefaultSMTPStartTLS = true
DefaultSMTPTLSInsecureSkipVerify = false
DefaultSMTPToAddress = "root@localhost"
)
//go:embed assets/mail.txt
var mailTemplate string
type CIVars struct {
Build *domain.Build
Commit *domain.Commit
DeployTo string
Job *domain.Job
Prev *domain.Prev
PullRequest int
Remote *domain.Remote
Repo *domain.Repo
Tag string
Yaml *domain.Yaml
}
type templateVars struct {
CIVars *CIVars
Recipient string
SMTPSettings *domain.SMTPSettings
}
func (t *templateVars) TimeNowFormat(layout string) string {
return time.Now().Format(layout)
}
type Plugin struct {
smtpSettings *domain.SMTPSettings
}
// Exec will send emails over SMTP
func (p *Plugin) Exec(ctx context.Context, recipients []string, ciVars *CIVars) error {
exists := false
for _, recipient := range recipients {
if recipient == ciVars.Commit.Author.Email {
exists = true
break
}
}
if !exists {
recipients = append(recipients, ciVars.Commit.Author.Email)
}
tpl, err := template.New("mail").Parse(mailTemplate)
if err != nil {
return fmt.Errorf("failed to parse template: %w", err)
}
buf := make([]byte, 0)
buffer := bytes.NewBuffer(buf)
for _, recipient := range recipients {
err = tpl.Execute(buffer, &templateVars{
CIVars: ciVars,
Recipient: recipient,
SMTPSettings: p.smtpSettings,
})
if err != nil {
return fmt.Errorf("failed to generate template: %w", err)
}
err := p.sendMail(recipient, buffer)
if err != nil {
return fmt.Errorf("failed to send mail: %w", err)
}
buffer.Reset()
}
return nil
}
func (p *Plugin) sendMail(recipient string, r io.Reader) error {
// log.Printf("FROM_ADDRESS: %s", p.smtpSettings.FromAddress)
// log.Printf("FROM_NAME: %s", p.smtpSettings.FromName)
// log.Printf("HELO: %s", p.smtpSettings.HELOName)
// log.Printf("HOST: %s", p.smtpSettings.Host)
// log.Printf("PASSWORD: %s", p.smtpSettings.Password)
// log.Printf("USERNAME: %s", p.smtpSettings.Username)
// log.Printf("PORT: %v", p.smtpSettings.Port)
// log.Printf("START_TLS: %v", p.smtpSettings.StartTLS)
// log.Printf("INSECURE: %v", p.smtpSettings.TLSInsecureSkipVerify)
address := fmt.Sprintf("%s:%d", p.smtpSettings.Host, p.smtpSettings.Port)
tcpConn, err := net.Dial("tcp", address)
if err != nil {
return fmt.Errorf("failed to dial a connection to %s: %w", address, err)
}
defer func() { _ = tcpConn.Close() }()
smtpClient, err := smtp.NewClient(tcpConn, p.smtpSettings.Host)
if err != nil {
return fmt.Errorf("failed to initialize a new smtp client: %w", err)
}
defer func() { _ = smtpClient.Close() }()
err = smtpClient.Hello(p.smtpSettings.HELOName)
if err != nil {
return fmt.Errorf("failed to send helo command: %w", err)
}
// #nosec G402
err = smtpClient.StartTLS(&tls.Config{
InsecureSkipVerify: p.smtpSettings.TLSInsecureSkipVerify,
MinVersion: tls.VersionTLS12,
ServerName: p.smtpSettings.Host,
})
if err != nil {
return fmt.Errorf("failed initialize starttls session: %w", err)
}
smtpAuth := smtp.PlainAuth(p.smtpSettings.FromAddress, p.smtpSettings.FromAddress, p.smtpSettings.Password, p.smtpSettings.Host)
err = smtpClient.Auth(smtpAuth)
if err != nil {
return fmt.Errorf("failed to authenticate client: %w", err)
}
err = smtpClient.Mail(p.smtpSettings.FromAddress)
if err != nil {
return fmt.Errorf("failed to sent mail command: %w", err)
}
err = smtpClient.Rcpt(recipient)
if err != nil {
return fmt.Errorf("failed to sent rcpt command for %s: %w", recipient, err)
}
wc, err := smtpClient.Data()
if err != nil {
return fmt.Errorf("failed to send data command: %w", err)
}
defer func() { _ = wc.Close() }()
_, err = io.Copy(wc, r)
if err != nil {
return fmt.Errorf("failed to copy input from passed reader to smtp writer: %w", err)
}
err = smtpClient.Quit()
if err != nil {
return fmt.Errorf("failed to send quit command: %w", err)
}
return nil
}
func NewPlugin(config *domain.SMTPSettings) *Plugin {
return &Plugin{
smtpSettings: config,
}
}

37
renovate.json Normal file
View File

@ -0,0 +1,37 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"assignees": [ "volker.raschek" ],
"automergeStrategy": "merge-commit",
"automergeType": "pr",
"labels": [ "renovate" ],
"packageRules": [
{
"description": "Automatically update minor and patch versions of used drone-ci images",
"addLabels": [ "renovate/droneci", "renovate/automerge" ],
"automerge": true,
"matchManagers": "droneci",
"matchUpdateTypes": [ "minor", "patch"]
},
{
"description": "Automatically update minor and patch versions of go modules",
"addLabels": [ "renovate/gomod", "renovate/automerge" ],
"automerge": true,
"matchBaseBranches": [ "master" ],
"matchManagers": [ "gomod" ],
"matchUpdateTypes": [ "minor", "patch" ]
},
{
"description": "Prepare MR for major update minor of go modules",
"addLabels": [ "renovate/gomod" ],
"automerge": false,
"matchBaseBranches": [ "master" ],
"matchManagers": [ "gomod" ],
"matchUpdateTypes": [ "major" ]
}
],
"postUpdateOptions": [
"gomodTidy"
],
"rebaseLabel": "renovate/rebase",
"rebaseWhen": "behind-base-branch"
}