Compare commits

..

14 Commits

Author SHA1 Message Date
lddsb da0e336e25 fix: default image cdn url 2020-01-20 11:31:41 +08:00
lddsb 88dcf2d231 remove main_test.go 2019-03-09 00:23:13 +08:00
lddsb 38be4d2982 optimized drone ci trigger 2019-03-08 23:51:01 +08:00
lddsb 0529a41a36 add codecov plugin 2019-03-08 21:27:43 +08:00
lddsb 45450d3115 update readme add codecov icon 2019-03-08 18:55:28 +08:00
lddsb 3beae7de72 update readme for base icon 2019-03-08 18:52:45 +08:00
Dee Luo 6615f9033d update license to MIT 2019-03-08 18:39:16 +08:00
lddsb d8514a85d7 improve unit testing 2019-03-08 17:29:57 +08:00
lddsb dc155b39c7 optimized by go report card 2019-02-22 23:49:59 +08:00
lddsb 6722997241 fix "too many instance variables 2019-02-22 17:04:34 +08:00
lddsb c100d9e56f remove useless code 2019-02-22 12:00:42 +08:00
lddsb 9dad7f2a03 add build status to readme 2019-02-21 09:42:14 +08:00
lddsb 20aaaf3454 format .drone.yml [CI SKIP] 2019-02-19 17:37:37 +08:00
lddsb 74c317b189 add drone auto build support 2019-02-07 12:23:04 +08:00
10 changed files with 313 additions and 678 deletions
+36 -24
View File
@@ -7,28 +7,40 @@ workspace:
path: src/github.com/lddsb/drone-dingtalk-message
steps:
- name: build
image: golang
commands:
- go get -u github.com/golang/dep/cmd/dep
- dep ensure
- CGO_ENABLED=0 GOOS=linux go build -a -o drone-dingtalk-message .
- ./drone-dingtalk-message -h
- name: publish
image: plugins/docker
when:
branch:
- beta
status:
- success
event:
- push
settings:
repo: lddsb/drone-dingtalk-message
dockerfile: Dockerfile
tags: beta
username:
from_secret: docker_username
password:
from_secret: docker_password
- name: build
image: golang
commands:
- go get -u github.com/golang/dep/cmd/dep
- dep ensure
- CGO_ENABLED=0 GOOS=linux go build -a -o drone-dingtalk-message .
- go test -race -coverprofile=coverage.txt -covermode=atomic
- ./drone-dingtalk-message -h
- name: codecov
image: plugins/codecov
when:
status:
- success
settings:
token:
from_secret: codecov_token
- name: publish
image: plugins/docker
when:
status:
- success
event:
- tag
settings:
repo: lddsb/drone-dingtalk-message
dockerfile: Dockerfile
tags:
- latest
- 1.0.0
username:
from_secret: docker_username
password:
from_secret: docker_password
trigger:
branch:
- master
+2 -1
View File
@@ -1,4 +1,5 @@
dive.log
drone-dingtalk-message
.idea
vendor
vendor
coverage.txt
Generated
+9
View File
@@ -12,6 +12,14 @@
revision = "23d116af351c84513e1946b527c88823e476be13"
version = "v1.3.0"
[[projects]]
branch = "master"
digest = "1:9142979c770f3d0f3c42c2eec532048bbbe2571134da91e8946cd8610c85c04b"
name = "github.com/lddsb/dingtalk-webhook"
packages = ["."]
pruneopts = "UT"
revision = "b4abe34b5fa9af8ea7d5f28c02bd314558b21f7f"
[[projects]]
digest = "1:b24d38b282bacf9791408a080f606370efa3d364e4b5fd9ba0f7b87786d3b679"
name = "github.com/urfave/cli"
@@ -25,6 +33,7 @@
analyzer-version = 1
input-imports = [
"github.com/joho/godotenv/autoload",
"github.com/lddsb/dingtalk-webhook",
"github.com/urfave/cli",
]
solver-name = "gps-cdcl"
+4
View File
@@ -36,3 +36,7 @@
[prune]
go-tests = true
unused-packages = true
[[constraint]]
branch = "master"
name = "github.com/lddsb/dingtalk-webhook"
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2019 Dee Luo
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.
+1
View File
@@ -1,4 +1,5 @@
# Drone CI DingTalk Message Plugin
[![Build Status](https://drone.lddsb.com/api/badges/lddsb/drone-dingtalk-message/status.svg)](https://drone.lddsb.com/lddsb/drone-dingtalk-message) [![Go Report Card](https://goreportcard.com/badge/github.com/lddsb/drone-dingtalk-message)](https://goreportcard.com/report/github.com/lddsb/drone-dingtalk-message) [![codecov](https://codecov.io/gh/lddsb/drone-dingtalk-message/branch/master/graph/badge.svg)](https://codecov.io/gh/lddsb/drone-dingtalk-message) [![codebeat badge](https://codebeat.co/badges/23f68b84-1fd2-4f29-8467-9285c1e0facc)](https://codebeat.co/projects/github-com-lddsb-drone-dingtalk-message-master) [![LICENSE: MIT](https://img.shields.io/github/license/lddsb/drone-dingtalk-message.svg?style=flat-square)](LICENSE)
### Drone CI Plugin Config
`0.8.x`
+49 -245
View File
@@ -9,6 +9,7 @@ import (
"github.com/urfave/cli"
)
// Version of cli
var Version = "0.1.1202"
func main() {
@@ -25,6 +26,11 @@ func main() {
app.Action = run
app.Version = Version
app.Flags = []cli.Flag{
cli.BoolFlag{
Name: "config.debug",
Usage: "debug mode",
EnvVar: "PLUGIN_DEBUG",
},
cli.StringFlag{
Name: "config.token,access_token,token",
Usage: "dingtalk webhook access token",
@@ -37,7 +43,7 @@ func main() {
EnvVar: "PLUGIN_LANG",
},
cli.StringFlag{
Name: "config.message.type,msg_type,message_type,type",
Name: "config.message.type,message_type",
Usage: "dingtalk message type, like text, markdown, action card, link and feed card...",
EnvVar: "PLUGIN_MSG_TYPE,PLUGIN_TYPE,PLUGIN_MESSAGE_TYPE",
},
@@ -51,22 +57,6 @@ func main() {
Usage: "at someone in a dingtalk group need this guy bind's mobile",
EnvVar: "PLUGIN_MSG_AT_MOBILES",
},
cli.BoolFlag{
Name: "drone",
Usage: "indicates the runtime environment is Drone",
EnvVar: "DRONE",
},
cli.StringFlag{
Name: "branch",
Usage: "providers the branch for the current build",
EnvVar: "DRONE_BRANCH",
},
// commit args start
cli.StringFlag{
Name: "remote.url",
Usage: "git remote url",
EnvVar: "DRONE_REMOTE_URL",
},
cli.StringFlag{
Name: "commit.author.avatar",
Usage: "providers the author avatar url for the current commit",
@@ -98,161 +88,16 @@ func main() {
Usage: "providers the commit message for the current build",
EnvVar: "DRONE_COMMIT_MESSAGE",
},
cli.StringFlag{
Name: "commit.ref",
Usage: "providers the reference for the current build",
EnvVar: "DRONE_COMMIT_REF",
},
cli.StringFlag{
Name: "commit.sha",
Usage: "providers the commit sha for the current build",
EnvVar: "DRONE_COMMIT_SHA",
},
// commit args end
cli.StringFlag{
Name: "git.url.http",
Usage: "providers the repository git+http url",
EnvVar: "DRONE_GIT_HTTP_URL",
},
cli.StringFlag{
Name: "git.url.ssh",
Usage: "providers the repository git+ssh url",
EnvVar: "DRONE_GIT_SSH_URL",
},
cli.StringFlag{
Name: "machine",
Usage: "providers the Drone agent hostname",
EnvVar: "DRONE_MACHINE",
},
cli.StringFlag{
Name: "pull.request",
Usage: "providers the pull request number for the current build.This value is only set if the build event is of type pull request",
EnvVar: "DRONE_PULL_REQUEST",
},
// repo args start
cli.StringFlag{
Name: "repo.fullname",
Usage: "providers the full name of the repository",
EnvVar: "DRONE_REPO",
},
cli.StringFlag{
Name: "repo.owner",
Usage: "repository owner",
EnvVar: "DRONE_REPO_OWNER",
},
cli.StringFlag{
Name: "repo.branch",
Usage: "providers the default repository branch(e.g.master)",
EnvVar: "DRONE_REPO_BRANCH",
},
cli.StringFlag{
Name: "repo.link",
Usage: "providers the repository http link",
EnvVar: "DRONE_REPO_LINK",
},
cli.StringFlag{
Name: "repo.name",
Usage: "providers the repository name",
EnvVar: "DRONE_REPO_NAME",
},
cli.StringFlag{
Name: "repo.avatar",
Usage: "repository avatar",
EnvVar: "DRONE_REPO_AVATAR",
},
cli.StringFlag{
Name: "repo.namespace",
Usage: "providers the repository namespace(e.g. account owner)",
EnvVar: "DRONE_REPO_NAMESPACE",
},
cli.BoolFlag{
Name: "repo.private",
Usage: "indicates the repository is public or private",
EnvVar: "DRONE_REPO_PRIVATE",
},
cli.BoolFlag{
Name: "repo.trusted",
Usage: "repository is trusted",
EnvVar: "DRONE_REPO_TRUSTED",
},
// repo args end
cli.StringFlag{
Name: "runner.host",
Usage: "provider are Drone agent hostname",
EnvVar: "DRONE_RUNNER_HOST",
},
cli.StringFlag{
Name: "runner.hostname",
Usage: "providers the Drone agent hostname",
EnvVar: "DRONE_RUNNER_HOSTNAME",
},
cli.StringFlag{
Name: "runner.platform",
Usage: "providers the Drone agent os and architecture",
EnvVar: "DRONE_RUNNER_PLATFORM",
},
cli.StringFlag{
Name: "runner.label",
Usage: "404 not found",
EnvVar: "DRONE_RUNNER_LABEL",
},
cli.StringFlag{
Name: "source.branch",
Usage: "providers the source branch for a pull request",
EnvVar: "DRONE_SOURCE_BRANCH",
},
cli.StringFlag{
Name: "target.branch",
Usage: "providers the target branch for a pull request",
EnvVar: "DRONE_TARGET_BRANCH",
},
cli.StringFlag{
Name: "system.host",
Usage: "providers the Drone server hostname",
EnvVar: "DRONE_SYSTEM_HOST",
},
cli.StringFlag{
Name: "system.hostname",
Usage: "providers the Drone server hostname",
EnvVar: "DRONE_SYSTEM_HOSTNAME",
},
cli.StringFlag{
Name: "system.version",
Usage: "providers the Drone server version",
EnvVar: "DRONE_SYSTEM_VERSION",
},
cli.StringFlag{
Name: "tag",
Usage: "providers the tag name for the current build.This value is only set if the build event is of type tag",
EnvVar: "DRONE_TAG",
},
// build args start
cli.StringFlag{
Name: "build.event",
Value: "push",
Usage: "build event",
EnvVar: "DRONE_BUILD_EVENT",
},
cli.IntFlag{
Name: "build.number",
Usage: "build number",
EnvVar: "DRONE_BUILD_NUMBER",
},
cli.IntFlag{
Name: "build.created",
Usage: "build created",
EnvVar: "DRONE_BUILD_CREATED",
},
cli.IntFlag{
Name: "build.started",
Usage: "build started",
EnvVar: "DRONE_BUILD_STARTED",
},
cli.IntFlag{
Name: "build.finished",
Usage: "build finished",
EnvVar: "DRONE_BUILD_FINISHED",
},
cli.StringFlag{
Name: "build.status",
Usage: "build status",
@@ -264,37 +109,6 @@ func main() {
Usage: "build link",
EnvVar: "DRONE_BUILD_LINK",
},
cli.StringFlag{
Name: "build.deploy",
Usage: "build deployment target",
EnvVar: "DRONE_DEPLOY_TO",
},
cli.BoolFlag{
Name: "yaml.verified",
Usage: "build yaml is verified",
EnvVar: "DRONE_YAML_VERIFIED",
},
cli.BoolFlag{
Name: "yaml.signed",
Usage: "build yaml is signed",
EnvVar: "DRONE_YAML_SIGNED",
},
// build args end
cli.Float64Flag{
Name: "job.started",
Usage: "job started",
EnvVar: "DRONE_JOB_STARTED",
},
cli.Float64Flag{
Name: "job.finished",
Usage: "job finished",
EnvVar: "DRONE_JOB_FINISHED",
},
cli.StringFlag{
Name: "ci.repo.link",
Usage: "ci repo link",
EnvVar: "CI_REPO_LINK",
},
cli.StringFlag{
Name: "config.success.pic.url",
Usage: "config success picture url",
@@ -330,77 +144,67 @@ func main() {
Usage: "link sha source page or not",
EnvVar: "PLUGIN_SHA_LINK,PLUGIN_MESSAGE_SHA_LINK",
},
cli.IntFlag{
Name: "config.retry.time",
Usage: "time out retry times, default 3",
Value: 3,
EnvVar: "PLUGIN_RETRY_TIME,PLUGIN_RETRY",
},
}
if err := app.Run(os.Args); nil != err {
log.Println(err)
os.Exit(1)
}
}
// run with args
func run(c *cli.Context) {
plugin := Plugin{
// repo info
Repo: Repo{
FullName: c.String("repo.fullname"),
Owner: c.String("repo.owner"),
Name: c.String("repo.name"),
},
// build info
Build: Build{
Action: c.String("build.action"),
Number: c.Int("build.number"),
Started: c.Float64("build.started"),
Created: c.Float64("build.created"),
Event: c.String("build.event"),
Status: c.String("build.status"),
Link: c.String("build.link"),
},
Commit: Commit{
Sha: c.String("commit.sha"),
Branch: c.String("commit.branch"),
Message: c.String("commit.message"),
Link: c.String("commit.link"),
Authors: struct {
Avatar string
Email string
Name string
}{
Avatar: c.String("commit.author.avatar"),
Email: c.String("commit.author.email"),
Name: c.String("commit.author.name"),
Drone: Drone{
// repo info
Repo: Repo{
FullName: c.String("repo.fullname"),
},
// build info
Build: Build{
Status: c.String("build.status"),
Link: c.String("build.link"),
},
Commit: Commit{
Sha: c.String("commit.sha"),
Branch: c.String("commit.branch"),
Message: c.String("commit.message"),
Link: c.String("commit.link"),
Authors: struct {
Avatar string
Email string
Name string
}{
Avatar: c.String("commit.author.avatar"),
Email: c.String("commit.author.email"),
Name: c.String("commit.author.name"),
},
},
},
// custom config
Config: Config{
AccessToken: c.String("config.token"),
Lang: c.String("config.lang"),
IsAtALL: c.Bool("config.message.at.all"),
MsgType: c.String("config.message.type"),
Mobiles: c.String("config.message.at.mobiles"),
SuccessPicUrl: c.String("config.success.pic.url"),
FailurePicUrl: c.String("config.failure.pic.url"),
SuccessColor: c.String("config.success.color"),
FailureColor: c.String("config.failure.color"),
WithColor: c.Bool("config.message.color"),
WithPic: c.Bool("config.message.pic"),
LinkSha: c.Bool("config.message.sha.link"),
RetryTime: c.Int("config.retry.time"),
AccessToken: c.String("config.token"),
//Lang: c.String("config.lang"),
IsAtALL: c.Bool("config.message.at.all"),
MsgType: c.String("config.message.type"),
Mobiles: c.String("config.message.at.mobiles"),
Debug: c.Bool("config.debug"),
},
CI: CI{
RepoLink: c.String("ci.repo.link"),
Extra: Extra{
Pic: ExtraPic{
WithPic: c.Bool("config.message.pic"),
SuccessPicURL: c.String("config.success.pic.url"),
FailurePicURL: c.String("config.failure.pic.url"),
},
Color: ExtraColor{
SuccessColor: c.String("config.success.color"),
FailureColor: c.String("config.failure.color"),
WithColor: c.Bool("config.message.color"),
},
LinkSha: c.Bool("config.message.sha.link"),
},
}
if err := plugin.Exec(); nil != err {
fmt.Println(err)
os.Exit(1)
}
}
+132 -167
View File
@@ -4,210 +4,175 @@ import (
"errors"
"fmt"
"log"
"net"
"strings"
webhook "github.com/lddsb/dingtalk-webhook"
)
type (
// repo base info
// Repo `repo base info`
Repo struct {
Owner string // providers the repository owner name
Name string // providers the repository name
Branch string // providers the default repository branch(e.g.master)
Link string // providers the repository http link
NameSpace string // providers the repository namespace(e.g.account owner)
Private bool // indicates the repository is public or private
Visibility string // providers the repository visibility level.Possible values are public,private and internal
SCM string // providers the repository version control system
FullName string // repository full name
FullName string // repository full name
}
// build info
// Build `build info`
Build struct {
Action string // document description not found
Created float64 // providers the date and time when the build was created in the system
Event string // providers the current build event
Number int // providers the current build number
Started float64 // providers the date and time when the build was started
Status string // providers the current build status
Link string // providers the current build link
Status string // providers the current build status
Link string // providers the current build link
}
// commit info
// Commit `commit info`
Commit struct {
After string // providers the commit sha for the current build
Author string // providers the author username for the current commit
Before string // providers the parent commit sha for the current build
Branch string // providers the branch for the current commit
Link string // providers the http link to the current commit in the remote source code management system(e.g.GitHub)
Message string // providers the commit message for the current build
Ref string // providers the reference for the current build
Sha string // providers the commit sha for the current build
// repo author info
Authors struct {
Avatar string // providers the author avatar for the current commit
Email string // providers the author email for the current commit
Name string // providers the author name for the current commit
}
Authors CommitAuthors
}
// git url info
Git struct {
HttpUrl string // providers the repository git+http url
SSHUrl string // providers the repository git+ssh url
// CommitAuthors `commit author info`
CommitAuthors struct {
Avatar string // providers the author avatar for the current commit
Email string // providers the author email for the current commit
Name string // providers the author name for the current commit
}
// Drone runner info
Runner struct {
Host string // providers the Drone agent hostname
Hostname string // providers the Drone agent hostname
Platform string // providers the Drone agent os and architecture
Label string // document description not found
// Drone `drone info`
Drone struct {
Repo Repo
Build Build
Commit Commit
}
// Drone system info
System struct {
Host string // providers the Drone server hostname
Hostname string // providers the Drone server hostname
Version string // providers the Drone server version
}
// Drone CI Info
CI struct {
RepoLink string
}
// plugin private config
// Config `plugin private config`
Config struct {
AccessToken string
Message string
Lang string
IsAtALL bool
Mobiles string
Username string
AvatarURL string
MsgType string
Debug bool
AccessToken string
IsAtALL bool
Mobiles string
Username string
MsgType string
}
// MessageConfig `DingTalk message struct`
MessageConfig struct {
ActionCard ActionCard
}
// ActionCard `action card message struct`
ActionCard struct {
LinkUrls string
LinkTitles string
HideAvatar bool
BtnOrientation bool
PicURL string
MsgURL string
SuccessPicUrl string
FailurePicUrl string
SuccessColor string
FailureColor string
WithColor bool
WithPic bool
LinkSha bool
RetryTime int
}
// plugin all config
// Extra `extra variables`
Extra struct {
Color ExtraColor
Pic ExtraPic
LinkSha bool
}
// ExtraPic `extra config for pic`
ExtraPic struct {
WithPic bool
SuccessPicURL string
FailurePicURL string
}
// ExtraColor `extra config for color`
ExtraColor struct {
WithColor bool
SuccessColor string
FailureColor string
}
// Plugin `plugin all config`
Plugin struct {
CI CI
Git Git
Runner Runner
System System
Commit Commit
Repo Repo
Build Build
Config Config
WebHook *WebHook
Drone Drone
Config Config
Extra Extra
}
)
// Exec `execute webhook`
func (p *Plugin) Exec() error {
log.Println("start execute sending...")
var err error
if 0 == len(p.Config.AccessToken) {
msg := "missing dingtalk access token"
log.Println(msg)
return errors.New(msg)
}
log.Println("access token pass...")
p.WebHook = NewWebHook(p.Config.AccessToken)
mobiles := strings.Split(p.Config.Mobiles, ",")
linkUrls := strings.Split(p.Config.LinkUrls, ",")
linkTitles := strings.Split(p.Config.LinkTitles, ",")
log.Println("sending message type: " + p.Config.MsgType)
var err error
retryTime := 1
for retryTime <= p.Config.RetryTime {
log.Printf("start a %d try", retryTime)
switch strings.ToLower(p.Config.MsgType) {
case "markdown":
err = p.WebHook.SendMarkdownMsg(
"You have a new message...",
p.baseTpl(),
p.Config.IsAtALL,
mobiles...
)
case "text":
err = p.WebHook.SendTextMsg(p.baseTpl(), p.Config.IsAtALL, mobiles...)
case "actioncard":
err = p.WebHook.SendActionCardMsg(
"A actionCard title",
p.baseTpl(),
linkUrls,
linkTitles,
p.Config.HideAvatar,
p.Config.BtnOrientation,
)
case "link":
err = p.WebHook.SendLinkMsg(p.Build.Status, p.baseTpl(), p.Commit.Authors.Avatar, p.Build.Link)
default:
err = errors.New("not support message type")
}
if netErr, ok := err.(net.Error); ok && netErr.Timeout() {
retryTime++
} else {
break
}
if 6 > len(p.Drone.Commit.Sha) {
return errors.New("commit sha cannot short than 6")
}
if nil != err {
log.Println(err)
return err
newWebhook := webhook.NewWebHook(p.Config.AccessToken)
mobiles := strings.Split(p.Config.Mobiles, ",")
switch strings.ToLower(p.Config.MsgType) {
case "markdown":
err = newWebhook.SendMarkdownMsg("You have a new message...", p.baseTpl(), p.Config.IsAtALL, mobiles...)
case "text":
err = newWebhook.SendTextMsg(p.baseTpl(), p.Config.IsAtALL, mobiles...)
case "link":
err = newWebhook.SendLinkMsg(p.Drone.Build.Status, p.baseTpl(), p.Drone.Commit.Authors.Avatar, p.Drone.Build.Link)
default:
msg := "not support message type"
err = errors.New(msg)
}
log.Println("send " + p.Config.MsgType + " message success!")
return nil
if err == nil {
log.Println("send message success!")
}
return err
}
// markdownTpl `output the tpl of markdown`
func (p *Plugin) markdownTpl() string {
var tpl string
// title
title := fmt.Sprintf(" %s *Branch Build %s*",
strings.Title(p.Commit.Branch),
strings.Title(p.Build.Status))
strings.Title(p.Drone.Commit.Branch),
strings.Title(p.Drone.Build.Status))
// with color on title
if p.Config.WithColor {
if p.Extra.Color.WithColor {
title = fmt.Sprintf("<font color=%s>%s</font>", p.getColor(), title)
}
tpl = fmt.Sprintf("# %s \n", title)
// with pic
if p.Config.WithPic {
if p.Extra.Pic.WithPic {
tpl += fmt.Sprintf("![%s](%s)\n\n",
p.Build.Status,
p.getPicUrl())
p.Drone.Build.Status,
p.getPicURL())
}
// commit message
commitMsg := fmt.Sprintf("%s", p.Commit.Message)
if p.Config.WithColor {
commitMsg := fmt.Sprintf("%s", p.Drone.Commit.Message)
if p.Extra.Color.WithColor {
commitMsg = fmt.Sprintf("<font color=%s>%s</font>", p.getColor(), commitMsg)
}
tpl += commitMsg + "\n\n"
// sha info
commitSha := p.Commit.Sha
if p.Config.LinkSha {
commitSha = fmt.Sprintf("[Click To %s Commit Detail Page](%s)", commitSha[:6], p.Commit.Link)
commitSha := p.Drone.Commit.Sha
if p.Extra.LinkSha {
commitSha = fmt.Sprintf("[Click To %s Commit Detail Page](%s)", commitSha[:6], p.Drone.Commit.Link)
}
tpl += commitSha + "\n\n"
// author info
authorInfo := fmt.Sprintf("`%s(%s)`", p.Commit.Authors.Name, p.Commit.Authors.Email)
authorInfo := fmt.Sprintf("`%s(%s)`", p.Drone.Commit.Authors.Name, p.Drone.Commit.Authors.Email)
tpl += authorInfo + "\n\n"
// build detail link
buildDetail := fmt.Sprintf("[Click To The Build Detail Page %s](%s)",
p.getEmoticon(),
p.Build.Link)
p.Drone.Build.Link)
tpl += buildDetail
return tpl
}
@@ -223,20 +188,20 @@ func (p *Plugin) baseTpl() string {
@%s
%s (%s)
`,
p.Build.Status,
strings.TrimSpace(p.Commit.Message),
p.Repo.FullName,
p.Commit.Branch,
p.Commit.Sha,
p.Commit.Authors.Name,
p.Commit.Authors.Email)
p.Drone.Build.Status,
strings.TrimSpace(p.Drone.Commit.Message),
p.Drone.Repo.FullName,
p.Drone.Commit.Branch,
p.Drone.Commit.Sha,
p.Drone.Commit.Authors.Name,
p.Drone.Commit.Authors.Email)
case "link":
tpl = fmt.Sprintf(`%s(%s) @%s %s(%s)`,
p.Repo.FullName,
p.Commit.Branch,
p.Commit.Sha[:6],
p.Commit.Authors.Name,
p.Commit.Authors.Email)
p.Drone.Repo.FullName,
p.Drone.Commit.Branch,
p.Drone.Commit.Sha[:6],
p.Drone.Commit.Authors.Name,
p.Drone.Commit.Authors.Email)
case "actionCard":
// coming soon
@@ -247,13 +212,13 @@ func (p *Plugin) baseTpl() string {
/**
get emoticon
*/
*/
func (p *Plugin) getEmoticon() string {
emoticons := make(map[string]string)
emoticons["success"] = ":)"
emoticons["failure"] = ":("
emoticon, ok := emoticons[p.Build.Status]
emoticon, ok := emoticons[p.Drone.Build.Status]
if ok {
return emoticon
}
@@ -263,21 +228,21 @@ func (p *Plugin) getEmoticon() string {
/**
get picture url
*/
func (p *Plugin) getPicUrl() string {
*/
func (p *Plugin) getPicURL() string {
pics := make(map[string]string)
// success picture url
pics["success"] = "https://ws4.sinaimg.cn/large/006tNc79gy1fz05g5a7utj30he0bfjry.jpg"
if p.Config.SuccessPicUrl != "" {
pics["success"] = p.Config.SuccessPicUrl
pics["success"] = "https://wx1.sinaimg.cn/large/006tNc79gy1fz05g5a7utj30he0bfjry.jpg"
if p.Extra.Pic.SuccessPicURL != "" {
pics["success"] = p.Extra.Pic.SuccessPicURL
}
// failure picture url
pics["failure"] = "https://ws1.sinaimg.cn/large/006tNc79gy1fz0b4fghpnj30hd0bdmxn.jpg"
if p.Config.FailurePicUrl != "" {
pics["failure"] = p.Config.FailurePicUrl
pics["failure"] = "https://wx1.sinaimg.cn/large/006tNc79gy1fz0b4fghpnj30hd0bdmxn.jpg"
if p.Extra.Pic.FailurePicURL != "" {
pics["failure"] = p.Extra.Pic.FailurePicURL
}
url, ok := pics[p.Build.Status]
url, ok := pics[p.Drone.Build.Status]
if ok {
return url
}
@@ -287,21 +252,21 @@ func (p *Plugin) getPicUrl() string {
/**
get color for message title
*/
*/
func (p *Plugin) getColor() string {
colors := make(map[string]string)
// success color
colors["success"] = "#008000"
if p.Config.SuccessColor != "" {
colors["success"] = "#" + p.Config.SuccessColor
if p.Extra.Color.SuccessColor != "" {
colors["success"] = "#" + p.Extra.Color.SuccessColor
}
// failure color
colors["failure"] = "#FF0000"
if p.Config.FailureColor != "" {
colors["failure"] = "#" + p.Config.FailureColor
if p.Extra.Color.FailureColor != "" {
colors["failure"] = "#" + p.Extra.Color.FailureColor
}
color, ok := colors[p.Build.Status]
color, ok := colors[p.Drone.Build.Status]
if ok {
return color
}
+59
View File
@@ -0,0 +1,59 @@
package main
import (
"testing"
)
func TestPlugin(t *testing.T) {
p := Plugin{}
err := p.Exec()
if nil == err {
t.Error("access token empty error should be catch!")
}
p.Config.AccessToken = "example-access-token"
err = p.Exec()
if nil == err {
t.Error("commit sha length error should be catch!")
}
p.Drone.Commit.Sha = "53729847dfksj"
err = p.Exec()
if nil == err {
t.Error("not support message type error should be catch!")
}
p.Config.MsgType = "text"
err = p.Exec()
if nil == err {
t.Error("access token invalid error should be catch!")
}
p.Config.MsgType = "link"
err = p.Exec()
if nil == err {
t.Error("access token invalid error should be catch!")
}
p.Extra.Color.WithColor = true
p.Extra.Color.FailureColor = "#555555"
p.Extra.Color.SuccessColor = "#222222"
p.Extra.Pic.WithPic = true
p.Extra.Pic.FailurePicURL = "https://www.baidu.com"
p.Extra.Pic.SuccessPicURL = "https://www.baidu.com"
p.Extra.LinkSha = true
// p.Drone.Build.Status = "failure"
p.Config.MsgType = "markdown"
err = p.Exec()
if nil == err {
t.Error("access token invalid error should be catch!")
}
p.Drone.Build.Status = "failure"
err = p.Exec()
if nil == err {
t.Error("access token invalid error should be catch!")
}
t.Log("plugin testing finished")
}
-241
View File
@@ -1,241 +0,0 @@
package main
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net/http"
"regexp"
)
// link message struct
type LinkMsg struct {
Title string `json:"title"`
MessageURL string `json:"messageURL"`
PicURL string `json:"picURL"`
}
// action card message struct
type ActionCard struct {
Text string `json:"text"`
Title string `json:"title"`
SingleTitle string `json:"singleTitle"`
SingleURL string `json:"singleURL"`
BtnOrientation string `json:"btnOrientation"`
HideAvatar string `json:"hideAvatar"` // robot message avatar
Buttons []struct {
Title string `json:"title"`
ActionURL string `json:"actionURL"`
} `json:"btns"`
}
// payload
type PayLoad struct {
MsgType string `json:"msgtype"`
Text struct {
Content string `json:"content"`
} `json:"text"`
Link struct {
Title string `json:"title"`
Text string `json:"text"`
PicUrl string `json:"picUrl"`
MessageUrl string `json:"messageUrl"`
} `json:"link"`
Markdown struct {
Title string `json:"title"`
Text string `json:"text"`
} `json:"markdown"`
ActionCard ActionCard `json:"actionCard"`
FeedCard struct {
Links []LinkMsg `json:"links"`
} `json:"feedCard"`
At struct {
AtMobiles []string `json:"atMobiles"`
IsAtAll bool `json:"isAtAll"`
} `json:"at"`
}
// web hook base config
type WebHook struct {
AccessToken string `json:"accessToken"`
}
func NewWebHook(accessToken string) *WebHook {
return &WebHook{AccessToken: accessToken}
}
type Response struct {
ErrorCode int `json:"errcode"`
ErrorMessage string `json:"errmsg"`
}
var baseApi = "https://oapi.dingtalk.com/robot/send?access_token="
var reg = `^1([38][0-9]|14[57]|5[^4])\d{8}$`
var regx = regexp.MustCompile(reg)
// real send request to api
func (w *WebHook) sendPayload(payload *PayLoad) error {
// get config
bs, err := json.Marshal(payload)
if nil != err {
return err
}
// request api
resp, err := http.Post(baseApi+w.AccessToken, "application/json", bytes.NewReader(bs))
if nil != err {
return err
}
// read response body
body, err := ioutil.ReadAll(resp.Body)
if nil != err {
return err
}
// api unusual
if 200 != resp.StatusCode {
return fmt.Errorf("%d: %s", resp.StatusCode, string(body))
}
var result Response
// json decode
err = json.Unmarshal(body, &result)
if nil != err {
return err
}
if 0 != result.ErrorCode {
return fmt.Errorf("%d: %s", result.ErrorCode, result.ErrorMessage)
}
return nil
}
// text message
func (w *WebHook) SendTextMsg(content string, isAtAll bool, mobiles ...string) error {
// send request
return w.sendPayload(&PayLoad{
MsgType: "text",
Text: struct {
Content string `json:"content"`
}{
Content: content,
},
At: struct {
AtMobiles []string `json:"atMobiles"`
IsAtAll bool `json:"isAtAll"`
}{
AtMobiles: mobiles,
IsAtAll: isAtAll,
},
})
}
// with link message
func (w *WebHook) SendLinkMsg(title, content, picURL, msgURL string) error {
return w.sendPayload(&PayLoad{
MsgType: "link",
Link: struct {
Title string `json:"title"`
Text string `json:"text"`
PicUrl string `json:"picUrl"`
MessageUrl string `json:"messageUrl"`
}{
Title: title,
Text: content,
PicUrl: picURL,
MessageUrl: msgURL,
},
})
}
// send markdown msg
func (w *WebHook) SendMarkdownMsg(title, content string, isAtAll bool, mobiles ...string) error {
firstLine := false
for _, mobile := range mobiles {
if regx.MatchString(mobile) {
if false == firstLine {
content += "#####"
}
content += " @" + mobile
firstLine = true
}
}
// send request
return w.sendPayload(&PayLoad{
MsgType: "markdown",
Markdown: struct {
Title string `json:"title"`
Text string `json:"text"`
}{
Title: title,
Text: content,
},
At: struct {
AtMobiles []string `json:"atMobiles"`
IsAtAll bool `json:"isAtAll"`
}{
AtMobiles: mobiles,
IsAtAll: isAtAll,
},
})
}
// send single action card
func (w *WebHook) SendActionCardMsg(title, content string, linkTitles, linkUrls []string, hideAvatar, btnOrientation bool) error {
// validation is empty
if 0 == len(linkTitles) || 0 == len(linkUrls) {
return errors.New("links or titles is empty")
}
// validation is equal
if len(linkUrls) != len(linkTitles) {
return errors.New("links length and titles length is not equal")
}
// hide robot avatar
var strHideAvatar = "0"
if hideAvatar {
strHideAvatar = "1"
}
// button sort
var strBtnOrientation = "0"
if btnOrientation {
strBtnOrientation = "1"
}
// button struct
var buttons []struct {
Title string `json:"title"`
ActionURL string `json:"actionURL"`
}
// inject to button
for i := 0; i < len(linkTitles); i++ {
buttons = append(buttons, struct {
Title string `json:"title"`
ActionURL string `json:"actionURL"`
}{
Title: linkTitles[i],
ActionURL: linkUrls[i],
})
}
// send request
return w.sendPayload(&PayLoad{
MsgType: "actionCard",
ActionCard: ActionCard{
Title: title,
Text: content,
HideAvatar: strHideAvatar,
BtnOrientation: strBtnOrientation,
Buttons: buttons,
},
})
}
// send link card message
func (w *WebHook) SendLinkCardMsg(messages []LinkMsg) error {
return w.sendPayload(&PayLoad{
MsgType: "feedCard",
FeedCard: struct {
Links []LinkMsg `json:"links"`
}{
Links: messages,
},
})
}