Compare commits

..

18 Commits

Author SHA1 Message Date
Bo-Yi Wu 6a809efc79 ci(actions): bump codecov-action from v6 to v7
- Update codecov/codecov-action from v6 to v7
2026-06-13 15:46:29 +08:00
Bo-Yi Wu 2f461525af chore: bump go directive to 1.25.10
- Update go.mod go directive from 1.25.9 to 1.25.10
2026-05-08 21:19:20 +08:00
Bo-Yi Wu 5f9d683257 ci: bump golangci-lint to v2.12
- Upgrade golangci-lint version from v2.11 to v2.12
2026-05-08 20:13:39 +08:00
Bo-Yi Wu 66afccb389 ci(actions): bump trivy-action to v0.36.0 and codecov-action to v6 2026-04-25 16:51:02 +08:00
Bo-Yi Wu 8c87ebd4ef docs(readme): add Trivy security scan badge 2026-04-16 23:23:44 +08:00
Bo-Yi Wu 156f3c6cb1 fix(deps): bump golang.org/x/crypto to v0.50.0 to fix CVE-2025-58181 and CVE-2025-47914 2026-04-16 23:18:55 +08:00
Bo-Yi Wu 8c8da8b643 fix(docker): bump alpine to 3.23 to clear HIGH CVEs 2026-04-16 23:15:42 +08:00
Bo-Yi Wu d7493e77f5 ci: add trivy workflow and gate docker push on image scan 2026-04-16 23:00:58 +08:00
Bo-Yi Wu 1917781d94 ci: enable check-latest in docker and goreleaser workflows 2026-04-16 22:42:42 +08:00
Bo-Yi Wu c08c2995d2 ci: enable check-latest for setup-go to fetch newest patch 2026-04-16 21:15:35 +08:00
Bo-Yi Wu 55f880d64f ci: pin golangci-lint to v2.11 2026-04-16 21:11:11 +08:00
Bo-Yi Wu 983705ffd7 ci: bump GitHub Actions and add Go 1.25/1.26 to test matrix 2026-04-16 21:03:21 +08:00
Bo-Yi Wu 523c4bb724 chore: bump go directive to 1.25.9 2026-04-16 20:57:50 +08:00
appleboy 2cbd8efffa test: update color conversion tests for new calculation logic
- Update expected output values in color conversion tests to reflect new color calculation logic

