captive-cli 1.3.0 → 1.4.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/lib/captive/cli/dependencies.rb +1 -0
- data/lib/captive/cli.rb +4 -0
- data/lib/captive/commands/domain_announcer.rb +32 -0
- data/lib/captive/commands/domain_wait.rb +44 -0
- data/lib/captive/commands/domains_commands.rb +108 -0
- data/lib/captive/dashboard/app_path.rb +14 -0
- data/lib/captive/dashboard/domains_resource.rb +27 -0
- data/lib/captive/dashboard/env_vars_resource.rb +3 -4
- data/lib/captive/dashboard/scheduled_tasks_resource.rb +3 -4
- data/lib/captive/errors.rb +1 -0
- data/lib/captive/loader.rb +5 -0
- data/lib/captive/presenter/domain.rb +51 -0
- data/lib/captive/shell_completions.rb +3 -1
- data/lib/captive/version.rb +1 -1
- metadata +7 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 377f77798282f817bcd1632482c5075ca10f8c65f1b5ce9abc4e7d3f1aeae2ee
|
|
4
|
+
data.tar.gz: ef1dc980b18c772c703a7b94ee35a3c35049b22d84ffeca742ee5ce569a32f7e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e058db4108f3da4be9dd9c6e5699f1cdb5419463cfe93300cd96dbd8bf612894f23bf97fab095480edc22e70a9c98f4e172e56edb78e8c1108bac3c66b1918be
|
|
7
|
+
data.tar.gz: 8f199adf232216645878442735570e5ee04a70dd4a8f43031bd0e24d67c12f7af7f5ab8ba7e4de2accfb6143a5801f09fa5b173cf80ed88a9e43ceff5d31a3f2
|
|
@@ -24,6 +24,7 @@ module Captive
|
|
|
24
24
|
def apps_api = @apps_api ||= Dashboard::AppsResource.new(api: dashboard)
|
|
25
25
|
def env_api = @env_api ||= Dashboard::EnvVarsResource.new(api: dashboard)
|
|
26
26
|
def crons_api = @crons_api ||= Dashboard::ScheduledTasksResource.new(api: dashboard)
|
|
27
|
+
def domains_api = @domains_api ||= Dashboard::DomainsResource.new(api: dashboard)
|
|
27
28
|
|
|
28
29
|
def active_kubeconfig
|
|
29
30
|
env_kc = ENV["KUBECONFIG"]
|
data/lib/captive/cli.rb
CHANGED
|
@@ -6,6 +6,7 @@ require_relative "cli/dependencies"
|
|
|
6
6
|
require_relative "commands/apps_commands"
|
|
7
7
|
require_relative "commands/env_commands"
|
|
8
8
|
require_relative "commands/crons_commands"
|
|
9
|
+
require_relative "commands/domains_commands"
|
|
9
10
|
require_relative "commands/db_commands"
|
|
10
11
|
require_relative "commands/user_login"
|
|
11
12
|
require_relative "commands/auth_commands"
|
|
@@ -62,6 +63,9 @@ module Captive
|
|
|
62
63
|
desc "crons SUBCOMMAND", "Tâches planifiées d'une app"
|
|
63
64
|
subcommand "crons", Commands::CronsCommands
|
|
64
65
|
|
|
66
|
+
desc "domains SUBCOMMAND", "Domaines d'une app"
|
|
67
|
+
subcommand "domains", Commands::DomainsCommands
|
|
68
|
+
|
|
65
69
|
desc "auth SUBCOMMAND", "Jeton pour kubectl", hide: true
|
|
66
70
|
subcommand "auth", Commands::AuthCommands
|
|
67
71
|
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../errors"
|
|
4
|
+
|
|
5
|
+
module Captive
|
|
6
|
+
module Commands
|
|
7
|
+
# Dit ce qu'est devenu un geste sur un Domaine. Sans attente, la demande est seulement
|
|
8
|
+
# acceptée ; avec une attente, le bloc la reçoit et rend le Domaine appliqué.
|
|
9
|
+
class DomainAnnouncer
|
|
10
|
+
def initialize(paint:, presenter:, waiter: nil)
|
|
11
|
+
@paint = paint
|
|
12
|
+
@presenter = presenter
|
|
13
|
+
@waiter = waiter
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def call(message)
|
|
17
|
+
return puts(@paint.green(" ✓ #{message} : #{CLIDependencies::ASYNC_NOTICE}")) unless @waiter
|
|
18
|
+
|
|
19
|
+
applied(message, yield(@waiter))
|
|
20
|
+
rescue PollTimeout => timeout
|
|
21
|
+
abort @paint.red("✗ Timeout : #{timeout.message}")
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
def applied(message, entry)
|
|
27
|
+
puts @paint.green(" ✓ #{message} : appliqué")
|
|
28
|
+
puts " #{@presenter.line(entry)}" if entry
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../poller"
|
|
4
|
+
require_relative "../errors"
|
|
5
|
+
|
|
6
|
+
module Captive
|
|
7
|
+
module Commands
|
|
8
|
+
# Attend que le cluster reflète un geste sur un Domaine : ni en attente ni échoué, et
|
|
9
|
+
# présent (absent après une suppression). Le DNS n'entre pas en compte : il dépend du
|
|
10
|
+
# propriétaire du Domaine, pas de Captive.
|
|
11
|
+
class DomainWait
|
|
12
|
+
def initialize(domains:, poller: nil, timeout: 600)
|
|
13
|
+
@domains = domains
|
|
14
|
+
@poller = poller || Poller.new(interval: 10, timeout: timeout)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Rend le Domaine tel que le cluster le montre une fois le geste appliqué.
|
|
18
|
+
def until_applied(app, hostname)
|
|
19
|
+
entry = nil
|
|
20
|
+
poll(app, hostname) { |found| (entry = found) && !found["pending"] }
|
|
21
|
+
entry
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def until_gone(app, hostname)
|
|
25
|
+
poll(app, hostname, &:nil?)
|
|
26
|
+
nil
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
def poll(app, hostname)
|
|
32
|
+
@poller.poll_until(context: "#{hostname} sur #{app}") do
|
|
33
|
+
yield check(hostname, @domains.find(app, hostname))
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def check(hostname, entry)
|
|
38
|
+
return entry unless entry&.fetch("failed", false)
|
|
39
|
+
|
|
40
|
+
raise DomainChangeFailed, "#{hostname} : #{entry["failure_reason"] || "échec"}"
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Captive
|
|
4
|
+
module Commands
|
|
5
|
+
# Les Domaines d'une app via le dashboard. Chaque geste est appliqué plus tard par
|
|
6
|
+
# captive-platform ; --wait attend que le cluster le reflète.
|
|
7
|
+
class DomainsCommands < Thor
|
|
8
|
+
include CLIDependencies
|
|
9
|
+
|
|
10
|
+
def self.exit_on_failure? = true
|
|
11
|
+
|
|
12
|
+
WAIT_OPTIONS = {
|
|
13
|
+
wait: { type: :boolean, desc: "Attend que le changement soit appliqué" },
|
|
14
|
+
timeout: { type: :numeric, default: 600, desc: "Attente max en secondes avec --wait" },
|
|
15
|
+
}.freeze
|
|
16
|
+
|
|
17
|
+
def self.wait_options
|
|
18
|
+
WAIT_OPTIONS.each { |name, settings| method_option(name, **settings) }
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
desc "list APP", "Liste les domaines de l'app et l'état de leur DNS"
|
|
22
|
+
long_desc <<~DESC
|
|
23
|
+
\x05Exemple :
|
|
24
|
+
\x05 captive domains list my-app
|
|
25
|
+
\x05 captive domains list my-app --refresh
|
|
26
|
+
DESC
|
|
27
|
+
method_option :refresh, type: :boolean, desc: "Vérifie à nouveau le DNS sans attendre le cache"
|
|
28
|
+
def list(app)
|
|
29
|
+
entries = options[:refresh] ? domains_api.recheck(app) : domains_api.list(app)
|
|
30
|
+
return puts(paint.dim("Aucun domaine.")) if entries.empty?
|
|
31
|
+
|
|
32
|
+
entries.each { |entry| puts presenter.line(entry) }
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
desc "add APP DOMAINE", "Ajoute un domaine à l'app"
|
|
36
|
+
long_desc <<~DESC
|
|
37
|
+
L'enregistrement DNS à créer s'affiche ensuite dans `captive domains list`.
|
|
38
|
+
|
|
39
|
+
\x05Exemple :
|
|
40
|
+
\x05 captive domains add my-app www.example.fr --wait
|
|
41
|
+
DESC
|
|
42
|
+
wait_options
|
|
43
|
+
def add(app, hostname)
|
|
44
|
+
domains_api.add(app, hostname)
|
|
45
|
+
announcer.call("#{hostname} ajouté") { |wait| wait.until_applied(app, hostname) }
|
|
46
|
+
puts paint.dim(" DNS à créer : captive domains list #{app}") unless options[:wait]
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
desc "rm APP DOMAINE", "Supprime un domaine de l'app"
|
|
50
|
+
long_desc <<~DESC
|
|
51
|
+
Le domaine cesse aussitôt de servir l'app : il faut le retaper pour confirmer.
|
|
52
|
+
|
|
53
|
+
\x05Exemple :
|
|
54
|
+
\x05 captive domains rm my-app www.example.fr
|
|
55
|
+
\x05 captive domains rm my-app www.example.fr --yes
|
|
56
|
+
DESC
|
|
57
|
+
method_option :yes, type: :boolean, desc: "Supprime sans demander de confirmation"
|
|
58
|
+
wait_options
|
|
59
|
+
def rm(app, hostname)
|
|
60
|
+
confirm_removal(hostname)
|
|
61
|
+
domains_api.remove(app, hostname)
|
|
62
|
+
announcer.call("#{hostname} supprimé") { |wait| wait.until_gone(app, hostname) }
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
desc "redirect APP DOMAINE", "Redirige un domaine vers un autre domaine de l'app"
|
|
66
|
+
long_desc <<~DESC
|
|
67
|
+
\x05Exemple :
|
|
68
|
+
\x05 captive domains redirect my-app example.fr --to www.example.fr
|
|
69
|
+
DESC
|
|
70
|
+
method_option :to, type: :string, required: true, desc: "Domaine servi vers lequel rediriger"
|
|
71
|
+
method_option :status, type: :numeric, default: 301, enum: [ 301, 302 ], desc: "Code HTTP de redirection"
|
|
72
|
+
wait_options
|
|
73
|
+
def redirect(app, hostname)
|
|
74
|
+
target = options[:to]
|
|
75
|
+
domains_api.redirect(app, hostname, { to: target, status: options[:status] })
|
|
76
|
+
announcer.call("#{hostname} redirigé vers #{target}") { |wait| wait.until_applied(app, hostname) }
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
desc "serve APP DOMAINE", "Repasse un domaine redirigé en domaine servi"
|
|
80
|
+
long_desc <<~DESC
|
|
81
|
+
\x05Exemple :
|
|
82
|
+
\x05 captive domains serve my-app example.fr
|
|
83
|
+
DESC
|
|
84
|
+
wait_options
|
|
85
|
+
def serve(app, hostname)
|
|
86
|
+
domains_api.serve(app, hostname)
|
|
87
|
+
announcer.call("#{hostname} servi") { |wait| wait.until_applied(app, hostname) }
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
private
|
|
91
|
+
|
|
92
|
+
def presenter = Presenter::Domain.new(paint: paint)
|
|
93
|
+
def domain_wait = DomainWait.new(domains: domains_api, timeout: options[:timeout])
|
|
94
|
+
|
|
95
|
+
def announcer
|
|
96
|
+
DomainAnnouncer.new(paint: paint, presenter: presenter, waiter: (domain_wait if options[:wait]))
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def confirm_removal(hostname)
|
|
100
|
+
return if options[:yes]
|
|
101
|
+
return if ask("Retape #{hostname} pour confirmer la suppression :").to_s.strip == hostname
|
|
102
|
+
|
|
103
|
+
warn paint.red("Abandon : rien n'a été supprimé.")
|
|
104
|
+
exit 1
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Captive
|
|
4
|
+
module Dashboard
|
|
5
|
+
# Le chemin d'une collection rattachée à une app (`/apps/:app/<collection>`), et d'un de
|
|
6
|
+
# ses éléments, chaque segment encodé.
|
|
7
|
+
AppPath = Data.define(:collection) do
|
|
8
|
+
def call(app, key = nil)
|
|
9
|
+
base = "/apps/#{DashboardApi.segment(app)}/#{collection}"
|
|
10
|
+
key ? "#{base}/#{DashboardApi.segment(key)}" : base
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Captive
|
|
4
|
+
module Dashboard
|
|
5
|
+
# Les Domaines d'une app : lus du cluster, modifiés via captive-platform (réponse 202).
|
|
6
|
+
# `recheck` relit le DNS sans attendre le cache ; une redirection est `{ to:, status: }`.
|
|
7
|
+
class DomainsResource
|
|
8
|
+
PATH = AppPath.new("domains")
|
|
9
|
+
|
|
10
|
+
def initialize(api:)
|
|
11
|
+
@api = api
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def list(app) = @api.get(path(app))
|
|
15
|
+
def recheck(app) = @api.get("#{path(app)}?refresh=1")
|
|
16
|
+
def find(app, hostname) = list(app).find { |domain| domain["hostname"] == hostname }
|
|
17
|
+
def add(app, hostname) = @api.post(path(app), { hostname: hostname })
|
|
18
|
+
def remove(app, hostname) = @api.delete(path(app, hostname))
|
|
19
|
+
def serve(app, hostname) = @api.patch(path(app, hostname), { mode: "serve" })
|
|
20
|
+
def redirect(app, hostname, rule) = @api.patch(path(app, hostname), rule)
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
def path(...) = PATH.call(...)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -4,6 +4,8 @@ module Captive
|
|
|
4
4
|
module Dashboard
|
|
5
5
|
# Les variables d'environnement d'une app : lues du Secret, écrites par set-env.
|
|
6
6
|
class EnvVarsResource
|
|
7
|
+
PATH = AppPath.new("env_vars")
|
|
8
|
+
|
|
7
9
|
def initialize(api:)
|
|
8
10
|
@api = api
|
|
9
11
|
end
|
|
@@ -18,10 +20,7 @@ module Captive
|
|
|
18
20
|
|
|
19
21
|
private
|
|
20
22
|
|
|
21
|
-
def path(
|
|
22
|
-
base = "/apps/#{DashboardApi.segment(app)}/env_vars"
|
|
23
|
-
key ? "#{base}/#{DashboardApi.segment(key)}" : base
|
|
24
|
-
end
|
|
23
|
+
def path(...) = PATH.call(...)
|
|
25
24
|
end
|
|
26
25
|
end
|
|
27
26
|
end
|
|
@@ -4,6 +4,8 @@ module Captive
|
|
|
4
4
|
module Dashboard
|
|
5
5
|
# Les tâches planifiées d'une app : lues du cluster, déclarées via captive-platform.
|
|
6
6
|
class ScheduledTasksResource
|
|
7
|
+
PATH = AppPath.new("scheduled_tasks")
|
|
8
|
+
|
|
7
9
|
def initialize(api:)
|
|
8
10
|
@api = api
|
|
9
11
|
end
|
|
@@ -23,10 +25,7 @@ module Captive
|
|
|
23
25
|
|
|
24
26
|
private
|
|
25
27
|
|
|
26
|
-
def path(
|
|
27
|
-
base = "/apps/#{DashboardApi.segment(app)}/scheduled_tasks"
|
|
28
|
-
name ? "#{base}/#{DashboardApi.segment(name)}" : base
|
|
29
|
-
end
|
|
28
|
+
def path(...) = PATH.call(...)
|
|
30
29
|
end
|
|
31
30
|
end
|
|
32
31
|
end
|
data/lib/captive/errors.rb
CHANGED
data/lib/captive/loader.rb
CHANGED
|
@@ -13,11 +13,14 @@ require_relative "one_off_spec"
|
|
|
13
13
|
require_relative "session_id"
|
|
14
14
|
require_relative "poller"
|
|
15
15
|
require_relative "dashboard_api"
|
|
16
|
+
require_relative "dashboard/app_path"
|
|
16
17
|
require_relative "dashboard/apps_resource"
|
|
17
18
|
require_relative "dashboard/env_vars_resource"
|
|
18
19
|
require_relative "dashboard/scheduled_tasks_resource"
|
|
20
|
+
require_relative "dashboard/domains_resource"
|
|
19
21
|
require_relative "presenter/app_info"
|
|
20
22
|
require_relative "presenter/scheduled_task"
|
|
23
|
+
require_relative "presenter/domain"
|
|
21
24
|
require_relative "activity_trail"
|
|
22
25
|
require_relative "errors"
|
|
23
26
|
require_relative "ui/spinner"
|
|
@@ -28,6 +31,8 @@ require_relative "commands/one_off_session"
|
|
|
28
31
|
require_relative "commands/run"
|
|
29
32
|
require_relative "commands/logs"
|
|
30
33
|
require_relative "commands/apps_restart"
|
|
34
|
+
require_relative "commands/domain_wait"
|
|
35
|
+
require_relative "commands/domain_announcer"
|
|
31
36
|
require_relative "commands/db_dump"
|
|
32
37
|
require_relative "commands/db_restore"
|
|
33
38
|
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Captive
|
|
4
|
+
module Presenter
|
|
5
|
+
# Une ligne par Domaine : son nom, son état (servi, redirigé, en attente, échoué) et ce que
|
|
6
|
+
# dit le DNS. Un geste en cours masque le diagnostic, qui ne vaut que pour l'état atteint.
|
|
7
|
+
class Domain
|
|
8
|
+
HEALTH = { "certificate_pending" => "certificat en cours", "invalid" => "route refusée" }.freeze
|
|
9
|
+
|
|
10
|
+
def initialize(paint:)
|
|
11
|
+
@paint = paint
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def line(entry)
|
|
15
|
+
change = change(entry)
|
|
16
|
+
[ entry["hostname"], change || reached(entry), (health(entry) unless change) ].compact.join(" ")
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
# Un geste en attente ou échoué ; nil quand le Domaine est dans l'état demandé.
|
|
22
|
+
def change(entry)
|
|
23
|
+
return failure(entry) if entry["failed"]
|
|
24
|
+
|
|
25
|
+
@paint.yellow("en attente (#{entry["pending_change"]})") if entry["pending"]
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def reached(entry)
|
|
29
|
+
redirect = entry["redirect"]
|
|
30
|
+
return "→ #{redirect["to"]} (#{redirect["status"]})" if redirect
|
|
31
|
+
|
|
32
|
+
entry["primary"] ? "servi, principal" : "servi"
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def failure(entry)
|
|
36
|
+
reason = entry["failure_reason"]
|
|
37
|
+
@paint.red(reason ? "échoué : #{reason}" : "échoué")
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def health(entry)
|
|
41
|
+
status = entry["health"]
|
|
42
|
+
return @paint.green("✓") if status == "valid"
|
|
43
|
+
|
|
44
|
+
expected = entry.dig("dns", "expected")
|
|
45
|
+
return @paint.red("DNS à créer : #{expected.values_at("type", "name").join(" ")} → #{expected["value"]}") if expected
|
|
46
|
+
|
|
47
|
+
@paint.yellow(HEALTH.fetch(status, status.to_s))
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -2,12 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
module Captive
|
|
4
4
|
module ShellCompletions
|
|
5
|
-
ROOT_COMMANDS = %w[apps env crons db login whoami console run logs version completions].freeze
|
|
5
|
+
ROOT_COMMANDS = %w[apps env crons domains db login whoami console run logs version completions].freeze
|
|
6
6
|
|
|
7
7
|
SUBCOMMANDS = {
|
|
8
8
|
"apps" => %w[list info restart rebuild],
|
|
9
9
|
"env" => %w[list set unset],
|
|
10
10
|
"crons" => %w[list add edit rm suspend resume trigger runs],
|
|
11
|
+
"domains" => %w[list add rm redirect serve],
|
|
11
12
|
"db" => %w[restore],
|
|
12
13
|
}.freeze
|
|
13
14
|
|
|
@@ -23,6 +24,7 @@ module Captive
|
|
|
23
24
|
apps) COMPREPLY=($(compgen -W "#{SUBCOMMANDS["apps"].join(" ")}" -- "$cur")); return ;;
|
|
24
25
|
env) COMPREPLY=($(compgen -W "#{SUBCOMMANDS["env"].join(" ")}" -- "$cur")); return ;;
|
|
25
26
|
crons) COMPREPLY=($(compgen -W "#{SUBCOMMANDS["crons"].join(" ")}" -- "$cur")); return ;;
|
|
27
|
+
domains) COMPREPLY=($(compgen -W "#{SUBCOMMANDS["domains"].join(" ")}" -- "$cur")); return ;;
|
|
26
28
|
db) COMPREPLY=($(compgen -W "#{SUBCOMMANDS["db"].join(" ")}" -- "$cur")); return ;;
|
|
27
29
|
esac
|
|
28
30
|
|
data/lib/captive/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: captive-cli
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Captive Studio
|
|
@@ -120,6 +120,9 @@ files:
|
|
|
120
120
|
- lib/captive/commands/db_psql.rb
|
|
121
121
|
- lib/captive/commands/db_restore.rb
|
|
122
122
|
- lib/captive/commands/db_target.rb
|
|
123
|
+
- lib/captive/commands/domain_announcer.rb
|
|
124
|
+
- lib/captive/commands/domain_wait.rb
|
|
125
|
+
- lib/captive/commands/domains_commands.rb
|
|
123
126
|
- lib/captive/commands/env_commands.rb
|
|
124
127
|
- lib/captive/commands/env_set.rb
|
|
125
128
|
- lib/captive/commands/logs.rb
|
|
@@ -128,7 +131,9 @@ files:
|
|
|
128
131
|
- lib/captive/commands/user_login.rb
|
|
129
132
|
- lib/captive/credentials.rb
|
|
130
133
|
- lib/captive/credentials_store.rb
|
|
134
|
+
- lib/captive/dashboard/app_path.rb
|
|
131
135
|
- lib/captive/dashboard/apps_resource.rb
|
|
136
|
+
- lib/captive/dashboard/domains_resource.rb
|
|
132
137
|
- lib/captive/dashboard/env_vars_resource.rb
|
|
133
138
|
- lib/captive/dashboard/scheduled_tasks_resource.rb
|
|
134
139
|
- lib/captive/dashboard_api.rb
|
|
@@ -146,6 +151,7 @@ files:
|
|
|
146
151
|
- lib/captive/poller.rb
|
|
147
152
|
- lib/captive/presenter/ansi_stripper.rb
|
|
148
153
|
- lib/captive/presenter/app_info.rb
|
|
154
|
+
- lib/captive/presenter/domain.rb
|
|
149
155
|
- lib/captive/presenter/log_colorizer.rb
|
|
150
156
|
- lib/captive/presenter/log_line.rb
|
|
151
157
|
- lib/captive/presenter/scheduled_task.rb
|