cpl 0.6.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/command_docs.yml +24 -0
- data/.github/workflows/rspec.yml +8 -5
- data/.github/workflows/rubocop.yml +5 -2
- data/.overcommit.yml +3 -0
- data/.rubocop.yml +6 -0
- data/Gemfile.lock +18 -3
- data/README.md +67 -20
- data/Rakefile +9 -4
- data/cpl.gemspec +3 -0
- data/docs/commands.md +72 -5
- data/examples/circleci.yml +1 -1
- data/examples/controlplane.yml +3 -0
- data/lib/command/base.rb +61 -28
- data/lib/command/build_image.rb +15 -3
- data/lib/command/copy_image_from_upstream.rb +1 -1
- data/lib/command/info.rb +9 -5
- data/lib/command/maintenance.rb +37 -0
- data/lib/command/maintenance_off.rb +58 -0
- data/lib/command/maintenance_on.rb +58 -0
- data/lib/command/maintenance_set_page.rb +34 -0
- data/lib/command/no_command.rb +1 -1
- data/lib/command/promote_app_from_upstream.rb +2 -2
- data/lib/command/ps_start.rb +22 -5
- data/lib/command/ps_stop.rb +22 -5
- data/lib/command/run.rb +30 -10
- data/lib/command/run_cleanup.rb +99 -0
- data/lib/command/run_detached.rb +18 -11
- data/lib/command/setup_app.rb +3 -3
- data/lib/core/config.rb +39 -32
- data/lib/core/controlplane.rb +72 -16
- data/lib/core/controlplane_api.rb +39 -0
- data/lib/core/controlplane_api_direct.rb +13 -3
- data/lib/core/scripts.rb +2 -2
- data/lib/cpl/version.rb +1 -1
- data/script/check_command_docs +3 -0
- data/templates/daily-task.yml +30 -0
- data/templates/maintenance.yml +24 -0
- metadata +54 -3
- /data/script/{generate_commands_docs → update_command_docs} +0 -0
data/lib/core/config.rb
CHANGED
@@ -15,24 +15,15 @@ class Config
|
|
15
15
|
@app = options[:app]
|
16
16
|
|
17
17
|
load_app_config
|
18
|
-
|
19
|
-
@apps = config[:apps]
|
20
|
-
|
21
|
-
pick_current_config if app
|
22
|
-
warn_deprecated_options if current
|
18
|
+
load_apps
|
23
19
|
end
|
24
20
|
|
25
21
|
def [](key)
|
26
22
|
ensure_current_config!
|
27
23
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
elsif old_key && current.key?(old_key)
|
32
|
-
current.fetch(old_key)
|
33
|
-
else
|
34
|
-
raise "Can't find option '#{key}' for app '#{app}' in 'controlplane.yml'."
|
35
|
-
end
|
24
|
+
raise "Can't find option '#{key}' for app '#{app}' in 'controlplane.yml'." unless current.key?(key)
|
25
|
+
|
26
|
+
current.fetch(key)
|
36
27
|
end
|
37
28
|
|
38
29
|
def script_path
|
@@ -49,8 +40,8 @@ class Config
|
|
49
40
|
raise "Can't find current config, please specify an app." unless current
|
50
41
|
end
|
51
42
|
|
52
|
-
def ensure_current_config_app!(
|
53
|
-
raise "Can't find app '#{
|
43
|
+
def ensure_current_config_app!(app_name)
|
44
|
+
raise "Can't find app '#{app_name}' in 'controlplane.yml'." unless current
|
54
45
|
end
|
55
46
|
|
56
47
|
def ensure_config!
|
@@ -61,20 +52,36 @@ class Config
|
|
61
52
|
raise "Can't find key 'apps' in 'controlplane.yml'." unless config[:apps]
|
62
53
|
end
|
63
54
|
|
64
|
-
def ensure_config_app!(
|
65
|
-
raise "App '#{
|
55
|
+
def ensure_config_app!(app_name, app_options)
|
56
|
+
raise "App '#{app_name}' is empty in 'controlplane.yml'." unless app_options
|
57
|
+
end
|
58
|
+
|
59
|
+
def app_matches_current?(app_name, app_options)
|
60
|
+
app && (app_name.to_s == app || (app_options[:match_if_app_name_starts_with] && app.start_with?(app_name.to_s)))
|
66
61
|
end
|
67
62
|
|
68
|
-
def pick_current_config
|
69
|
-
|
70
|
-
|
71
|
-
|
63
|
+
def pick_current_config(app_name, app_options)
|
64
|
+
@current = app_options
|
65
|
+
@org = self[:cpln_org]
|
66
|
+
ensure_current_config_app!(app_name)
|
67
|
+
end
|
68
|
+
|
69
|
+
def load_apps # rubocop:disable Metrics/MethodLength
|
70
|
+
@apps = config[:apps].to_h do |app_name, app_options|
|
71
|
+
ensure_config_app!(app_name, app_options)
|
72
|
+
|
73
|
+
app_options_with_new_keys = app_options.to_h do |key, value|
|
74
|
+
new_key = new_option_keys[key]
|
75
|
+
new_key ? [new_key, value] : [key, value]
|
76
|
+
end
|
77
|
+
|
78
|
+
if app_matches_current?(app_name, app_options_with_new_keys)
|
79
|
+
pick_current_config(app_name, app_options_with_new_keys)
|
80
|
+
warn_deprecated_options(app_options)
|
81
|
+
end
|
72
82
|
|
73
|
-
|
74
|
-
@org = self[:cpln_org]
|
75
|
-
break
|
83
|
+
[app_name, app_options_with_new_keys]
|
76
84
|
end
|
77
|
-
ensure_current_config_app!(app)
|
78
85
|
end
|
79
86
|
|
80
87
|
def load_app_config
|
@@ -100,19 +107,19 @@ class Config
|
|
100
107
|
end
|
101
108
|
end
|
102
109
|
|
103
|
-
def
|
110
|
+
def new_option_keys
|
104
111
|
{
|
105
|
-
|
106
|
-
|
107
|
-
|
112
|
+
org: :cpln_org,
|
113
|
+
location: :default_location,
|
114
|
+
prefix: :match_if_app_name_starts_with
|
108
115
|
}
|
109
116
|
end
|
110
117
|
|
111
|
-
def warn_deprecated_options
|
112
|
-
deprecated_option_keys =
|
118
|
+
def warn_deprecated_options(app_options)
|
119
|
+
deprecated_option_keys = new_option_keys.filter { |old_key| app_options.key?(old_key) }
|
113
120
|
return if deprecated_option_keys.empty?
|
114
121
|
|
115
|
-
deprecated_option_keys.each do |
|
122
|
+
deprecated_option_keys.each do |old_key, new_key|
|
116
123
|
Shell.warn_deprecated("Option '#{old_key}' is deprecated, " \
|
117
124
|
"please use '#{new_key}' instead (in 'controlplane.yml').")
|
118
125
|
end
|
data/lib/core/controlplane.rb
CHANGED
@@ -35,10 +35,13 @@ class Controlplane # rubocop:disable Metrics/ClassLength
|
|
35
35
|
|
36
36
|
# image
|
37
37
|
|
38
|
-
def image_build(image, dockerfile:, push: true)
|
39
|
-
cmd = "
|
40
|
-
cmd += " --
|
38
|
+
def image_build(image, dockerfile:, build_args: [], push: true)
|
39
|
+
cmd = "docker build -t #{image} -f #{dockerfile}"
|
40
|
+
build_args.each { |build_arg| cmd += " --build-arg #{build_arg}" }
|
41
|
+
cmd += " #{config.app_dir}"
|
41
42
|
perform!(cmd)
|
43
|
+
|
44
|
+
image_push(image) if push
|
42
45
|
end
|
43
46
|
|
44
47
|
def image_query(app_name = config.app, org_name = config.org)
|
@@ -97,7 +100,7 @@ class Controlplane # rubocop:disable Metrics/ClassLength
|
|
97
100
|
gvc_data = fetch_gvc(a_gvc)
|
98
101
|
return gvc_data if gvc_data
|
99
102
|
|
100
|
-
raise "Can't find
|
103
|
+
raise "Can't find app '#{gvc}', please create it with 'cpl setup-app -a #{config.app}'."
|
101
104
|
end
|
102
105
|
|
103
106
|
def gvc_delete(a_gvc = gvc)
|
@@ -122,7 +125,13 @@ class Controlplane # rubocop:disable Metrics/ClassLength
|
|
122
125
|
workload_data = fetch_workload(workload)
|
123
126
|
return workload_data if workload_data
|
124
127
|
|
125
|
-
raise "Can't find workload '#{workload}', please create it with 'cpl
|
128
|
+
raise "Can't find workload '#{workload}', please create it with 'cpl apply-template #{workload} -a #{config.app}'."
|
129
|
+
end
|
130
|
+
|
131
|
+
def query_workloads(workload, partial_match: false)
|
132
|
+
op = partial_match ? "~" : "="
|
133
|
+
|
134
|
+
api.query_workloads(org: org, gvc: gvc, workload: workload, op_type: op)
|
126
135
|
end
|
127
136
|
|
128
137
|
def workload_get_replicas(workload, location:)
|
@@ -136,6 +145,10 @@ class Controlplane # rubocop:disable Metrics/ClassLength
|
|
136
145
|
$CHILD_STATUS.success? ? YAML.safe_load(result) : nil
|
137
146
|
end
|
138
147
|
|
148
|
+
def fetch_workload_deployments(workload)
|
149
|
+
api.workload_deployments(workload: workload, gvc: gvc, org: org)
|
150
|
+
end
|
151
|
+
|
139
152
|
def workload_set_image_ref(workload, container:, image:)
|
140
153
|
cmd = "cpln workload update #{workload} #{gvc_org}"
|
141
154
|
cmd += " --set spec.containers.#{container}.image=/org/#{config.org}/image/#{image}"
|
@@ -143,10 +156,26 @@ class Controlplane # rubocop:disable Metrics/ClassLength
|
|
143
156
|
perform!(cmd)
|
144
157
|
end
|
145
158
|
|
146
|
-
def
|
159
|
+
def set_workload_env_var(workload, container:, name:, value:)
|
160
|
+
data = fetch_workload!(workload)
|
161
|
+
data["spec"]["containers"].each do |container_data|
|
162
|
+
next unless container_data["name"] == container
|
163
|
+
|
164
|
+
container_data["env"].each do |env_data|
|
165
|
+
next unless env_data["name"] == name
|
166
|
+
|
167
|
+
env_data["value"] = value
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
api.update_workload(org: org, gvc: gvc, workload: workload, data: data)
|
172
|
+
end
|
173
|
+
|
174
|
+
def set_workload_suspend(workload, value)
|
147
175
|
data = fetch_workload!(workload)
|
148
176
|
data["spec"]["defaultOptions"]["suspend"] = value
|
149
|
-
|
177
|
+
|
178
|
+
api.update_workload(org: org, gvc: gvc, workload: workload, data: data)
|
150
179
|
end
|
151
180
|
|
152
181
|
def workload_force_redeployment(workload)
|
@@ -155,15 +184,8 @@ class Controlplane # rubocop:disable Metrics/ClassLength
|
|
155
184
|
perform!(cmd)
|
156
185
|
end
|
157
186
|
|
158
|
-
def
|
159
|
-
|
160
|
-
cmd += " 2> /dev/null"
|
161
|
-
perform(cmd)
|
162
|
-
end
|
163
|
-
|
164
|
-
def workload_delete!(workload)
|
165
|
-
cmd = "cpln workload delete #{workload} #{gvc_org}"
|
166
|
-
perform!(cmd)
|
187
|
+
def delete_workload(workload)
|
188
|
+
api.delete_workload(org: org, gvc: gvc, workload: workload)
|
167
189
|
end
|
168
190
|
|
169
191
|
def workload_connect(workload, location:, container: nil, shell: nil)
|
@@ -180,6 +202,40 @@ class Controlplane # rubocop:disable Metrics/ClassLength
|
|
180
202
|
perform!(cmd)
|
181
203
|
end
|
182
204
|
|
205
|
+
# domain
|
206
|
+
|
207
|
+
def find_domain_route(data)
|
208
|
+
port = data["spec"]["ports"].find { |current_port| current_port["number"] == 80 || current_port["number"] == 443 }
|
209
|
+
return nil if port.nil? || port["routes"].nil?
|
210
|
+
|
211
|
+
route = port["routes"].find { |current_route| current_route["prefix"] == "/" }
|
212
|
+
return nil if route.nil?
|
213
|
+
|
214
|
+
route
|
215
|
+
end
|
216
|
+
|
217
|
+
def find_domain_for(workloads)
|
218
|
+
domains = api.list_domains(org: org)["items"]
|
219
|
+
domains.find do |domain_data|
|
220
|
+
route = find_domain_route(domain_data)
|
221
|
+
next false if route.nil?
|
222
|
+
|
223
|
+
workloads.any? { |workload| route["workloadLink"].split("/").last == workload }
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
def get_domain_workload(data)
|
228
|
+
route = find_domain_route(data)
|
229
|
+
route["workloadLink"].split("/").last
|
230
|
+
end
|
231
|
+
|
232
|
+
def set_domain_workload(data, workload)
|
233
|
+
route = find_domain_route(data)
|
234
|
+
route["workloadLink"] = "/org/#{org}/gvc/#{gvc}/workload/#{workload}"
|
235
|
+
|
236
|
+
api.update_domain(org: org, domain: data["name"], data: data)
|
237
|
+
end
|
238
|
+
|
183
239
|
# logs
|
184
240
|
|
185
241
|
def logs(workload:)
|
@@ -33,6 +33,29 @@ class ControlplaneApi
|
|
33
33
|
api_json_direct("/logs/org/#{org}/loki/api/v1/query_range?#{params}", method: :get, host: :logs)
|
34
34
|
end
|
35
35
|
|
36
|
+
def query_workloads(org:, gvc:, workload:, op_type:) # rubocop:disable Metrics/MethodLength
|
37
|
+
body = {
|
38
|
+
kind: "string",
|
39
|
+
spec: {
|
40
|
+
match: "all",
|
41
|
+
terms: [
|
42
|
+
{
|
43
|
+
rel: "gvc",
|
44
|
+
op: "=",
|
45
|
+
value: gvc
|
46
|
+
},
|
47
|
+
{
|
48
|
+
property: "name",
|
49
|
+
op: op_type,
|
50
|
+
value: workload
|
51
|
+
}
|
52
|
+
]
|
53
|
+
}
|
54
|
+
}
|
55
|
+
|
56
|
+
api_json("/org/#{org}/workload/-query", method: :post, body: body)
|
57
|
+
end
|
58
|
+
|
36
59
|
def workload_list(org:, gvc:)
|
37
60
|
api_json("/org/#{org}/gvc/#{gvc}/workload", method: :get)
|
38
61
|
end
|
@@ -45,10 +68,26 @@ class ControlplaneApi
|
|
45
68
|
api_json("/org/#{org}/gvc/#{gvc}/workload/#{workload}", method: :get)
|
46
69
|
end
|
47
70
|
|
71
|
+
def update_workload(org:, gvc:, workload:, data:)
|
72
|
+
api_json("/org/#{org}/gvc/#{gvc}/workload/#{workload}", method: :patch, body: data)
|
73
|
+
end
|
74
|
+
|
48
75
|
def workload_deployments(org:, gvc:, workload:)
|
49
76
|
api_json("/org/#{org}/gvc/#{gvc}/workload/#{workload}/deployment", method: :get)
|
50
77
|
end
|
51
78
|
|
79
|
+
def delete_workload(org:, gvc:, workload:)
|
80
|
+
api_json("/org/#{org}/gvc/#{gvc}/workload/#{workload}", method: :delete)
|
81
|
+
end
|
82
|
+
|
83
|
+
def list_domains(org:)
|
84
|
+
api_json("/org/#{org}/domain", method: :get)
|
85
|
+
end
|
86
|
+
|
87
|
+
def update_domain(org:, domain:, data:)
|
88
|
+
api_json("/org/#{org}/domain/#{domain}", method: :patch, body: data)
|
89
|
+
end
|
90
|
+
|
52
91
|
private
|
53
92
|
|
54
93
|
# switch between cpln rest and api
|
@@ -1,7 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
class ControlplaneApiDirect
|
4
|
-
API_METHODS = {
|
4
|
+
API_METHODS = {
|
5
|
+
get: Net::HTTP::Get,
|
6
|
+
patch: Net::HTTP::Patch,
|
7
|
+
post: Net::HTTP::Post,
|
8
|
+
put: Net::HTTP::Put,
|
9
|
+
delete: Net::HTTP::Delete
|
10
|
+
}.freeze
|
5
11
|
API_HOSTS = { api: "https://api.cpln.io", logs: "https://logs.cpln.io" }.freeze
|
6
12
|
|
7
13
|
# API_TOKEN_REGEX = Regexp.union(
|
@@ -11,11 +17,12 @@ class ControlplaneApiDirect
|
|
11
17
|
|
12
18
|
API_TOKEN_REGEX = /^[\w\-._]+$/.freeze
|
13
19
|
|
14
|
-
def call(url, method:, host: :api) # rubocop:disable Metrics/MethodLength
|
20
|
+
def call(url, method:, host: :api, body: nil) # rubocop:disable Metrics/MethodLength
|
15
21
|
uri = URI("#{API_HOSTS[host]}#{url}")
|
16
22
|
request = API_METHODS[method].new(uri)
|
17
23
|
request["Content-Type"] = "application/json"
|
18
24
|
request["Authorization"] = api_token
|
25
|
+
request.body = body.to_json if body
|
19
26
|
|
20
27
|
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(request) }
|
21
28
|
|
@@ -31,13 +38,16 @@ class ControlplaneApiDirect
|
|
31
38
|
end
|
32
39
|
end
|
33
40
|
|
41
|
+
# rubocop:disable Style/ClassVars
|
34
42
|
def api_token
|
35
43
|
return @@api_token if defined?(@@api_token)
|
36
44
|
|
37
|
-
@@api_token = ENV.fetch("CPLN_TOKEN",
|
45
|
+
@@api_token = ENV.fetch("CPLN_TOKEN", nil)
|
46
|
+
@@api_token = `cpln profile token`.chomp if @@api_token.nil?
|
38
47
|
return @@api_token if @@api_token.match?(API_TOKEN_REGEX)
|
39
48
|
|
40
49
|
raise "Unknown API token format. " \
|
41
50
|
"Please re-run 'cpln profile login' or set the correct CPLN_TOKEN env variable."
|
42
51
|
end
|
52
|
+
# rubocop:enable Style/ClassVars
|
43
53
|
end
|
data/lib/core/scripts.rb
CHANGED
@@ -10,7 +10,7 @@ module Scripts
|
|
10
10
|
-H "Authorization: ${CONTROLPLANE_TOKEN}" -s | grep -o '"replicas":[0-9]*' | grep -o '[0-9]*')
|
11
11
|
|
12
12
|
if [ "$REPLICAS_QTY" -gt 0 ]; then
|
13
|
-
echo "-- MULTIPLE REPLICAS ATTEMPT
|
13
|
+
echo "-- MULTIPLE REPLICAS ATTEMPT: $REPLICAS_QTY --"
|
14
14
|
exit -1
|
15
15
|
fi
|
16
16
|
SHELL
|
@@ -24,7 +24,7 @@ module Scripts
|
|
24
24
|
|
25
25
|
# NOTE: please escape all '/' as '//' (as it is ruby interpolation here as well)
|
26
26
|
def http_dummy_server_ruby
|
27
|
-
'require "socket";s=TCPServer.new(ENV["PORT"]);' \
|
27
|
+
'require "socket";s=TCPServer.new(ENV["PORT"] || 80);' \
|
28
28
|
'loop do c=s.accept;c.puts("HTTP/1.1 200 OK\\nContent-Length: 2\\n\\nOk");c.close end'
|
29
29
|
end
|
30
30
|
|
data/lib/cpl/version.rb
CHANGED
@@ -0,0 +1,30 @@
|
|
1
|
+
kind: workload
|
2
|
+
name: daily-task
|
3
|
+
spec:
|
4
|
+
# https://docs.controlplane.com/reference/workload#cron-configuration
|
5
|
+
type: cron
|
6
|
+
job:
|
7
|
+
# Run daily job at 2am, see cron docs
|
8
|
+
schedule: 0 2 * * *
|
9
|
+
# Never or OnFailure
|
10
|
+
restartPolicy: Never
|
11
|
+
containers:
|
12
|
+
- name: daily-task
|
13
|
+
args:
|
14
|
+
- bundle
|
15
|
+
- exec
|
16
|
+
- rails
|
17
|
+
- db:prepare
|
18
|
+
cpu: 50m
|
19
|
+
inheritEnv: true
|
20
|
+
image: '/org/APP_ORG/image/APP_IMAGE'
|
21
|
+
memory: 256Mi
|
22
|
+
defaultOptions:
|
23
|
+
autoscaling:
|
24
|
+
maxScale: 1
|
25
|
+
capacityAI: false
|
26
|
+
firewallConfig:
|
27
|
+
external:
|
28
|
+
outboundAllowCIDR:
|
29
|
+
- 0.0.0.0/0
|
30
|
+
identityLink: /org/APP_ORG/gvc/APP_GVC/identity/APP_GVC-identity
|
@@ -0,0 +1,24 @@
|
|
1
|
+
kind: workload
|
2
|
+
name: maintenance
|
3
|
+
spec:
|
4
|
+
type: standard
|
5
|
+
containers:
|
6
|
+
- name: maintenance
|
7
|
+
env:
|
8
|
+
- name: PORT
|
9
|
+
value: '3000'
|
10
|
+
- name: PAGE_URL
|
11
|
+
value: ''
|
12
|
+
image: 'shakacode/maintenance-mode'
|
13
|
+
ports:
|
14
|
+
- number: 3000
|
15
|
+
protocol: http
|
16
|
+
defaultOptions:
|
17
|
+
autoscaling:
|
18
|
+
maxScale: 1
|
19
|
+
capacityAI: false
|
20
|
+
timeoutSeconds: 60
|
21
|
+
firewallConfig:
|
22
|
+
external:
|
23
|
+
inboundAllowCIDR:
|
24
|
+
- 0.0.0.0/0
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cpl
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Justin Gordon
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2023-
|
12
|
+
date: 2023-05-29 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: debug
|
@@ -151,6 +151,48 @@ dependencies:
|
|
151
151
|
- - "~>"
|
152
152
|
- !ruby/object:Gem::Version
|
153
153
|
version: 0.22.0
|
154
|
+
- !ruby/object:Gem::Dependency
|
155
|
+
name: timecop
|
156
|
+
requirement: !ruby/object:Gem::Requirement
|
157
|
+
requirements:
|
158
|
+
- - "~>"
|
159
|
+
- !ruby/object:Gem::Version
|
160
|
+
version: 0.9.6
|
161
|
+
type: :development
|
162
|
+
prerelease: false
|
163
|
+
version_requirements: !ruby/object:Gem::Requirement
|
164
|
+
requirements:
|
165
|
+
- - "~>"
|
166
|
+
- !ruby/object:Gem::Version
|
167
|
+
version: 0.9.6
|
168
|
+
- !ruby/object:Gem::Dependency
|
169
|
+
name: vcr
|
170
|
+
requirement: !ruby/object:Gem::Requirement
|
171
|
+
requirements:
|
172
|
+
- - "~>"
|
173
|
+
- !ruby/object:Gem::Version
|
174
|
+
version: 6.1.0
|
175
|
+
type: :development
|
176
|
+
prerelease: false
|
177
|
+
version_requirements: !ruby/object:Gem::Requirement
|
178
|
+
requirements:
|
179
|
+
- - "~>"
|
180
|
+
- !ruby/object:Gem::Version
|
181
|
+
version: 6.1.0
|
182
|
+
- !ruby/object:Gem::Dependency
|
183
|
+
name: webmock
|
184
|
+
requirement: !ruby/object:Gem::Requirement
|
185
|
+
requirements:
|
186
|
+
- - "~>"
|
187
|
+
- !ruby/object:Gem::Version
|
188
|
+
version: 3.18.1
|
189
|
+
type: :development
|
190
|
+
prerelease: false
|
191
|
+
version_requirements: !ruby/object:Gem::Requirement
|
192
|
+
requirements:
|
193
|
+
- - "~>"
|
194
|
+
- !ruby/object:Gem::Version
|
195
|
+
version: 3.18.1
|
154
196
|
description: CLI for providing Heroku-like platform-as-a-service on Control Plane
|
155
197
|
email:
|
156
198
|
- justin@shakacode.com
|
@@ -160,6 +202,7 @@ executables:
|
|
160
202
|
extensions: []
|
161
203
|
extra_rdoc_files: []
|
162
204
|
files:
|
205
|
+
- ".github/workflows/command_docs.yml"
|
163
206
|
- ".github/workflows/rspec.yml"
|
164
207
|
- ".github/workflows/rubocop.yml"
|
165
208
|
- ".gitignore"
|
@@ -197,6 +240,10 @@ files:
|
|
197
240
|
- lib/command/info.rb
|
198
241
|
- lib/command/latest_image.rb
|
199
242
|
- lib/command/logs.rb
|
243
|
+
- lib/command/maintenance.rb
|
244
|
+
- lib/command/maintenance_off.rb
|
245
|
+
- lib/command/maintenance_on.rb
|
246
|
+
- lib/command/maintenance_set_page.rb
|
200
247
|
- lib/command/no_command.rb
|
201
248
|
- lib/command/open.rb
|
202
249
|
- lib/command/promote_app_from_upstream.rb
|
@@ -205,6 +252,7 @@ files:
|
|
205
252
|
- lib/command/ps_start.rb
|
206
253
|
- lib/command/ps_stop.rb
|
207
254
|
- lib/command/run.rb
|
255
|
+
- lib/command/run_cleanup.rb
|
208
256
|
- lib/command/run_detached.rb
|
209
257
|
- lib/command/setup_app.rb
|
210
258
|
- lib/command/test.rb
|
@@ -222,10 +270,13 @@ files:
|
|
222
270
|
- lib/main.rb
|
223
271
|
- rakelib/create_release.rake
|
224
272
|
- script/add_command
|
225
|
-
- script/
|
273
|
+
- script/check_command_docs
|
226
274
|
- script/rename_command
|
275
|
+
- script/update_command_docs
|
276
|
+
- templates/daily-task.yml
|
227
277
|
- templates/gvc.yml
|
228
278
|
- templates/identity.yml
|
279
|
+
- templates/maintenance.yml
|
229
280
|
- templates/memcached.yml
|
230
281
|
- templates/postgres.yml
|
231
282
|
- templates/rails.yml
|
File without changes
|