Initial commit. Should work, but no promises (yet).

This commit is contained in:
Stanislav Nikitin 2019-10-16 20:19:23 +05:00
commit 9893315796
No known key found for this signature in database
GPG Key ID: 106900B32F8192EE
10 changed files with 431 additions and 0 deletions

3
README.md Normal file
View File

@ -0,0 +1,3 @@
# Discordrone
This is Drone CI plugin for pushing messages about CI/CD things into Discord channels using webhooks.

143
env/env.go vendored Normal file
View File

@ -0,0 +1,143 @@
package env
import (
// other
"github.com/vrischmann/envconfig"
"fmt"
)
var Data *environmentData
type environmentData struct {
CI bool `envconfig:"optional"`
IsDrone bool `envconfig:"DRONE,optional"`
Debug bool `envconfig:"optional"`
Drone struct {
Branch string `envconfig:"optional"`
Build struct {
Action string `envconfig:"optional"`
Created int `envconfig:"optional"`
Event string `envconfig:"optional"`
Finished int `envconfig:"optional"`
Link string `envconfig:"optional"`
Number int `envconfig:"optional"`
Parent string `envconfig:"optional"`
Started int `envconfig:"optional"`
Status string `envconfig:"optional"`
}
CommitHash string `envconfig:"DRONE_COMMIT,optional"`
Commit struct {
After string `envconfig:"optional"`
AuthorName string `envconfig:"DRONE_COMMIT_AUTHOR,optional"`
Author struct {
Avatar string `envconfig:"optional"`
Email string `envconfig:"optional"`
Name string `envconfig:"optional"`
}
Before string `envconfig:"optional"`
Branch string `envconfig:"optional"`
Link string `envconfig:"optional"`
Message string `envconfig:"optional"`
Ref string `envconfig:"optional"`
Sha string `envconfig:"optional"`
}
Deploy struct {
To string `envconfig:"optional"`
}
Failed struct {
Stages string `envconfig:"optional"`
Steps string `envconfig:"optional"`
}
Git struct {
HTTP struct {
URL string `envconfig:"optional"`
}
SSH struct {
URL string `envconfig:"optional"`
}
}
Pull struct {
Request string `envconfig:"optional"`
}
Remote struct {
URL string `envconfig:"optional"`
}
RepoName string `envconfig:"DRONE_REPO_NAME,optional"`
Repo struct {
Branch string `envconfig:"optional"`
Link string `envconfig:"optional"`
Name string `envconfig:"optional"`
Namespace string `envconfig:"optional"`
Owner string `envconfig:"optional"`
Private bool `envconfig:"optional"`
SCM string `envconfig:"optional"`
Visibility string `envconfig:"optional"`
}
SemverData string `envconfig:"DRONE_SEMVER,optional"`
Semver struct {
Build string `envconfig:"optional"`
Error string `envconfig:"optional"`
Major int `envconfig:"optional"`
Minor int `envconfig:"optional"`
Patch int `envconfig:"optional"`
Prerelease string `envconfig:"optional"`
Short string `envconfig:"optional"`
}
Source struct {
Branch string `envconfig:"optional"`
}
Stage struct {
Arch string `envconfig:"optional"`
Depends struct {
On string `envconfig:"optional"`
}
Finished int `envconfig:"optional"`
Kind string `envconfig:"optional"`
Machine string `envconfig:"optional"`
Name string `envconfig:"optional"`
Number int `envconfig:"optional"`
OS string `envconfig:"optional"`
Started int `envconfig:"optional"`
Status string `envconfig:"optional"`
Type string `envconfig:"optional"`
Variant string `envconfig:"optional"`
}
Step struct {
Name string `envconfig:"optional"`
Number int `envconfig:"optional"`
}
System struct {
Host string `envconfig:"optional"`
Hostname string `envconfig:"optional"`
Proto string `envconfig:"optional"`
Version string `envconfig:"optional"`
}
Tag string `envconfig:"optional"`
Target struct {
Branch string `envconfig:"optional"`
}
}
Plugin struct {
Avatar struct {
URL string `envconfig:"optional"`
}
Color string `envconfig:"optional"`
Message string `envconfig:"optional"`
Webhook struct {
ID string `envconfig:"optional"`
Token string `envconfig:"optional"`
}
TTS bool `envconfig:"optional"`
Username string `envconfig:"optional"`
}
}
func ParseEnv() {
Data = &environmentData{}
err := envconfig.Init(Data)
if err != nil {
fmt.Println(err)
}
}

10
go.mod Normal file
View File

