circleci-cli 0.6.3 → 1.0.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.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +1 -1
  3. data/README.md +21 -19
  4. data/bin/circle +2 -2
  5. data/bin/console +1 -1
  6. data/{circler.gemspec → circleci-cli.gemspec} +3 -3
  7. data/exe/{circle → circleci-cli} +2 -2
  8. data/lib/circleci/cli.rb +77 -0
  9. data/lib/circleci/cli/command.rb +16 -0
  10. data/lib/circleci/cli/command/base_command.rb +45 -0
  11. data/lib/circleci/cli/command/browse_command.rb +24 -0
  12. data/lib/circleci/cli/command/build_command.rb +19 -0
  13. data/lib/circleci/cli/command/builds_command.rb +25 -0
  14. data/lib/circleci/cli/command/cancel_command.rb +23 -0
  15. data/lib/circleci/cli/command/projects_command.rb +16 -0
  16. data/lib/circleci/cli/command/retry_command.rb +23 -0
  17. data/lib/circleci/cli/command/watch_command.rb +77 -0
  18. data/lib/circleci/cli/networking.rb +9 -0
  19. data/lib/circleci/cli/networking/pusher_client.rb +62 -0
  20. data/lib/circleci/cli/printer.rb +11 -0
  21. data/lib/circleci/cli/printer/build_printer.rb +63 -0
  22. data/lib/circleci/cli/printer/project_printer.rb +37 -0
  23. data/lib/circleci/cli/printer/step_printer.rb +57 -0
  24. data/lib/circleci/cli/response.rb +13 -0
  25. data/lib/circleci/cli/response/account.rb +21 -0
  26. data/lib/circleci/cli/response/action.rb +39 -0
  27. data/lib/circleci/cli/response/build.rb +104 -0
  28. data/lib/circleci/cli/response/project.rb +21 -0
  29. data/lib/circleci/cli/response/step.rb +22 -0
  30. data/lib/circleci/cli/version.rb +7 -0
  31. data/lib/circleci_cli.rb +7 -0
  32. metadata +29 -25
  33. data/lib/circler.rb +0 -6
  34. data/lib/circler/cli.rb +0 -88
  35. data/lib/circler/command/base_command.rb +0 -41
  36. data/lib/circler/command/browse_command.rb +0 -20
  37. data/lib/circler/command/build_command.rb +0 -15
  38. data/lib/circler/command/builds_command.rb +0 -21
  39. data/lib/circler/command/cancel_command.rb +0 -19
  40. data/lib/circler/command/projects_command.rb +0 -12
  41. data/lib/circler/command/retry_command.rb +0 -19
  42. data/lib/circler/command/watch_command.rb +0 -73
  43. data/lib/circler/networking/pusher_client.rb +0 -56
  44. data/lib/circler/printer/build_printer.rb +0 -59
  45. data/lib/circler/printer/project_printer.rb +0 -33
  46. data/lib/circler/printer/step_printer.rb +0 -53
  47. data/lib/circler/response/account.rb +0 -17
  48. data/lib/circler/response/action.rb +0 -35
  49. data/lib/circler/response/build.rb +0 -100
  50. data/lib/circler/response/project.rb +0 -17
  51. data/lib/circler/response/step.rb +0 -18
  52. data/lib/circler/version.rb +0 -5
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'circleci/cli/networking/pusher_client'
4
+
5
+ module CircleCI
6
+ module CLI
7
+ module Networking; end
8
+ end
9
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'pusher-client'
4
+
5
+ module CircleCI
6
+ module CLI
7
+ module Networking
8
+ class CircleCIPusherClient
9
+ def connect
10
+ PusherClient.logger.level = Logger::ERROR
11
+ @socket = PusherClient::Socket.new(app_key, pusher_options)
12
+ @socket.connect(true)
13
+ end
14
+
15
+ def bind(channel, event)
16
+ @socket.subscribe(channel)
17
+ @socket[channel].bind(event) do |data|
18
+ yield data
19
+ end
20
+ end
21
+
22
+ def bind_event_json(channel, event)
23
+ bind(channel, event) do |data|
24
+ JSON.parse(data).each { |json| yield(json) }
25
+ end
26
+ end
27
+
28
+ def unsubscribe(channel)
29
+ @socket.unsubscribe(channel)
30
+ end
31
+
32
+ private
33
+
34
+ def app_key
35
+ '1cf6e0e755e419d2ac9a'
36
+ end
37
+
38
+ def pusher_options
39
+ {
40
+ secure: true,
41
+ auth_method: proc { |a, b| auth(a, b) },
42
+ logger: Logger.new('/dev/null')
43
+ }
44
+ end
45
+
46
+ def auth(socket_id, channel)
47
+ data = { socket_id: socket_id, channel_name: channel.name }
48
+ token = ENV['CIRCLE_CI_TOKEN'] || ask('Circle CI token ? :')
49
+ res = connection.post("/auth/pusher?circle-token=#{token}", data)
50
+ JSON.parse(res.body)['auth']
51
+ end
52
+
53
+ def connection
54
+ Faraday.new(url: 'https://circleci.com') do |f|
55
+ f.request :url_encoded
56
+ f.adapter Faraday.default_adapter
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'circleci/cli/printer/project_printer'
4
+ require 'circleci/cli/printer/build_printer'
5
+ require 'circleci/cli/printer/step_printer'
6
+
7
+ module CircleCI
8
+ module CLI
9
+ module Printer; end
10
+ end
11
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CircleCI
4
+ module CLI
5
+ module Printer
6
+ class BuildPrinter
7
+ def initialize(builds, pretty: true)
8
+ @builds = builds
9
+ @pretty = pretty
10
+ end
11
+
12
+ def to_s
13
+ @pretty ? print_pretty : print_compact
14
+ end
15
+
16
+ private
17
+
18
+ def print_compact
19
+ rows.map { |row| pad_columns_by_space(row, max_row_widths) }.join("\n")
20
+ end
21
+
22
+ def print_pretty
23
+ Terminal::Table.new(title: title, headings: headings, rows: rows).to_s
24
+ end
25
+
26
+ def title
27
+ build = @builds.first
28
+ "Recent Builds / #{build.project_name}".green
29
+ end
30
+
31
+ def headings
32
+ %w[Number Status Branch Author Commit Duration StartTime]
33
+ end
34
+
35
+ def rows
36
+ @builds.map(&:information)
37
+ end
38
+
39
+ def max_row_widths
40
+ @builds
41
+ .map(&:information)
42
+ .map { |array| array.map(&:to_s).map(&:size) }
43
+ .transpose
44
+ .map(&:max)
45
+ end
46
+
47
+ def pad_columns_by_space(columns, max_widths)
48
+ columns
49
+ .map
50
+ .with_index { |column, i| pad_column_by_space(column, max_widths, i) }
51
+ .join(' ')
52
+ .to_s
53
+ end
54
+
55
+ def pad_column_by_space(column, max_widths, index)
56
+ column_string = column.to_s
57
+ spaces = ' ' * (max_widths[index] - column_string.size)
58
+ column_string + spaces
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CircleCI
4
+ module CLI
5
+ module Printer
6
+ class ProjectPrinter
7
+ attr_accessor :compact
8
+ def initialize(projects, pretty: true)
9
+ @projects = projects
10
+ @pretty = pretty
11
+ end
12
+
13
+ def to_s
14
+ @pretty ? print_pretty : print_compact
15
+ end
16
+
17
+ private
18
+
19
+ def print_compact
20
+ @projects
21
+ .map(&:information)
22
+ .map { |array| array.join('/').to_s }
23
+ .sort
24
+ .join("\n")
25
+ end
26
+
27
+ def print_pretty
28
+ Terminal::Table.new(
29
+ title: 'Projects'.green,
30
+ headings: ['User name', 'Repository name'],
31
+ rows: @projects.map(&:information)
32
+ ).to_s
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CircleCI
4
+ module CLI
5
+ module Printer
6
+ class StepPrinter
7
+ def initialize(steps, pretty: true)
8
+ @steps = steps
9
+ @pretty = pretty
10
+ end
11
+
12
+ def to_s
13
+ Terminal::Table.new do |t|
14
+ @steps
15
+ .group_by(&:type)
16
+ .each do |key, steps|
17
+ t << :separator
18
+ t << [{ value: key.green, alignment: :center, colspan: 2 }]
19
+ steps.each { |s| print_actions(t, s) }
20
+ end
21
+ end.to_s
22
+ end
23
+
24
+ private
25
+
26
+ def colorize_by_status(string, status)
27
+ case status
28
+ when 'success', 'fixed' then string.green
29
+ when 'canceled' then string.yellow
30
+ when 'failed', 'timedout' then string.red
31
+ when 'no_tests', 'not_run' then string.light_black
32
+ else string
33
+ end
34
+ end
35
+
36
+ def format_time(time)
37
+ return '' unless time
38
+
39
+ minute = format('%02d', time / 1000 / 60)
40
+ second = format('%02d', (time / 1000) % 60)
41
+ "#{minute}:#{second}"
42
+ end
43
+
44
+ def print_actions(table, step)
45
+ table << :separator
46
+ step.actions.each do |a|
47
+ table << [
48
+ colorize_by_status(a.name.slice(0..120), a.status),
49
+ format_time(a.run_time_millis)
50
+ ]
51
+ table << [{ value: a.log, alignment: :left, colspan: 2 }] if a.failed? && a.log
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'circleci/cli/response/account'
4
+ require 'circleci/cli/response/project'
5
+ require 'circleci/cli/response/build'
6
+ require 'circleci/cli/response/step'
7
+ require 'circleci/cli/response/action'
8
+
9
+ module CircleCI
10
+ module CLI
11
+ module Response; end
12
+ end
13
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CircleCI
4
+ module CLI
5
+ module Response
6
+ class Account
7
+ def initialize(hash)
8
+ @hash = hash
9
+ end
10
+
11
+ def user_name
12
+ @hash['name']
13
+ end
14
+
15
+ def self.me
16
+ Account.new(CircleCi::User.me.body)
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CircleCI
4
+ module CLI
5
+ module Response
6
+ class Action
7
+ attr_reader :name, :status, :run_time_millis
8
+ def initialize(hash)
9
+ @hash = hash
10
+ @name = hash['name']
11
+ @status = hash['status']
12
+ @run_time_millis = hash['run_time_millis']
13
+ end
14
+
15
+ def log
16
+ request(@hash['output_url'])
17
+ .map do |r|
18
+ r['message']
19
+ .gsub(/\r\n/, "\n")
20
+ .gsub(/\e\[A\r\e\[2K/, '')
21
+ .scan(/.{1,120}/)
22
+ .join("\n")
23
+ end
24
+ .join("\n")
25
+ end
26
+
27
+ def failed?
28
+ @status == 'timedout' || @status == 'failed'
29
+ end
30
+
31
+ private
32
+
33
+ def request(url)
34
+ JSON.parse(Faraday.new(url).get.body)
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,104 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CircleCI
4
+ module CLI
5
+ module Response
6
+ class Build
7
+ class << self
8
+ def all(username, reponame)
9
+ CircleCi::Project.new(username, reponame, 'github').recent_builds
10
+ .body
11
+ .map { |b| Build.new(b) }
12
+ end
13
+
14
+ def branch(username, reponame, branch)
15
+ CircleCi::Project.new(username, reponame, 'github').recent_builds_branch(branch)
16
+ .body
17
+ .map { |b| Build.new(b) }
18
+ end
19
+
20
+ def get(username, reponame, number)
21
+ Build.new(CircleCi::Build.new(username, reponame, 'github', number).get.body)
22
+ end
23
+
24
+ def retry(username, reponame, number)
25
+ Build.new(CircleCi::Build.new(username, reponame, 'github', number).retry.body)
26
+ end
27
+
28
+ def cancel(username, reponame, number)
29
+ Build.new(CircleCi::Build.new(username, reponame, 'github', number).cancel.body)
30
+ end
31
+ end
32
+
33
+ def initialize(hash)
34
+ @hash = hash
35
+ end
36
+
37
+ def username
38
+ @hash['username']
39
+ end
40
+
41
+ def reponame
42
+ @hash['reponame']
43
+ end
44
+
45
+ def status
46
+ @hash['status']
47
+ end
48
+
49
+ def build_number
50
+ @hash['build_num']
51
+ end
52
+
53
+ def running?
54
+ status == 'running'
55
+ end
56
+
57
+ def channel_name
58
+ "private-#{username}@#{reponame}@#{build_number}@vcs-github@0"
59
+ end
60
+
61
+ def project_name
62
+ "#{username}/#{reponame}"
63
+ end
64
+
65
+ def information
66
+ [
67
+ @hash['build_num'],
68
+ colorize_by_status(@hash['status'], @hash['status']),
69
+ colorize_by_status(@hash['branch'], @hash['status']),
70
+ @hash['author_name'],
71
+ (@hash['subject'] || '').slice(0..60),
72
+ format_time(@hash['build_time_millis']),
73
+ @hash['start_time']
74
+ ]
75
+ end
76
+
77
+ def steps
78
+ hash = @hash['steps'].group_by { |s| s['actions'].first['type'] }
79
+ hash.flat_map { |type, value| value.map { |v| Step.new(type, v) } }
80
+ end
81
+
82
+ private
83
+
84
+ def colorize_by_status(string, status)
85
+ case status
86
+ when 'success', 'fixed' then string.green
87
+ when 'canceled' then string.yellow
88
+ when 'failed' then string.red
89
+ when 'no_tests', 'not_run' then string.light_black
90
+ else string
91
+ end
92
+ end
93
+
94
+ def format_time(time)
95
+ return '' unless time
96
+
97
+ minute = format('%02d', time / 1000 / 60)
98
+ second = format('%02d', (time / 1000) % 60)
99
+ "#{minute}:#{second}"
100
+ end
101
+ end
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CircleCI
4
+ module CLI
5
+ module Response
6
+ class Project
7
+ def initialize(hash)
8
+ @hash = hash
9
+ end
10
+
11
+ def information
12
+ [@hash['username'], @hash['reponame']]
13
+ end
14
+
15
+ def self.all
16
+ CircleCi::Projects.new.get.body.map { |p| Project.new(p) }
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end