circleci-cli 1.0.0 → 2.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5210a33cd66ffb1d7cdf4294653d0a8b0098ad7755689ead6ff50419377937ca
4
- data.tar.gz: 16e6f04777f3046d6e0c67d3919d39713236da8a2cf9393497cf62b5310a271b
3
+ metadata.gz: 4823a93a5df3fed802d98f9131b3792b9fc7c51e371688c70bf09233f804a49c
4
+ data.tar.gz: 8ede76476f63b3bf97641fa996229de56c5f8177b624080fcba2eb6c2f9c945b
5
5
  SHA512:
6
- metadata.gz: 6166152b480e9accfbbb5321496a14b5fa92ea5277c629d4ce96ebbd4b5bfd520a803afebe713fcba873f2945e23f75743e24671155f018f157f7e373a50860f
7
- data.tar.gz: ec1d2459890466344fc641fad40b0e1375d997ce3979d6babf5259d4227124cd6c0c4976dbcdb7007ea3cfad656bc1218508f76a12edadceecf06b553902840e
6
+ metadata.gz: b01c02e60ad803dca24506568397c243c86f0cebe00d8b4cecc881ac0a39e299fd8410a355aae03f3c5763909ea7f095e11c08f3d0a2d2f7067544de5f5fcb5d
7
+ data.tar.gz: df4a2d04fc9d52ed27e64630c788773431d4ca6ded37879480c26d3e654133d1459d6a901b0bde633f577be56ea4ef67d9cc831d3506589c62fd3de592430102
@@ -8,9 +8,9 @@ update_bundler: &update_bundler
8
8
  bundle_install: &bundle_install
9
9
  run:
10
10
  name: bundle install
11
- command: bundle update --bundler && bundle install --path vendor/bundle --jobs 4
11
+ command: bundle install --path vendor/bundle --jobs 4
12
12
 
13
- restore_bundle_cache: &restore_bundle_cache
13
+ git statusrestore_bundle_cache: &restore_bundle_cache
14
14
  restore_cache:
15
15
  key: cache-bundler-{{ checksum "Gemfile.lock" }}
16
16
 
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- circleci-cli (1.0.0)
4
+ circleci-cli (2.0.0)
5
5
  circleci (~> 2.0.2)
6
6
  colorize (~> 0.8.1)
7
7
  faraday (>= 0.14, < 0.16)
@@ -133,4 +133,4 @@ DEPENDENCIES
133
133
  simplecov
134
134
 
135
135
  BUNDLED WITH
136
- 1.17.2
136
+ 2.0.2
@@ -64,6 +64,7 @@ module CircleCI
64
64
  desc 'watch', 'watch a build in real time'
65
65
  method_option :project, aliases: 'p', type: :string, banner: 'user/project'
66
66
  method_option :build, aliases: 'n', type: :numeric, banner: 'build-number'
67
+ method_option :verbose, aliases: 'v', type: :boolean, banner: 'verbose'
67
68
  def watch
68
69
  Command::WatchCommand.run(options)
69
70
  end
@@ -1,70 +1,67 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'circleci/cli/command/watch_command/build_repository'
4
+ require 'circleci/cli/command/watch_command/build_watcher'
5
+
3
6
  module CircleCI
4
7
  module CLI
5
8
  module Command
6
9
  class WatchCommand < BaseCommand
7
10
  class << self
8
- def run(options)
11
+ def run(options) # rubocop:disable Metrics/MethodLength
9
12
  setup_token
10
- setup_client
11
13
 
12
- build = get_build(options)
14
+ @options = options
15
+ @repository = BuildRepository.new(*project_name(options).split('/'))
16
+ @client = Networking::CircleCIPusherClient.new.tap(&:connect)
17
+ @build_watcher = nil
18
+
19
+ bind_status_event
13
20
 
14
- if build&.running?
15
- start_watch(build)
16
- wait_until_finish
17
- finalize(build, build.channel_name)
18
- else
19
- say 'The build is not running'
21
+ loop do
22
+ stop_existing_watcher_if_needed
23
+ start_watcher_if_needed
24
+ sleep 1
20
25
  end
