cpl 0.6.0 → 1.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/.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
|