@ -0,0 +1,10 @@
module go.dev.pztrn.name/discordrone
go 1.13
require (
github.com/drone/drone-template-lib v1.0.0
github.com/stretchr/testify v1.4.0 // indirect
github.com/vrischmann/envconfig v1.2.0
gopkg.in/yaml.v2 v2.2.4 // indirect
)

38
go.sum Normal file
View File

@ -0,0 +1,38 @@
github.com/Masterminds/goutils v1.1.0 h1:zukEsf/1JZwCMgHiK3GZftabmxiCw4apj3a28RPBiVg=
github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
github.com/Masterminds/semver v1.4.2 h1:WBLTQ37jOCzSLtXNdoo8bNM8876KhNqOKvrlGITgsTc=
github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
github.com/Masterminds/sprig v2.18.0+incompatible h1:QoGhlbC6pter1jxKnjMFxT8EqsLuDE6FEcNbWEpw+lI=
github.com/Masterminds/sprig v2.18.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o=
github.com/aymerick/raymond v2.0.2+incompatible h1:VEp3GpgdAnv9B2GFyTvqgcKvY+mfKMjPOA3SbKLtnU0=
github.com/aymerick/raymond v2.0.2+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g=
github.com/bouk/monkey v1.0.0 h1:k6z8fLlPhETfn5l9rlWVE7Q6B23DoaqosTdArvNQRdc=
github.com/bouk/monkey v1.0.0/go.mod h1:PG/63f4XEUlVyW1ttIeOJmJhhe1+t9EC/je3eTjvFhE=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/drone/drone-template-lib v1.0.0 h1:PNBBfUhifRnrPCoWBlTitk3jipXdv8u8WLbIf7h7j00=
github.com/drone/drone-template-lib v1.0.0/go.mod h1:Hqy1tgqPH5mtbFOZmow19l4jOkZvp+WZ00cB4W3MJhg=
github.com/google/uuid v1.1.0 h1:Jf4mxPC/ziBnoPIdpQdPJ9OeiomAUHLvxmPRSPH9m4s=
github.com/google/uuid v1.1.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/huandu/xstrings v1.2.0 h1:yPeWdRnmynF7p+lLYz0H2tthW9lqhMJrQV/U7yy4wX0=
github.com/huandu/xstrings v1.2.0/go.mod h1:DvyZB1rfVYsBIigL8HwpZgxHwXozlTgGqn63UyNX5k4=
github.com/imdario/mergo v0.3.7 h1:Y+UAYTZ7gDEuOfhxKWy+dvb5dRQ6rJjFSdX2HZY1/gI=
github.com/imdario/mergo v0.3.7/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/tkuchiki/faketime v0.0.0-20170607100027-a4500a4f4643 h1:ii/sHfgFMByozryLeiDmn1ClZ/Pena4NgpJ4P7UuX9o=
github.com/tkuchiki/faketime v0.0.0-20170607100027-a4500a4f4643/go.mod h1:RXY/TXAwGGL36IKDjrHFMcjpUrEiyWSEtLhFPw3UWF0=
github.com/vrischmann/envconfig v1.2.0 h1:5/u4fI34/g3m0SdTQj/6f3r640jv9E5+yTXIZOWsxk0=
github.com/vrischmann/envconfig v1.2.0/go.mod h1:c5DuUlkzfsnspy1g7qiqryPCsW+NjsrLsYq4zhwsoHo=
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67 h1:ng3VDlRp5/DHpSWl02R4rM9I+8M2rhmsuLwAMmkLQWE=
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
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.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

24
main.go Normal file
View File

@ -0,0 +1,24 @@
package main
import (
// stdlib
"log"
"os"
// local
"go.dev.pztrn.name/discordrone/env"
"go.dev.pztrn.name/discordrone/message"
)
func main() {
env.ParseEnv()
if env.Data.Debug {
log.Printf("Starting Discordrone with parameters: %+v\n", env.Data)
}
err := message.Process()
if err != nil {
log.Fatalln("Error appeared:", err)
os.Exit(1)
}
}

43
message/payload.go Normal file
View File

@ -0,0 +1,43 @@
package message
type (
// Payload that will be sent to Discord.
payload struct {
Wait bool `json:"wait"`
Content string `json:"content"`
Username string `json:"username"`
AvatarURL string `json:"avatar_url"`
TTS bool `json:"tts"`
Embeds []EmbedObject `json:"embeds"`
}
// EmbedFooterObject for Embed Footer Structure.
EmbedFooterObject struct {
Text string `json:"text"`
IconURL string `json:"icon_url"`
}
// EmbedAuthorObject for Embed Author Structure
EmbedAuthorObject struct {
Name string `json:"name"`
URL string `json:"url"`
IconURL string `json:"icon_url"`
}
// EmbedFieldObject for Embed Field Structure
EmbedFieldObject struct {
Name string `json:"name"`
Value string `json:"value"`
}
// EmbedObject is for Embed Structure
EmbedObject struct {
Title string `json:"title"`
Description string `json:"description"`
URL string `json:"url"`
Color int `json:"color"`
Footer EmbedFooterObject `json:"footer"`
Author EmbedAuthorObject `json:"author"`
Fields []EmbedFieldObject `json:"fields"`
}
)

