Create bounties for existing issues when enabling a repository

* Create bounties for existing bounty-tagged issues when a repository is
toggled on
* added commiteth.bounties ns for sharing code

Fixes: #12
This commit is contained in:
Teemu Patja 2017-01-22 10:59:40 +02:00
parent f89a83ea32
commit 985b72754d
No known key found for this signature in database
GPG Key ID: F5B7035E6580FD4C
5 changed files with 108 additions and 55 deletions

View File

@ -0,0 +1,46 @@
(ns commiteth.bounties
(:require [commiteth.db.issues :as issues]
[commiteth.db.users :as users]
[commiteth.db.repositories :as repos]
[commiteth.eth.core :as eth]
[commiteth.github.core :as github]
[commiteth.eth.core :as eth]
[clojure.tools.logging :as log]))
(def ^:const label-name "bounty")
(defn has-bounty-label?
[issue]
(let [labels (:labels issue)]
(some #(= label-name (:name %)) labels)))
(defn add-bounty-for-issue [repo-map issue]
(log/debug "add-bounty-for-issue" issue)
(let [{issue-id :id
issue-number :number
issue-title :title} issue
{repo :repo
repo-id :repo_id
user :login} repo-map
created-issue (issues/create repo-id issue-id issue-number issue-title)
repo-owner (:address (users/get-repo-owner repo-id))]
(log/info (format "Issue %s/%s/%s labeled as bounty" user repo issue-number))
(if (= 1 created-issue)
(let [transaction-hash (eth/deploy-contract repo-owner)]
(log/info "Contract deployed, transaction-hash:" transaction-hash )
(issues/update-transaction-hash issue-id transaction-hash))
(log/debug "Issue already exists in DB, ignoring"))))
(defn add-bounties-for-existing-issues [repo-map]
(let [{repo :repo
user :login} repo-map
issues (github/get-issues user repo)
bounty-issues (filter has-bounty-label? issues)]
(log/debug bounty-issues)
(log/debug "adding bounties for"
(count bounty-issues) " existing issues")
(doall
(map (partial add-bounty-for-issue repo-map) bounty-issues))))

View File

@ -10,7 +10,7 @@
[mount.core :as mount])
(:gen-class))
(def cli-options
(def ^:const cli-options
[["-p" "--port PORT" "Port number"
:parse-fn #(Integer/parseInt %)]])
@ -58,4 +58,3 @@ repl-server
(System/exit 0))
:else
(start-app args)))

View File

@ -160,7 +160,8 @@
(defn update-comment
[user repo comment-id issue-number balance]
(let [comment (generate-comment user repo issue-number balance)]
(log/debug (str "Updating " user "/" repo "/" issue-number " comment #" comment-id " with contents: " comment))
(log/debug (str "Updating " user "/" repo "/" issue-number
" comment #" comment-id " with contents: " comment))
(let [req (make-patch-request "repos/%s/%s/issues/comments/%s"
[user repo comment-id]
(assoc (self-auth-params) :body comment))]
@ -170,6 +171,11 @@
[user repo issue-number]
(issues/specific-issue user repo issue-number (self-auth-params)))
(defn get-issues
[user repo]
(issues/issues user repo))
(defn get-issue-events
[user repo issue-id]
(issues/issue-events user repo issue-id (self-auth-params)))

View File

@ -7,7 +7,8 @@
[buddy.auth :refer [authenticated?]]
[commiteth.db.users :as users]
[commiteth.db.repositories :as repositories]
[commiteth.db.bounties :as bounties]
[commiteth.db.bounties :as bounties-db]
[commiteth.bounties :as bounties]
[commiteth.github.core :as github]
[clojure.tools.logging :as log]
[commiteth.eth.core :as eth]))
@ -56,13 +57,15 @@
:current-user user
(ok (repositories/get-enabled (:id user))))
(GET "/bounties" []
(ok (bounties/list-all-bounties)))
(ok (bounties-db/list-all-bounties)))
(POST "/bounty/:issue{[0-9]{1,9}}/payout" {:keys [params]}
:auth-rules authenticated?
:current-user user
(let [{issue :issue
payout-hash :payout-hash} params
result (bounties/update-payout-hash (Integer/parseInt issue) payout-hash)]
result (bounties-db/update-payout-hash
(Integer/parseInt issue)
payout-hash)]
(if (= 1 result)
(ok)
(internal-server-error))))
@ -72,7 +75,7 @@
(ok (map #(conj % (let [balance (:balance %)]
{:balance-eth (eth/hex->eth balance 6)
:balance-wei (eth/hex->big-integer balance)}))
(bounties/list-owner-bounties (:id user)))))
(bounties-db/list-owner-bounties (:id user)))))
(POST "/repository/toggle" {:keys [params]}
:auth-rules authenticated?
:current-user user
@ -85,10 +88,11 @@
(repositories/create (merge params {:user_id user-id}))
(repositories/toggle repo-id))]
(if (:enabled result)
;; @todo: do we really want to make this call at this moment?
(let [created-hook (github/add-webhook repo token)]
(log/debug "Created webhook:" created-hook)
(future
(github/create-label repo token)
(repositories/update-hook-id repo-id (:id created-hook)))
(repositories/update-hook-id repo-id (:id created-hook))
(bounties/add-bounties-for-existing-issues result)))
(github/remove-webhook repo (:hook_id result) token))
result)))))

