cpl 0.4.1 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.overcommit.yml +10 -0
- data/Gemfile.lock +10 -3
- data/README.md +6 -0
- data/cpl.gemspec +1 -0
- data/docs/commands.md +51 -3
- data/googlee2da545df05d92f9.html +1 -0
- data/lib/command/base.rb +65 -7
- data/lib/command/build_image.rb +6 -5
- data/lib/command/cleanup_old_images.rb +8 -7
- data/lib/command/cleanup_stale_apps.rb +11 -9
- data/lib/command/config.rb +30 -15
- data/lib/command/copy_image_from_upstream.rb +110 -0
- data/lib/command/delete.rb +10 -12
- data/lib/command/deploy_image.rb +6 -5
- data/lib/command/env.rb +2 -2
- data/lib/command/exists.rb +4 -4
- data/lib/command/info.rb +233 -0
- data/lib/command/latest_image.rb +2 -2
- data/lib/command/logs.rb +4 -4
- data/lib/command/no_command.rb +3 -3
- data/lib/command/open.rb +4 -4
- data/lib/command/promote_app_from_upstream.rb +58 -0
- data/lib/command/ps.rb +10 -13
- data/lib/command/ps_restart.rb +9 -6
- data/lib/command/ps_start.rb +7 -6
- data/lib/command/ps_stop.rb +7 -6
- data/lib/command/run.rb +5 -5
- data/lib/command/run_detached.rb +7 -5
- data/lib/command/setup.rb +71 -13
- data/lib/command/test.rb +2 -2
- data/lib/command/version.rb +2 -2
- data/lib/core/config.rb +26 -19
- data/lib/core/controlplane.rb +77 -11
- data/lib/core/controlplane_api.rb +12 -0
- data/lib/core/controlplane_api_cli.rb +1 -1
- data/lib/core/controlplane_api_direct.rb +2 -2
- data/lib/core/shell.rb +25 -3
- data/lib/cpl/version.rb +1 -1
- data/lib/cpl.rb +19 -10
- data/lib/deprecated_commands.json +6 -0
- data/script/add_command +37 -0
- data/script/generate_commands_docs +5 -5
- data/script/rename_command +43 -0
- metadata +24 -2
data/lib/command/setup.rb
CHANGED
@@ -9,7 +9,7 @@ module Command
|
|
9
9
|
app_option(required: true)
|
10
10
|
].freeze
|
11
11
|
DESCRIPTION = "Applies application-specific configs from templates"
|
12
|
-
LONG_DESCRIPTION = <<~
|
12
|
+
LONG_DESCRIPTION = <<~DESC
|
13
13
|
- Applies application-specific configs from templates (e.g., for every review-app)
|
14
14
|
- Publishes (creates or updates) those at Control Plane infrastructure
|
15
15
|
- Picks templates from the `.controlplane/templates` directory
|
@@ -23,8 +23,8 @@ module Command
|
|
23
23
|
APP_ORG - organization
|
24
24
|
APP_IMAGE - will use latest app image
|
25
25
|
```
|
26
|
-
|
27
|
-
EXAMPLES = <<~
|
26
|
+
DESC
|
27
|
+
EXAMPLES = <<~EX
|
28
28
|
```sh
|
29
29
|
# Applies single template.
|
30
30
|
cpl setup redis -a $APP_NAME
|
@@ -32,31 +32,89 @@ module Command
|
|
32
32
|
# Applies several templates (practically creating full app).
|
33
33
|
cpl setup gvc postgres redis rails -a $APP_NAME
|
34
34
|
```
|
35
|
-
|
35
|
+
EX
|
36
|
+
|
37
|
+
def call # rubocop:disable Metrics/MethodLength
|
38
|
+
@app_status = :existing
|
39
|
+
@created_workloads = []
|
40
|
+
@failed_workloads = []
|
36
41
|
|
37
|
-
def call
|
38
42
|
config.args.each do |template|
|
39
43
|
filename = "#{config.app_cpln_dir}/templates/#{template}.yml"
|
40
|
-
|
41
|
-
|
42
|
-
|
44
|
+
|
45
|
+
step("Applying template '#{template}'", abort_on_error: false) do
|
46
|
+
unless File.exist?(filename)
|
47
|
+
report_failure(template)
|
48
|
+
|
49
|
+
raise "Can't find template '#{template}' at '#{filename}', please create it."
|
50
|
+
end
|
51
|
+
|
52
|
+
apply_template(filename)
|
53
|
+
if $CHILD_STATUS.success?
|
54
|
+
report_success(template)
|
55
|
+
else
|
56
|
+
report_failure(template)
|
57
|
+
end
|
58
|
+
|
59
|
+
$CHILD_STATUS.success?
|
60
|
+
end
|
43
61
|
end
|
62
|
+
|
63
|
+
print_app_status
|
64
|
+
print_created_workloads
|
65
|
+
print_failed_workloads
|
44
66
|
end
|
45
67
|
|
46
68
|
private
|
47
69
|
|
48
|
-
def ensure_template!(template, filename)
|
49
|
-
Shell.abort("Can't find template '#{template}' at '#{filename}', please create it.") unless File.exist?(filename)
|
50
|
-
end
|
51
|
-
|
52
70
|
def apply_template(filename)
|
53
71
|
data = File.read(filename)
|
54
72
|
.gsub("APP_GVC", config.app)
|
55
73
|
.gsub("APP_LOCATION", config[:default_location])
|
56
|
-
.gsub("APP_ORG", config
|
74
|
+
.gsub("APP_ORG", config.org)
|
57
75
|
.gsub("APP_IMAGE", latest_image)
|
58
76
|
|
59
77
|
cp.apply(YAML.safe_load(data))
|
60
78
|
end
|
79
|
+
|
80
|
+
def report_success(template)
|
81
|
+
if template == "gvc"
|
82
|
+
@app_status = :success
|
83
|
+
else
|
84
|
+
@created_workloads.push(template)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def report_failure(template)
|
89
|
+
if template == "gvc"
|
90
|
+
@app_status = :failure
|
91
|
+
else
|
92
|
+
@failed_workloads.push(template)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def print_app_status
|
97
|
+
return if @app_status == :existing
|
98
|
+
|
99
|
+
if @app_status == :success
|
100
|
+
progress.puts("\n#{Shell.color("Created app '#{config.app}'.", :green)}")
|
101
|
+
else
|
102
|
+
progress.puts("\n#{Shell.color("Failed to create app '#{config.app}'.", :red)}")
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def print_created_workloads
|
107
|
+
return unless @created_workloads.any?
|
108
|
+
|
109
|
+
workloads = @created_workloads.map { |template| " - #{template}" }.join("\n")
|
110
|
+
progress.puts("\n#{Shell.color('Created workloads:', :green)}\n#{workloads}")
|
111
|
+
end
|
112
|
+
|
113
|
+
def print_failed_workloads
|
114
|
+
return unless @failed_workloads.any?
|
115
|
+
|
116
|
+
workloads = @failed_workloads.map { |template| " - #{template}" }.join("\n")
|
117
|
+
progress.puts("\n#{Shell.color('Failed to create workloads:', :red)}\n#{workloads}")
|
118
|
+
end
|
61
119
|
end
|
62
120
|
end
|
data/lib/command/test.rb
CHANGED
data/lib/command/version.rb
CHANGED
@@ -4,10 +4,10 @@ module Command
|
|
4
4
|
class Version < Base
|
5
5
|
NAME = "version"
|
6
6
|
DESCRIPTION = "Displays the current version of the CLI"
|
7
|
-
LONG_DESCRIPTION = <<~
|
7
|
+
LONG_DESCRIPTION = <<~DESC
|
8
8
|
- Displays the current version of the CLI
|
9
9
|
- Can also be done with `cpl --version` or `cpl -v`
|
10
|
-
|
10
|
+
DESC
|
11
11
|
|
12
12
|
def call
|
13
13
|
puts Cpl::VERSION
|
data/lib/core/config.rb
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
class Config
|
4
4
|
attr_reader :config, :current,
|
5
|
-
:app, :app_dir,
|
5
|
+
:org, :app, :apps, :app_dir,
|
6
6
|
# command line options
|
7
7
|
:args, :options
|
8
8
|
|
@@ -11,9 +11,13 @@ class Config
|
|
11
11
|
def initialize(args, options)
|
12
12
|
@args = args
|
13
13
|
@options = options
|
14
|
+
@org = options[:org]
|
14
15
|
@app = options[:app]
|
15
16
|
|
16
17
|
load_app_config
|
18
|
+
|
19
|
+
@apps = config[:apps]
|
20
|
+
|
17
21
|
pick_current_config if app
|
18
22
|
warn_deprecated_options if current
|
19
23
|
end
|
@@ -27,7 +31,7 @@ class Config
|
|
27
31
|
elsif old_key && current.key?(old_key)
|
28
32
|
current.fetch(old_key)
|
29
33
|
else
|
30
|
-
|
34
|
+
raise "Can't find option '#{key}' for app '#{app}' in 'controlplane.yml'."
|
31
35
|
end
|
32
36
|
end
|
33
37
|
|
@@ -42,34 +46,33 @@ class Config
|
|
42
46
|
private
|
43
47
|
|
44
48
|
def ensure_current_config!
|
45
|
-
|
49
|
+
raise "Can't find current config, please specify an app." unless current
|
46
50
|
end
|
47
51
|
|
48
52
|
def ensure_current_config_app!(app)
|
49
|
-
|
53
|
+
raise "Can't find app '#{app}' in 'controlplane.yml'." unless current
|
50
54
|
end
|
51
55
|
|
52
56
|
def ensure_config!
|
53
|
-
|
57
|
+
raise "'controlplane.yml' is empty." unless config
|
54
58
|
end
|
55
59
|
|
56
60
|
def ensure_config_apps!
|
57
|
-
|
61
|
+
raise "Can't find key 'apps' in 'controlplane.yml'." unless config[:apps]
|
58
62
|
end
|
59
63
|
|
60
64
|
def ensure_config_app!(app, options)
|
61
|
-
|
65
|
+
raise "App '#{app}' is empty in 'controlplane.yml'." unless options
|
62
66
|
end
|
63
67
|
|
64
68
|
def pick_current_config
|
65
|
-
ensure_config!
|
66
|
-
ensure_config_apps!
|
67
69
|
config[:apps].each do |c_app, c_data|
|
68
70
|
ensure_config_app!(c_app, c_data)
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
71
|
+
next unless c_app.to_s == app || (c_data[:match_if_app_name_starts_with] && app.start_with?(c_app.to_s))
|
72
|
+
|
73
|
+
@current = c_data
|
74
|
+
@org = self[:cpln_org]
|
75
|
+
break
|
73
76
|
end
|
74
77
|
ensure_current_config_app!(app)
|
75
78
|
end
|
@@ -78,6 +81,8 @@ class Config
|
|
78
81
|
config_file = find_app_config_file
|
79
82
|
@config = YAML.safe_load_file(config_file, symbolize_names: true, aliases: true)
|
80
83
|
@app_dir = Pathname.new(config_file).parent.parent.to_s
|
84
|
+
ensure_config!
|
85
|
+
ensure_config_apps!
|
81
86
|
end
|
82
87
|
|
83
88
|
def find_app_config_file
|
@@ -90,7 +95,7 @@ class Config
|
|
90
95
|
path = path.parent
|
91
96
|
|
92
97
|
if path.root?
|
93
|
-
|
98
|
+
raise "Can't find project config file at 'project_folder/#{CONFIG_FILE_LOCATIION}', please create it."
|
94
99
|
end
|
95
100
|
end
|
96
101
|
end
|
@@ -104,11 +109,13 @@ class Config
|
|
104
109
|
end
|
105
110
|
|
106
111
|
def warn_deprecated_options
|
107
|
-
old_option_keys.
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
+
deprecated_option_keys = old_option_keys.filter { |_new_key, old_key| current.key?(old_key) }
|
113
|
+
return if deprecated_option_keys.empty?
|
114
|
+
|
115
|
+
deprecated_option_keys.each do |new_key, old_key|
|
116
|
+
Shell.warn_deprecated("Option '#{old_key}' is deprecated, " \
|
117
|
+
"please use '#{new_key}' instead (in 'controlplane.yml').")
|
112
118
|
end
|
119
|
+
$stderr.puts
|
113
120
|
end
|
114
121
|
end
|
data/lib/core/controlplane.rb
CHANGED
@@ -7,7 +7,30 @@ class Controlplane # rubocop:disable Metrics/ClassLength
|
|
7
7
|
@config = config
|
8
8
|
@api = ControlplaneApi.new
|
9
9
|
@gvc = config.app
|
10
|
-
@org = config
|
10
|
+
@org = config.org
|
11
|
+
end
|
12
|
+
|
13
|
+
# profile
|
14
|
+
|
15
|
+
def profile_switch(profile)
|
16
|
+
ENV["CPLN_PROFILE"] = profile
|
17
|
+
end
|
18
|
+
|
19
|
+
def profile_exists?(profile)
|
20
|
+
cmd = "cpln profile get #{profile} -o yaml"
|
21
|
+
perform_yaml(cmd).length.positive?
|
22
|
+
end
|
23
|
+
|
24
|
+
def profile_create(profile, token)
|
25
|
+
cmd = "cpln profile create #{profile} --token #{token}"
|
26
|
+
cmd += " > /dev/null" if Shell.tmp_stderr
|
27
|
+
perform!(cmd)
|
28
|
+
end
|
29
|
+
|
30
|
+
def profile_delete(profile)
|
31
|
+
cmd = "cpln profile delete #{profile}"
|
32
|
+
cmd += " > /dev/null" if Shell.tmp_stderr
|
33
|
+
perform!(cmd)
|
11
34
|
end
|
12
35
|
|
13
36
|
# image
|
@@ -18,8 +41,8 @@ class Controlplane # rubocop:disable Metrics/ClassLength
|
|
18
41
|
perform!(cmd)
|
19
42
|
end
|
20
43
|
|
21
|
-
def image_query(app_name = config.app)
|
22
|
-
cmd = "cpln image query --org #{
|
44
|
+
def image_query(app_name = config.app, org_name = config.org)
|
45
|
+
cmd = "cpln image query --org #{org_name} -o yaml --max -1 --prop repository=#{app_name}"
|
23
46
|
perform_yaml(cmd)
|
24
47
|
end
|
25
48
|
|
@@ -27,8 +50,36 @@ class Controlplane # rubocop:disable Metrics/ClassLength
|
|
27
50
|
api.image_delete(org: org, image: image)
|
28
51
|
end
|
29
52
|
|
53
|
+
def image_login(org_name = config.org)
|
54
|
+
cmd = "cpln image docker-login --org #{org_name}"
|
55
|
+
cmd += " > /dev/null 2>&1" if Shell.tmp_stderr
|
56
|
+
perform!(cmd)
|
57
|
+
end
|
58
|
+
|
59
|
+
def image_pull(image)
|
60
|
+
cmd = "docker pull #{image}"
|
61
|
+
cmd += " > /dev/null" if Shell.tmp_stderr
|
62
|
+
perform!(cmd)
|
63
|
+
end
|
64
|
+
|
65
|
+
def image_tag(old_tag, new_tag)
|
66
|
+
cmd = "docker tag #{old_tag} #{new_tag}"
|
67
|
+
cmd += " > /dev/null" if Shell.tmp_stderr
|
68
|
+
perform!(cmd)
|
69
|
+
end
|
70
|
+
|
71
|
+
def image_push(image)
|
72
|
+
cmd = "docker push #{image}"
|
73
|
+
cmd += " > /dev/null" if Shell.tmp_stderr
|
74
|
+
perform!(cmd)
|
75
|
+
end
|
76
|
+
|
30
77
|
# gvc
|
31
78
|
|
79
|
+
def fetch_gvcs
|
80
|
+
api.gvc_list(org: org)
|
81
|
+
end
|
82
|
+
|
32
83
|
def gvc_query(app_name = config.app)
|
33
84
|
# When `match_if_app_name_starts_with` is `true`, we query for any gvc containing the name,
|
34
85
|
# otherwise we query for a gvc with the exact name.
|
@@ -38,15 +89,15 @@ class Controlplane # rubocop:disable Metrics/ClassLength
|
|
38
89
|
perform_yaml(cmd)
|
39
90
|
end
|
40
91
|
|
41
|
-
def fetch_gvc(a_gvc = gvc)
|
42
|
-
api.gvc_get(gvc: a_gvc, org:
|
92
|
+
def fetch_gvc(a_gvc = gvc, a_org = org)
|
93
|
+
api.gvc_get(gvc: a_gvc, org: a_org)
|
43
94
|
end
|
44
95
|
|
45
96
|
def fetch_gvc!(a_gvc = gvc)
|
46
97
|
gvc_data = fetch_gvc(a_gvc)
|
47
98
|
return gvc_data if gvc_data
|
48
99
|
|
49
|
-
|
100
|
+
raise "Can't find GVC '#{gvc}', please create it with 'cpl setup gvc -a #{config.app}'."
|
50
101
|
end
|
51
102
|
|
52
103
|
def gvc_delete(a_gvc = gvc)
|
@@ -55,6 +106,14 @@ class Controlplane # rubocop:disable Metrics/ClassLength
|
|
55
106
|
|
56
107
|
# workload
|
57
108
|
|
109
|
+
def fetch_workloads(a_gvc = gvc)
|
110
|
+
api.workload_list(gvc: a_gvc, org: org)
|
111
|
+
end
|
112
|
+
|
113
|
+
def fetch_workloads_by_org(a_org = org)
|
114
|
+
api.workload_list_by_org(org: a_org)
|
115
|
+
end
|
116
|
+
|
58
117
|
def fetch_workload(workload)
|
59
118
|
api.workload_get(workload: workload, gvc: gvc, org: org)
|
60
119
|
end
|
@@ -63,7 +122,7 @@ class Controlplane # rubocop:disable Metrics/ClassLength
|
|
63
122
|
workload_data = fetch_workload(workload)
|
64
123
|
return workload_data if workload_data
|
65
124
|
|
66
|
-
|
125
|
+
raise "Can't find workload '#{workload}', please create it with 'cpl setup #{workload} -a #{config.app}'."
|
67
126
|
end
|
68
127
|
|
69
128
|
def workload_get_replicas(workload, location:)
|
@@ -73,7 +132,8 @@ class Controlplane # rubocop:disable Metrics/ClassLength
|
|
73
132
|
|
74
133
|
def workload_set_image_ref(workload, container:, image:)
|
75
134
|
cmd = "cpln workload update #{workload} #{gvc_org}"
|
76
|
-
cmd += " --set spec.containers.#{container}.image=/org/#{config
|
135
|
+
cmd += " --set spec.containers.#{container}.image=/org/#{config.org}/image/#{image}"
|
136
|
+
cmd += " > /dev/null" if Shell.tmp_stderr
|
77
137
|
perform!(cmd)
|
78
138
|
end
|
79
139
|
|
@@ -85,6 +145,7 @@ class Controlplane # rubocop:disable Metrics/ClassLength
|
|
85
145
|
|
86
146
|
def workload_force_redeployment(workload)
|
87
147
|
cmd = "cpln workload force-redeployment #{workload} #{gvc_org}"
|
148
|
+
cmd += " > /dev/null" if Shell.tmp_stderr
|
88
149
|
perform!(cmd)
|
89
150
|
end
|
90
151
|
|
@@ -126,12 +187,17 @@ class Controlplane # rubocop:disable Metrics/ClassLength
|
|
126
187
|
|
127
188
|
# apply
|
128
189
|
|
129
|
-
def apply(data)
|
190
|
+
def apply(data) # rubocop:disable Metrics/MethodLength
|
130
191
|
Tempfile.create do |f|
|
131
192
|
f.write(data.to_yaml)
|
132
193
|
f.rewind
|
133
194
|
cmd = "cpln apply #{gvc_org} --file #{f.path} > /dev/null"
|
134
|
-
|
195
|
+
if Shell.tmp_stderr
|
196
|
+
cmd += " 2> #{Shell.tmp_stderr.path}"
|
197
|
+
perform(cmd)
|
198
|
+
else
|
199
|
+
perform!(cmd)
|
200
|
+
end
|
135
201
|
end
|
136
202
|
end
|
137
203
|
|
@@ -147,7 +213,7 @@ class Controlplane # rubocop:disable Metrics/ClassLength
|
|
147
213
|
|
148
214
|
def perform_yaml(cmd)
|
149
215
|
result = `#{cmd}`
|
150
|
-
|
216
|
+
$CHILD_STATUS.success? ? YAML.safe_load(result) : exit(false)
|
151
217
|
end
|
152
218
|
|
153
219
|
def gvc_org
|
@@ -1,6 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
class ControlplaneApi
|
4
|
+
def gvc_list(org:)
|
5
|
+
api_json("/org/#{org}/gvc", method: :get)
|
6
|
+
end
|
7
|
+
|
4
8
|
def gvc_get(org:, gvc:)
|
5
9
|
api_json("/org/#{org}/gvc/#{gvc}", method: :get)
|
6
10
|
end
|
@@ -29,6 +33,14 @@ class ControlplaneApi
|
|
29
33
|
api_json_direct("/logs/org/#{org}/loki/api/v1/query_range?#{params}", method: :get, host: :logs)
|
30
34
|
end
|
31
35
|
|
36
|
+
def workload_list(org:, gvc:)
|
37
|
+
api_json("/org/#{org}/gvc/#{gvc}/workload", method: :get)
|
38
|
+
end
|
39
|
+
|
40
|
+
def workload_list_by_org(org:)
|
41
|
+
api_json("/org/#{org}/workload", method: :get)
|
42
|
+
end
|
43
|
+
|
32
44
|
def workload_get(org:, gvc:, workload:)
|
33
45
|
api_json("/org/#{org}/gvc/#{gvc}/workload/#{workload}", method: :get)
|
34
46
|
end
|
@@ -37,7 +37,7 @@ class ControlplaneApiDirect
|
|
37
37
|
@@api_token = ENV.fetch("CPLN_TOKEN", `cpln profile token`.chomp) # rubocop:disable Style/ClassVars
|
38
38
|
return @@api_token if @@api_token.match?(API_TOKEN_REGEX)
|
39
39
|
|
40
|
-
|
41
|
-
|
40
|
+
raise "Unknown API token format. " \
|
41
|
+
"Please re-run 'cpln profile login' or set the correct CPLN_TOKEN env variable."
|
42
42
|
end
|
43
43
|
end
|
data/lib/core/shell.rb
CHANGED
@@ -1,6 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
class Shell
|
4
|
+
class << self
|
5
|
+
attr_reader :tmp_stderr
|
6
|
+
end
|
7
|
+
|
4
8
|
def self.shell
|
5
9
|
@shell ||= Thor::Shell::Color.new
|
6
10
|
end
|
@@ -9,6 +13,24 @@ class Shell
|
|
9
13
|
@stderr ||= $stderr
|
10
14
|
end
|
11
15
|
|
16
|
+
def self.use_tmp_stderr
|
17
|
+
@tmp_stderr = Tempfile.create
|
18
|
+
|
19
|
+
yield
|
20
|
+
|
21
|
+
@tmp_stderr.close
|
22
|
+
@tmp_stderr = nil
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.write_to_tmp_stderr(message)
|
26
|
+
tmp_stderr.write(message)
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.read_from_tmp_stderr
|
30
|
+
tmp_stderr.rewind
|
31
|
+
tmp_stderr.read.strip
|
32
|
+
end
|
33
|
+
|
12
34
|
def self.color(message, color_key)
|
13
35
|
shell.set_color(message, color_key)
|
14
36
|
end
|
@@ -18,14 +40,14 @@ class Shell
|
|
18
40
|
end
|
19
41
|
|
20
42
|
def self.warn(message)
|
21
|
-
stderr.puts(color("WARNING: #{message}
|
43
|
+
stderr.puts(color("WARNING: #{message}", :yellow))
|
22
44
|
end
|
23
45
|
|
24
46
|
def self.warn_deprecated(message)
|
25
|
-
stderr.puts(color("DEPRECATED: #{message}
|
47
|
+
stderr.puts(color("DEPRECATED: #{message}", :yellow))
|
26
48
|
end
|
27
49
|
|
28
50
|
def self.abort(message)
|
29
|
-
Kernel.abort(color("ERROR: #{message}
|
51
|
+
Kernel.abort(color("ERROR: #{message}", :red))
|
30
52
|
end
|
31
53
|
end
|
data/lib/cpl/version.rb
CHANGED
data/lib/cpl.rb
CHANGED
@@ -70,12 +70,17 @@ module Cpl
|
|
70
70
|
end
|
71
71
|
|
72
72
|
def self.deprecated_commands
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
73
|
+
@deprecated_commands ||= begin
|
74
|
+
deprecated_commands_file_path = "#{__dir__}/deprecated_commands.json"
|
75
|
+
deprecated_commands_data = File.binread(deprecated_commands_file_path)
|
76
|
+
deprecated_commands = JSON.parse(deprecated_commands_data)
|
77
|
+
deprecated_commands.to_h do |old_command_name, new_command_name|
|
78
|
+
file_name = new_command_name.gsub(/[^A-Za-z]/, "_")
|
79
|
+
class_name = file_name.split("_").map(&:capitalize).join
|
80
|
+
|
81
|
+
[old_command_name, Object.const_get("::Command::#{class_name}")]
|
82
|
+
end
|
83
|
+
end
|
79
84
|
end
|
80
85
|
|
81
86
|
def self.all_base_commands
|
@@ -113,6 +118,7 @@ module Cpl
|
|
113
118
|
if deprecated
|
114
119
|
::Shell.warn_deprecated("Command '#{command_key}' is deprecated, " \
|
115
120
|
"please use '#{name}' instead.")
|
121
|
+
$stderr.puts
|
116
122
|
end
|
117
123
|
|
118
124
|
args = if provided_args.length.positive?
|
@@ -123,13 +129,16 @@ module Cpl
|
|
123
129
|
|
124
130
|
raise_args_error.call(args, nil) if (args.empty? && requires_args) || (!args.empty? && !requires_args)
|
125
131
|
|
126
|
-
|
132
|
+
begin
|
133
|
+
config = Config.new(args, options)
|
127
134
|
|
128
|
-
|
135
|
+
command_class.new(config).call
|
136
|
+
rescue RuntimeError => e
|
137
|
+
::Shell.abort(e.message)
|
138
|
+
end
|
129
139
|
end
|
130
140
|
rescue StandardError => e
|
131
|
-
|
132
|
-
logger.puts("Unable to load command: #{e.message}")
|
141
|
+
::Shell.abort("Unable to load command: #{e.message}")
|
133
142
|
end
|
134
143
|
end
|
135
144
|
end
|
data/script/add_command
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
command_name = ARGV[0]&.downcase
|
5
|
+
|
6
|
+
abort("ERROR: Must provide command name.") unless command_name
|
7
|
+
|
8
|
+
file_name = command_name.gsub(/[^A-Za-z]/, "_")
|
9
|
+
file_path = "#{__dir__}/../lib/command/#{file_name}.rb"
|
10
|
+
|
11
|
+
abort("ERROR: Command '#{command_name}' already exists.") if File.exist?(file_path)
|
12
|
+
|
13
|
+
class_name = file_name.split("_").map(&:capitalize).join
|
14
|
+
|
15
|
+
file_data =
|
16
|
+
<<~DATA
|
17
|
+
# frozen_string_literal: true
|
18
|
+
|
19
|
+
module Command
|
20
|
+
class #{class_name} < Base
|
21
|
+
# See `base.rb` for other constants to add here
|
22
|
+
NAME = "#{command_name}"
|
23
|
+
OPTIONS = [
|
24
|
+
# Add options here
|
25
|
+
].freeze
|
26
|
+
DESCRIPTION = "Add description here"
|
27
|
+
LONG_DESCRIPTION = <<~DESC
|
28
|
+
- Add long description here
|
29
|
+
DESC
|
30
|
+
|
31
|
+
def call
|
32
|
+
# Add command logic here
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
DATA
|
37
|
+
File.binwrite(file_path, file_data)
|
@@ -41,9 +41,9 @@ end
|
|
41
41
|
|
42
42
|
commands_str = commands_str_arr.join("\n\n")
|
43
43
|
|
44
|
-
|
45
|
-
|
46
|
-
<<~
|
44
|
+
file_path = "#{__dir__}/../docs/commands.md"
|
45
|
+
file_data =
|
46
|
+
<<~DATA
|
47
47
|
<!-- NOTE: This file is automatically generated by running `script/generate_commands_docs`. Do NOT edit it manually. -->
|
48
48
|
|
49
49
|
### Common Options
|
@@ -58,5 +58,5 @@ commands_data =
|
|
58
58
|
### Commands
|
59
59
|
|
60
60
|
#{commands_str}
|
61
|
-
|
62
|
-
File.binwrite(
|
61
|
+
DATA
|
62
|
+
File.binwrite(file_path, file_data)
|