Signed-off-by: appleboy <appleboy.tw@gmail.com>
2025-07-05 22:46:25 +08:00
Bo-Yi Wu f2b9ede051 refactor: refactor messaging and file upload with improved error handling (#66)
- Refactor message and file sending logic into separate handleMessages and handleFiles methods
- Stream file uploads via io.Copy rather than loading the entire content into memory
- Add centralized http.Client with timeout for all requests
- Enhance error handling throughout by returning more descriptive and wrapped errors
- Improve response validation for file and message uploads, checking HTTP status and parsing error details
- Update tests to cover plain text messages, embed messages, file uploads, color conversion, and combined features
- Add tests using assert.Error/assert.NoError and checking specific error messages
- Simplify and clarify configuration validation logic

Signed-off-by: appleboy <appleboy.tw@gmail.com>
2025-07-05 21:02:30 +08:00
appleboy 1bdf20515c ci: update golangci-lint workflow to use latest action and linter
- Update golangci-lint GitHub Action from v7 to v8 and set linter version to v2.1

Signed-off-by: appleboy <appleboy.tw@gmail.com>
2025-07-05 20:28:04 +08:00
appleboy 7678d611f9 chore: update and streamline dependency management
- Bump github.com/urfave/cli/v2 to v2.27.7
- Update indirect dependencies: github.com/Masterminds/semver/v3, github.com/Masterminds/sprig/v3, github.com/cpuguy83/go-md2man/v2, github.com/spf13/cast, golang.org/x/crypto, and golang.org/x/sys to newer versions
- Replace github.com/imdario/mergo with dario.cat/mergo as an indirect dependency

Signed-off-by: appleboy <appleboy.tw@gmail.com>
2025-07-05 20:27:24 +08:00
Bo-Yi Wu 0e20c42ccd docs: update notification docs for Woodpecker 3.x status changes (#65)
* docs: update notification docs for Woodpecker 3.x status changes

- Add documentation note explaining that in Woodpecker 3.x, build.status is always "success", which affects message templates
- Recommend using the when.status condition to separate success and failure notifications, with example YAML provided
- Clarify that this issue results from upstream Woodpecker CI changes and is not fixable in the plugin

Signed-off-by: appleboy <appleboy.tw@gmail.com>

* Update DOCS.md

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>

---------

Signed-off-by: appleboy <appleboy.tw@gmail.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2025-07-05 20:26:09 +08:00
11 changed files with 397 additions and 237 deletions
+3 -3
View File
@@ -38,11 +38,11 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v6
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
uses: github/codeql-action/init@v4
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
@@ -51,4 +51,4 @@ jobs:
# queries: ./path/to/local/query, your-org/your-repo/queries@main
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
uses: github/codeql-action/analyze@v4
+40 -8
View File
@@ -10,16 +10,22 @@ on:
branches:
- "master"
permissions:
contents: read
packages: write
security-events: write
jobs:
build-docker:
runs-on: ubuntu-latest
steps:
- name: Setup go
uses: actions/setup-go@v5
uses: actions/setup-go@v6
with:
go-version: "^1"
check-latest: true
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
fetch-depth: 0
@@ -28,19 +34,19 @@ jobs:
make build_linux_amd64
make build_linux_arm64
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
uses: docker/setup-qemu-action@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
uses: docker/setup-buildx-action@v4
- name: Login to Docker Hub
uses: docker/login-action@v3
uses: docker/login-action@v4
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
@@ -48,7 +54,7 @@ jobs:
- name: Docker meta
id: docker-meta
uses: docker/metadata-action@v5
uses: docker/metadata-action@v6
with:
images: |
${{ github.repository }}
@@ -59,8 +65,34 @@ jobs:
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
- name: Build image for scanning
uses: docker/build-push-action@v7
with:
context: .
file: docker/Dockerfile
platforms: linux/amd64
push: false
load: true
tags: drone-discord:scan
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@v0.36.0
with:
image-ref: "drone-discord:scan"
format: "sarif"
output: "trivy-image-results.sarif"
severity: "CRITICAL,HIGH"
exit-code: '1'
- name: Upload Trivy scan results to GitHub Security tab
uses: github/codeql-action/upload-sarif@v4
if: always()
with:
sarif_file: "trivy-image-results.sarif"
category: "trivy-docker-image"
- name: Build and push
uses: docker/build-push-action@v6
uses: docker/build-push-action@v7
with:
context: .
platforms: linux/amd64,linux/arm64
+4 -3
View File
@@ -13,16 +13,17 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Setup go
uses: actions/setup-go@v5
uses: actions/setup-go@v6
with:
go-version: "^1"
check-latest: true
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v6
uses: goreleaser/goreleaser-action@v7
with:
# either 'goreleaser' (default) or 'goreleaser-pro'
distribution: goreleaser
+12 -10
View File
@@ -9,18 +9,19 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Setup go
uses: actions/setup-go@v5
uses: actions/setup-go@v6
with:
go-version: "^1"
check-latest: true
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Setup golangci-lint
uses: golangci/golangci-lint-action@v7
uses: golangci/golangci-lint-action@v9
with:
version: v2.0
version: v2.12
args: --verbose
- uses: hadolint/hadolint-action@v3.1.0
- uses: hadolint/hadolint-action@v3.3.0
name: hadolint for Dockerfile
with:
dockerfile: docker/Dockerfile
@@ -29,7 +30,7 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest]
go: [1.23, 1.24]
go: [1.25, 1.26]
include:
- os: ubuntu-latest
go-build: ~/.cache/go-build
@@ -40,16 +41,17 @@ jobs:
GOPROXY: https://proxy.golang.org
steps:
- name: Set up Go ${{ matrix.go }}
uses: actions/setup-go@v5
uses: actions/setup-go@v6
with:
go-version: ${{ matrix.go }}
check-latest: true
- name: Checkout Code
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
ref: ${{ github.ref }}
- uses: actions/cache@v4
- uses: actions/cache@v5
with:
path: |
${{ matrix.go-build }}
@@ -65,6 +67,6 @@ jobs:
go test -v -covermode=atomic -coverprofile=coverage.out
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v5
uses: codecov/codecov-action@v7
with:
flags: ${{ matrix.os }},go-${{ matrix.go }}
+84
View File
@@ -0,0 +1,84 @@
name: Trivy Security Scan
on:
push:
branches:
- master
pull_request:
branches:
- master
schedule:
- cron: "0 0 * * *"
workflow_dispatch:
permissions:
contents: read
security-events: write
jobs:
trivy-repo-scan:
name: Trivy Repository Scan
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Run Trivy vulnerability scanner (repo)
uses: aquasecurity/trivy-action@v0.36.0
with:
scan-type: "fs"
scan-ref: "."
format: "sarif"
output: "trivy-repo-results.sarif"
severity: "CRITICAL,HIGH"
- name: Upload Trivy scan results to GitHub Security tab
uses: github/codeql-action/upload-sarif@v4
if: always()
with:
sarif_file: "trivy-repo-results.sarif"
trivy-image-scan:
name: Trivy Image Scan
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Setup go
uses: actions/setup-go@v6
with:
go-version-file: go.mod
check-latest: true
- name: Build binary
run: |
make build_linux_amd64
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
- name: Build Docker image for scanning
uses: docker/build-push-action@v7
with:
context: .
file: docker/Dockerfile
platforms: linux/amd64
push: false
load: true
tags: drone-discord:scan
- name: Run Trivy vulnerability scanner (image)
uses: aquasecurity/trivy-action@v0.36.0
with:
image-ref: "drone-discord:scan"
format: "sarif"
output: "trivy-image-results.sarif"
severity: "CRITICAL,HIGH"
- name: Upload Trivy image scan results to GitHub Security tab
uses: github/codeql-action/upload-sarif@v4
if: always()
with:
sarif_file: "trivy-image-results.sarif"
category: "trivy-image"
+1
View File
@@ -5,6 +5,7 @@
[Drone](https://www.drone.io/) / [Woodpecker](https://woodpecker-ci.org/) plugin for sending messages to Discord channels using Webhooks.
[![GoDoc](https://godoc.org/github.com/appleboy/drone-discord?status.svg)](https://godoc.org/github.com/appleboy/drone-discord)
[![Trivy Security Scan](https://github.com/appleboy/drone-discord/actions/workflows/trivy.yml/badge.svg?branch=master)](https://github.com/appleboy/drone-discord/actions/workflows/trivy.yml)
[![codecov](https://codecov.io/gh/appleboy/drone-discord/branch/master/graph/badge.svg)](https://codecov.io/gh/appleboy/drone-discord)
[![Go Report Card](https://goreportcard.com/badge/github.com/appleboy/drone-discord)](https://goreportcard.com/report/github.com/appleboy/drone-discord)
[![Docker Pulls](https://img.shields.io/docker/pulls/appleboy/drone-discord.svg)](https://hub.docker.com/r/appleboy/drone-discord/)
+1 -1
View File
@@ -1,4 +1,4 @@
FROM alpine:3.21
FROM alpine:3.23
ARG TARGETOS
ARG TARGETARCH
+9 -9
View File
@@ -1,24 +1,24 @@
module github.com/appleboy/drone-discord
go 1.23.0
go 1.25.10
require (
github.com/appleboy/drone-template-lib v1.3.0
github.com/joho/godotenv v1.5.1
github.com/stretchr/testify v1.10.0
github.com/urfave/cli/v2 v2.27.6
github.com/urfave/cli/v2 v2.27.7
github.com/yassinebenaid/godump v0.11.1
)
require (
dario.cat/mergo v1.0.2 // indirect
github.com/Masterminds/goutils v1.1.1 // indirect
github.com/Masterminds/semver/v3 v3.2.1 // indirect
github.com/Masterminds/sprig/v3 v3.2.3 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect
github.com/Masterminds/semver/v3 v3.4.0 // indirect
github.com/Masterminds/sprig/v3 v3.3.0 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/huandu/xstrings v1.5.0 // indirect
github.com/imdario/mergo v0.3.16 // indirect
github.com/mailgun/raymond/v2 v2.0.48 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
@@ -26,9 +26,9 @@ require (
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/shopspring/decimal v1.4.0 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/spf13/cast v1.6.0 // indirect
github.com/spf13/cast v1.9.2 // indirect
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect
golang.org/x/crypto v0.36.0 // indirect
golang.org/x/sys v0.31.0 // indirect
golang.org/x/crypto v0.50.0 // indirect
golang.org/x/sys v0.43.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
+18 -57
View File
@@ -1,30 +1,26 @@
dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8=
dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA=
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0=
github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA=
github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM=
github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=
github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
github.com/Masterminds/sprig/v3 v3.3.0 h1:mQh0Yrg1XPo6vjYXgtf5OtijNAKJRNcTdOOGZe3tPhs=
github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSCzdgBfDb35Lz0=
github.com/appleboy/drone-template-lib v1.3.0 h1:aX36/1za3v8JsEyBeMY1Bp/VNRtZa8qPYkfkjBszW+A=
github.com/appleboy/drone-template-lib v1.3.0/go.mod h1:edlmXkFMKYAVypff8r2oN7aFlHfOZE5sLyPEnRHONeA=
github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc=
github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/cpuguy83/go-md2man/v2 v2.0.7 h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3sHPnBo=
github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
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/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI=
github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4=
github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
@@ -33,10 +29,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/mailgun/raymond/v2 v2.0.48 h1:5dmlB680ZkFG2RN/0lvTAghrSxIESeu9/2aeDqACtjw=
github.com/mailgun/raymond/v2 v2.0.48/go.mod h1:lsgvL50kgt1ylcFJYZiULi5fjPBkkhNfj4KA0W54Z18=
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@@ -45,65 +39,32 @@ github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZV
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/spf13/cast v1.9.2 h1:SsGfm7M8QOFtEzumm7UZrZdLLquNdzFYfIbEXntcFbE=
github.com/spf13/cast v1.9.2/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
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.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.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/urfave/cli/v2 v2.27.6 h1:VdRdS98FNhKZ8/Az8B7MTyGQmpIr36O1EHybx/LaZ4g=
github.com/urfave/cli/v2 v2.27.6/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ=
github.com/urfave/cli/v2 v2.27.7 h1:bH59vdhbjLv3LAvIu6gd0usJHgoTTPhCFib8qqOwXYU=
github.com/urfave/cli/v2 v2.27.7/go.mod h1:CyNAG/xg+iAOg0N4MPGZqVmv2rCoP267496AOXUZjA4=
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4=
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM=
github.com/yassinebenaid/godump v0.11.1 h1:SPujx/XaYqGDfmNh7JI3dOyCUVrG0bG2duhO3Eh2EhI=
github.com/yassinebenaid/godump v0.11.1/go.mod h1:dc/0w8wmg6kVIvNGAzbKH1Oa54dXQx8SNKh4dPRyW44=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/crypto v0.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI=
golang.org/x/crypto v0.50.0/go.mod h1:3muZ7vA7PBCE6xgPX7nkzzjiUq87kRItoJQM1Yo8S+Q=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/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-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI=
golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/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=
+101 -77
View File
@@ -126,35 +126,32 @@ type (
// Plugin values.
Plugin struct {
GitHub GitHub
Repo Repo
Build Build
Source Source
Config Config
Payload Payload
Commit Commit
GitHub GitHub
Repo Repo
Build Build
Source Source
Config Config
Payload Payload
Commit Commit
httpClient *http.Client
}
)
func (c *Config) validate() error {
var missingFields []string
if c.webhookURL != "" {
_, err := url.Parse(c.webhookURL)
if err != nil {
if _, err := url.Parse(c.webhookURL); err != nil {
return fmt.Errorf("invalid webhook url: %w", err)
}
return nil
}
if c.webhookURL == "" {
if c.WebhookID == "" {
missingFields = append(missingFields, "WebhookID")
}
if c.WebhookToken == "" {
missingFields = append(missingFields, "WebhookToken")
}
var missingFields []string
if c.WebhookID == "" {
missingFields = append(missingFields, "WebhookID")
}
if c.WebhookToken == "" {
missingFields = append(missingFields, "WebhookToken")
}
if len(missingFields) > 0 {
return fmt.Errorf("missing discord config: %s", strings.Join(missingFields, ", "))
}
@@ -178,17 +175,12 @@ func templateMessage(t string, plugin Plugin) (string, error) {
func fileUploadRequest(ctx context.Context, uri string, params map[string]string, paramName, path string) (*http.Request, error) {
// Clean and check path
path = filepath.Clean(path)
if _, err := os.Stat(path); err != nil {
file, err := os.Open(path)
if err != nil {
return nil, fmt.Errorf("file %s not accessible: %w", path, err)
}
defer file.Close()
// Read file content
content, err := os.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("failed to read file %s: %w", path, err)
}
// Create multipart form
body := &bytes.Buffer{}
writer := multipart.NewWriter(body)
@@ -197,7 +189,9 @@ func fileUploadRequest(ctx context.Context, uri string, params map[string]string
if err != nil {
return nil, fmt.Errorf("failed to create form file: %w", err)
}
if _, err = part.Write(content); err != nil {
// Stream file content
if _, err = io.Copy(part, file); err != nil {
return nil, fmt.Errorf("failed to write file content: %w", err)
}
@@ -224,65 +218,84 @@ func fileUploadRequest(ctx context.Context, uri string, params map[string]string
// Exec executes the plugin.
func (p *Plugin) Exec(ctx context.Context) error {
// init http client
p.httpClient = &http.Client{
Timeout: 15 * time.Second,
}
if err := p.Config.validate(); err != nil {
return fmt.Errorf("failed to validate config: %w", err)
}
// check if message is empty
messages := []string{}
if err := p.handleMessages(ctx); err != nil {
return err
}
if err := p.handleFiles(ctx); err != nil {
return err
}
return nil
}
// handleMessages sends all configured messages.
func (p *Plugin) handleMessages(ctx context.Context) error {
// 1. Handle empty message (default template)
if len(p.Config.Message) == 0 {
object := p.Template()
p.Payload.Embeds = []EmbedObject{object}
if err := p.SendMessage(ctx); err != nil {
return fmt.Errorf("failed to send default message: %w", err)
}
return nil
}
// 2. Handle custom messages
for _, m := range p.Config.Message {
if m == "" {
continue
}
messages = append(messages, m)
}
if len(messages) == 0 {
object := p.Template()
p.Payload.Embeds = []EmbedObject{object}
err := p.SendMessage(ctx)
txt, err := templateMessage(m, *p)
if err != nil {
return err
return fmt.Errorf("failed to render template: %w", err)
}
// With color, messages are grouped as embeds
if p.Config.Color != "" {
object := p.DefaultTemplate(txt)
p.Payload.Embeds = append(p.Payload.Embeds, object)
} else {
// Without color, send as plain text immediately
p.Payload.Content = txt
if err := p.SendMessage(ctx); err != nil {
return fmt.Errorf("failed to send plain text message: %w", err)
}
// Reset for next message
p.Clear()
}
}
if len(messages) > 0 {
for _, m := range messages {
txt, err := templateMessage(m, *p)
if err != nil {
return err
}
if len(p.Config.Color) != 0 {
object := p.DefaultTemplate(txt)
p.Payload.Embeds = append(p.Payload.Embeds, object)
} else {
p.Payload.Content = txt
err = p.SendMessage(ctx)
if err != nil {
return err
}
}
}
if len(p.Payload.Embeds) > 0 {
err := p.SendMessage(ctx)
if err != nil {
return err
}
// 3. Send grouped embeds if any
if len(p.Payload.Embeds) > 0 {
if err := p.SendMessage(ctx); err != nil {
return fmt.Errorf("failed to send embed messages: %w", err)
}
}
return nil
}
// handleFiles sends all configured files.
func (p *Plugin) handleFiles(ctx context.Context) error {
for _, f := range p.Config.File {
if f == "" {
continue
}
err := p.SendFile(ctx, f)
if err != nil {
return err
if err := p.SendFile(ctx, f); err != nil {
return fmt.Errorf("failed to send file %s: %w", f, err)
}
}
return nil
}
@@ -311,12 +324,25 @@ func (p *Plugin) SendFile(ctx context.Context, file string) error {
file,
)
if err != nil {
return err
return fmt.Errorf("failed to create file upload request: %w", err)
}
client := &http.Client{}
_, err = client.Do(request)
resp, err := p.httpClient.Do(request)
if err != nil {
return err
return fmt.Errorf("failed to send file: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode >= http.StatusBadRequest {
bodyBytes, err := io.ReadAll(resp.Body)
if err != nil {
return fmt.Errorf("failed to read response body: %w", err)
}
var jsonResponse map[string]interface{}
if err := json.Unmarshal(bodyBytes, &jsonResponse); err != nil {
return fmt.Errorf("failed to send file, status code: %d, body: %s", resp.StatusCode, string(bodyBytes))
}
return fmt.Errorf("failed to send file, status code: %d, error: %s, code: %v", resp.StatusCode, jsonResponse["message"], jsonResponse["code"])
}
return nil
@@ -327,25 +353,23 @@ func (p *Plugin) SendMessage(ctx context.Context) error {
webhookURL := p.Config.GetWebhookURL()
b := new(bytes.Buffer)
if err := json.NewEncoder(b).Encode(p.Payload); err != nil {
return err
return fmt.Errorf("failed to encode payload: %w", err)
}
req, err := http.NewRequestWithContext(ctx, http.MethodPost, webhookURL, b)
if err != nil {
return err
return fmt.Errorf("failed to create request: %w", err)
}
req.Header.Set("Content-Type", "application/json; charset=utf-8")
client := &http.Client{
Timeout: 10 * time.Second,
}
resp, err := client.Do(req)
resp, err := p.httpClient.Do(req)
if err != nil {
return err
return fmt.Errorf("failed to send message: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusNoContent {
// 200 and 204 are both valid status codes for webhooks.
if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusNoContent {
bodyBytes, err := io.ReadAll(resp.Body)
if err != nil {
return fmt.Errorf("failed to read response body: %w", err)
+124 -69
View File
@@ -10,14 +10,48 @@ import (
)
func TestMissingConfig(t *testing.T) {
var plugin Plugin
plugin := Plugin{}
err := plugin.Exec(context.Background())
assert.NotNil(t, err)
assert.Error(t, err)
assert.Contains(t, err.Error(), "missing discord config")
}
func TestTemplate(t *testing.T) {
func TestSendPlainTextMessage(t *testing.T) {
plugin := Plugin{
Config: Config{
WebhookID: os.Getenv("WEBHOOK_ID"),
WebhookToken: os.Getenv("WEBHOOK_TOKEN"),
Message: []string{"Hello, world!", "This is a test."},
},
Payload: Payload{
Username: "test-bot",
},
}
err := plugin.Exec(context.Background())
assert.NoError(t, err)
}
func TestSendEmbedMessage(t *testing.T) {
plugin := Plugin{
Config: Config{
WebhookID: os.Getenv("WEBHOOK_ID"),
WebhookToken: os.Getenv("WEBHOOK_TOKEN"),
Message: []string{"This is an embed message."},
Color: "#48f442",
},
Payload: Payload{
Username: "embed-bot",
},
}
err := plugin.Exec(context.Background())
assert.NoError(t, err)
}
func TestSendDefaultMessage(t *testing.T) {
plugin := Plugin{
Repo: Repo{
Name: "go-hello",
@@ -26,99 +60,120 @@ func TestTemplate(t *testing.T) {
Commit: Commit{
Author: "appleboy",
Branch: "master",
Message: "update by drone discord plugin. \r\n update by drone discord plugin.",
Message: "feat: new feature",
Avatar: "https://avatars0.githubusercontent.com/u/21979?v=3&s=100",
},
Build: Build{
Number: 101,
Status: "success",
Link: "https://github.com/appleboy/go-hello",
Event: "tag",
Event: "push",
},
Source: Source{
Branch: "feature/awesome-feature",
},
Config: Config{
WebhookID: os.Getenv("WEBHOOK_ID"),
WebhookToken: os.Getenv("WEBHOOK_TOKEN"),
Message: []string{"test one message from drone testing", "test two message from drone testing"},
File: []string{"./images/discord-logo.png"},
Drone: true,
},
Payload: Payload{
Username: "drone",
TTS: false,
Wait: false,
Username: "default-bot",
},
}
err := plugin.Exec(context.Background())
assert.Nil(t, err)
plugin.Clear()
plugin.Config.Message = []string{"I am appleboy"}
plugin.Payload.TTS = true
plugin.Payload.Wait = true
err = plugin.Exec(context.Background())
assert.Nil(t, err)
// send success embed message
plugin.Config.Message = []string{}
plugin.Payload.TTS = false
plugin.Payload.Wait = false
plugin.Clear()
err = plugin.Exec(context.Background())
assert.Nil(t, err)
// send success embed message
plugin.Build.Status = "failure"
plugin.Commit.Message = "send failure embed message"
plugin.Clear()
err = plugin.Exec(context.Background())
assert.Nil(t, err)
time.Sleep(1 * time.Second)
// send default embed message
plugin.Build.Status = "test"
plugin.Commit.Message = "send default embed message"
plugin.Clear()
err = plugin.Exec(context.Background())
assert.Nil(t, err)
// change color for embed message
plugin.Config.Color = "#4842f4"
plugin.Commit.Message = "Change embed color to #4842f4"
plugin.Clear()
err = plugin.Exec(context.Background())
assert.Nil(t, err)
assert.NoError(t, err)
}
func TestDefaultTemplate(t *testing.T) {
func TestSendFile(t *testing.T) {
// Create a dummy file for testing
dummyFile, err := os.Create("test_file.txt")
assert.NoError(t, err)
_, err = dummyFile.WriteString("This is a test file.")
assert.NoError(t, err)
dummyFile.Close()
defer os.Remove("test_file.txt")
plugin := Plugin{
Config: Config{
WebhookID: os.Getenv("WEBHOOK_ID"),
WebhookToken: os.Getenv("WEBHOOK_TOKEN"),
Message: []string{"default message 1", "default message 2"},
Color: "#48f442",
File: []string{"test_file.txt"},
},
Payload: Payload{
Username: "drone-ci",
TTS: false,
Wait: false,
Username: "file-bot",
},
}
time.Sleep(1 * time.Second)
plugin.Clear()
err := plugin.Exec(context.Background())
assert.Nil(t, err)
plugin.Config.Color = "#f4be41"
time.Sleep(1 * time.Second)
plugin.Clear()
err = plugin.Exec(context.Background())
assert.Nil(t, err)
assert.NoError(t, err)
}
func TestColorConversion(t *testing.T) {
tests := []struct {
name string
colorHex string
expectedInt int
buildStatus string
expectedFall int
}{
{"valid hex", "#ffaa00", 16755200, "success", 1752220},
{"invalid hex", "not-a-hex", 16724530, "failure", 16724530},
{"status success", "", 0, "success", 1754624},
{"status failure", "", 0, "failure", 16724530},
{"status killed", "", 0, "killed", 16724530},
{"status default", "", 0, "running", 16767280},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
p := Plugin{
Config: Config{Color: tt.colorHex},
Build: Build{Status: tt.buildStatus},
}
if tt.colorHex != "" {
assert.Equal(t, tt.expectedInt, p.Color())
} else {
assert.Equal(t, tt.expectedFall, p.Color())
}
})
}
}
func TestExecWithAllFeatures(t *testing.T) {
time.Sleep(1 * time.Second)
// Create a dummy file for testing
dummyFile, err := os.Create("test_all.txt")
assert.NoError(t, err)
_, err = dummyFile.WriteString("This is a test file for a combined test.")
assert.NoError(t, err)
dummyFile.Close()
defer os.Remove("test_all.txt")
plugin := Plugin{
Repo: Repo{
Name: "go-hello",
Namespace: "appleboy",
},
Commit: Commit{
Author: "appleboy",
Message: "Combined test with multiple features",
},
Build: Build{
Status: "success",
Link: "http://example.com",
},
Config: Config{
WebhookID: os.Getenv("WEBHOOK_ID"),
WebhookToken: os.Getenv("WEBHOOK_TOKEN"),
Message: []string{"First line of embed.", "Second line."},
File: []string{"test_all.txt"},
Color: "#32a852",
Drone: true,
},
Payload: Payload{
Username: "super-bot",
},
}
err = plugin.Exec(context.Background())
assert.NoError(t, err)
}