Skip to content

Commit

Permalink
Initial poc
Browse files Browse the repository at this point in the history
  • Loading branch information
Nicolas committed Jul 20, 2022
0 parents commit a565019
Show file tree
Hide file tree
Showing 4 changed files with 167 additions and 0 deletions.
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# jelease - A newreleases.io ➡️ Jira connector

Automatically create Jira tickets when a newreleases.io release is detected using webhooks.

## Configuration:

The application requires the following environment variables to be set:
- `PORT`: The port the application is expecting traffic on
- `JIRA_URL`: The URL of your Jira instance
- `JIRA_USER`: Jira username to authenticate API requests
- `JIRA_TOKEN`: Jira API token, can also be a password in self-hosted instances
- `JIRA_PROJECT`: Jira Project key the tickets will be created in

## Usage

Send newreleases.io webhooks to the `host:port/webhook` route.
13 changes: 13 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
module github.2rioffice.com/nicolas-mohr/jilease

go 1.18

require github.com/andygrunwald/go-jira v1.15.1

require (
github.com/fatih/structs v1.1.0 // indirect
github.com/golang-jwt/jwt/v4 v4.3.0 // indirect
github.com/google/go-querystring v1.1.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/trivago/tgo v1.0.7 // indirect
)
18 changes: 18 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
github.com/andygrunwald/go-jira v1.15.1 h1:6J9aYKb9sW8bxv3pBLYBrs0wdsFrmGI5IeTgWSKWKc8=
github.com/andygrunwald/go-jira v1.15.1/go.mod h1:GIYN1sHOIsENWUZ7B4pDeT/nxEtrZpE8l0987O67ZR8=
github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
github.com/golang-jwt/jwt/v4 v4.3.0 h1:kHL1vqdqWNfATmA0FNMdmZNMyZI1U6O31X4rlIPoBog=
github.com/golang-jwt/jwt/v4 v4.3.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o=
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/trivago/tgo v1.0.7 h1:uaWH/XIy9aWYWpjm2CU3RpcqZXmX2ysQ9/Go+d9gyrM=
github.com/trivago/tgo v1.0.7/go.mod h1:w4dpD+3tzNIIiIfkWWa85w5/B77tlvdZckQ+6PkFnhc=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
120 changes: 120 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package main

import (
"encoding/json"
"errors"
"fmt"
"io"
"io/ioutil"
"net/http"
"os"

jira "github.com/andygrunwald/go-jira"
)

const (
defaultPort = "8080"
)

var (
jiraClient *jira.Client
jiraProject string
)

// Some fields omitted for simplicity, check out the documentation at https://newreleases.io/webhooks
type NewRelease struct {
Provider string `json:"provider"`
Project string `json:"project"`
Version string `json:"version"`
}

func ReleaseToJiraIssue(newRelease NewRelease) jira.Issue {
return jira.Issue{
Fields: &jira.IssueFields{
Description: "Update issue generated by newreleases.io",
Project: jira.Project{
Key: jiraProject,
},
Type: jira.IssueType{
Name: "Task",
},
Summary: fmt.Sprintf("Update %v to version %v", newRelease.Project, newRelease.Version),
},
}
}

func getRoot(w http.ResponseWriter, r *http.Request) {
fmt.Printf("got / request\n")
io.WriteString(w, "Pong!\n")
}

func postWebhook(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed)
}
// parse newreleases.io webhook
decoder := json.NewDecoder(r.Body)
var newRelease NewRelease
err := decoder.Decode(&newRelease)
if err != nil {
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
fmt.Fprintf(os.Stderr, "Couldn't decode body to json: %v\n error: %v\n", r.Body, err)
}
fmt.Println(newRelease)

// create jira ticket
i := ReleaseToJiraIssue(newRelease)
issue, response, err := jiraClient.Issue.Create(&i)
if err != nil {
fmt.Printf("%+v\n", err)
body, _ := ioutil.ReadAll(response.Body)
fmt.Printf("Error response from Jira: %+v\n", string(body))
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)

}
fmt.Printf("Created issue %v\n", issue.ID)
}

func main() {
// jira setup
jiraUrl, jiraUrlPresent := os.LookupEnv("JIRA_URL")
jiraUser, jiraUserPresent := os.LookupEnv("JIRA_USER")
jiraToken, jiraTokenPresent := os.LookupEnv("JIRA_TOKEN")
var jiraProjectPresent bool
jiraProject, jiraProjectPresent = os.LookupEnv("JIRA_PROJECT")
if !(jiraUserPresent && jiraTokenPresent && jiraUrlPresent && jiraProjectPresent) {
fmt.Printf("Missing jira environment variables.\nJIRA_URL present: %v\nJIRA_USER present: %v\nJIRA_TOKEN present: %v\nJIRA_PROJECT present: %v\n",
jiraUrlPresent, jiraTokenPresent, jiraUserPresent, jiraProjectPresent)
return
}

fmt.Printf("Jira URL: %v\n", jiraUrl)
tp := jira.BasicAuthTransport{
Username: jiraUser,
Password: jiraToken,
}

var err error
jiraClient, err = jira.NewClient(tp.Client(), jiraUrl)
if err != nil {
panic(err)
}

// http server setup
port, portPresent := os.LookupEnv("PORT")
if !portPresent {
port = defaultPort
fmt.Printf("No port specified, defaulting to %v\n", defaultPort)
}

http.HandleFunc("/webhook", postWebhook)
http.HandleFunc("/", getRoot)
fmt.Printf("Listening on port %v\n", port)
err = http.ListenAndServe(fmt.Sprintf(":%v", port), nil)
if errors.Is(err, http.ErrServerClosed) {
fmt.Printf("server closed\n")
} else if err != nil {
fmt.Printf("error starting server: %s\n", err)
os.Exit(1)
}
}

0 comments on commit a565019

Please sign in to comment.