circleci-cli 1.0.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|