circle-cli 0.0.2 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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