circle-cli 0.0.2 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,83 @@
1
+ require 'circle/cli/model'
2
+ require 'circle/cli/step'
3
+
4
+ module Circle
5
+ module CLI
6
+ class Build < Model
7
+ attr_reader :project
8
+
9
+ def initialize(project, build = {})
10
+ @project = project
11
+ super(build)
12
+ end
13
+
14
+ def preload
15
+ tests
16
+ details
17
+ self
18
+ end
19
+
20
+ def build_num
21
+ self[:build_num]
22
+ end
23
+
24
+ def finished?
25
+ !self[:outcome].nil?
26
+ end
27
+
28
+ def failed?
29
+ self[:outcome] == 'failed'
30
+ end
31
+
32
+ def status
33
+ self[:status].tr('_', ' ').capitalize
34
+ end
35
+
36
+ def subject
37
+ truncate self[:subject]
38
+ end
39
+
40
+ def color
41
+ color_for self[:status]
42
+ end
43
+
44
+ def formatted_start_time
45
+ format_time(self[:start_time]) || 'Not started'
46
+ end
47
+
48
+ def formatted_stop_time
49
+ format_time(self[:start_time]) || 'Not finished'
50
+ end
51
+
52
+ def cancel!
53
+ project.request CircleCi::Build, :cancel, build_num
54
+ end
55
+
56
+ def details
57
+ @details ||= project.request CircleCi::Build, :get, build_num
58
+ end
59
+
60
+ def steps
61
+ details['steps'].map { |step| Step.new(step) }
62
+ end
63
+
64
+ def tests
65
+ @tests ||= project.request(CircleCi::Build, :tests, build_num)['tests']
66
+ end
67
+
68
+ def passing_tests
69
+ tests.reject(&failure_filter)
70
+ end
71
+
72
+ def failing_tests
73
+ tests.select(&failure_filter)
74
+ end
75
+
76
+ private
77
+
78
+ def failure_filter
79
+ lambda { |t| t['result'] == 'failure' }
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,43 @@
1
+ module Circle
2
+ module CLI
3
+ class Model
4
+ TIME_FORMAT = '%-b %-e, %-l:%M %p'
5
+
6
+ STATUS_COLORS = {
7
+ green: %w(fixed success),
8
+ yellow: %w(running retried not_run queued scheduled not_running no_tests),
9
+ red: %w(canceled infrastructure_fail timedout failed)
10
+ }
11
+
12
+ attr_reader :attributes
13
+
14
+ def initialize(attributes = {})
15
+ @attributes = attributes
16
+ end
17
+
18
+ def [](key)
19
+ @attributes[key.to_s]
20
+ end
21
+
22
+ private
23
+
24
+ def format_time(value)
25
+ Time.parse(value).strftime(TIME_FORMAT) if value
26
+ rescue ArgumentError
27
+ end
28
+
29
+ def color_for(value)
30
+ case value
31
+ when *STATUS_COLORS[:green] then :green
32
+ when *STATUS_COLORS[:yellow] then :yellow
33
+ when *STATUS_COLORS[:red] then :red
34
+ else :blue
35
+ end
36
+ end
37
+
38
+ def truncate(str, len = 50)
39
+ str && str.length > len ? "#{str[0..(len - 4)]}..." : str
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,52 @@
1
+ require 'circleci'
2
+ require 'circle/cli/build'
3
+
4
+ module Circle
5
+ module CLI
6
+ class Project
7
+ attr_reader :repo
8
+
9
+ def initialize(repo)
10
+ @repo = repo
11
+ configure
12
+ end
13
+
14
+ def build!
15
+ request CircleCi::Project, :build_branch, repo.branch_name
16
+ end
17
+
18
+ def builds
19
+ @builds ||= to_builds(request(CircleCi::Project, :recent_builds_branch, repo.branch_name))
20
+ end
21
+
22
+ def recent_builds
23
+ @recent_builds ||= to_builds(request(CircleCi::Project, :recent_builds))
24
+ end
25
+
26
+ def latest
27
+ builds.first
28
+ end
29
+
30
+ def clear_cache!
31
+ @builds = nil
32
+ @recent_builds = nil
33
+ end
34
+
35
+ def request(klass, action, *args)
36
+ klass.send(action, repo.user_name, repo.project, *args).body
37
+ end
38
+
39
+ private
40
+
41
+ def configure
42
+ CircleCi.configure do |config|
43
+ config.token = repo.circle_token
44
+ end
45
+ end
46
+
47
+ def to_builds(arr)
48
+ arr.map { |build| Build.new(self, build) }
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,50 @@
1
+ require 'gitable'
2
+ require 'rugged'
3
+
4
+ module Circle
5
+ module CLI
6
+ class Repo
7
+ attr_reader :errors, :options
8
+
9
+ def initialize(options = {})
10
+ @options = options
11
+ end
12
+
13
+ def uri
14
+ Gitable::URI.parse(origin.url)
15
+ end
16
+
17
+ def user_name
18
+ uri.path.split('/').first
19
+ end
20
+
21
+ def project
22
+ uri.project_name
23
+ end
24
+
25
+ def branch_name
26
+ options.fetch 'branch' do
27
+ repo.head.name.sub(/^refs\/heads\//, '')
28
+ end
29
+ end
30
+
31
+ def circle_token
32
+ repo.config['circleci.token'] || ENV['CIRCLE_CLI_TOKEN']
33
+ end
34
+
35
+ def circle_token=(token)
36
+ repo.config['circleci.token'] = token
37
+ end
38
+
39
+ private
40
+
41
+ def repo
42
+ @repo ||= Rugged::Repository.new(options[:repo])
43
+ end
44
+
45
+ def origin
46
+ @origin ||= repo.remotes.find { |r| r.name == 'origin' }
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,26 @@
1
+ require 'circle/cli/model'
2
+
3
+ module Circle
4
+ module CLI
5
+ class Step < Model
6
+ def color
7
+ color_for self[:actions].first['status']
8
+ end
9
+
10
+ def duration
11
+ return unless ms = self[:run_time_millis]
12
+
13
+ hours = (ms / (1000 * 60 * 60)) % 24
14
+ minutes = (ms / (1000 * 60)) % 60
15
+ seconds = (ms / 1000) % 60
16
+
17
+ message = []
18
+ message << "#{hours}h" unless hours.zero?
19
+ message << "#{minutes}m" unless minutes.zero?
20
+ message << "#{seconds}s" unless seconds.zero?
21
+ message << "#{ms}ms" if message.empty?
22
+ message.join(' ')
23
+ end
24
+ end
25
+ end
26
+ end
@@ -1,5 +1,5 @@
1
1
  module Circle
2
- class CLI
3
- VERSION = '0.0.2'
2
+ module CLI
3
+ VERSION = '0.1.0'
4
4
  end
5
5
  end
@@ -0,0 +1,71 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: get
5
+ uri: https://circleci.com/api/v1/project/mtchavez/rb-array-sorting/5?circle-token=123
6
+ body:
7
+ encoding: US-ASCII
8
+ string: ''
9
+ headers:
10
+ accept:
11
+ - application/json
12
+ accept-encoding:
13
+ - gzip, deflate
14
+ content-type:
15
+ - application/json
16
+ user-agent:
17
+ - Ruby
18
+ response:
19
+ status:
20
+ code: 200
21
+ message: !binary |-
22
+ T0s=
23
+ headers:
24
+ !binary "Y29udGVudC1lbmNvZGluZw==":
25
+ - !binary |-
26
+ Z3ppcA==
27
+ !binary "Y29udGVudC10eXBl":
28
+ - !binary |-
29
+ YXBwbGljYXRpb24vanNvbjsgY2hhcnNldD11dGYtOA==
30
+ !binary "ZGF0ZQ==":
31
+ - !binary |-
32
+ V2VkLCAxNyBBcHIgMjAxMyAwNTowODozNCBHTVQ=
33
+ !binary "c2VydmVy":
34
+ - !binary |-
35
+ bmdpbngvMS4xLjE5
36
+ !binary "c2V0LWNvb2tpZQ==":
37
+ - !binary |-
38
+ cmluZy1zZXNzaW9uPTcwNDY1OWFhLTU1NmQtNDA4NC1iZTJmLWU5NDgyYTk0
39
+ MTc5ZDtQYXRoPS87SHR0cE9ubHk7RXhwaXJlcz1XZWQsIDE2IEFwciAyMDE0
40
+ IDE0OjQ5OjM1ICswMDAwO01heC1BZ2U9MzE1MzYwMDA7U2VjdXJl
41
+ !binary "Y29udGVudC1sZW5ndGg=":
42
+ - !binary |-
43
+ Nzg3
44
+ !binary "Y29ubmVjdGlvbg==":
45
+ - !binary |-
46
+ a2VlcC1hbGl2ZQ==
47
+ body:
48
+ encoding: ASCII-8BIT
49
+ string: !binary |-
50
+ H4sIAAAAAAAAAJWUS2/UMBDHv4rlSy/7SPbVNgIEAg4cuAAnHoocZ5KYOnaw
51
+ x11C1e/OONlsdysK4rRZz3jmN4+/73hpDeZFULrkmQlaz7i0basQweVGtMAz
52
+ /roRt/CLz3jhhJENnbTCkz2e2LKn//QlAjbW5aXAeCVdb7dJcnW52vDTgH+y
53
+ OkCnwPPsy7cZ74QTWoPmWXp6D1qh9JAXsYH9Qg5EL8ecCnstCr8gdwpHYF0M
54
+ dscP9NKaStXBAaOrbKyUcCUqa0bHQvgmj8mEOTbBBZOjaiFvldaK/FbX23VE
55
+ MqhMgMntgRddgJhdOBwuUuZVkq7nyWaeXn5K1tlmmyVXnyn1X7mOGE/YW/Be
56
+ 1FO7YrE8S2YcfiqkEsojmA04uoApn+RJ08ijTAk/hyhEj4FCcx+kpERkjFfL
57
+ IdhhOWgDQMOxT8pUjpbBBYmEmlfDnEYT9l3Meu5BIaPPFOD+2z0xDsWdt3ud
58
+ bFczvm/idlXKeZwfJzcu2n+txL+H4tF2/+iTVhXIXmoYkIzyDUSe4EkJ2R1X
59
+ Ph8/x03QtlYm8qGc5PNYTlMJhn584ewN0OQr0PplHS0D/T2hheI7SJoA/2gd
60
+ sr3ChvkOpGfkzj68ffXm/dvxtACSZyvcjT/flLtpFnvhjDL1g5XO3iHT1t54
61
+ ptUNsD0waYMuzQUymhw4huCReUDa+9qzyjrW2+BY52ykWrAPMHhZFk+fCdY4
62
+ qJ5fNIidz5ZLqRx1TKpYzLK00i9pvQNppr948ZV/HMOOEYcBs9CxyeMrf7YU
63
+ LxjdCi0YjDlqIJY4TigXjMh9E2GZNbpnKIhfsAr2rCWNEveCx+36bovDS3aQ
64
+ Nj04fW6rE6kQXGyFsXms1vMTKZyc3UqfO7hVnl6O+IrB5UZWSSF3u83merMr
65
+ 0lJc73bbtNytxHq3Kq+21SbdJXxacBPa4V2LcYKLk5+6VNP0QjH0aNqXpSvm
66
+ wjnRzz1NfRzagxgroT0tWRdxbOR8rM6Dwx/lebCd6nBCPMc6G17dPM22TB+E
67
+ +XjLfwQIUOYCnxLf/W9rTc/2hQYAAA==
68
+ http_version: !binary |-
69
+ MS4x
70
+ recorded_at: Wed, 17 Apr 2013 05:08:34 GMT
71
+ recorded_with: VCR 2.4.0
@@ -0,0 +1,68 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: get
5
+ uri: https://circleci.com/api/v1/project/mtchavez/rb-array-sorting?circle-token=123
6
+ body:
7
+ encoding: US-ASCII
8
+ string: ''
9
+ headers:
10
+ accept:
11
+ - application/json
12
+ accept-encoding:
13
+ - gzip, deflate
14
+ content-type:
15
+ - application/json
16
+ user-agent:
17
+ - Ruby
18
+ response:
19
+ status:
20
+ code: 200
21
+ message: !binary |-
22
+ T0s=
23
+ headers:
24
+ !binary "Y29udGVudC1lbmNvZGluZw==":
25
+ - !binary |-
26
+ Z3ppcA==
27
+ !binary "Y29udGVudC10eXBl":
28
+ - !binary |-
29
+ YXBwbGljYXRpb24vanNvbjsgY2hhcnNldD11dGYtOA==
30
+ !binary "ZGF0ZQ==":
31
+ - !binary |-
32
+ V2VkLCAxNyBBcHIgMjAxMyAwNTowMToyNCBHTVQ=
33
+ !binary "c2VydmVy":
34
+ - !binary |-
35
+ bmdpbngvMS4xLjE5
36
+ !binary "c2V0LWNvb2tpZQ==":
37
+ - !binary |-
38
+ cmluZy1zZXNzaW9uPWNiNTdkMTk4LTJmOTQtNDZmZS1iMTYwLWNjM2E4ZjM1
39
+ YjM0YTtQYXRoPS87SHR0cE9ubHk7RXhwaXJlcz1XZWQsIDE2IEFwciAyMDE0
40
+ IDIxOjE3OjU3ICswMDAwO01heC1BZ2U9MzE1MzYwMDA7U2VjdXJl
41
+ !binary "Y29udGVudC1sZW5ndGg=":
42
+ - !binary |-
43
+ NjY3
44
+ !binary "Y29ubmVjdGlvbg==":
45
+ - !binary |-
46
+ a2VlcC1hbGl2ZQ==
47
+ body:
48
+ encoding: ASCII-8BIT
49
+ string: !binary |-
50
+ H4sIAAAAAAAAAJWUTU/cMBCG/4rlC5f9SHazASKKqFoOPfRCeyqgyHEmG7OO
51
+ ndpjViniv3ecsHypHHpKPH49fux3xtcPvLYGyyooXfPCBK1nXNquU4jgSiM6
52
+ 4AX/0op7+MNnvHLCyJYinfA0HyO2HmhMfyJga11ZC4xL0vVmkyQnx6uMv074
53
+ r1kH6BR4XlzfzngvnNAaNC/S1+ugE0qP+yK2sF/Ikehi2lPhoEXlFySPRPEo
54
+ JaoOyk5prSjxOtmsZnzfRtJGOY/z6bzP0P+R3qNwOKYn/SpJ1/Mkm6fHP5N1
55
+ kW2K5OTXqLH9h5I0jRKtGpCD1DAiGeVbiDzB060WD1z5cvpFF4DUdqtM5EN5
56
+ sOK9NYcjGPr4ytkdCFM3oPXFNs6M9I+EFqo7kEjCH9Yh2ytsme9BekZydnX5
57
+ +ev3yylaAVndCbfzlL0D78V2NOmB49DHrffCGWW2L7MU+4ZMW7vzTKsdsD0w
58
+ aYOuzREyZRpwDMEj84BICz1rrGODDY71zkaqBbuCUWVZjJ4J1jpoPh21iL0v
59
+ lkupHN2YVPEwy9pKv+yECVQvw9H5Df8xpZ0yjgaz0LOD4oafLcU5o1WhA4Nx
60
+ jy0QS7QT6gUjct9GWGaNHhgK4hesgT3rlAnEveCPVJ93tnrqiqlXYvEOpW0O
61
+ YxuQ4OJVGFvG0/qpZjD4t7F76UsH98orG51N4TiTTVLJPM+y0yyv0lqc5vkm
62
+ rfOVWOer+mTTZGmePBe4Cd3YIzFPcNH5wy1tyb1QjXd0qJelq+bCOTHMPbk+
63
+ mRbrsyZcXjRCeyqyPuLYyPn0DFCvg6ayPAjIQkd974LE4KBsxoJ7mosDeH5B
64
+ JsS3WG/M27Yfsy3Tl8Z8X+W/AwSoS4EfNd/j7V+eK+AY0wQAAA==
65
+ http_version: !binary |-
66
+ MS4x
67
+ recorded_at: Wed, 17 Apr 2013 05:01:24 GMT
68
+ recorded_with: VCR 2.4.0
@@ -0,0 +1,72 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: get
5
+ uri: https://circleci.com/api/v1/project/mtchavez/rb-array-sorting/tree/master?circle-token=123
6
+ body:
7
+ encoding: US-ASCII
8
+ string: ''
9
+ headers:
10
+ accept:
11
+ - application/json
12
+ accept-encoding:
13
+ - gzip, deflate
14
+ content-type:
15
+ - application/json
16
+ user-agent:
17
+ - Ruby
18
+ response:
19
+ status:
20
+ code: 200
21
+ message: OK
22
+ headers:
23
+ access-control-allow-origin:
24
+ - '*'
25
+ content-encoding:
26
+ - gzip
27
+ content-type:
28
+ - application/json; charset=utf-8
29
+ date:
30
+ - Tue, 21 Jan 2014 13:38:24 GMT
31
+ server:
32
+ - nginx/1.2.6 (Ubuntu)
33
+ set-cookie:
34
+ - ring-session=02DWkHLeQaAe9qrSOMCl0xyOuXOo8EZ9ZvEs%2FyluWUo%3D--ILSIt%2BetjtBwYEQ2%2BjgqhwijkeYkDwHfrd1OEZAg6H8%3D;Secure;Max-Age=31536000;Expires=Wed,
35
+ 21 Jan 2015 11:06:54 +0000;HttpOnly;Path=/
36
+ x-circleci-identity:
37
+ - i-ca2c9be4
38
+ x-route:
39
+ - /api/v1/project/:username/:repo/tree/:branch
40
+ content-length:
41
+ - '997'
42
+ connection:
43
+ - keep-alive
44
+ body:
45
+ encoding: ASCII-8BIT
46
+ string: !binary |-
47
+ H4sIAAAAAAAAAO2Zy3LbNhSGXwXDjTcURfAmiZNm0mmz6KKbpKs6Hg5IHoqI
48
+ QUDFxRrW43cPQEpR1DpO7ZGdTMWVJOAA+HFunyhd3nq14LooDWW1l3PDmO9V
49
+ ouuo1iALTjrwcu+XltzA357vlZLwqrUjHVF23o2Iuref7TtidCtkURPtluA4
50
+ TcNwuYgSO8VFDf/e/D5LCVpSUHvjDZGEMWBejr9cCR2hbFChdQvboBr0vRkV
51
+ UN0zUqrAmjt97mKFph0UHWWM2q2jNPK9betku+P6g/ZH7KtUWwAnJYPPblOa
52
+ SD0cZbeIQhzPwmSGF3+EaY5xHkZBHK7+dEu12DxklmbYmTHaQNVXzJk1lFPV
53
+ 2qN8zyjr9/zWo6oY32ppwFqLNeVOuq72wfpn8Pa34/ZFlVJcA+F1A4y9WbuZ
54
+ 4WJ3Vp4pP0KlreF7ITXaUt0itYFKIWuO3r39+dff346jJdhk6Ii8Vnb3DpQi
55
+ axe6y1tP9xt39JZITvn6MGvHftOICXGtEKPXgLaAKmFYzS80orwBiTQojRRo
56
+ bRcq1AiJemEk2kjhVAXoHQxWArnRD94rgloJzU8XrdYblc/nFZXWZxV115nX
57
+ olLzjnBjs6i/eP1+3HbcccgMZDZoP/9qTl5/8JBdYzrg2p2xBqvFhRXqAFnl
58
+ qnVikeCsR5pY/QQ1sEUd5cbqDry7K9/7KMpd3YxpMeRYIZohhzcSbqgwqlCm
59
+ qqxPGsOOa08YbYU7R3FROF+oIWOINup47KZShdtMUeHijmGRVE1YVlmWJKsk
60
+ K3FNVlmW4jqLSJxF9TJtEpyFn0uCm87L03EfI11e7D24trE15eC/fTbNZTkj
61
+ UpJ+pmxOjCF1GVxbuV7eEKbgcDeXnfeUXRy6urvvKl8IwjYBbSyKsdaLGrRL
62
+ 1iGnTt6VHu5EOwWPd818XDl/RESe1Nme0LOeVtmjusfl2E7ccaRcdVQ2VDA0
63
+ zV3W2KqXNmzSVNpIKJrhNrs59+HQX8c0OQ7IUbWv2weiklpVDZDxEEbWLk3v
64
+ viLU9/4yYKAuiP5Kjw6XtpXf+WdIzxjH34OeOFik4X+hZ5jFEz0ner4EPZOJ
65
+ nhM9z4OeyQnpiYNoFZ0pPaM0e3F6hnmcBDjLvkHPwSyJoomeEz1fgp7xRM+J
66
+ nudBz/hU9LQ9Og6Wq+xc6blafA96xkGCo2/TMw4W4w+8Ez0nej43PaOJnhM9
67
+ z4Oe0UnpGWY/9LPnZeTHfuKnV8/18214+PezoVLp2eiC5yZpnCdpHi6DZfgg
68
+ SQczjINlmkwk/T+SdPft8MeCKX4emI5XmaA4QfH0UMQngeK+K0cL90h59QnH
69
+ qTuEDiQAAA==
70
+ http_version: '1.1'
71
+ recorded_at: Tue, 21 Jan 2014 13:38:24 GMT
72
+ recorded_with: VCR 2.4.0