26
+ rescue Interrupt
27
+ say 'Exited'
21
28
  end
22
29
 
23
30
  private
24
31
 
25
- def setup_client
26
- @client = Networking::CircleCIPusherClient.new
27
- @client.connect
32
+ def bind_status_event
33
+ @client.bind("private-#{Response::Account.me.pusher_id}", 'call') { @repository.update }
28
34
  end
29
35
 
30
- def get_build(options)
31
- username, reponame = project_name(options).split('/')
32
- number = build_number options
33
- Response::Build.get(username, reponame, number)
34
- end
36
+ def stop_existing_watcher_if_needed
37
+ return if @build_watcher.nil?
35
38
 
36
- def start_watch(build)
37
- @running = true
38
- text = "Start watching #{build.project_name} ##{build.build_number}"
39
- print_bordered text
40
- TerminalNotifier.notify text
39
+ build = @repository.build_for(@build_watcher.build.build_number)
40
+ return if build.nil? || !build.finished?
41
41
 
42
- bind_event_handling build.channel_name
42
+ @build_watcher.stop(build.status)
43
+ @build_watcher = nil
44
+ show_interrupted_build_results
43
45
  end
44
46
 
45
- def bind_event_handling(channel)
46
- @client.bind_event_json(channel, 'newAction') do |json|
47
- print_bordered json['log']['name'].green
48
- end
49
-
50
- @client.bind_event_json(channel, 'appendAction') do |json|
51
- say json['out']['message']
52
- end
47
+ def start_watcher_if_needed
48
+ build_to_watch = @repository.builds_to_show.select(&:running?).first
49
+ return unless build_to_watch && @build_watcher.nil?
53
50
 
54
- @client.bind_event_json(channel, 'updateAction') do |json|
55
- @running = json['log']['name'] != 'Disable SSH'
56
- end
51
+ show_interrupted_build_results
52
+ @repository.mark_as_shown(build_to_watch.build_number)
53
+ @build_watcher = BuildWatcher.new(build_to_watch, verbose: @options.verbose)
54
+ @build_watcher.start
57
55
  end
58
56
 
59
- def wait_until_finish
60
- sleep(1) while @running
61
- end
62
-
63
- def finalize(build, channel)
64
- @client.unsubscribe(channel)
65
- text = "Finish watching #{build.project_name} ##{build.build_number}"
66
- print_bordered text.blue
67
- TerminalNotifier.notify text
57
+ def show_interrupted_build_results # rubocop:disable Metrics/AbcSize
58
+ @repository.builds_to_show.select(&:finished?).each do |build|
59
+ b = Response::Build.get(build.username, build.reponame, build.build_number)
60
+ title = "✅ Result of #{build.project_name} ##{build.build_number} completed in background".light_black
61
+ say Printer::BuildPrinter.header_for(build, title)
62
+ say Printer::StepPrinter.new(b.steps, pretty: @options.verbose).to_s
63
+ @repository.mark_as_shown(b.build_number)
64
+ end
68
65
  end
69
66
 
