circleci-cli 2.3.0 → 4.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/.all-contributorsrc +12 -1
- data/.github/dependabot.yml +38 -0
- data/.github/workflows/release.yaml +41 -0
- data/.github/workflows/test.yml +20 -3
- data/.gitignore +57 -0
- data/.rubocop.yml +12 -1
- data/.ruby-version +1 -0
- data/CHANGELOG.md +182 -7
- data/Gemfile.lock +163 -100
- data/Guardfile +1 -1
- data/README.md +38 -29
- data/circleci-cli.gemspec +25 -20
- data/lib/circleci/cli/command/base_command.rb +7 -14
- data/lib/circleci/cli/command/build_command.rb +1 -1
- data/lib/circleci/cli/command/builds_command.rb +9 -9
- data/lib/circleci/cli/command/projects_command.rb +1 -1
- data/lib/circleci/cli/command/watch_command/build_repository.rb +7 -2
- data/lib/circleci/cli/command/watch_command/build_watcher.rb +18 -11
- data/lib/circleci/cli/command/watch_command.rb +6 -1
- data/lib/circleci/cli/networking/pusher_client.rb +16 -22
- data/lib/circleci/cli/printer/build_printer.rb +9 -9
- data/lib/circleci/cli/printer/project_printer.rb +2 -1
- data/lib/circleci/cli/printer/step_printer.rb +9 -9
- data/lib/circleci/cli/printer.rb +25 -1
- data/lib/circleci/cli/response/action.rb +1 -0
- data/lib/circleci/cli/response/build.rb +7 -7
- data/lib/circleci/cli/response/step.rb +1 -2
- data/lib/circleci/cli/version.rb +1 -1
- data/lib/circleci/cli.rb +119 -31
- metadata +77 -44
- data/.circleci/config.yml +0 -68
@@ -7,7 +7,6 @@ module CircleCI
|
|
7
7
|
attr_reader :build
|
8
8
|
|
9
9
|
def initialize(build, verbose: false)
|
10
|
-
@client = Networking::CircleCIPusherClient.new.tap(&:connect)
|
11
10
|
@build = build
|
12
11
|
@verbose = verbose
|
13
12
|
@messages = Hash.new { |h, k| h[k] = [] }
|
@@ -19,14 +18,14 @@ module CircleCI
|
|
19
18
|
end
|
20
19
|
|
21
20
|
def stop(status)
|
22
|
-
|
21
|
+
client.unsubscribe("#{@build.channel_name}@0")
|
23
22
|
notify_stopped(status)
|
24
23
|
end
|
25
24
|
|
26
25
|
private
|
27
26
|
|
28
27
|
def bind_event_handling(channel) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
29
|
-
|
28
|
+
client.bind_event_json(channel, 'newAction') do |json|
|
30
29
|
if @verbose
|
31
30
|
print_bordered json['log']['name']
|
32
31
|
else
|
@@ -34,7 +33,7 @@ module CircleCI
|
|
34
33
|
end
|
35
34
|
end
|
36
35
|
|
37
|
-
|
36
|
+
client.bind_event_json(channel, 'appendAction') do |json|
|
38
37
|
if @verbose
|
39
38
|
Thor::Shell::Basic.new.say(json['out']['message'], nil, false)
|
40
39
|
else
|
@@ -42,28 +41,32 @@ module CircleCI
|
|
42
41
|
end
|
43
42
|
end
|
44
43
|
|
45
|
-
|
44
|
+
client.bind_event_json(channel, 'updateAction') do |json|
|
46
45
|
next if @verbose
|
47
46
|
|
48
47
|
case json['log']['status']
|
49
48
|
when 'success'
|
50
|
-
puts "\e[2K\r#{json['log']['name']
|
49
|
+
puts "\e[2K\r#{Printer.colorize_green(json['log']['name'])}"
|
51
50
|
when 'failed'
|
52
|
-
puts "\e[2K\r#{json['log']['name']
|
51
|
+
puts "\e[2K\r#{Printer.colorize_red(json['log']['name'])}"
|
53
52
|
@messages[json['step']].each(&method(:say))
|
54
53
|
end
|
55
54
|
end
|
56
55
|
end
|
57
56
|
|
58
57
|
def notify_started
|
59
|
-
|
60
|
-
|
58
|
+
say Printer::BuildPrinter.header_for(
|
59
|
+
@build,
|
60
|
+
"👀 Start watching #{@build.project_name} ##{@build.build_number}"
|
61
|
+
)
|
61
62
|
end
|
62
63
|
|
63
64
|
def notify_stopped(status)
|
64
65
|
text = case status
|
65
|
-
when 'success'
|
66
|
-
|
66
|
+
when 'success'
|
67
|
+
Printer.colorize_green("🎉 #{@build.project_name} ##{@build.build_number} has succeeded!")
|
68
|
+
when 'failed'
|
69
|
+
Printer.colorize_red("😥 #{@build.project_name} ##{@build.build_number} has failed...")
|
67
70
|
end
|
68
71
|
|
69
72
|
@verbose ? print_bordered(text) : say(text)
|
@@ -72,6 +75,10 @@ module CircleCI
|
|
72
75
|
def print_bordered(text)
|
73
76
|
say Terminal::Table.new(rows: [[text]], style: { width: 120 }).to_s
|
74
77
|
end
|
78
|
+
|
79
|
+
def client
|
80
|
+
@client ||= Networking::CircleCIPusherClient.new.tap(&:connect)
|
81
|
+
end
|
75
82
|
end
|
76
83
|
end
|
77
84
|
end
|
@@ -13,7 +13,12 @@ module CircleCI
|
|
13
13
|
|
14
14
|
username, reponame = project_name(options).split('/')
|
15
15
|
@options = options
|
16
|
-
@repository = BuildRepository.new(
|
16
|
+
@repository = BuildRepository.new(
|
17
|
+
username,
|
18
|
+
reponame,
|
19
|
+
branch: branch_name(options),
|
20
|
+
user: options.user
|
21
|
+
)
|
17
22
|
@client = Networking::CircleCIPusherClient.new.tap(&:connect)
|
18
23
|
@build_watcher = nil
|
19
24
|
|
@@ -8,45 +8,39 @@ module CircleCI
|
|
8
8
|
class CircleCIPusherClient
|
9
9
|
def connect
|
10
10
|
PusherClient.logger.level = Logger::ERROR
|
11
|
-
|
12
|
-
@socket.connect(true)
|
11
|
+
socket.connect(true)
|
13
12
|
end
|
14
13
|
|
15
|
-
def bind(channel, event)
|
16
|
-
|
17
|
-
|
18
|
-
yield data
|
19
|
-
end
|
14
|
+
def bind(channel, event, &)
|
15
|
+
socket.subscribe(channel)
|
16
|
+
socket[channel].bind(event, &)
|
20
17
|
end
|
21
18
|
|
22
|
-
def bind_event_json(channel, event)
|
23
|
-
bind(channel, event)
|
24
|
-
JSON.parse(data).each { |json| yield(json) }
|
25
|
-
end
|
19
|
+
def bind_event_json(channel, event, &)
|
20
|
+
bind(channel, event) { |data| JSON.parse(data).each(&) }
|
26
21
|
end
|
27
22
|
|
28
23
|
def unsubscribe(channel)
|
29
|
-
|
24
|
+
socket.unsubscribe(channel)
|
30
25
|
end
|
31
26
|
|
32
27
|
private
|
33
28
|
|
34
|
-
def
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
def pusher_options
|
39
|
-
{
|
29
|
+
def socket
|
30
|
+
@socket ||= PusherClient::Socket.new(
|
31
|
+
'1cf6e0e755e419d2ac9a',
|
40
32
|
secure: true,
|
41
33
|
auth_method: proc { |a, b| auth(a, b) },
|
42
34
|
logger: Logger.new('/dev/null')
|
43
|
-
|
35
|
+
)
|
44
36
|
end
|
45
37
|
|
46
38
|
def auth(socket_id, channel)
|
47
|
-
|
48
|
-
|
49
|
-
|
39
|
+
token = ENV.fetch('CIRCLE_CI_TOKEN', nil) || ask('Circle CI token ? :')
|
40
|
+
res = connection.post(
|
41
|
+
"/auth/pusher?circle-token=#{token}",
|
42
|
+
{ socket_id:, channel_name: channel.name }
|
43
|
+
)
|
50
44
|
JSON.parse(res.body)['auth']
|
51
45
|
end
|
52
46
|
|
@@ -7,17 +7,18 @@ module CircleCI
|
|
7
7
|
class << self
|
8
8
|
def header_for(build, title)
|
9
9
|
texts = [
|
10
|
-
[
|
11
|
-
[
|
12
|
-
[
|
13
|
-
[
|
10
|
+
["Project: #{build.project_name}"],
|
11
|
+
["Build: #{build.build_number}"],
|
12
|
+
["Author: #{build.author_name}"],
|
13
|
+
["Workflow: #{build.workflow_name}/#{build.workflow_job_name}"]
|
14
14
|
]
|
15
|
-
Terminal::Table.new(title
|
15
|
+
Terminal::Table.new(title:, rows: texts, style: { width: 120 }).to_s
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
19
|
-
def initialize(builds, pretty: true)
|
19
|
+
def initialize(builds, project_name, pretty: true)
|
20
20
|
@builds_to_show = builds
|
21
|
+
@project_name = project_name
|
21
22
|
@pretty = pretty
|
22
23
|
end
|
23
24
|
|
@@ -32,12 +33,11 @@ module CircleCI
|
|
32
33
|
end
|
33
34
|
|
34
35
|
def print_pretty
|
35
|
-
Terminal::Table.new(title
|
36
|
+
Terminal::Table.new(title:, headings:, rows:).to_s
|
36
37
|
end
|
37
38
|
|
38
39
|
def title
|
39
|
-
|
40
|
-
"Recent Builds / #{build.project_name}".green
|
40
|
+
Printer.colorize_green("Recent Builds / #{@project_name}")
|
41
41
|
end
|
42
42
|
|
43
43
|
def headings
|
@@ -5,6 +5,7 @@ module CircleCI
|
|
5
5
|
module Printer
|
6
6
|
class ProjectPrinter
|
7
7
|
attr_accessor :compact
|
8
|
+
|
8
9
|
def initialize(projects, pretty: true)
|
9
10
|
@projects = projects
|
10
11
|
@pretty = pretty
|
@@ -26,7 +27,7 @@ module CircleCI
|
|
26
27
|
|
27
28
|
def print_pretty
|
28
29
|
Terminal::Table.new(
|
29
|
-
title: 'Projects'
|
30
|
+
title: Printer.colorize_green('Projects'),
|
30
31
|
headings: ['User name', 'Repository name'],
|
31
32
|
rows: @projects.map(&:information)
|
32
33
|
).to_s
|
@@ -9,14 +9,14 @@ module CircleCI
|
|
9
9
|
@pretty = pretty
|
10
10
|
end
|
11
11
|
|
12
|
-
def to_s # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
12
|
+
def to_s # rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
13
13
|
if @pretty
|
14
14
|
Terminal::Table.new do |t|
|
15
15
|
@steps
|
16
16
|
.group_by(&:type)
|
17
17
|
.each do |key, steps|
|
18
18
|
t << :separator
|
19
|
-
t << [{ value: key
|
19
|
+
t << [{ value: Printer.colorize_green(key), alignment: :center, colspan: 2 }]
|
20
20
|
steps.each { |s| print_actions(t, s) }
|
21
21
|
end
|
22
22
|
end.to_s
|
@@ -26,7 +26,7 @@ module CircleCI
|
|
26
26
|
step.actions.map do |a|
|
27
27
|
"#{colorize_by_status(a.name.slice(0..120), a.status)}\n#{"#{a.log}\n" if a.failed? && a.log}"
|
28
28
|
end
|
29
|
-
end.flatten.join
|
29
|
+
end.flatten.join
|
30
30
|
end.join("\n")
|
31
31
|
end
|
32
32
|
end
|
@@ -35,10 +35,10 @@ module CircleCI
|
|
35
35
|
|
36
36
|
def colorize_by_status(string, status)
|
37
37
|
case status
|
38
|
-
when 'success', 'fixed' then string
|
39
|
-
when 'canceled' then string
|
40
|
-
when 'failed', 'timedout' then string
|
41
|
-
when 'no_tests', 'not_run' then string
|
38
|
+
when 'success', 'fixed' then Printer.colorize_green(string)
|
39
|
+
when 'canceled' then Printer.colorize_yellow(string)
|
40
|
+
when 'failed', 'timedout' then Printer.colorize_red(string)
|
41
|
+
when 'no_tests', 'not_run' then Printer.colorize_light_black(string)
|
42
42
|
else string
|
43
43
|
end
|
44
44
|
end
|
@@ -46,8 +46,8 @@ module CircleCI
|
|
46
46
|
def format_time(time)
|
47
47
|
return '' unless time
|
48
48
|
|
49
|
-
minute = format('
|
50
|
-
second = format('
|
49
|
+
minute = format('%<time>02d', time: time / 1000 / 60)
|
50
|
+
second = format('%<time>02d', time: (time / 1000) % 60)
|
51
51
|
"#{minute}:#{second}"
|
52
52
|
end
|
53
53
|
|
data/lib/circleci/cli/printer.rb
CHANGED
@@ -6,6 +6,30 @@ require 'circleci/cli/printer/step_printer'
|
|
6
6
|
|
7
7
|
module CircleCI
|
8
8
|
module CLI
|
9
|
-
module Printer
|
9
|
+
module Printer
|
10
|
+
class << self
|
11
|
+
def colorize_red(string)
|
12
|
+
colorize(string, '0;31;49')
|
13
|
+
end
|
14
|
+
|
15
|
+
def colorize_green(string)
|
16
|
+
colorize(string, '0;32;49')
|
17
|
+
end
|
18
|
+
|
19
|
+
def colorize_yellow(string)
|
20
|
+
colorize(string, '0;33;49')
|
21
|
+
end
|
22
|
+
|
23
|
+
def colorize_light_black(string)
|
24
|
+
colorize(string, '0;90;49')
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def colorize(string, color_code)
|
30
|
+
"\e[#{color_code}m#{string}\e[0m"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
10
34
|
end
|
11
35
|
end
|
@@ -39,7 +39,7 @@ module CircleCI
|
|
39
39
|
attr_reader :username, :build_number, :reponame, :branch, :status, :author_name, :start_time,
|
40
40
|
:user, :workflow_name, :workflow_job_name
|
41
41
|
|
42
|
-
def initialize(hash)
|
42
|
+
def initialize(hash) # rubocop:disable Metrics/MethodLength
|
43
43
|
@hash = hash
|
44
44
|
@username = hash['username']
|
45
45
|
@build_number = hash['build_num']
|
@@ -90,10 +90,10 @@ module CircleCI
|
|
90
90
|
|
91
91
|
def colorize_by_status(string, status)
|
92
92
|
case status
|
93
|
-
when 'success', 'fixed' then string
|
94
|
-
when 'canceled' then string
|
95
|
-
when 'failed' then string
|
96
|
-
when 'no_tests', 'not_run' then string
|
93
|
+
when 'success', 'fixed' then Printer.colorize_green(string)
|
94
|
+
when 'canceled' then Printer.colorize_yellow(string)
|
95
|
+
when 'failed' then Printer.colorize_red(string)
|
96
|
+
when 'no_tests', 'not_run' then Printer.colorize_light_black(string)
|
97
97
|
else string
|
98
98
|
end
|
99
99
|
end
|
@@ -101,8 +101,8 @@ module CircleCI
|
|
101
101
|
def format_time(time)
|
102
102
|
return '' unless time
|
103
103
|
|
104
|
-
minute = format('
|
105
|
-
second = format('
|
104
|
+
minute = format('%<time>02d', time: time / 1000 / 60)
|
105
|
+
second = format('%<time>02d', time: (time / 1000) % 60)
|
106
106
|
"#{minute}:#{second}"
|
107
107
|
end
|
108
108
|
end
|
data/lib/circleci/cli/version.rb
CHANGED
data/lib/circleci/cli.rb
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require '
|
3
|
+
require 'circleci'
|
4
4
|
require 'faraday'
|
5
|
-
require 'launchy'
|
6
|
-
require 'terminal-table'
|
7
5
|
require 'highline/import'
|
8
|
-
require '
|
6
|
+
require 'launchy'
|
7
|
+
require 'ostruct'
|
9
8
|
require 'rugged'
|
10
|
-
require 'circleci'
|
11
9
|
require 'terminal-notifier'
|
10
|
+
require 'terminal-table'
|
11
|
+
require 'thor'
|
12
12
|
|
13
13
|
require 'circleci/cli/version'
|
14
14
|
require 'circleci/cli/command'
|
@@ -18,61 +18,149 @@ require 'circleci/cli/networking'
|
|
18
18
|
|
19
19
|
module CircleCI
|
20
20
|
module CLI
|
21
|
-
class Runner < Thor
|
22
|
-
|
23
|
-
|
21
|
+
class Runner < Thor # rubocop:disable Metrics/ClassLength
|
22
|
+
package_name 'circleci-cli'
|
23
|
+
|
24
|
+
class << self
|
25
|
+
def project
|
26
|
+
repository = Rugged::Repository.new('.')
|
27
|
+
origin = repository.remotes.find { |r| r.name == 'origin' }
|
28
|
+
regexp = %r{(?:git@|https://)github.com(?::|/)([\w_-]+/[.\w_-]+?)(?:\.git)*$}
|
29
|
+
return Regexp.last_match(1) if origin.url =~ regexp
|
30
|
+
|
31
|
+
nil
|
32
|
+
end
|
33
|
+
|
34
|
+
def branch_name
|
35
|
+
repository = Rugged::Repository.new('.')
|
36
|
+
head = repository.head
|
37
|
+
|
38
|
+
return nil unless head.branch?
|
39
|
+
|
40
|
+
head.name.sub(%r{\Arefs/heads/}, '')
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
desc 'projects', 'List projects'
|
45
|
+
method_option :pretty, type: :boolean, default: true, desc: 'Make output pretty'
|
24
46
|
def projects
|
25
47
|
Command::ProjectsCommand.run(options)
|
26
48
|
end
|
27
49
|
|
28
|
-
desc 'builds', '
|
29
|
-
method_option :project,
|
30
|
-
|
31
|
-
|
50
|
+
desc 'builds', 'List builds'
|
51
|
+
method_option :project,
|
52
|
+
aliases: 'p',
|
53
|
+
type: :string,
|
54
|
+
banner: 'user/project',
|
55
|
+
default: project,
|
56
|
+
desc: 'A project you want to get.'
|
57
|
+
method_option :branch,
|
58
|
+
aliases: 'b',
|
59
|
+
type: :string,
|
60
|
+
banner: 'some-branch',
|
61
|
+
default: branch_name,
|
62
|
+
desc: 'A branch name you want to filter with.'
|
63
|
+
method_option :all,
|
64
|
+
aliases: 'a',
|
65
|
+
type: :boolean,
|
66
|
+
default: false,
|
67
|
+
desc: 'Target all the branches. This option overwrites branch option.'
|
68
|
+
method_option :pretty, type: :boolean, banner: 'true/false', default: true, desc: 'Make output pretty.'
|
32
69
|
def builds
|
33
70
|
Command::BuildsCommand.run(options)
|
34
71
|
end
|
35
72
|
|
36
|
-
desc 'build', '
|
37
|
-
method_option :project,
|
38
|
-
|
39
|
-
|
73
|
+
desc 'build', 'Show the build result'
|
74
|
+
method_option :project,
|
75
|
+
aliases: 'p',
|
76
|
+
type: :string,
|
77
|
+
banner: 'user/project',
|
78
|
+
default: project,
|
79
|
+
desc: 'A project you want to get.'
|
80
|
+
method_option :build, aliases: 'n', type: :numeric, banner: 'build-number', desc: 'Build number you want to get.'
|
81
|
+
method_option :last, aliases: 'l', type: :boolean, default: false, desc: 'Get last failed build.'
|
82
|
+
method_option :pretty, type: :boolean, banner: 'true/false', default: true, desc: 'Make output pretty.'
|
40
83
|
def build
|
41
84
|
Command::BuildCommand.run(options)
|
42
85
|
end
|
43
86
|
|
44
|
-
desc 'browse', '
|
45
|
-
method_option :project,
|
46
|
-
|
87
|
+
desc 'browse', 'Open CircleCI website'
|
88
|
+
method_option :project,
|
89
|
+
aliases: 'p',
|
90
|
+
type: :string,
|
91
|
+
banner: 'user/project',
|
92
|
+
default: project,
|
93
|
+
desc: 'A project you want to get.'
|
94
|
+
method_option :build,
|
95
|
+
aliases: 'n',
|
96
|
+
type: :numeric,
|
97
|
+
banner: 'build-number',
|
98
|
+
desc: 'Build number you want to browse.'
|
47
99
|
def browse
|
48
100
|
Command::BrowseCommand.run(options)
|
49
101
|
end
|
50
102
|
|
51
|
-
desc 'retry', '
|
52
|
-
method_option :project,
|
53
|
-
|
54
|
-
|
103
|
+
desc 'retry', 'Retry a build'
|
104
|
+
method_option :project,
|
105
|
+
aliases: 'p',
|
106
|
+
type: :string,
|
107
|
+
banner: 'user/project',
|
108
|
+
default: project,
|
109
|
+
desc: 'A project you want to get.'
|
110
|
+
method_option :build,
|
111
|
+
aliases: 'n',
|
112
|
+
type: :numeric,
|
113
|
+
banner: 'build-number',
|
114
|
+
desc: 'Build number you want to retry.'
|
115
|
+
method_option :last, aliases: 'l', type: :boolean, desc: 'Retry last failed build.'
|
55
116
|
def retry
|
56
117
|
Command::RetryCommand.run(options)
|
57
118
|
end
|
58
119
|
|
59
|
-
desc 'cancel', '
|
60
|
-
method_option :project,
|
61
|
-
|
120
|
+
desc 'cancel', 'Cancel a build'
|
121
|
+
method_option :project,
|
122
|
+
aliases: 'p',
|
123
|
+
type: :string,
|
124
|
+
default: project,
|
125
|
+
desc: 'A project you want to get.'
|
126
|
+
method_option :build,
|
127
|
+
aliases: 'n',
|
128
|
+
type: :numeric,
|
129
|
+
banner: 'build-number',
|
130
|
+
desc: 'Build number you want to cancel.'
|
62
131
|
def cancel
|
63
132
|
Command::CancelCommand.run(options)
|
64
133
|
end
|
65
134
|
|
66
|
-
desc 'watch', '
|
67
|
-
method_option :project,
|
68
|
-
|
135
|
+
desc 'watch', 'Watch builds in real time'
|
136
|
+
method_option :project,
|
137
|
+
aliases: 'p',
|
138
|
+
type: :string,
|
139
|
+
banner: 'user/project',
|
140
|
+
default: project,
|
141
|
+
desc: 'A project you want to get.'
|
142
|
+
method_option :branch,
|
143
|
+
aliases: 'b',
|
144
|
+
type: :string,
|
145
|
+
banner: 'some-branch',
|
146
|
+
default: branch_name,
|
147
|
+
desc: 'A branch name you want to filter with.'
|
148
|
+
method_option :all,
|
149
|
+
aliases: 'a',
|
150
|
+
type: :boolean,
|
151
|
+
default: false,
|
152
|
+
desc: 'Target all the branches. This option overwrites branch option.'
|
69
153
|
method_option :user, aliases: 'u', type: :string, banner: 'user'
|
70
|
-
method_option :verbose,
|
154
|
+
method_option :verbose,
|
155
|
+
aliases: 'v',
|
156
|
+
type: :boolean,
|
157
|
+
default: false,
|
158
|
+
desc: 'Show all the build logs including successful build steps.'
|
71
159
|
def watch
|
72
160
|
Command::WatchCommand.run(options)
|
73
161
|
end
|
74
162
|
|
75
|
-
desc 'version', '
|
163
|
+
desc 'version', 'Show gem version'
|
76
164
|
def version
|
77
165
|
say CircleCI::CLI::VERSION
|
78
166
|
end
|