This commit is contained in:
parent
fd7d6f3ba5
commit
cab88875b1
20
Dockerfile
20
Dockerfile
@ -1,12 +1,24 @@
|
|||||||
FROM python:3.11.4-slim
|
FROM golang:1.20.5-alpine AS build-stage
|
||||||
|
|
||||||
RUN apt-get update && apt-get install -y git toilet \
|
WORKDIR /app
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
|
||||||
|
COPY go.mod go.sum ./
|
||||||
|
RUN go mod download
|
||||||
|
|
||||||
|
COPY *.go ./
|
||||||
|
|
||||||
|
RUN CGO_ENABLED=0 GOOS=linux go build -o /entrypoint
|
||||||
|
|
||||||
|
|
||||||
|
FROM python:3.11.4-slim AS runtime-stage
|
||||||
|
|
||||||
|
# RUN apt-get update && apt-get install -y git toilet \
|
||||||
|
# && rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
# renovate: datasource=pypi depName=ansible
|
# renovate: datasource=pypi depName=ansible
|
||||||
ENV ANSIBLE_VERSION=8.1.0
|
ENV ANSIBLE_VERSION=8.1.0
|
||||||
RUN pip3 install --no-cache-dir ansible==${ANSIBLE_VERSION} dnspython passlib netaddr
|
RUN pip3 install --no-cache-dir ansible==${ANSIBLE_VERSION} dnspython passlib netaddr
|
||||||
|
|
||||||
COPY entrypoint /bin/entrypoint
|
COPY --from=build-stage /entrypoint /bin/entrypoint
|
||||||
|
|
||||||
ENTRYPOINT [ "/bin/entrypoint" ]
|
ENTRYPOINT [ "/bin/entrypoint" ]
|
44
README.md
44
README.md
@ -6,39 +6,43 @@ kind: pipeline
|
|||||||
name: default
|
name: default
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Check ansible syntax
|
check-ansible-syntax:
|
||||||
image: harbor.sebclem.fr/sebclem/drone-ansible-runner
|
image: git.sebclem.fr/sebclem/woodpecker-ansible-runner
|
||||||
|
pull: true
|
||||||
settings:
|
settings:
|
||||||
playbook: sites.yml
|
playbook: playbooks/install.yml
|
||||||
galaxy_file: roles/requirements.yml
|
galaxy_file: roles/requirements.yml
|
||||||
check_syntax: true
|
check_syntax: true
|
||||||
vault_token:
|
vault_token:
|
||||||
from_secret: ansible_vault_password
|
from_secret: ansible_vault_token
|
||||||
private_key:
|
|
||||||
from_secret: ansible_private_key
|
|
||||||
when:
|
when:
|
||||||
event:
|
- event: "push"
|
||||||
- push
|
branch: [main, master]
|
||||||
- custom
|
- event: [pull_request, manual, deployment]
|
||||||
|
|
||||||
- name: Run ansible playbook
|
run-ansible-playbook:
|
||||||
image: harbor.sebclem.fr/sebclem/drone-ansible-runner
|
image: git.sebclem.fr/sebclem/woodpecker-ansible-runner
|
||||||
|
pull: true
|
||||||
settings:
|
settings:
|
||||||
verbosity: ${verbosity=1}
|
verbosity: ${verbosity=0}
|
||||||
|
diff: ${diff=false}
|
||||||
|
check: ${check=false}
|
||||||
limit: ${limit}
|
limit: ${limit}
|
||||||
tags: ${tags}
|
tags: ${tags}
|
||||||
playbook: sites.yml
|
playbook: playbooks/install.yml
|
||||||
galaxy_file: roles/requirements.yml
|
galaxy_file: roles/requirements.yml
|
||||||
check_syntax: true
|
|
||||||
vault_token:
|
vault_token:
|
||||||
from_secret: ansible_vault_password
|
from_secret: ansible_vault_token
|
||||||
private_key:
|
private_key:
|
||||||
from_secret: ansible_private_key
|
from_secret: ansible_private_key
|
||||||
limit: harbor.home
|
|
||||||
when:
|
when:
|
||||||
event:
|
environment: production
|
||||||
- promote
|
event: deployment
|
||||||
- rollback
|
|
||||||
- custom
|
when:
|
||||||
|
- event: "push"
|
||||||
|
branch: [main, master]
|
||||||
|
- event: [pull_request, manual, deployment]
|
||||||
|
|
||||||
|
|
||||||
```
|
```
|
82
entrypoint
82
entrypoint
@ -1,82 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
check=${PLUGIN_CHECK_SYNTAX:-false}
|
|
||||||
verbosity=${PLUGIN_VERBOSITY:-0}
|
|
||||||
|
|
||||||
C_RESET="\e[39m"
|
|
||||||
C_RED="\e[31m"
|
|
||||||
C_YELLOW="\e[33m"
|
|
||||||
C_BLUE="\e[34m"
|
|
||||||
|
|
||||||
run_command(){
|
|
||||||
echo "➡️ $@"
|
|
||||||
$@
|
|
||||||
}
|
|
||||||
echo -e "${C_BLUE}"
|
|
||||||
toilet -f smblock Woodpecker Ansible
|
|
||||||
toilet -f smblock " Runner"
|
|
||||||
echo -e "${C_RESET}"
|
|
||||||
|
|
||||||
if [ -z "$PLUGIN_PLAYBOOK" ]; then
|
|
||||||
echo -e "⚠️ ${C_RED}'playbook' setting not defined, ABORT!${C_RESET}"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -z "$PLUGIN_PRIVATE_KEY" ]; then
|
|
||||||
echo -e "⚠️ ${C_YELLOW}[WARN] 'private_key' setting not defined !${C_RESET}"
|
|
||||||
else
|
|
||||||
|
|
||||||
run_command "mkdir /root/.ssh"
|
|
||||||
echo "$PLUGIN_PRIVATE_KEY" > /root/.ssh/id_ed25519
|
|
||||||
run_command chmod 400 /root/.ssh/id_ed25519
|
|
||||||
fi
|
|
||||||
|
|
||||||
args=("$PLUGIN_PLAYBOOK")
|
|
||||||
|
|
||||||
if [ -n "$PLUGIN_VAULT_TOKEN" ]; then
|
|
||||||
echo "💼 Adding vault token to 'credentials/ci_vault_token'"
|
|
||||||
run_command "mkdir credentials"
|
|
||||||
echo "$PLUGIN_VAULT_TOKEN" > credentials/ci_vault_token
|
|
||||||
args+=("--vault-password-file=credentials/ci_vault_token")
|
|
||||||
echo "⚙️ Clean ansible.cfg"
|
|
||||||
echo "➡️ sed -i '/vault_password_file.*/d' ./ansible.cfg"
|
|
||||||
sed -i '/vault_password_file.*/d' ./ansible.cfg
|
|
||||||
echo ""
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -f "ansible-ci.cfg" ]; then
|
|
||||||
echo "⚙️ ansible-ci.cfg is present, using it."
|
|
||||||
rm ansible.cfg
|
|
||||||
mv ansible-ci.cfg ansible.cfg
|
|
||||||
echo ""
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -n "$PLUGIN_GALAXY_FILE" ]; then
|
|
||||||
echo "🚀 Installing Galaxy dependencies ($PLUGIN_GALAXY_FILE)"
|
|
||||||
run_command "ansible-galaxy install -r $PLUGIN_GALAXY_FILE --force"
|
|
||||||
echo ""
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ $check = true ]]; then
|
|
||||||
args+=("--syntax-check")
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ $verbosity != "0" ]]; then
|
|
||||||
verb="-"
|
|
||||||
for i in `seq 1 $verbosity`; do
|
|
||||||
verb+="v"
|
|
||||||
done
|
|
||||||
args+=("$verb")
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ -n "$PLUGIN_LIMIT" ]]; then
|
|
||||||
args+=("--limit" "$PLUGIN_LIMIT")
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ -n "$PLUGIN_TAGS" ]]; then
|
|
||||||
args+=("--tags" "$PLUGIN_TAGS")
|
|
||||||
fi
|
|
||||||
|
|
||||||
run_command "export ANSIBLE_HOST_KEY_CHECKING=False"
|
|
||||||
echo "➡️ ansible-playbook ${args[@]}"
|
|
||||||
ansible-playbook "${args[@]}"
|
|
219
entrypoint.go
Normal file
219
entrypoint.go
Normal file
@ -0,0 +1,219 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"go.uber.org/zap"
|
||||||
|
"go.uber.org/zap/zapcore"
|
||||||
|
)
|
||||||
|
|
||||||
|
type pluginConfig struct {
|
||||||
|
check_syntax bool
|
||||||
|
check bool
|
||||||
|
diff bool
|
||||||
|
verbosity int64
|
||||||
|
limit *string
|
||||||
|
tags *string
|
||||||
|
privateKey *string
|
||||||
|
vaultToken *string
|
||||||
|
galaxyFile *string
|
||||||
|
playbook *string
|
||||||
|
}
|
||||||
|
|
||||||
|
func runCommand(sugar *zap.SugaredLogger, args ...string) {
|
||||||
|
sugar.Infof("➡️ %s", args)
|
||||||
|
cmd := exec.Command(args[0], args[1:]...)
|
||||||
|
cmd.Env = os.Environ()
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
err := cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getPluginConfig(sugar *zap.SugaredLogger) pluginConfig {
|
||||||
|
config := pluginConfig{}
|
||||||
|
|
||||||
|
check_syntax, present := os.LookupEnv("PLUGIN_CHECK_SYNTAX")
|
||||||
|
if !present {
|
||||||
|
config.check_syntax = false
|
||||||
|
} else {
|
||||||
|
converted, err := strconv.ParseBool(check_syntax)
|
||||||
|
if err != nil {
|
||||||
|
sugar.Fatal(err)
|
||||||
|
}
|
||||||
|
config.check_syntax = converted
|
||||||
|
}
|
||||||
|
|
||||||
|
check, present := os.LookupEnv("PLUGIN_CHECK")
|
||||||
|
if !present {
|
||||||
|
config.check = false
|
||||||
|
} else {
|
||||||
|
converted, err := strconv.ParseBool(check)
|
||||||
|
if err != nil {
|
||||||
|
sugar.Fatal(err)
|
||||||
|
}
|
||||||
|
config.check = converted
|
||||||
|
}
|
||||||
|
|
||||||
|
diff, present := os.LookupEnv("PLUGIN_DIFF")
|
||||||
|
if !present {
|
||||||
|
config.diff = false
|
||||||
|
} else {
|
||||||
|
converted, err := strconv.ParseBool(diff)
|
||||||
|
if err != nil {
|
||||||
|
sugar.Fatal(err)
|
||||||
|
}
|
||||||
|
config.diff = converted
|
||||||
|
}
|
||||||
|
|
||||||
|
verbosity, present := os.LookupEnv("PLUGIN_VERBOSITY")
|
||||||
|
if !present {
|
||||||
|
config.verbosity = 0
|
||||||
|
} else {
|
||||||
|
converted, err := strconv.ParseInt(verbosity, 10, 0)
|
||||||
|
if err != nil {
|
||||||
|
sugar.Fatal(err)
|
||||||
|
}
|
||||||
|
config.verbosity = converted
|
||||||
|
}
|
||||||
|
|
||||||
|
limit, present := os.LookupEnv("PLUGIN_LIMIT")
|
||||||
|
if !present {
|
||||||
|
config.limit = nil
|
||||||
|
} else {
|
||||||
|
config.limit = &limit
|
||||||
|
}
|
||||||
|
|
||||||
|
tags, present := os.LookupEnv("PLUGIN_TAGS")
|
||||||
|
if !present {
|
||||||
|
config.tags = nil
|
||||||
|
} else {
|
||||||
|
config.tags = &tags
|
||||||
|
}
|
||||||
|
|
||||||
|
privateKey, present := os.LookupEnv("PLUGIN_PRIVATE_KEY")
|
||||||
|
if !present {
|
||||||
|
sugar.Warn("⚠️ 'private_key' setting not defined !")
|
||||||
|
config.privateKey = nil
|
||||||
|
} else {
|
||||||
|
config.privateKey = &privateKey
|
||||||
|
}
|
||||||
|
|
||||||
|
vaultToken, present := os.LookupEnv("PLUGIN_VAULT_TOKEN")
|
||||||
|
if !present {
|
||||||
|
config.vaultToken = nil
|
||||||
|
} else {
|
||||||
|
config.vaultToken = &vaultToken
|
||||||
|
}
|
||||||
|
|
||||||
|
galaxyFile, present := os.LookupEnv("PLUGIN_GALAXY_FILE")
|
||||||
|
if !present {
|
||||||
|
config.galaxyFile = nil
|
||||||
|
} else {
|
||||||
|
config.galaxyFile = &galaxyFile
|
||||||
|
}
|
||||||
|
|
||||||
|
playbook, present := os.LookupEnv("PLUGIN_PLAYBOOK")
|
||||||
|
if !present {
|
||||||
|
sugar.Fatal("⚠️ 'playbook' setting not defined, ABORT!")
|
||||||
|
} else {
|
||||||
|
config.playbook = &playbook
|
||||||
|
}
|
||||||
|
|
||||||
|
return config
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// encoderConfig := zapcore.EncoderConfig{
|
||||||
|
// MessageKey: "message",
|
||||||
|
// LevelKey: "level",
|
||||||
|
// EncodeLevel: zapcore.CapitalColorLevelEncoder,
|
||||||
|
// EncodeDuration: zapcore.StringDurationEncoder,
|
||||||
|
// EncodeCaller: zapcore.ShortCallerEncoder,
|
||||||
|
// }
|
||||||
|
// encoder := zapcore.NewConsoleEncoder(encoderConfig)
|
||||||
|
// core := zapcore.NewCore(encoder, os.Stdout, zapcore.InfoLevel)
|
||||||
|
config := zap.NewProductionConfig()
|
||||||
|
config.EncoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder
|
||||||
|
config.EncoderConfig.TimeKey = ""
|
||||||
|
config.Encoding = "console"
|
||||||
|
config.DisableCaller = true
|
||||||
|
config.DisableStacktrace = true
|
||||||
|
logger, _ := config.Build()
|
||||||
|
defer logger.Sync()
|
||||||
|
sugar := logger.Sugar()
|
||||||
|
|
||||||
|
sugar.Info("Woodpecker Ansible Runner")
|
||||||
|
pluginConfig := getPluginConfig(sugar)
|
||||||
|
|
||||||
|
if pluginConfig.privateKey != nil {
|
||||||
|
runCommand(sugar, "mkdir", "/root/.ssh")
|
||||||
|
err := os.WriteFile("/root/.ssh/id_ed25519", []byte(*pluginConfig.privateKey), 0400)
|
||||||
|
if err != nil {
|
||||||
|
sugar.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
args := []string{*pluginConfig.playbook}
|
||||||
|
|
||||||
|
if pluginConfig.vaultToken != nil {
|
||||||
|
sugar.Info("💼 Adding vault token to 'credentials/ci_vault_token'")
|
||||||
|
runCommand(sugar, "mkdir", "credentials")
|
||||||
|
err := os.WriteFile("credentials/ci_vault_token", []byte(*pluginConfig.vaultToken), 0644)
|
||||||
|
if err != nil {
|
||||||
|
sugar.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
args = append(args, "--vault-password-file=credentials/ci_vault_token")
|
||||||
|
|
||||||
|
sugar.Info("⚙️ Clean ansible.cfg")
|
||||||
|
runCommand(sugar, "sed", "-i", "/vault_password_file.*/d", "./ansible.cfg")
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := os.Stat("ansible-ci.cfg"); err == nil {
|
||||||
|
sugar.Info("⚙️ ansible-ci.cfg is present, using it.")
|
||||||
|
os.Remove("ansible.cfg")
|
||||||
|
os.Rename("ansible-ci.cfg", "ansible.cfg")
|
||||||
|
}
|
||||||
|
|
||||||
|
if pluginConfig.galaxyFile != nil {
|
||||||
|
sugar.Infof("🚀 Installing Galaxy dependencies (%s)", *pluginConfig.galaxyFile)
|
||||||
|
runCommand(sugar, "ansible-galaxy", "install", "-r", *pluginConfig.galaxyFile, "--force")
|
||||||
|
}
|
||||||
|
|
||||||
|
if pluginConfig.check_syntax {
|
||||||
|
args = append(args, "--syntax-check")
|
||||||
|
}
|
||||||
|
|
||||||
|
if pluginConfig.check {
|
||||||
|
args = append(args, "--check")
|
||||||
|
}
|
||||||
|
|
||||||
|
if pluginConfig.diff {
|
||||||
|
args = append(args, "--diff")
|
||||||
|
}
|
||||||
|
|
||||||
|
if pluginConfig.verbosity != 0 {
|
||||||
|
verb := "-"
|
||||||
|
for i := 0; i < int(pluginConfig.verbosity); i++ {
|
||||||
|
verb += "v"
|
||||||
|
}
|
||||||
|
args = append(args, verb)
|
||||||
|
}
|
||||||
|
|
||||||
|
if pluginConfig.limit != nil {
|
||||||
|
args = append(args, "--limit", *pluginConfig.limit)
|
||||||
|
}
|
||||||
|
|
||||||
|
if pluginConfig.tags != nil {
|
||||||
|
args = append(args, "--tags", *pluginConfig.tags)
|
||||||
|
}
|
||||||
|
|
||||||
|
// runCommand(sugar, "export", "ANSIBLE_HOST_KEY_CHECKING=False")
|
||||||
|
command := append([]string{"ansible-playbook"}, args...)
|
||||||
|
runCommand(sugar, command...)
|
||||||
|
}
|
10
go.mod
Normal file
10
go.mod
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
module woodpecker-ansible-runner
|
||||||
|
|
||||||
|
go 1.20
|
||||||
|
|
||||||
|
require go.uber.org/zap v1.24.0
|
||||||
|
|
||||||
|
require (
|
||||||
|
go.uber.org/atomic v1.7.0 // indirect
|
||||||
|
go.uber.org/multierr v1.6.0 // indirect
|
||||||
|
)
|
18
go.sum
Normal file
18
go.sum
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
|
||||||
|
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/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||||
|
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/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
|
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
|
||||||
|
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
|
||||||
|
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||||
|
go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI=
|
||||||
|
go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
|
||||||
|
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
||||||
|
go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60=
|
||||||
|
go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
57
old.yml
57
old.yml
@ -1,57 +0,0 @@
|
|||||||
name: Build Docker
|
|
||||||
kind: pipeline
|
|
||||||
type: docker
|
|
||||||
steps:
|
|
||||||
|
|
||||||
- name: Only build image
|
|
||||||
image: plugins/docker
|
|
||||||
settings:
|
|
||||||
username:
|
|
||||||
from_secret: docker_username
|
|
||||||
password:
|
|
||||||
from_secret: docker_password
|
|
||||||
repo: harbor.sebclem.fr/sebclem/drone-ansible-runner
|
|
||||||
cache_from: harbor.sebclem.fr/sebclem/drone-ansible-runner
|
|
||||||
registry: harbor.sebclem.fr
|
|
||||||
tags: latest
|
|
||||||
dry_run: true
|
|
||||||
when:
|
|
||||||
target:
|
|
||||||
exclude:
|
|
||||||
- production
|
|
||||||
|
|
||||||
- name: Build and Push docker
|
|
||||||
image: plugins/docker
|
|
||||||
settings:
|
|
||||||
username:
|
|
||||||
from_secret: docker_username
|
|
||||||
password:
|
|
||||||
from_secret: docker_password
|
|
||||||
repo: harbor.sebclem.fr/sebclem/drone-ansible-runner
|
|
||||||
cache_from: harbor.sebclem.fr/sebclem/drone-ansible-runner
|
|
||||||
registry: harbor.sebclem.fr
|
|
||||||
tags: latest
|
|
||||||
when:
|
|
||||||
target:
|
|
||||||
- production
|
|
||||||
|
|
||||||
- name: Notify
|
|
||||||
image: drillster/drone-email
|
|
||||||
settings:
|
|
||||||
host:
|
|
||||||
from_secret: mail_host
|
|
||||||
username:
|
|
||||||
from_secret: mail_username
|
|
||||||
password:
|
|
||||||
from_secret: mail_password
|
|
||||||
from:
|
|
||||||
from_secret: mail_from
|
|
||||||
when:
|
|
||||||
status: [ changed, failure ]
|
|
||||||
|
|
||||||
trigger:
|
|
||||||
event:
|
|
||||||
- push
|
|
||||||
- custom
|
|
||||||
- promote
|
|
||||||
- rollback
|
|
Loading…
Reference in New Issue
Block a user