ask-local 0.1.1 → 0.2.1
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/CHANGELOG.md +127 -54
- data/README.md +61 -9
- data/lib/ask/local/cli/boot.rb +145 -174
- data/lib/ask/local/cli/routes.rb +17 -13
- data/lib/ask/local/cli/system.rb +332 -70
- data/lib/ask/local/cli.rb +4 -2
- data/lib/ask/local/command.rb +30 -0
- data/lib/ask/local/config.rb +342 -59
- data/lib/ask/local/hosts.rb +8 -0
- data/lib/ask/local/ownership.rb +0 -7
- data/lib/ask/local/procfile.rb +139 -0
- data/lib/ask/local/proxy.rb +12 -1
- data/lib/ask/local/resolver.rb +62 -94
- data/lib/ask/local/runner.rb +8 -3
- data/lib/ask/local/trust.rb +21 -11
- data/lib/ask/local/version.rb +1 -1
- data/lib/ask/skills/ask-local/SKILL.md +57 -41
- data/lib/ask-local.rb +2 -0
- metadata +3 -1
data/lib/ask/local/cli/boot.rb
CHANGED
|
@@ -3,109 +3,143 @@
|
|
|
3
3
|
module Ask
|
|
4
4
|
module Local
|
|
5
5
|
class CLI
|
|
6
|
-
# Boot commands:
|
|
7
|
-
#
|
|
8
|
-
#
|
|
6
|
+
# Boot commands: `ask-local`, `ask-local start`, `ask-local run`.
|
|
7
|
+
# The config file is mandatory — missing file prints the fix and exits.
|
|
8
|
+
# `boot_all` fans out over every process declared in config/local.yml.
|
|
9
|
+
# No inference, no Procfile at boot, no single-process default.
|
|
9
10
|
module BootCommand
|
|
10
|
-
# Frameworks that ignore $PORT get explicit flags (portless lesson:
|
|
11
|
-
# Vite/Astro/Expo need --port; Jekyll/Middleman/Bridgetown do too).
|
|
12
|
-
# Only injects when the user hasn't already set a port.
|
|
13
11
|
PORT_IGNORING = %w[jekyll middleman bridgetown].freeze
|
|
14
12
|
|
|
15
13
|
module_function
|
|
16
14
|
|
|
15
|
+
# Bare `ask-local` / `ask-local start` / `ask-local run`.
|
|
16
|
+
# Reads config/local.yml via the resolver, ensures the proxy,
|
|
17
|
+
# boots every process, supervises the tree, cleans up on exit.
|
|
17
18
|
def run_inferred(ctx, args)
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
tlds: opts[:tld], use_branch: opts[:branch])
|
|
22
|
-
hostnames = Resolver.hostnames(resolved)
|
|
19
|
+
variant = ENV["ASK_LOCAL_VARIANT"]
|
|
20
|
+
opts = ctx.parse_flags(args, %i[variant tld force])
|
|
21
|
+
resolved = resolve!(ctx, variant: opts[:variant] || variant, tld: opts[:tld])
|
|
23
22
|
ensure_proxy!(ctx)
|
|
24
|
-
|
|
25
|
-
runner = Runner.new(store: ctx.store)
|
|
26
|
-
if Framework.managed?(framework) && opts[:rest].empty?
|
|
27
|
-
boot_managed(ctx, runner, resolved, hostnames, opts)
|
|
28
|
-
else
|
|
29
|
-
command = opts[:rest].empty? ? default_command(framework, opts[:proc]) : opts[:rest]
|
|
30
|
-
boot_run(ctx, runner, resolved, hostnames, command, opts)
|
|
31
|
-
end
|
|
23
|
+
boot_all(ctx, resolved, opts)
|
|
32
24
|
end
|
|
33
25
|
|
|
34
26
|
def run_explicit(ctx, args)
|
|
35
|
-
|
|
36
|
-
resolved = Resolver.resolve(Dir.pwd, name: opts[:name],
|
|
37
|
-
service: opts[:service], variant: opts[:variant],
|
|
38
|
-
tlds: opts[:tld], use_branch: opts[:branch])
|
|
39
|
-
hostnames = Resolver.hostnames(resolved)
|
|
40
|
-
ensure_proxy!(ctx)
|
|
41
|
-
framework = Framework.detect(Dir.pwd)
|
|
42
|
-
command = opts[:rest].empty? ? default_command(framework, opts[:proc]) : opts[:rest]
|
|
43
|
-
boot_run(ctx, Runner.new(store: ctx.store), resolved, hostnames, command, opts)
|
|
27
|
+
run_inferred(ctx, args)
|
|
44
28
|
end
|
|
45
29
|
|
|
46
|
-
def run_named(ctx, name,
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
30
|
+
def run_named(ctx, name, _args)
|
|
31
|
+
$stderr.puts "Error: `ask-local #{name}` is no longer supported."
|
|
32
|
+
$stderr.puts " All processes come from config/local.yml. Run `ask-local init` to create one."
|
|
33
|
+
exit 1
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Core boot loop: iterate processes from config/local.yml,
|
|
37
|
+
# classify HTTP vs background, spawn each, register routes for
|
|
38
|
+
# HTTP processes, then supervise the tree.
|
|
39
|
+
def boot_all(ctx, resolved, opts)
|
|
40
|
+
service = resolved.app
|
|
41
|
+
tld = resolved.tld
|
|
42
|
+
host = resolved.host
|
|
43
|
+
processes = resolved.processes
|
|
44
|
+
|
|
45
|
+
if processes.empty?
|
|
46
|
+
$stderr.puts "Error: no processes in config/local.yml. Add at least one."
|
|
53
47
|
exit 1
|
|
54
48
|
end
|
|
55
|
-
boot_run(ctx, Runner.new(store: ctx.store), resolved, hostnames, opts[:rest], opts)
|
|
56
|
-
end
|
|
57
49
|
|
|
58
|
-
|
|
59
|
-
primary
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
app = runner.boot_managed(name: resolved.app, hostname: primary,
|
|
64
|
-
url: url, dir: Dir.pwd, force: opts[:force])
|
|
65
|
-
register_all(ctx, hostnames, app, force: opts[:force], spec: { "dir" => File.expand_path(Dir.pwd) })
|
|
66
|
-
puts "\n -> #{url}\n"
|
|
67
|
-
ctx.report_unresolved(hostnames)
|
|
68
|
-
trap_cleanup(ctx, hostnames)
|
|
69
|
-
supervise_backend(ctx, hostnames, app)
|
|
70
|
-
end
|
|
50
|
+
primary = Resolver.primary_proc(resolved)
|
|
51
|
+
if primary.nil?
|
|
52
|
+
$stderr.puts "Error: no HTTP process (proxy: true) found in config/local.yml."
|
|
53
|
+
exit 1
|
|
54
|
+
end
|
|
71
55
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
puts "
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
56
|
+
runner = Runner.new(store: ctx.store)
|
|
57
|
+
children = []
|
|
58
|
+
routes_registered = []
|
|
59
|
+
|
|
60
|
+
puts "ask-local (#{service})"
|
|
61
|
+
puts "--"
|
|
62
|
+
|
|
63
|
+
processes.each do |proc_name, entry|
|
|
64
|
+
next if entry["proxy"] == false
|
|
65
|
+
|
|
66
|
+
hostname = Resolver.hostname_for(resolved, proc_name)
|
|
67
|
+
next unless hostname
|
|
68
|
+
|
|
69
|
+
url = Hostname.url(hostname, port: ctx.proxy_port, tls: ctx.proxy_tls)
|
|
70
|
+
hostnames = [hostname]
|
|
71
|
+
puts " [#{proc_name}] #{url}"
|
|
72
|
+
|
|
73
|
+
# Each process cmd runs through the shell so $PORT (and other
|
|
74
|
+
# env refs) expand — same trust boundary as a Procfile line
|
|
75
|
+
# (repo code, not user input). Compound lines are refused.
|
|
76
|
+
cmd = entry["cmd"].to_s
|
|
77
|
+
if cmd.match?(Ask::Local::Procfile::COMPOUND)
|
|
78
|
+
$stderr.puts " [#{proc_name}] ERROR: compound line (&&, ||, |, ;) — run explicitly: ask-local run -- #{cmd}"
|
|
79
|
+
next
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
port = opts[:app_port] || Ports.find_free
|
|
83
|
+
shell_cmd = ["sh", "-c", cmd]
|
|
84
|
+
# Always allow the proxied hostname in Rails dev (Rails ignores
|
|
85
|
+
# this env var when not a Rails app — safe for every framework).
|
|
86
|
+
app = runner.boot_run(name: proc_name, hostname: hostname, url: url,
|
|
87
|
+
dir: Dir.pwd, command: shell_cmd, port: port, force: opts[:force],
|
|
88
|
+
rails_dev_host: hostname)
|
|
89
|
+
register_all(ctx, hostnames, app, force: opts[:force],
|
|
90
|
+
spec: { "dir" => File.expand_path(Dir.pwd), "proc" => proc_name })
|
|
91
|
+
routes_registered << { hostnames: hostnames, app: app }
|
|
92
|
+
|
|
93
|
+
puts " -> #{url}"
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
background = processes.select { |_, v| v["proxy"] == false }
|
|
97
|
+
unless background.empty?
|
|
98
|
+
puts " [background] #{background.keys.join(', ')}"
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
puts
|
|
102
|
+
ctx.report_unresolved(routes_registered.flat_map { |r| r[:hostnames] })
|
|
103
|
+
|
|
104
|
+
# Supervisor: exit when ANY child dies (loud cleanup).
|
|
105
|
+
all_pids = routes_registered.map { |r| r[:app].pid }
|
|
106
|
+
trap_cleanup(ctx, routes_registered.flat_map { |r| r[:hostnames] }, all_pids)
|
|
107
|
+
supervise_tree(ctx, routes_registered.flat_map { |r| r[:hostnames] }, all_pids)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def build_env(ctx, resolved, entry)
|
|
111
|
+
env = {}
|
|
112
|
+
# Merge config env.clear
|
|
113
|
+
config_env = resolved.secrets || {}
|
|
114
|
+
entry_env = entry["env"] || {}
|
|
115
|
+
(entry_env["clear"] || {}).each { |k, v| env[k] = v }
|
|
116
|
+
# Merge secrets from config/local.secrets
|
|
117
|
+
secret_keys = entry_env["secret"] || []
|
|
118
|
+
secret_keys.each do |k|
|
|
119
|
+
env[k] = config_env[k] if config_env.key?(k)
|
|
120
|
+
end
|
|
121
|
+
# Host env (dotenv from .env) already in ENV
|
|
122
|
+
env
|
|
87
123
|
end
|
|
88
124
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
# spawned detached (so Ctrl+C in the CLI never SIGINTs the app),
|
|
92
|
-
# which rules out Process.wait — detached children are already
|
|
93
|
-
# reaped. Poll liveness at 2Hz: prompt enough for crash cleanup
|
|
94
|
-
# without spinning.
|
|
95
|
-
def supervise_backend(ctx, hostnames, app)
|
|
96
|
-
until !ProxyControl.pid_alive?(app.pid)
|
|
125
|
+
def supervise_tree(ctx, hostnames, pids)
|
|
126
|
+
loop do
|
|
97
127
|
sleep 0.5
|
|
128
|
+
if pids.any? { |pid| !ProxyControl.pid_alive?(pid) }
|
|
129
|
+
puts "\nA process exited — cleaning up all routes."
|
|
130
|
+
cleanup_routes(ctx, hostnames)
|
|
131
|
+
# Kill remaining children
|
|
132
|
+
pids.each do |pid|
|
|
133
|
+
Process.kill("TERM", pid) rescue nil
|
|
134
|
+
end
|
|
135
|
+
exit 0
|
|
136
|
+
end
|
|
98
137
|
end
|
|
99
|
-
puts "\nBackend exited — cleaning up."
|
|
100
|
-
cleanup_routes(ctx, hostnames)
|
|
101
|
-
exit 0
|
|
102
138
|
end
|
|
103
139
|
|
|
104
140
|
def inject_port_flags(command, port)
|
|
105
141
|
return command if command.empty?
|
|
106
142
|
return command if command.any? { |a| a.match?(/\A(-p|--port)(=|\z)/) }
|
|
107
|
-
# A literal $PORT already present (e.g. Procfile `web: x --port $PORT`,
|
|
108
|
-
# foreman --port passthrough): injecting again would double-set it.
|
|
109
143
|
return command if command.any? { |a| a.include?("$PORT") }
|
|
110
144
|
|
|
111
145
|
bin = File.basename(command.first.to_s)
|
|
@@ -113,85 +147,33 @@ module Ask
|
|
|
113
147
|
(command.length > 2 && PORT_IGNORING.include?(File.basename(command[2].to_s)))
|
|
114
148
|
return command unless needs_flags
|
|
115
149
|
|
|
116
|
-
|
|
117
|
-
flags.concat(jekyll_livereload_flags(port)) if jekyll_command?(command)
|
|
118
|
-
command + flags
|
|
119
|
-
end
|
|
120
|
-
|
|
121
|
-
# Jekyll's livereload runs its own server on a second port
|
|
122
|
-
# (default 35729) serving the livereload.js WebSocket. It cannot go
|
|
123
|
-
# through the proxy (one route = one backend), so pin it next to the
|
|
124
|
-
# main port and document the direct URL. Only when the app enables
|
|
125
|
-
# livereload in _config.yml; explicit user flags always win.
|
|
126
|
-
JEKYLL_LIVERELOAD_DEFAULT_PORT = 35_729
|
|
127
|
-
|
|
128
|
-
def jekyll_command?(command)
|
|
129
|
-
command.any? { |a| File.basename(a.to_s) == "jekyll" }
|
|
130
|
-
end
|
|
131
|
-
|
|
132
|
-
def jekyll_livereload_flags(port)
|
|
133
|
-
return [] unless File.file?("_config.yml")
|
|
134
|
-
return [] unless File.read("_config.yml").match?(/^\s*livereload:\s*true/i)
|
|
135
|
-
return [] if port + 1 > Ports::MAX_PORT
|
|
136
|
-
|
|
137
|
-
["--livereload-port", (port + 1).to_s]
|
|
138
|
-
rescue SystemCallError
|
|
139
|
-
[]
|
|
140
|
-
end
|
|
141
|
-
|
|
142
|
-
def default_command(framework, proc_name = nil)
|
|
143
|
-
case framework
|
|
144
|
-
when :procfile
|
|
145
|
-
procfile_command(proc_name) || raise(Error,
|
|
146
|
-
proc_name ? "Procfile has no '#{proc_name}' process, or its line is " \
|
|
147
|
-
"compound (&&, ||, |, ;) — ask-local cannot inject PORT safely. " \
|
|
148
|
-
"Run explicitly instead: ask-local run -- <command>" :
|
|
149
|
-
"Procfile.dev first line is compound (&&, ||, |, ;) or unreadable — " \
|
|
150
|
-
"ask-local cannot inject PORT safely. Run explicitly instead: " \
|
|
151
|
-
"ask-local run -- <command>")
|
|
152
|
-
when :jekyll then %w[bundle exec jekyll serve]
|
|
153
|
-
when :bridgetown then %w[bin/bridgetown start]
|
|
154
|
-
when :middleman then %w[bundle exec middleman server]
|
|
155
|
-
else raise(Error, "No command given and no bootable app detected. Usage: ask-local run -- <command>")
|
|
156
|
-
end
|
|
150
|
+
command + ["--port", port.to_s, "--host", "127.0.0.1"]
|
|
157
151
|
end
|
|
158
152
|
|
|
159
|
-
#
|
|
160
|
-
# (the overmind `-P web` convention teams already use).
|
|
161
|
-
#
|
|
162
|
-
# Trust boundary: the Procfile line is repo code, so `sh -c` is
|
|
163
|
-
# safe here the way it would not be for user-supplied input.
|
|
153
|
+
# Legacy procfile parsing for backwards compat.
|
|
164
154
|
def procfile_command(process = nil)
|
|
165
|
-
path =
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
return nil unless
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
["sh", "-c", cmd]
|
|
180
|
-
end
|
|
181
|
-
|
|
182
|
-
def trap_cleanup(ctx, hostnames)
|
|
155
|
+
path = ::Ask::Local::Procfile.find_file(Dir.pwd)
|
|
156
|
+
return nil unless path
|
|
157
|
+
lines = ::Ask::Local::Procfile.parse_file(path)
|
|
158
|
+
line = if process
|
|
159
|
+
lines.find { |l| l.name == process }
|
|
160
|
+
else
|
|
161
|
+
lines.first
|
|
162
|
+
end
|
|
163
|
+
return nil unless line
|
|
164
|
+
line.compound ? nil : ["sh", "-c", line.command]
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def trap_cleanup(ctx, hostnames, pids = [])
|
|
183
168
|
%w[INT TERM].each do |sig|
|
|
184
169
|
trap(sig) do
|
|
185
170
|
cleanup_routes(ctx, hostnames)
|
|
171
|
+
pids.each { |pid| Process.kill("TERM", pid) rescue nil }
|
|
186
172
|
exit 0
|
|
187
173
|
end
|
|
188
174
|
end
|
|
189
175
|
end
|
|
190
176
|
|
|
191
|
-
# Primary hostname is registered by the runner; secondaries (extra
|
|
192
|
-
# TLDs) share the same backend. Every hostname gets a backend sidecar
|
|
193
|
-
# so `ask-local stop` finds the process from any of them, and the
|
|
194
|
-
# daemon supervisor needs the spec on every hostname.
|
|
195
177
|
def register_all(ctx, hostnames, app, force:, spec: nil)
|
|
196
178
|
hostnames[1..].each do |h|
|
|
197
179
|
ctx.store.add_route(h, app.target, Process.pid, kind: app.kind,
|
|
@@ -207,8 +189,6 @@ module Ask
|
|
|
207
189
|
nil
|
|
208
190
|
end
|
|
209
191
|
|
|
210
|
-
# Foreground boot semantics (portless model): leaving = routes gone
|
|
211
|
-
# AND backend stopped. No orphans on Ctrl+C, TERM, or clean exit.
|
|
212
192
|
def cleanup_routes(ctx, hostnames)
|
|
213
193
|
hostnames.each do |h|
|
|
214
194
|
begin
|
|
@@ -225,51 +205,42 @@ module Ask
|
|
|
225
205
|
end
|
|
226
206
|
end
|
|
227
207
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
# webhooks). If 443 cannot be bound, this is a hard error pointing
|
|
235
|
-
# at `ask-local setup`.
|
|
236
|
-
#
|
|
237
|
-
# Explicit opt-in is different: `ask-local proxy start -p 1355`
|
|
238
|
-
# means you asked for a port in the URL, and ASK_LOCAL_URL carries
|
|
239
|
-
# it faithfully. That path never flows through here.
|
|
208
|
+
def resolve!(ctx, variant: nil, tld: nil)
|
|
209
|
+
# The resolver calls Config.load, which raises ConfigError if
|
|
210
|
+
# config/local.yml is missing — exactly what we want.
|
|
211
|
+
Ask::Local::Resolver.resolve(Dir.pwd, variant: variant, tld: tld)
|
|
212
|
+
end
|
|
213
|
+
|
|
240
214
|
def ensure_proxy!(ctx)
|
|
241
215
|
port = ctx.proxy_port
|
|
242
216
|
tls = ctx.proxy_tls
|
|
243
217
|
if ProxyControl.listening?(port)
|
|
244
218
|
return if ProxyControl.ours?(port, tls: tls)
|
|
245
|
-
|
|
246
219
|
$stderr.puts "Error: port #{port} is in use by another process."
|
|
247
|
-
$stderr.puts " Stop it, or
|
|
220
|
+
$stderr.puts " Stop it, or ask-local proxy start -p <port>"
|
|
248
221
|
exit 1
|
|
249
222
|
end
|
|
250
|
-
|
|
251
223
|
privileged = port < 1024 && !ProxyControl.root?
|
|
252
224
|
if privileged && !ctx.interactive?
|
|
253
|
-
$stderr.puts "Error: proxy is not running and port #{port} needs root
|
|
254
|
-
$stderr.puts "
|
|
255
|
-
$stderr.puts "
|
|
256
|
-
$stderr.puts "
|
|
257
|
-
$stderr.puts " sudo ask-local
|
|
225
|
+
$stderr.puts "Error: proxy is not running and port #{port} needs root."
|
|
226
|
+
$stderr.puts " Human: run this once — ask-local setup"
|
|
227
|
+
$stderr.puts " Agent/CI: pre-provision passwordless sudo once —"
|
|
228
|
+
$stderr.puts " ask-local sudoers > /tmp/ask-local.sudoers"
|
|
229
|
+
$stderr.puts " sudo install -o root -g wheel -m 440 /tmp/ask-local.sudoers /etc/sudoers.d/ask-local"
|
|
230
|
+
$stderr.puts " Or start the proxy by hand: sudo ask-local proxy start"
|
|
258
231
|
exit 1
|
|
259
232
|
end
|
|
260
|
-
puts "Starting proxy#{privileged ? " (
|
|
233
|
+
puts "Starting proxy#{privileged ? " (sudo)" : ""}..."
|
|
261
234
|
begin
|
|
262
|
-
ProxyControl.spawn_daemon(store: ctx.store, port: port, tls: tls, sudo: privileged)
|
|
235
|
+
Ask::Local::ProxyControl.spawn_daemon(store: ctx.store, port: port, tls: tls, sudo: privileged)
|
|
263
236
|
rescue Ask::Local::ProxyNotRunningError => e
|
|
264
237
|
$stderr.puts "Error: #{e.message.lines.first&.strip}"
|
|
265
|
-
$stderr.puts "
|
|
266
|
-
$stderr.puts " ask-local setup"
|
|
238
|
+
$stderr.puts " Fix once: ask-local setup"
|
|
267
239
|
exit 1
|
|
268
240
|
end
|
|
269
241
|
rescue Errno::EACCES
|
|
270
242
|
$stderr.puts "Error: permission denied binding port #{port}."
|
|
271
|
-
$stderr.puts "
|
|
272
|
-
$stderr.puts " ask-local setup"
|
|
243
|
+
$stderr.puts " Fix once: ask-local setup"
|
|
273
244
|
exit 1
|
|
274
245
|
end
|
|
275
246
|
end
|
data/lib/ask/local/cli/routes.rb
CHANGED
|
@@ -19,13 +19,11 @@ module Ask
|
|
|
19
19
|
raise Error, "Usage: ask-local get <name> [--service s] [--variant v] [--tld t]" unless name
|
|
20
20
|
|
|
21
21
|
opts = ctx.parse_flags(args[1..] || [], %i[service variant tld])
|
|
22
|
-
context = Resolver.resolve(Dir.pwd,
|
|
23
|
-
variant: opts[:variant], tlds: opts[:tld], use_branch: false)
|
|
22
|
+
context = Resolver.resolve(Dir.pwd, variant: opts[:variant])
|
|
24
23
|
hostnames = Hostname.build(
|
|
25
24
|
app: Sanitize.hostname_label(name),
|
|
26
|
-
|
|
27
|
-
variant: context.variant
|
|
28
|
-
tlds: context.tlds
|
|
25
|
+
tlds: Array(context.tld || Ask::Local::Hostname::DEFAULT_TLD),
|
|
26
|
+
variant: context.variant
|
|
29
27
|
)
|
|
30
28
|
puts Hostname.url(hostnames.first, port: ctx.proxy_port, tls: ctx.proxy_tls)
|
|
31
29
|
end
|
|
@@ -206,9 +204,11 @@ module Ask
|
|
|
206
204
|
end
|
|
207
205
|
payload = {
|
|
208
206
|
app: resolved.app, app_source: resolved.sources[:app],
|
|
209
|
-
|
|
207
|
+
tld: resolved.tld, tld_source: resolved.sources[:tld],
|
|
208
|
+
host: resolved.host, host_source: resolved.sources[:host],
|
|
210
209
|
variant: resolved.variant, variant_source: resolved.sources[:variant],
|
|
211
|
-
|
|
210
|
+
urls: urls,
|
|
211
|
+
processes: resolved.processes.keys,
|
|
212
212
|
framework: Framework.detect(Dir.pwd).to_s
|
|
213
213
|
}
|
|
214
214
|
if json
|
|
@@ -217,9 +217,13 @@ module Ask
|
|
|
217
217
|
return
|
|
218
218
|
end
|
|
219
219
|
puts "app: #{payload[:app]} (from #{payload[:app_source]})"
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
220
|
+
if payload[:host]
|
|
221
|
+
puts "host: #{payload[:host]} (from #{payload[:host_source]})"
|
|
222
|
+
else
|
|
223
|
+
puts "tld: #{payload[:tld]} (from #{payload[:tld_source]})"
|
|
224
|
+
end
|
|
225
|
+
puts "variant: #{payload[:variant] || "(none)"} (from #{payload[:variant_source] || "no overlay file, flag, or env"})"
|
|
226
|
+
puts "processes: #{payload[:processes].join(", ")}"
|
|
223
227
|
puts "urls:"
|
|
224
228
|
urls.each { |u| puts " #{u}" }
|
|
225
229
|
puts "framework: #{payload[:framework]}"
|
|
@@ -231,10 +235,10 @@ module Ask
|
|
|
231
235
|
url =
|
|
232
236
|
if name
|
|
233
237
|
opts = ctx.parse_flags(args[1..] || [], %i[service variant tld])
|
|
234
|
-
context = Resolver.resolve(Dir.pwd,
|
|
235
|
-
variant: opts[:variant], tlds: opts[:tld], use_branch: false)
|
|
238
|
+
context = Resolver.resolve(Dir.pwd, variant: opts[:variant], tld: opts[:tld])
|
|
236
239
|
hostnames = Hostname.build(app: Sanitize.hostname_label(name),
|
|
237
|
-
|
|
240
|
+
tlds: Array(context.tld || Ask::Local::Hostname::DEFAULT_TLD),
|
|
241
|
+
variant: context.variant)
|
|
238
242
|
Hostname.url(hostnames.first, port: ctx.proxy_port, tls: ctx.proxy_tls)
|
|
239
243
|
else
|
|
240
244
|
resolved = Resolver.resolve(Dir.pwd)
|