8
message/prepare.go Normal file
View File

@ -0,0 +1,8 @@
package message
// stdlib
// local
// other
//"github.com/drone/drone-template-lib/template"

55
message/process.go Normal file
View File

@ -0,0 +1,55 @@
package message
import (
// stdlib
"errors"
"log"
// local
"go.dev.pztrn.name/discordrone/env"
// other
"github.com/drone/drone-template-lib/template"
)
// Process starts message(s) processing.
func Process() error {
if env.Data.Debug {
log.Println("Preparing message for sending...")
}
// Webhook data should present.
if env.Data.Plugin.Webhook.ID == "" && env.Data.Plugin.Webhook.Token == "" {
return errors.New("webhook id or token is missing")
}
// Payload.
p := &payload{
Embeds: []EmbedObject{
createEmbed(),
},
}
// If no additional message was passed - send what we got.
if len(env.Data.Plugin.Message) == 0 {
err := sendMessage(p)
if err != nil {
return err
}
}
// If message was set - format it.
if len(env.Data.Plugin.Message) > 0 {
text, err := template.RenderTrim(env.Data.Plugin.Message, env.Data)
if err != nil {
return err
}
p.Content = text
err1 := sendMessage(p)
if err1 != nil {
return err1
}
}
return nil
}

28
message/send_message.go Normal file
View File

@ -0,0 +1,28 @@
package message
import (
// stdlib
"bytes"
"encoding/json"
"fmt"
"net/http"
// local
"go.dev.pztrn.name/discordrone/env"
)
// Sends message to Discord.
func sendMessage(message interface{}) error {
webhookURL := fmt.Sprintf("https://discordapp.com/api/webhooks/%s/%s", env.Data.Plugin.Webhook.ID, env.Data.Plugin.Webhook.Token)
b := new(bytes.Buffer)
if err := json.NewEncoder(b).Encode(message); err != nil {
return err
}
_, err := http.Post(webhookURL, "application/json; charset=utf-8", b)
if err != nil {
return err
}
return nil
}

79
message/template.go Normal file
View File

@ -0,0 +1,79 @@
package message
import (
// stdlib
"strconv"
"strings"
// local
"go.dev.pztrn.name/discordrone/env"
)
const (
// DroneIconURL default drone logo url
droneIconURL = "https://c1.staticflickr.com/5/4236/34957940160_435d83114f_z.jpg"
// DroneDesc default drone description
droneDesc = "Powered by DiscoDrone Plugin"
)
func createEmbed() EmbedObject {
// Create initial payload object.
// Description.
// Color.
embed := EmbedObject{
Title: env.Data.Drone.Commit.Message,
URL: env.Data.Drone.Build.Link,
Author: EmbedAuthorObject{
Name: env.Data.Drone.Commit.Author.Name,
IconURL: env.Data.Drone.Commit.Author.Avatar,
},
Footer: EmbedFooterObject{
Text: droneDesc,
IconURL: droneIconURL,
},
}
// Compose description.
var description string
// ToDo: promote/rollback?
switch env.Data.Drone.Build.Event {
case "pull_request":
var branch string
if env.Data.Drone.Commit.Ref != "" {
branch = env.Data.Drone.Commit.Ref
} else {
branch = env.Data.Drone.Commit.Branch
}
description = env.Data.Drone.Commit.Author.Name + " updated pull request " + branch
case "push":
description = env.Data.Drone.Commit.Author.Name + " pushed to " + env.Data.Drone.Commit.Branch
case "tag":
description = env.Data.Drone.Commit.Author.Name + " pushed tag " + env.Data.Drone.Commit.Branch
}
embed.Description = description
// Compose color.
var color int
if env.Data.Plugin.Color != "" {
env.Data.Plugin.Color = strings.Replace(env.Data.Plugin.Color, "#", "", -1)
if s, err := strconv.ParseInt(env.Data.Plugin.Color, 16, 32); err == nil {
color = int(s)
}
}
switch env.Data.Drone.Build.Status {
case "success":
// green
color = 0x1ac600
case "failure", "error", "killed":
// red
color = 0xff3232
default:
// yellow
color = 0xffd930
}
embed.Color = color
return embed
}