Commit a43d6df6 authored by zauberstuhl's avatar zauberstuhl

Initial commit

parents
server.db
This diff is collapsed.
# TheFederation Github Integration Server
//
// TheFederation Github Integration Server
// Copyright (C) 2018 Lukas Matt <lukas@zauberstuhl.de>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
package main
import (
"fmt"
"net/http"
"golang.org/x/oauth2"
"github.com/google/go-github/github"
"database/sql"
_ "github.com/mattn/go-sqlite3"
"context"
)
func frontend(w http.ResponseWriter, r *http.Request) {
ctx := context.Background()
accessToken := r.URL.Query().Get("access_token")
repo := r.URL.Query().Get("repo")
if accessToken != "" && repo != "" {
db, err := sql.Open("sqlite3", "./server.db")
if err != nil {
fmt.Fprintf(w, "Database Failure :(")
return
}
defer db.Close()
ctx := context.Background()
ts := oauth2.StaticTokenSource(
&oauth2.Token{AccessToken: accessToken},
)
tc := oauth2.NewClient(ctx, ts)
client := github.NewClient(tc)
name := "web"
secret := Secret(16)
hook := github.Hook{
Name: &name, Events: []string{"pull_request"},
Config: map[string]interface{}{
"url": serverDomain + "/hook",
"secret": secret,
},
}
_, _, err = client.Repositories.CreateHook(ctx, "ganggo", "ganggo", &hook)
if err != nil {
fmt.Fprintf(w, "Create Hook Failure :(")
return
}
_, err = db.Exec(fmt.Sprintf(`insert into repos(slug, token, secret)
values('%s', '%s', '%s');`, repo, accessToken, secret,
)); if err != nil {
fmt.Fprintf(w, "Database Insert Failure :(")
return
}
fmt.Fprintf(w, "Success :) You can undo it by simply revoking permissions on Github.")
return
}
code := r.URL.Query().Get("code")
if code != "" {
tok, err := conf.Exchange(ctx, code)
if err != nil {
fmt.Fprintf(w, "Token Failure :(")
} else {
tc := conf.Client(ctx, tok)
client := github.NewClient(tc)
list, _, err := client.Repositories.List(ctx, "", &github.RepositoryListOptions{})
if err != nil {
fmt.Fprintf(w, "Repository List Failure :(")
return
}
fmt.Fprintf(w, `<!DOCTYPE html>
<html>
<body>
<p>You are authenticated :) Please add your repository:</p>
<form method="GET">
<input type="hidden" name="access_token">%s</input>
<select name="repo">`, tok.AccessToken)
for _, repo := range list {
fmt.Fprintf(w, `<option value="%s">%s</option>`, repo.FullName, repo.FullName)
}
fmt.Fprintf(w, `
</select>
</form>
</body>
</html>`)
}
} else {
url := conf.AuthCodeURL("state", oauth2.AccessTypeOffline)
http.Redirect(w, r, url, http.StatusMovedPermanently)
}
}
//
// TheFederation Github Integration Server
// Copyright (C) 2018 Lukas Matt <lukas@zauberstuhl.de>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
package main
import (
"fmt"
"net/http"
"golang.org/x/oauth2"
oauth2Github "golang.org/x/oauth2/github"
"time"
"math/rand"
"database/sql"
_ "github.com/mattn/go-sqlite3"
"flag"
)
var (
runes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890")
travisToken, serverDomain string
travisEndpoint = "https://api.travis-ci.org/repo/"
travisSlug = "thefederationinfo%2Ffederation-tests"
travisRequests = travisEndpoint + travisSlug + "/requests"
conf = &oauth2.Config{
Scopes: []string{"admin:repo_hook"},
Endpoint: oauth2Github.Endpoint,
}
)
func init() {
rand.Seed(time.Now().UnixNano())
flag.StringVar(&serverDomain, "server-domain", "localhost:8080",
"Specify the endpoint your server is running on. " +
"This is important for e.g. github callbacks!")
flag.StringVar(&travisToken, "travis-token", "",
"Specify the travis token for triggering builds (required)")
flag.StringVar(&conf.ClientID, "github-id", "",
"Specify the github client id (required)")
flag.StringVar(&conf.ClientSecret, "github-secret", "",
"Specify the github client secret (required)")
db, err := sql.Open("sqlite3", "./server.db")
if err != nil {
panic(err)
}
defer db.Close()
_, err = db.Exec(`create table repos(slug text, token text, secret text);`)
if err != nil {
fmt.Println(err)
}
}
func Secret(n int) string {
b := make([]rune, n)
for i := range b {
b[i] = runes[rand.Intn(len(runes))]
}
return string(b)
}
func main() {
flag.Parse()
if travisToken == "" || conf.ClientID == "" || conf.ClientSecret == "" {
flag.Usage()
return
}
http.HandleFunc("/", frontend)
http.HandleFunc("/hook", webhook)
fmt.Println("Running webserver on :8080")
fmt.Println(http.ListenAndServe(":8080", nil))
}
//
// TheFederation Github Integration Server
// Copyright (C) 2018 Lukas Matt <lukas@zauberstuhl.de>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
package main
import (
"fmt"
"net/http"
"strings"
"encoding/json"
"io/ioutil"
)
type TravisRequest struct {
Type string `json:"@type"`
}
func triggerTravisBuild(matrix []string) bool {
var requestJson = `{
"request": {
"branch": "master",
"config": {
"env": {
"matrix": [%s]
}
}
}
}`
// travis trigger build
req, err := http.NewRequest("POST", travisRequests,
strings.NewReader(fmt.Sprintf(
requestJson, strings.Join(matrix, ","),
)))
if err != nil {
fmt.Println(err)
return false
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Accept", "application/json")
req.Header.Set("Travis-API-Version", "3")
req.Header.Set("Authorization", "token " + travisToken)
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
fmt.Println(err)
return false
}
defer resp.Body.Close()
b, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Println(err)
return false
}
var request TravisRequest
err = json.Unmarshal(b, &request)
if err != nil {
fmt.Println(string(b), err)
return false
}
return request.Type == "pending"
}
//
// TheFederation Github Integration Server
// Copyright (C) 2018 Lukas Matt <lukas@zauberstuhl.de>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
package main
import (
"fmt"
"net/http"
"github.com/google/go-github/github"
"database/sql"
_ "github.com/mattn/go-sqlite3"
"io/ioutil"
)
func webhook(w http.ResponseWriter, r *http.Request) {
db, err := sql.Open("sqlite3", "./server.db")
if err != nil {
fmt.Println(err)
fmt.Fprintf(w, `{"error":"database error"}`)
}
defer db.Close()
defer r.Body.Close()
b, err := ioutil.ReadAll(r.Body)
if err != nil {
fmt.Println(err)
fmt.Fprintf(w, `{"error":"invalid body"}`)
}
event, err := github.ParseWebHook(github.WebHookType(r), b)
if err != nil {
fmt.Println(err)
fmt.Fprintf(w, `{"error":"invalid payload"}`)
return
}
switch event := event.(type) {
case *github.PullRequest:
stmt, err := db.Prepare("select secret from repos where slug like ?")
if err != nil {
fmt.Println(err)
fmt.Fprintf(w, `{"error":"database error"}`)
return
}
defer stmt.Close()
var secret string
err = stmt.QueryRow(event.Base.Repo.FullName).Scan(&secret)
if err != nil {
fmt.Println(err)
fmt.Fprintf(w, `{"error":"repo not registered"}`)
return
}
// validate payload
_, err = github.ValidatePayload(r, []byte(secret))
if err != nil {
fmt.Println(err)
fmt.Fprintf(w, `{"error":"invalid signature"}`)
return
}
matrix := []string{fmt.Sprintf(
`"PRREPO=%s PRSHA=%s"`,
event.Head.Repo.CloneURL,
event.Head.SHA,
)}
go triggerTravisBuild(matrix)
fmt.Fprintf(w, `{}`)
default:
fmt.Println("Not supported event type", event)
fmt.Fprintf(w, `{"error":"unsupported event type"}`)
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment