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 +4 -4
- data/.circleci/config.yml +2 -2
- data/Gemfile.lock +2 -2
- data/lib/circleci/cli.rb +1 -0
- data/lib/circleci/cli/command/watch_command.rb +40 -43
- data/lib/circleci/cli/command/watch_command/build_repository.rb +35 -0
- data/lib/circleci/cli/command/watch_command/build_watcher.rb +78 -0
- data/lib/circleci/cli/printer/build_printer.rb +16 -4
- data/lib/circleci/cli/printer/step_printer.rb +20 -10
- data/lib/circleci/cli/response/account.rb +6 -4
- data/lib/circleci/cli/response/build.rb +20 -21
- data/lib/circleci/cli/version.rb +1 -1
- metadata +3 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4823a93a5df3fed802d98f9131b3792b9fc7c51e371688c70bf09233f804a49c
|
4
|
+
data.tar.gz: 8ede76476f63b3bf97641fa996229de56c5f8177b624080fcba2eb6c2f9c945b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b01c02e60ad803dca24506568397c243c86f0cebe00d8b4cecc881ac0a39e299fd8410a355aae03f3c5763909ea7f095e11c08f3d0a2d2f7067544de5f5fcb5d
|
7
|
+
data.tar.gz: df4a2d04fc9d52ed27e64630c788773431d4ca6ded37879480c26d3e654133d1459d6a901b0bde633f577be56ea4ef67d9cc831d3506589c62fd3de592430102
|
data/.circleci/config.yml
CHANGED
@@ -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
|
11
|
+
command: bundle install --path vendor/bundle --jobs 4
|
12
12
|
|
13
|
-
|
13
|
+
git statusrestore_bundle_cache: &restore_bundle_cache
|
14
14
|
restore_cache:
|
15
15
|
key: cache-bundler-{{ checksum "Gemfile.lock" }}
|
16
16
|
|
data/Gemfile.lock
CHANGED
data/lib/circleci/cli.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
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
|
26
|
-
@client
|
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
|
31
|
-
|
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
|
-
|
37
|
-
|
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
|
-
|
42
|
+
@build_watcher.stop(build.status)
|
43
|
+
@build_watcher = nil
|
44
|
+
show_interrupted_build_results
|
43
45
|
end
|
44
46
|
|
45
|
-
def
|
46
|
-
@
|
47
|
-
|
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
|
-
|
55
|
-
|
56
|
-
|
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
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
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
|
-
@
|
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 = @
|
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
|
-
@
|
48
|
+
@builds_to_show.map(&:information)
|
37
49
|
end
|
38
50
|
|
39
51
|
def max_row_widths
|
40
|
-
@
|
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
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
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
|
12
|
-
@hash['
|
11
|
+
def pusher_id
|
12
|
+
@hash['pusher_id']
|
13
13
|
end
|
14
14
|
|
15
|
-
|
16
|
-
|
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
|
38
|
-
|
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
|
54
|
-
status == '
|
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
|
-
|
68
|
-
colorize_by_status(
|
69
|
-
colorize_by_status(@hash['branch'],
|
70
|
-
|
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
|
-
|
72
|
+
start_time
|
74
73
|
]
|
75
74
|
end
|
76
75
|
|
data/lib/circleci/cli/version.rb
CHANGED
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:
|
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
|