70
67
  def print_bordered(text)
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CircleCI
4
+ module CLI
5
+ module Command
6
+ class BuildRepository
7
+ def initialize(username, reponame)
8
+ @username = username
9
+ @reponame = reponame
10
+ @builds = Response::Build.all(@username, @reponame)
11
+ @build_numbers_shown = @builds.select(&:finished?).map(&:build_number)
12
+ end
13
+
14
+ def update
15
+ @builds = (Response::Build.all(@username, @reponame) + @builds)
16
+ .uniq(&:build_number)
17
+ end
18
+
19
+ def mark_as_shown(build_number)
20
+ @build_numbers_shown = (@build_numbers_shown + [build_number]).uniq
21
+ end
22
+
23
+ def builds_to_show
24
+ @builds
25
+ .reject { |build| @build_numbers_shown.include?(build.build_number) }
26
+ .sort_by(&:build_number)
27
+ end
28
+
29
+ def build_for(build_number)
30
+ @builds.find { |build| build.build_number == build_number }
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CircleCI
4
+ module CLI
5
+ module Command
6
+ class BuildWatcher
7
+ attr_reader :build
8
+
9
+ def initialize(build, verbose: false)
10
+ @client = Networking::CircleCIPusherClient.new.tap(&:connect)
11
+ @build = build
12
+ @verbose = verbose
13
+ @messages = Hash.new { |h, k| h[k] = [] }
14
+ end
15
+
16
+ def start
17
+ bind_event_handling @build.channel_name
18
+ notify_started
19
+ end
20
+
21
+ def stop(status)
22
+ @client.unsubscribe(@build.channel_name + '@0')
23
+ notify_stopped(status)
24
+ end
25
+
26
+ private
27
+
28
+ def bind_event_handling(channel) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
29
+ @client.bind_event_json(channel, 'newAction') do |json|
30
+ if @verbose
31
+ print_bordered json['log']['name'].light_black
32
+ else
33
+ print json['log']['name'].light_black
34
+ end
35
+ end
36
+
37
+ @client.bind_event_json(channel, 'appendAction') do |json|
38
+ if @verbose
39
+ say json['out']['message']
40
+ else
41
+ @messages[json['step']] << json['out']['message']
42
+ end
43
+ end
44
+
45
+ @client.bind_event_json(channel, 'updateAction') do |json|
46
+ next if @verbose
47
+
48
+ case json['log']['status']
49
+ when 'success'
50
+ puts "\e[2K\r#{json['log']['name'].green}"
51
+ when 'failed'
52
+ puts "\e[2K\r#{json['log']['name'].red}"
53
+ @messages[json['step']].each(&method(:say))
54
+ end
55
+ end
56
+ end
57
+
58
+ def notify_started
59
+ title = "👀 Start watching #{@build.project_name} ##{@build.build_number}".light_black
60
+ say Printer::BuildPrinter.header_for(@build, title)
61
+ end
62
+
63
+ def notify_stopped(status)
64
+ text = case status
65
+ when 'success' then "🎉 #{@build.project_name} ##{@build.build_number} has succeeded!".green
66
+ when 'failed' then "😥 #{@build.project_name} ##{@build.build_number} has failed...".red
67
+ end
68
+
69
+ @verbose ? print_bordered(text) : say(text)
70
+ end
71
+
72
+ def print_bordered(text)
73
+ say Terminal::Table.new(rows: [[text]], style: { width: 120 }).to_s
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
@@ -4,8 +4,20 @@ module CircleCI
4
4
  module CLI
5
5
  module Printer
6
6
  class BuildPrinter
7
+ class << self
8
+ def header_for(build, title) # rubocop:disable Metrics/AbcSize
9
+ texts = [
10
+ ['Project: '.light_black + build.project_name],
11
+ ['Build: '.light_black + build.build_number.to_s],
12
+ ['Author: '.light_black + build.author_name],
13
+ ['Workflow: '.light_black + "#{build.workflow_name}/#{build.workflow_job_name}"]
14
+ ]
15
+ Terminal::Table.new(title: title, rows: texts, style: { width: 120 }).to_s
16
+ end
17
+ end
18
+
7
19
  def initialize(builds, pretty: true)
8
- @builds = builds
20
+ @builds_to_show = builds
9
21
  @pretty = pretty
10
22
  end
11
23
 
@@ -24,7 +36,7 @@ module CircleCI
24
36
  end
25
37
 
26
38
  def title
27
- build = @builds.first
39
+ build = @builds_to_show.first
28
40
  "Recent Builds / #{build.project_name}".green
29
41
  end
30
42
 
@@ -33,11 +45,11 @@ module CircleCI
33
45
  end
34
46
 
35
47
  def rows
36
- @builds.map(&:information)
48
+ @builds_to_show.map(&:information)
37
49
  end
38
50
 
39
51
  def max_row_widths
40
- @builds
52
+ @builds_to_show
41
53
  .map(&:information)
42
54
  .map { |array| array.map(&:to_s).map(&:size) }
