forked from harness/gitness
-
Notifications
You must be signed in to change notification settings - Fork 0
/
hook.go
151 lines (133 loc) · 4.04 KB
/
hook.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
package handler
import (
"log"
"net/http"
"regexp"
"github.com/drone/drone/plugin/remote"
"github.com/drone/drone/server/datastore"
"github.com/drone/drone/server/worker"
"github.com/drone/drone/shared/build/script"
"github.com/drone/drone/shared/httputil"
"github.com/drone/drone/shared/model"
"github.com/goji/context"
"github.com/zenazn/goji/web"
)
// PostHook accepts a post-commit hook and parses the payload
// in order to trigger a build. The payload is specified to the
// remote system (ie GitHub) and will therefore get parsed by
// the appropriate remote plugin.
//
// GET /api/hook/:host
//
func PostHook(c web.C, w http.ResponseWriter, r *http.Request) {
var ctx = context.FromC(c)
var host = c.URLParams["host"]
var token = c.URLParams["token"]
var remote = remote.Lookup(host)
if remote == nil {
w.WriteHeader(http.StatusNotFound)
return
}
// parse the hook payload
hook, err := remote.ParseHook(r)
if err != nil {
log.Printf("Unable to parse hook. %s\n", err)
w.WriteHeader(http.StatusBadRequest)
return
}
if hook == nil {
w.WriteHeader(http.StatusOK)
return
}
// in some cases we have neither a hook nor error. An example
// would be GitHub sending a ping request to the URL, in which
// case we'll just exit quiely with an 'OK'
shouldSkip, _ := regexp.MatchString(`\[(?i:ci *skip|skip *ci)\]`, hook.Message)
if hook == nil || shouldSkip {
w.WriteHeader(http.StatusOK)
return
}
// fetch the repository from the database
repo, err := datastore.GetRepoName(ctx, remote.GetHost(), hook.Owner, hook.Repo)
if err != nil {
w.WriteHeader(http.StatusNotFound)
return
}
// each hook contains a token to verify the sender. If the token
// is not provided or does not match, exit
if len(repo.Token) == 0 || repo.Token != token {
log.Printf("Rejected post commit hook for %s. Token mismatch\n", repo.Name)
w.WriteHeader(http.StatusUnauthorized)
return
}
if repo.Active == false ||
(repo.PostCommit == false && len(hook.PullRequest) == 0) ||
(repo.PullRequest == false && len(hook.PullRequest) != 0) {
w.WriteHeader(http.StatusNotFound)
return
}
// fetch the user from the database that owns this repo
user, err := datastore.GetUser(ctx, repo.UserID)
if err != nil {
w.WriteHeader(http.StatusNotFound)
return
}
// Request a new token and update
user_token, err := remote.GetToken(user)
if user_token != nil {
user.Access = user_token.AccessToken
user.Secret = user_token.RefreshToken
user.TokenExpiry = user_token.Expiry
datastore.PutUser(ctx, user)
} else if err != nil {
log.Printf("Unable to refresh token. %s\n", err)
w.WriteHeader(http.StatusBadRequest)
return
}
// fetch the .drone.yml file from the database
yml, err := remote.GetScript(user, repo, hook)
if err != nil {
log.Printf("Unable to fetch .drone.yml file. %s\n", err)
w.WriteHeader(http.StatusBadRequest)
return
}
// verify the commit hooks branch matches the list of approved
// branches (unless it is a pull request). Note that we don't really
// care if parsing the yaml fails here.
s, _ := script.ParseBuild(string(yml))
if len(hook.PullRequest) == 0 && !s.MatchBranch(hook.Branch) {
w.WriteHeader(http.StatusOK)
return
}
commit := model.Commit{
RepoID: repo.ID,
Status: model.StatusEnqueue,
Sha: hook.Sha,
Branch: hook.Branch,
PullRequest: hook.PullRequest,
Timestamp: hook.Timestamp,
Message: hook.Message,
Config: string(yml),
}
commit.SetAuthor(hook.Author)
// inserts the commit into the database
if err := datastore.PostCommit(ctx, &commit); err != nil {
log.Printf("Unable to persist commit %s@%s. %s\n", commit.Sha, commit.Branch, err)
w.WriteHeader(http.StatusBadRequest)
return
}
owner, err := datastore.GetUser(ctx, repo.UserID)
if err != nil {
log.Printf("Unable to retrieve repository owner. %s.\n", err)
w.WriteHeader(http.StatusBadRequest)
return
}
// drop the items on the queue
go worker.Do(ctx, &worker.Work{
User: owner,
Repo: repo,
Commit: &commit,
Host: httputil.GetURL(r),
})
w.WriteHeader(http.StatusOK)
}