mirror of
https://github.com/bytedream/scripts.git
synced 2025-05-09 20:25:12 +02:00
460 lines
16 KiB
Go
460 lines
16 KiB
Go
package main
|
|
|
|
import (
|
|
"crypto/hmac"
|
|
"crypto/sha1"
|
|
"encoding/hex"
|
|
"encoding/json"
|
|
"github.com/gorilla/mux"
|
|
"io/ioutil"
|
|
"net/http"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"time"
|
|
)
|
|
|
|
const (
|
|
// the directory to clone the repos in
|
|
gitTree = ""
|
|
secret = "" // please do not hardcode this, use .env files or something similar
|
|
)
|
|
|
|
type payload interface{}
|
|
|
|
type PushPayload struct {
|
|
payload
|
|
|
|
Ref string `json:"ref"`
|
|
Before string `json:"before"`
|
|
After string `json:"after"`
|
|
Created bool `json:"created"`
|
|
Deleted bool `json:"deleted"`
|
|
Forced bool `json:"forced"`
|
|
BaseRef *string `json:"base_ref"`
|
|
Compare string `json:"compare"`
|
|
Commits []struct {
|
|
Sha string `json:"sha"`
|
|
ID string `json:"id"`
|
|
NodeID string `json:"node_id"`
|
|
TreeID string `json:"tree_id"`
|
|
Distinct bool `json:"distinct"`
|
|
Message string `json:"message"`
|
|
Timestamp string `json:"timestamp"`
|
|
URL string `json:"url"`
|
|
Author struct {
|
|
Name string `json:"name"`
|
|
Email string `json:"email"`
|
|
Username string `json:"username"`
|
|
} `json:"author"`
|
|
Committer struct {
|
|
Name string `json:"name"`
|
|
Email string `json:"email"`
|
|
Username string `json:"username"`
|
|
} `json:"committer"`
|
|
Added []string `json:"added"`
|
|
Removed []string `json:"removed"`
|
|
Modified []string `json:"modified"`
|
|
} `json:"commits"`
|
|
HeadCommit struct {
|
|
ID string `json:"id"`
|
|
NodeID string `json:"node_id"`
|
|
TreeID string `json:"tree_id"`
|
|
Distinct bool `json:"distinct"`
|
|
Message string `json:"message"`
|
|
Timestamp string `json:"timestamp"`
|
|
URL string `json:"url"`
|
|
Author struct {
|
|
Name string `json:"name"`
|
|
Email string `json:"email"`
|
|
Username string `json:"username"`
|
|
} `json:"author"`
|
|
Committer struct {
|
|
Name string `json:"name"`
|
|
Email string `json:"email"`
|
|
Username string `json:"username"`
|
|
} `json:"committer"`
|
|
Added []string `json:"added"`
|
|
Removed []string `json:"removed"`
|
|
Modified []string `json:"modified"`
|
|
} `json:"head_commit"`
|
|
Repository struct {
|
|
ID int64 `json:"id"`
|
|
NodeID string `json:"node_id"`
|
|
Name string `json:"name"`
|
|
FullName string `json:"full_name"`
|
|
Owner struct {
|
|
Login string `json:"login"`
|
|
ID int64 `json:"id"`
|
|
NodeID string `json:"node_id"`
|
|
AvatarURL string `json:"avatar_url"`
|
|
GravatarID string `json:"gravatar_id"`
|
|
URL string `json:"url"`
|
|
HTMLURL string `json:"html_url"`
|
|
FollowersURL string `json:"followers_url"`
|
|
FollowingURL string `json:"following_url"`
|
|
GistsURL string `json:"gists_url"`
|
|
StarredURL string `json:"starred_url"`
|
|
SubscriptionsURL string `json:"subscriptions_url"`
|
|
OrganizationsURL string `json:"organizations_url"`
|
|
ReposURL string `json:"repos_url"`
|
|
EventsURL string `json:"events_url"`
|
|
ReceivedEventsURL string `json:"received_events_url"`
|
|
Type string `json:"type"`
|
|
SiteAdmin bool `json:"site_admin"`
|
|
} `json:"owner"`
|
|
Private bool `json:"private"`
|
|
HTMLURL string `json:"html_url"`
|
|
Description string `json:"description"`
|
|
Fork bool `json:"fork"`
|
|
URL string `json:"url"`
|
|
ForksURL string `json:"forks_url"`
|
|
KeysURL string `json:"keys_url"`
|
|
CollaboratorsURL string `json:"collaborators_url"`
|
|
TeamsURL string `json:"teams_url"`
|
|
HooksURL string `json:"hooks_url"`
|
|
IssueEventsURL string `json:"issue_events_url"`
|
|
EventsURL string `json:"events_url"`
|
|
AssigneesURL string `json:"assignees_url"`
|
|
BranchesURL string `json:"branches_url"`
|
|
TagsURL string `json:"tags_url"`
|
|
BlobsURL string `json:"blobs_url"`
|
|
GitTagsURL string `json:"git_tags_url"`
|
|
GitRefsURL string `json:"git_refs_url"`
|
|
TreesURL string `json:"trees_url"`
|
|
StatusesURL string `json:"statuses_url"`
|
|
LanguagesURL string `json:"languages_url"`
|
|
StargazersURL string `json:"stargazers_url"`
|
|
ContributorsURL string `json:"contributors_url"`
|
|
SubscribersURL string `json:"subscribers_url"`
|
|
SubscriptionURL string `json:"subscription_url"`
|
|
CommitsURL string `json:"commits_url"`
|
|
GitCommitsURL string `json:"git_commits_url"`
|
|
CommentsURL string `json:"comments_url"`
|
|
IssueCommentURL string `json:"issue_comment_url"`
|
|
ContentsURL string `json:"contents_url"`
|
|
CompareURL string `json:"compare_url"`
|
|
MergesURL string `json:"merges_url"`
|
|
ArchiveURL string `json:"archive_url"`
|
|
DownloadsURL string `json:"downloads_url"`
|
|
IssuesURL string `json:"issues_url"`
|
|
PullsURL string `json:"pulls_url"`
|
|
MilestonesURL string `json:"milestones_url"`
|
|
NotificationsURL string `json:"notifications_url"`
|
|
LabelsURL string `json:"labels_url"`
|
|
ReleasesURL string `json:"releases_url"`
|
|
CreatedAt int64 `json:"created_at"`
|
|
UpdatedAt time.Time `json:"updated_at"`
|
|
PushedAt int64 `json:"pushed_at"`
|
|
GitURL string `json:"git_url"`
|
|
SSHURL string `json:"ssh_url"`
|
|
CloneURL string `json:"clone_url"`
|
|
SvnURL string `json:"svn_url"`
|
|
Homepage *string `json:"homepage"`
|
|
Size int64 `json:"size"`
|
|
StargazersCount int64 `json:"stargazers_count"`
|
|
WatchersCount int64 `json:"watchers_count"`
|
|
Language *string `json:"language"`
|
|
HasIssues bool `json:"has_issues"`
|
|
HasDownloads bool `json:"has_downloads"`
|
|
HasWiki bool `json:"has_wiki"`
|
|
HasPages bool `json:"has_pages"`
|
|
ForksCount int64 `json:"forks_count"`
|
|
MirrorURL *string `json:"mirror_url"`
|
|
OpenIssuesCount int64 `json:"open_issues_count"`
|
|
Forks int64 `json:"forks"`
|
|
OpenIssues int64 `json:"open_issues"`
|
|
Watchers int64 `json:"watchers"`
|
|
DefaultBranch string `json:"default_branch"`
|
|
Stargazers int64 `json:"stargazers"`
|
|
MasterBranch string `json:"master_branch"`
|
|
} `json:"repository"`
|
|
Pusher struct {
|
|
Name string `json:"name"`
|
|
Email string `json:"email"`
|
|
} `json:"pusher"`
|
|
Sender struct {
|
|
Login string `json:"login"`
|
|
ID int64 `json:"id"`
|
|
NodeID string `json:"node_id"`
|
|
AvatarURL string `json:"avatar_url"`
|
|
GravatarID string `json:"gravatar_id"`
|
|
URL string `json:"url"`
|
|
HTMLURL string `json:"html_url"`
|
|
FollowersURL string `json:"followers_url"`
|
|
FollowingURL string `json:"following_url"`
|
|
GistsURL string `json:"gists_url"`
|
|
StarredURL string `json:"starred_url"`
|
|
SubscriptionsURL string `json:"subscriptions_url"`
|
|
OrganizationsURL string `json:"organizations_url"`
|
|
ReposURL string `json:"repos_url"`
|
|
EventsURL string `json:"events_url"`
|
|
ReceivedEventsURL string `json:"received_events_url"`
|
|
Type string `json:"type"`
|
|
SiteAdmin bool `json:"site_admin"`
|
|
} `json:"sender"`
|
|
Installation struct {
|
|
ID int `json:"id"`
|
|
} `json:"installation"`
|
|
}
|
|
|
|
type RepositoryPayload struct {
|
|
Action string `json:"action"`
|
|
Repository struct {
|
|
ID int64 `json:"id"`
|
|
NodeID string `json:"node_id"`
|
|
Name string `json:"name"`
|
|
FullName string `json:"full_name"`
|
|
Owner struct {
|
|
Login string `json:"login"`
|
|
ID int64 `json:"id"`
|
|
NodeID string `json:"node_id"`
|
|
AvatarURL string `json:"avatar_url"`
|
|
GravatarID string `json:"gravatar_id"`
|
|
URL string `json:"url"`
|
|
HTMLURL string `json:"html_url"`
|
|
FollowersURL string `json:"followers_url"`
|
|
FollowingURL string `json:"following_url"`
|
|
GistsURL string `json:"gists_url"`
|
|
StarredURL string `json:"starred_url"`
|
|
SubscriptionsURL string `json:"subscriptions_url"`
|
|
OrganizationsURL string `json:"organizations_url"`
|
|
ReposURL string `json:"repos_url"`
|
|
EventsURL string `json:"events_url"`
|
|
ReceivedEventsURL string `json:"received_events_url"`
|
|
Type string `json:"type"`
|
|
SiteAdmin bool `json:"site_admin"`
|
|
} `json:"owner"`
|
|
Private bool `json:"private"`
|
|
HTMLURL string `json:"html_url"`
|
|
Description string `json:"description"`
|
|
Fork bool `json:"fork"`
|
|
URL string `json:"url"`
|
|
ForksURL string `json:"forks_url"`
|
|
KeysURL string `json:"keys_url"`
|
|
CollaboratorsURL string `json:"collaborators_url"`
|
|
TeamsURL string `json:"teams_url"`
|
|
HooksURL string `json:"hooks_url"`
|
|
IssueEventsURL string `json:"issue_events_url"`
|
|
EventsURL string `json:"events_url"`
|
|
AssigneesURL string `json:"assignees_url"`
|
|
BranchesURL string `json:"branches_url"`
|
|
TagsURL string `json:"tags_url"`
|
|
BlobsURL string `json:"blobs_url"`
|
|
GitTagsURL string `json:"git_tags_url"`
|
|
GitRefsURL string `json:"git_refs_url"`
|
|
TreesURL string `json:"trees_url"`
|
|
StatusesURL string `json:"statuses_url"`
|
|
LanguagesURL string `json:"languages_url"`
|
|
StargazersURL string `json:"stargazers_url"`
|
|
ContributorsURL string `json:"contributors_url"`
|
|
SubscribersURL string `json:"subscribers_url"`
|
|
SubscriptionURL string `json:"subscription_url"`
|
|
CommitsURL string `json:"commits_url"`
|
|
GitCommitsURL string `json:"git_commits_url"`
|
|
CommentsURL string `json:"comments_url"`
|
|
IssueCommentURL string `json:"issue_comment_url"`
|
|
ContentsURL string `json:"contents_url"`
|
|
CompareURL string `json:"compare_url"`
|
|
MergesURL string `json:"merges_url"`
|
|
ArchiveURL string `json:"archive_url"`
|
|
DownloadsURL string `json:"downloads_url"`
|
|
IssuesURL string `json:"issues_url"`
|
|
PullsURL string `json:"pulls_url"`
|
|
MilestonesURL string `json:"milestones_url"`
|
|
NotificationsURL string `json:"notifications_url"`
|
|
LabelsURL string `json:"labels_url"`
|
|
ReleasesURL string `json:"releases_url"`
|
|
CreatedAt time.Time `json:"created_at"`
|
|
UpdatedAt time.Time `json:"updated_at"`
|
|
PushedAt time.Time `json:"pushed_at"`
|
|
GitURL string `json:"git_url"`
|
|
SSHURL string `json:"ssh_url"`
|
|
CloneURL string `json:"clone_url"`
|
|
SvnURL string `json:"svn_url"`
|
|
Homepage *string `json:"homepage"`
|
|
Size int64 `json:"size"`
|
|
StargazersCount int64 `json:"stargazers_count"`
|
|
WatchersCount int64 `json:"watchers_count"`
|
|
Language *string `json:"language"`
|
|
HasIssues bool `json:"has_issues"`
|
|
HasDownloads bool `json:"has_downloads"`
|
|
HasWiki bool `json:"has_wiki"`
|
|
HasPages bool `json:"has_pages"`
|
|
ForksCount int64 `json:"forks_count"`
|
|
MirrorURL *string `json:"mirror_url"`
|
|
OpenIssuesCount int64 `json:"open_issues_count"`
|
|
Forks int64 `json:"forks"`
|
|
OpenIssues int64 `json:"open_issues"`
|
|
Watchers int64 `json:"watchers"`
|
|
DefaultBranch string `json:"default_branch"`
|
|
} `json:"repository"`
|
|
Organization struct {
|
|
Login string `json:"login"`
|
|
ID int64 `json:"id"`
|
|
NodeID string `json:"node_id"`
|
|
URL string `json:"url"`
|
|
ReposURL string `json:"repos_url"`
|
|
EventsURL string `json:"events_url"`
|
|
MembersURL string `json:"members_url"`
|
|
PublicMembersURL string `json:"public_members_url"`
|
|
AvatarURL string `json:"avatar_url"`
|
|
} `json:"organization"`
|
|
Sender struct {
|
|
Login string `json:"login"`
|
|
ID int64 `json:"id"`
|
|
NodeID string `json:"node_id"`
|
|
AvatarURL string `json:"avatar_url"`
|
|
GravatarID string `json:"gravatar_id"`
|
|
URL string `json:"url"`
|
|
HTMLURL string `json:"html_url"`
|
|
FollowersURL string `json:"followers_url"`
|
|
FollowingURL string `json:"following_url"`
|
|
GistsURL string `json:"gists_url"`
|
|
StarredURL string `json:"starred_url"`
|
|
SubscriptionsURL string `json:"subscriptions_url"`
|
|
OrganizationsURL string `json:"organizations_url"`
|
|
ReposURL string `json:"repos_url"`
|
|
EventsURL string `json:"events_url"`
|
|
ReceivedEventsURL string `json:"received_events_url"`
|
|
Type string `json:"type"`
|
|
SiteAdmin bool `json:"site_admin"`
|
|
} `json:"sender"`
|
|
}
|
|
|
|
type OnPayload func(payload payload, gw *GithubWebhook) error
|
|
|
|
type GithubWebhook struct {
|
|
pushEvents map[int64][]OnPayload
|
|
repositoryEvents map[int64][]OnPayload
|
|
|
|
repoNames map[int64]string
|
|
}
|
|
|
|
func (gw *GithubWebhook) AddPushEvent(repoID int64, function OnPayload) {
|
|
gw.pushEvents[repoID] = append(gw.pushEvents[repoID], function)
|
|
}
|
|
|
|
func (gw *GithubWebhook) AddRepositoryEvent(repoID int64, function OnPayload) {
|
|
gw.repositoryEvents[repoID] = append(gw.repositoryEvents[repoID], function)
|
|
}
|
|
|
|
func (gw *GithubWebhook) OnWebhook(w http.ResponseWriter, r *http.Request) {
|
|
event := r.Header.Get("X-GitHub-Event")
|
|
if event == "" {
|
|
return
|
|
}
|
|
|
|
payload, err := ioutil.ReadAll(r.Body)
|
|
if err != nil || len(payload) == 0 {
|
|
return
|
|
}
|
|
|
|
// when a secret is given, this checks if the webhook and the local secret are even
|
|
if secret != "" {
|
|
signature := r.Header.Get("X-Hub-Signature")
|
|
if len(signature) == 0 {
|
|
return
|
|
}
|
|
mac := hmac.New(sha1.New, []byte(secret))
|
|
_, _ = mac.Write(payload)
|
|
expectedMAC := hex.EncodeToString(mac.Sum(nil))
|
|
|
|
if !hmac.Equal([]byte(signature[5:]), []byte(expectedMAC)) {
|
|
return
|
|
}
|
|
}
|
|
|
|
switch event {
|
|
case "push":
|
|
var pushPayload PushPayload
|
|
if err = json.Unmarshal(payload, &pushPayload); err != nil {
|
|
return
|
|
}
|
|
if events, ok := gw.pushEvents[pushPayload.Repository.ID]; ok {
|
|
for _, event := range events {
|
|
if err := event(pushPayload, gw); err != nil {
|
|
panic(err)
|
|
}
|
|
}
|
|
}
|
|
case "repository":
|
|
var repositoryPayload RepositoryPayload
|
|
if err = json.Unmarshal(payload, &repositoryPayload); err != nil {
|
|
return
|
|
}
|
|
if events, ok := gw.repositoryEvents[repositoryPayload.Repository.ID]; ok {
|
|
for _, event := range events {
|
|
if err := event(repositoryPayload, gw); err != nil {
|
|
panic(err)
|
|
}
|
|
}
|
|
}
|
|
default:
|
|
return
|
|
}
|
|
}
|
|
|
|
func main() {
|
|
r := mux.NewRouter()
|
|
gw := &GithubWebhook{pushEvents: make(map[int64][]OnPayload), repositoryEvents: make(map[int64][]OnPayload), repoNames: make(map[int64]string)}
|
|
gw.AddPushEvent(364735060, updateLocal)
|
|
gw.AddRepositoryEvent(364735060, renameLocal)
|
|
// NOTE: To find out the id of a repository, go to the repo page, open the browser / developer console and execute `document.head.querySelector("meta[name=octolytics-dimension-repository_id]").content`
|
|
|
|
r.HandleFunc("/github_webhook", gw.OnWebhook).Methods("POST")
|
|
|
|
http.ListenAndServe(":8080", r)
|
|
}
|
|
|
|
func checkIfLocalPresent(gw *GithubWebhook, id int64, name string, cloneUrl string) error {
|
|
if _, ok := gw.repoNames[id]; !ok {
|
|
files, err := ioutil.ReadDir(gitTree)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, file := range files {
|
|
if file.IsDir() && file.Name() == name {
|
|
gw.repoNames[id] = name
|
|
return nil
|
|
}
|
|
}
|
|
|
|
cmd := exec.Command("git",
|
|
"clone", cloneUrl,
|
|
filepath.Join(gitTree, name))
|
|
if err = cmd.Run(); err != nil {
|
|
return err
|
|
}
|
|
gw.repoNames[id] = name
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func renameLocal(payload2 payload, gw *GithubWebhook) error {
|
|
repoPayload := payload2.(RepositoryPayload)
|
|
if repoPayload.Action == "rename" {
|
|
if err := checkIfLocalPresent(gw, repoPayload.Repository.ID, repoPayload.Repository.Name, repoPayload.Repository.CloneURL); err != nil {
|
|
return err
|
|
}
|
|
|
|
os.Rename(filepath.Join(gitTree, gw.repoNames[repoPayload.Repository.ID]), filepath.Join(gitTree, repoPayload.Repository.Name))
|
|
gw.repoNames[repoPayload.Repository.ID] = repoPayload.Repository.Name
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func updateLocal(payload2 payload, gw *GithubWebhook) error {
|
|
pushPayload := payload2.(PushPayload)
|
|
|
|
if err := checkIfLocalPresent(gw, pushPayload.Repository.ID, pushPayload.Repository.Name, pushPayload.Repository.CloneURL); err != nil {
|
|
return err
|
|
}
|
|
|
|
return exec.Command("git",
|
|
"-C", filepath.Join(gitTree, pushPayload.Repository.Name),
|
|
"pull",
|
|
"origin").Run()
|
|
}
|