43
55
  .transpose
@@ -9,16 +9,26 @@ module CircleCI
9
9
  @pretty = pretty
10
10
  end
11
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
12
+ def to_s # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
13
+ if @pretty
14
+ Terminal::Table.new do |t|
15
+ @steps
16
+ .group_by(&:type)
17
+ .each do |key, steps|
18
+ t << :separator
19
+ t << [{ value: key.green, alignment: :center, colspan: 2 }]
20
+ steps.each { |s| print_actions(t, s) }
21
+ end
22
+ end.to_s
23
+ else
24
+ @steps.group_by(&:type).map do |_, steps|
25
+ steps.map do |step|
26
+ step.actions.map do |a|
27
+ "#{colorize_by_status(a.name.slice(0..120), a.status)}\n#{"#{a.log}\n" if a.failed? && a.log}"
28
+ end
29
+ end.flatten.join('')
30
+ end.join("\n")
31
+ end
22
32
  end
23
33
 
24
34
  private
@@ -8,12 +8,14 @@ module CircleCI
8
8
  @hash = hash
9
9
  end
10
10
 
11
- def user_name
12
- @hash['name']
11
+ def pusher_id
12
+ @hash['pusher_id']
13
13
  end
14
14
 
15
- def self.me
16
- Account.new(CircleCi::User.me.body)
15
+ class << self
16
+ def me
17
+ Account.new(CircleCi::User.new.me.body)
18
+ end
17
19
  end
18
20
  end
19
21
  end
@@ -30,28 +30,27 @@ module CircleCI
30
30
  end
31
31
  end
32
32
 
33
+ attr_reader :username, :build_number, :reponame, :status, :author_name, :start_time,
34
+ :workflow_name, :workflow_job_name
35
+
33
36
  def initialize(hash)
34
37
  @hash = hash
38
+ @username = hash['username']
39
+ @build_number = hash['build_num']
40
+ @reponame = hash['reponame']
41
+ @status = hash['status']
42
+ @author_name = hash['author_name']
43
+ @start_time = hash['start_time']
44
+ @workflow_name = hash.dig('workflows', 'workflow_name')
45
+ @workflow_job_name = hash.dig('workflows', 'job_name')
35
46
  end
36
47
 
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']
48
+ def running?
49
+ status == 'running' || status || 'queued'
51
50
  end
52
51
 
53
- def running?
54
- status == 'running'
52
+ def finished?
53
+ status == 'success' || status == 'canceled' || status == 'failed' || status == 'no_tests'
55
54
  end
56
55
 
57
56
  def channel_name
@@ -64,13 +63,13 @@ module CircleCI
64
63
 
65
64
  def information
66
65
  [
67
- @hash['build_num'],
68
- colorize_by_status(@hash['status'], @hash['status']),
69
- colorize_by_status(@hash['branch'], @hash['status']),
70
- @hash['author_name'],
66
+ build_number,
67
+ colorize_by_status(status, status),
68
+ colorize_by_status(@hash['branch'], status),
69
+ author_name,
71
70
  (@hash['subject'] || '').slice(0..60),
72
71
  format_time(@hash['build_time_millis']),
73
- @hash['start_time']
72
+ start_time
74
73
  ]
75
74
  end
76
75
 
@@ -2,6 +2,6 @@
2
2
 
3
3
  module CircleCI
4
4
  module CLI
5
- VERSION = '1.0.0'
5
+ VERSION = '2.0.0'
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: circleci-cli
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - unhappychoice
@@ -326,6 +326,8 @@ files:
326
326
  - lib/circleci/cli/command/projects_command.rb
327
327
  - lib/circleci/cli/command/retry_command.rb
328
328
  - lib/circleci/cli/command/watch_command.rb
329
+ - lib/circleci/cli/command/watch_command/build_repository.rb
330
+ - lib/circleci/cli/command/watch_command/build_watcher.rb
329
331
  - lib/circleci/cli/networking.rb
330
332
  - lib/circleci/cli/networking/pusher_client.rb
331
333
  - lib/circleci/cli/printer.rb