View File

@ -4,13 +4,12 @@
[commiteth.db.pull-requests :as pull-requests]
[commiteth.db.issues :as issues]
[commiteth.db.users :as users]
[commiteth.eth.core :as eth]
[commiteth.bounties :as bounties]
[ring.util.http-response :refer [ok]]
[clojure.string :refer [join]]
[clojure.tools.logging :as log])
(:import [java.lang Integer]))
(def label-name "bounty")
(defn find-issue-event
[events type owner]
@ -19,29 +18,34 @@
(= type (:event %)))
events)))
(defn labeled-as-bounty?
[action issue]
(and
(= "labeled" action)
(= bounties/label-name (get-in issue [:label :name]))))
(defn find-commit-id
[user repo issue-number event-types]
(log/debug "find-commit-id" user repo issue-number event-types)
(some identity (map #(->
(github/get-issue-events user repo issue-number)
(find-issue-event % user)
:commit_id)
event-types)))
(defn handle-issue-labeled
[issue]
(let [{repo-id :id
repo :name
{user :login} :owner} (:repository issue)
{issue-id :id
issue-number :number
issue-title :title} (:issue issue)
created-issue (issues/create repo-id issue-id issue-number issue-title)
repo-owner (:address (users/get-repo-owner repo-id))]
(log/debug (format "Issue %s/%s/%s labeled as bounty" user repo issue-number))
(when (= 1 created-issue)
(issues/update-transaction-hash issue-id (eth/deploy-contract repo-owner)))))
[webhook-payload]
(let [{issue :issue} webhook-payload
{repo-id :id
repo-name :name
{login :login} :owner} (:repository webhook-payload)
repo-map {:repo repo-name :login login :repo_id repo-id}]
(bounties/add-bounty-for-issue repo-map issue)))
(defn handle-issue-closed
;; TODO: does not work in case the issue is closed on github web ui
[{{{user :login} :owner repo :name} :repository
{issue-id :id issue-number :number} :issue}]
(future
@ -49,7 +53,7 @@
(log/debug (format "Issue %s/%s/%s closed with commit %s" user repo issue-number commit-id))
(issues/close commit-id issue-id))))
(def keywords
(def ^:const keywords
[#"(?i)close\s+#(\d+)"
#"(?i)closes\s+#(\d+)"
#"(?i)closed\s+#(\d+)"
@ -70,16 +74,12 @@
(catch NumberFormatException _)))
(re-seq % pr-body)) keywords))
(defn has-bounty-label?
[issue]
(let [labels (:labels issue)]
(some #(= label-name (:name %)) labels)))
(defn validate-issue-number
"Checks if an issue has a bounty label attached and returns its number"
[user repo issue-number]
(when-let [issue (github/get-issue user repo issue-number)]
(when (has-bounty-label? issue)
(when (bounties/has-bounty-label? issue)
issue-number)))
(defn handle-pull-request-closed
@ -92,6 +92,7 @@
id :id
pr-number :number
pr-body :body} :pull_request}]
(log/debug "handle-pull-request-closed" owner repo repo-id login pr-body)
(future
(let [commit-id (find-commit-id owner repo pr-number ["merged"])
issue-number (->>
@ -99,6 +100,7 @@
(first)
(validate-issue-number owner repo))
m {:commit_id commit-id :issue_number issue-number}]
(log/debug "handle-pull-request-closed" commit-id issue-number)
(when (or commit-id issue-number)
(log/debug (format "Pull request %s/%s/%s closed with reference to %s"
login repo pr-number
@ -110,22 +112,18 @@
:user_id user-id}))
(users/create-user user-id login name nil nil)))))
(defn labeled-as-bounty?
[action issue]
(and
(= "labeled" action)
(= label-name (get-in issue [:label :name]))))
(defn handle-issue
[issue]
(when-let [action (:action issue)]
(when (labeled-as-bounty? action issue)
(handle-issue-labeled issue))
[webhook-payload]
(when-let [action (:action webhook-payload)]
(log/debug "handle-issue")
(when (labeled-as-bounty? action webhook-payload)
(handle-issue-labeled webhook-payload))
(when (and
(= "closed" action)
(has-bounty-label? (:issue issue)))
(handle-issue-closed issue)))
(ok (str issue)))
(bounties/has-bounty-label? (:issue webhook-payload)))
(handle-issue-closed webhook-payload)))
(ok (str webhook-payload)))
(defn handle-pull-request
[pull-request]