-
Notifications
You must be signed in to change notification settings - Fork 0
/
core.clj
169 lines (151 loc) · 6.28 KB
/
core.clj
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
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
(ns io.axrs.cli.circle-ci.core
(:require
[cli-matic.core :as cli]
[com.rpl.specter :as sp]
[io.axrs.cli-tools.ansi :as ansi]
[io.axrs.cli-tools.env :as env]
[io.axrs.cli-tools.http :as http]
[io.axrs.cli-tools.print :as print]
[io.axrs.cli-tools.time :as time]
[io.jesi.backpack :as bp]
[slingshot.slingshot :refer [try+ throw+]]
[taoensso.encore :refer [assoc-some]]))
(defonce ^:private project-urls (atom {}))
(defn- token []
(env/get "CIRCLECI_TOKEN"))
(defn- get-projects []
(try+
(:body (http/get-json "https://circleci.com/api/v1.1/projects"
{:circle-token (token)}))
(catch http/client-error? {:as response}
(http/print-error response "Please ensure your CIRCLE_CI token is correct")
(throw+))))
(defn- get-project-url [project]
(if-let [url (get @project-urls project)]
url
(let [urls (reduce
(fn [m {:keys [username vcs-type reponame]}]
(assoc m reponame (str "https://circleci.com/api/v1.1/project/" vcs-type \/ username \/ reponame)))
{}
(get-projects))]
(reset! project-urls urls)
(get @project-urls project))))
(defn- get-recent [{:keys [limit project]}]
(try+
(when-let [url (if project
(get-project-url project)
"https://circleci.com/api/v1.1/recent-builds")]
(:body (http/get-json url
(assoc-some {:circle-token (token)}
:limit (some-> limit (min 100))))))
(catch http/client-error? {:as response}
(http/print-error response "Please ensure your CIRCLE_CI token is correct")
(throw+))))
(defn- utc-dates->local [{:keys [start-time stop-time queued-at build-url workflow-url] :as result} now]
(let [[start-time stop-time queued-at] (map time/->date-time [start-time stop-time queued-at])]
(assoc result
:build-link (ansi/hyperlink build-url "Build")
:workflow-link (ansi/hyperlink workflow-url "Workflow")
:run-time (some-> start-time (time/humanized-interval (or stop-time now)))
:start-time (time/->wall-str start-time)
:stop-time (time/->wall-str stop-time)
:queued-at (time/->wall-str queued-at))))
(defn- clean-result [now {{:keys [workflow-id] :as workflows} :workflows
:as result}]
(-> result
(dissoc :workflows)
(merge workflows)
(assoc :workflow-url (format "https://circleci.com/workflow-run/%s" workflow-id))
(utc-dates->local now)))
(defn- clean-results [results]
(let [now (time/now)]
(->> results
(map (partial clean-result now))
(map (bp/partial-right dissoc :circle-yml)))))
(defn- colorize-status [status]
(let [color-fn (cond
(= "failed" status) ansi/red
(= "success" status) ansi/green
(= "running" status) ansi/blue
:else identity)]
(color-fn status)))
(defn- colorize [{:as row}]
(sp/transform [:status some?] colorize-status row))
(defn- key-match? [k v]
(if v
(comp (partial = v) k)
any?))
(defn- filter-by-params [{:keys [branch job-name]} results]
(let [job-name? (key-match? :job-name job-name)
branch? (key-match? :branch branch)]
(filter #(and (branch? %)
(job-name? %))
results)))
(defonce ^:private default-cols
[:status :queued-at :run-time :reponame :branch :job-name :subject :build-link :workflow-link])
(defn- recent [{:keys [project cols extra-cols watch]
:or {cols default-cols}
:as params}]
(let [watch? (some-> watch pos?)
cols (if project (remove (bp/p= :reponame) cols) cols)]
(when watch?
(print/clear-screen))
(->> (get-recent params)
clean-results
(filter-by-params params)
(print/table colorize (concat cols extra-cols)))
(when watch?
(Thread/sleep (* 1000 watch))
(recur params))))
(defn- projects [{:as params}]
(->> (get-projects)
(sort-by :reponame)
(print/table colorize [:username :reponame :vcs-type :vcs-url])))
(defn- cols [{:as params}]
(->> (get-recent {:limit 1})
first
keys
(concat [:build-link :workflow-link])
sort
clojure.pprint/pprint))
(defonce ^:private cli-config
{:app {:command "circle-ci"
:description "A CircleCI CLI"
:version "0.2.0"}
:commands [
{:command "cols"
:description ["Prints a list of columns available for use in tabular outputs"]
:runs cols}
{:command "projects"
:description ["Prints a list of projects followed by the current CircleCI User"]
:runs projects}
{:command "recent"
:short "r"
:description ["Prints a tabular list of recent jobs"]
:opts [{:option "limit"
:as "The total number of jobs to fetch before filtering (max is 100)."
:type :int
:default 25}
{:option "project"
:as "Filters the results to a specific reponame"
:type :string}
{:option "job-name"
:as "Filters the results to a specific CircleCI Job name"
:type :string}
{:option "branch"
:as "Filters the results to a specific branch"
:type :string}
{:option "cols"
:as "Columns to print"
:type :edn}
{:option "extra-cols"
:as "Extra columns to print (appended to the end of cols)"
:type :edn}
{:option "watch"
:as "The number of seconds to wait before refresh"
:type :int}]
:runs recent}]})
(defn -main [& args]
(if-not (token)
(print/redln "No CIRCLECI_TOKEN defined.")
(cli/run-cmd args cli-config)))