46e610557aac3212cf852a63fd21ef94cc4f8769
[nit.git] / contrib / github_merge.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 # Query the Github PR API to perform a merge
16 module github_merge
17
18 import github::github_curl
19 import template
20
21 redef class Object
22 # Factorize cast
23 fun json_as_a: JsonArray do return self.as(JsonArray)
24 # Factorize cast
25 fun json_as_map: JsonObject do return self.as(JsonObject)
26 end
27
28 redef class GithubCurl
29 # Get a given pull request (PR)
30 fun getpr(number: Int): JsonObject
31 do
32 var pr = get_and_check("https://api.github.com/repos/nitlang/nit/pulls/{number}")
33 var prm = pr.json_as_map
34 var sha = prm["head"].json_as_map["sha"].to_s
35 var statuses = get_and_check("https://api.github.com/repos/nitlang/nit/statuses/{sha}")
36 prm["statuses"] = statuses
37 print "{prm["title"].to_s}: by {prm["user"].json_as_map["login"].to_s} (# {prm["number"].to_s})"
38 var mergeable = prm["mergeable"]
39 if mergeable != null then
40 print "\tmergeable: {mergeable.to_s}"
41 else
42 print "\tmergeable: unknown"
43 end
44 var st = prm["statuses"].json_as_a
45 if not st.is_empty then
46 print "\tstatus: {st[0].json_as_map["state"].to_s}"
47 else
48 print "\tstatus: not tested"
49 end
50 return prm
51 end
52
53 # Get reviewers of a PR
54 fun getrev(pr: JsonObject): Array[String]
55 do
56 var number = pr["number"].as(Int)
57 var user = pr["user"].json_as_map["login"].as(String)
58 var comments = new Array[nullable Object]
59 comments.add_all(get_and_check("https://api.github.com/repos/nitlang/nit/issues/{number}/comments").json_as_a)
60 comments.add_all(get_and_check("https://api.github.com/repos/nitlang/nit/pulls/{number}/comments").json_as_a)
61 var logins = new Array[String]
62 for c in comments do
63 var cm = c.json_as_map
64 var l = cm["user"].json_as_map["login"]
65 assert l isa String
66 if l != user and not logins.has(l) then logins.add(l)
67 end
68 var res = new Array[String]
69 for l in logins do
70 var u = get_and_check("https://api.github.com/users/{l}").json_as_map
71 if not u.has_key("name") or u["name"] == null or not u.has_key("email")or u["email"] == null then
72 print "No public name/email for user {l}"
73 continue
74 end
75 var r = "{u["name"].to_s} <{u["email"].to_s}>"
76 res.add r
77
78 end
79 return res
80 end
81
82 end
83
84 if "NIT_TESTING".environ == "true" then exit 0
85
86 var auth = get_github_oauth
87
88 if auth == "" then
89 print "Warning: no github oauth token, you can configure one with"
90 print " git config --add github.oauthtoken MYOAUTHTOKEN"
91 end
92
93 var curl = new GithubCurl(auth, "Merge-o-matic (nitlang/nit)")
94
95 if args.is_empty then
96 # Without args, list `ok_will_merge`
97 var x = curl.get_and_check("https://api.github.com/repos/nitlang/nit/issues?labels=ok_will_merge")
98 for y in x.json_as_a do
99 var number = y.json_as_map["number"].as(Int)
100 curl.getpr(number)
101 end
102 return
103 end
104
105 for arg in args do
106 # With a arg, merge the PR
107 var number = arg.to_i
108 var pr = curl.getpr(number)
109 var revs = curl.getrev(pr)
110
111 var mergemsg = new Template
112 mergemsg.add "Merge: {pr["title"].to_s}\n\n"
113 mergemsg.add "{pr["body"].to_s}\n\n"
114 mergemsg.add "Pull-Request: #{pr["number"].to_s}\n"
115 for r in revs do
116 mergemsg.add "Reviewed-by: {r}\n"
117 end
118 mergemsg.write_to_file("mergemsg")
119
120 var sha = pr["head"].json_as_map["sha"].as(String)
121 if system("git show -s --pretty=format:%h {sha}") != 0 then
122 print "Commit {sha} not in local repository; did you fetch github?"
123 return
124 end
125 if system("git merge-base --is-ancestor {sha} HEAD") == 0 then
126 print "Is already merged."
127 continue
128 end
129 if system("git merge --no-ff --no-commit {sha}") != 0 then
130 system("cp mergemsg `git rev-parse --git-dir`/MERGE_MSG")
131 print "Problem during merge... Let's do the commit manually."
132 return
133 end
134 system("git commit -F mergemsg")
135 print "The merge is made"
136 mergemsg.write_to(stdout)
137 end
138