ask-local 0.1.0 → 0.2.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/CHANGELOG.md +73 -32
- data/README.md +58 -9
- data/bin/askl +6 -0
- data/lib/ask/local/cli/boot.rb +150 -159
- data/lib/ask/local/cli/routes.rb +17 -13
- data/lib/ask/local/cli/system.rb +272 -2
- data/lib/ask/local/cli.rb +7 -3
- data/lib/ask/local/config.rb +342 -59
- data/lib/ask/local/procfile.rb +139 -0
- data/lib/ask/local/proxy_control.rb +38 -13
- data/lib/ask/local/resolver.rb +62 -94
- data/lib/ask/local/runner.rb +8 -3
- data/lib/ask/local/version.rb +1 -1
- data/lib/ask/skills/ask-local/SKILL.md +57 -33
- data/lib/ask-local.rb +1 -0
- metadata +4 -1
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)
|
data/lib/ask/local/cli/system.rb
CHANGED
|
@@ -263,6 +263,273 @@ module Ask
|
|
|
263
263
|
end
|
|
264
264
|
end
|
|
265
265
|
|
|
266
|
+
|
|
267
|
+
def init(ctx, _args)
|
|
268
|
+
config_path = File.join(Dir.pwd, Config::RELATIVE_PATH)
|
|
269
|
+
if File.file?(config_path)
|
|
270
|
+
$stderr.puts "config/local.yml already exists at #{config_path}"
|
|
271
|
+
return
|
|
272
|
+
end
|
|
273
|
+
FileUtils.mkdir_p(File.join(Dir.pwd, Config::RELATIVE_DIR))
|
|
274
|
+
|
|
275
|
+
# Migrate Procfile.dev / Procfile if present.
|
|
276
|
+
procfile = Ask::Local::Procfile.find_file(Dir.pwd)
|
|
277
|
+
service = Ask::Local::Sanitize.hostname_label(File.basename(Dir.pwd))
|
|
278
|
+
|
|
279
|
+
process_lines = []
|
|
280
|
+
if procfile
|
|
281
|
+
lines = Ask::Local::Procfile.parse_file(procfile)
|
|
282
|
+
lines.each do |l|
|
|
283
|
+
type = Ask::Local::Procfile.classify(l.name) == :background ? "false" : "true"
|
|
284
|
+
process_lines << " #{l.name}:"
|
|
285
|
+
process_lines << " cmd: #{l.command}"
|
|
286
|
+
process_lines << " proxy: #{type}"
|
|
287
|
+
process_lines << " # NOTE: compound line - run explicitly" if l.compound
|
|
288
|
+
end
|
|
289
|
+
else
|
|
290
|
+
# Default: a single web process. Detect Rails (Gemfile with
|
|
291
|
+
# rails) vs plain Rack (config.ru) and pick the right boot.
|
|
292
|
+
has_rails = File.file?(File.join(Dir.pwd, "Gemfile")) &&
|
|
293
|
+
File.read(File.join(Dir.pwd, "Gemfile")).match?(/gem ["']rails["']/)
|
|
294
|
+
cmd =
|
|
295
|
+
if has_rails
|
|
296
|
+
"bundle exec puma -b tcp://127.0.0.1:$PORT config.ru"
|
|
297
|
+
elsif File.file?(File.join(Dir.pwd, "config.ru"))
|
|
298
|
+
"puma -b tcp://127.0.0.1:$PORT config.ru"
|
|
299
|
+
else
|
|
300
|
+
"bin/rails server -p $PORT"
|
|
301
|
+
end
|
|
302
|
+
process_lines << " web:"
|
|
303
|
+
process_lines << " cmd: #{cmd}"
|
|
304
|
+
process_lines << " proxy: true"
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
content = <<~YAML
|
|
308
|
+
# config/local.yml — ask-local configuration (Kamal-style).
|
|
309
|
+
# This is the only source of truth. Run `ask-local` to boot everything.
|
|
310
|
+
# Overlay variants with config/local.<variant>.yml.
|
|
311
|
+
|
|
312
|
+
service: #{service}
|
|
313
|
+
|
|
314
|
+
proxy:
|
|
315
|
+
tld: localhost
|
|
316
|
+
|
|
317
|
+
processes:
|
|
318
|
+
#{process_lines.join("\n")}
|
|
319
|
+
|
|
320
|
+
env:
|
|
321
|
+
clear:
|
|
322
|
+
RAILS_ENV: development
|
|
323
|
+
YAML
|
|
324
|
+
File.write(config_path, content)
|
|
325
|
+
puts "Created #{config_path}"
|
|
326
|
+
puts " service: #{service}"
|
|
327
|
+
puts " processes from: #{procfile || 'defaults'}"
|
|
328
|
+
puts "Run `ask-local start` to boot."
|
|
329
|
+
end
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
def setup(ctx, args)
|
|
333
|
+
if args.include?("--help") || args.include?("-h")
|
|
334
|
+
puts <<~HELP
|
|
335
|
+
Usage: ask-local setup [--no-service]
|
|
336
|
+
|
|
337
|
+
One-shot workstation setup for clean https://<app>.localhost URLs:
|
|
338
|
+
|
|
339
|
+
1. Trust the local CA (no more browser warnings)
|
|
340
|
+
2. Serve port 443 (root service at boot, or sudo daemon now)
|
|
341
|
+
3. Sync /etc/hosts (Safari + custom TLDs)
|
|
342
|
+
4. Verify everything with doctor
|
|
343
|
+
|
|
344
|
+
--no-service skips the root service and starts a sudo daemon
|
|
345
|
+
instead (no boot persistence; good for ephemeral machines).
|
|
346
|
+
HELP
|
|
347
|
+
return
|
|
348
|
+
end
|
|
349
|
+
|
|
350
|
+
step("1/4 Trusting local CA") do
|
|
351
|
+
result = Ask::Local::Trust.trust
|
|
352
|
+
unless result[:trusted]
|
|
353
|
+
abort_setup("CA trust failed: #{result[:error]}",
|
|
354
|
+
"Run `ask-local trust` manually to see the underlying error,",
|
|
355
|
+
"then re-run `ask-local setup`.")
|
|
356
|
+
end
|
|
357
|
+
end
|
|
358
|
+
|
|
359
|
+
unless args.include?("--no-service")
|
|
360
|
+
step("2/4 Installing proxy service on port 443") do
|
|
361
|
+
unless ensure_root_service(ctx)
|
|
362
|
+
abort_setup("Could not install the proxy service.",
|
|
363
|
+
"Fallback: `ask-local setup --no-service` for a sudo daemon",
|
|
364
|
+
"without boot persistence.")
|
|
365
|
+
end
|
|
366
|
+
end
|
|
367
|
+
else
|
|
368
|
+
step("2/4 Starting proxy sudo daemon on port 443") do
|
|
369
|
+
unless ensure_sudo_daemon(ctx)
|
|
370
|
+
abort_setup("Could not start the proxy daemon on port 443.",
|
|
371
|
+
"Check the log, then re-run `ask-local setup`.")
|
|
372
|
+
end
|
|
373
|
+
end
|
|
374
|
+
end
|
|
375
|
+
|
|
376
|
+
step("3/4 Syncing /etc/hosts") do
|
|
377
|
+
hostnames = ctx.store.load_routes.map { |r| r["hostname"] }
|
|
378
|
+
unless Ask::Local::Hosts.sync(hostnames)
|
|
379
|
+
abort_setup("Could not write /etc/hosts.",
|
|
380
|
+
"Run `sudo ask-local hosts sync`, then re-run `ask-local setup`.")
|
|
381
|
+
end
|
|
382
|
+
end
|
|
383
|
+
|
|
384
|
+
step("4/4 Verifying with doctor") do
|
|
385
|
+
failed = Doctor.print(Doctor.run(store: ctx.store), out: $stdout)
|
|
386
|
+
if failed.zero?
|
|
387
|
+
puts "\nSetup complete: https://<app>.localhost URLs are ready."
|
|
388
|
+
puts "Try it: cd ~/code/myapp && ask-local"
|
|
389
|
+
else
|
|
390
|
+
abort_setup("Doctor reports #{failed} failing check(s) (see above).",
|
|
391
|
+
"Fix the reported issues, then re-run `ask-local setup`.")
|
|
392
|
+
end
|
|
393
|
+
end
|
|
394
|
+
end
|
|
395
|
+
|
|
396
|
+
def step(label)
|
|
397
|
+
puts "\n==> #{label}..."
|
|
398
|
+
yield
|
|
399
|
+
puts " ok"
|
|
400
|
+
end
|
|
401
|
+
|
|
402
|
+
def abort_setup(problem, *fixes)
|
|
403
|
+
$stderr.puts "\nSetup failed: #{problem}"
|
|
404
|
+
fixes.each { |f| $stderr.puts " #{f}" }
|
|
405
|
+
exit 1
|
|
406
|
+
end
|
|
407
|
+
|
|
408
|
+
# Install the root service (boot-persistent). Returns true when a
|
|
409
|
+
# proxy is up on 443 afterwards, false otherwise. Never falls back
|
|
410
|
+
# to a high port silently: a :port suffix in URLs would corrupt the
|
|
411
|
+
# stable-URL promise, so failure here is a hard error with guidance.
|
|
412
|
+
def ensure_root_service(ctx)
|
|
413
|
+
service_install(ctx, [])
|
|
414
|
+
wait_for_ours(ctx, 443, tls: true)
|
|
415
|
+
rescue Error, SystemCallError => e
|
|
416
|
+
warn " service install failed: #{e.message}"
|
|
417
|
+
false
|
|
418
|
+
end
|
|
419
|
+
|
|
420
|
+
# Sudo daemon for 443 without boot persistence (--no-service).
|
|
421
|
+
def ensure_sudo_daemon(ctx)
|
|
422
|
+
port, tls = 443, true
|
|
423
|
+
unless ctx.interactive?
|
|
424
|
+
warn " no TTY available for the sudo prompt."
|
|
425
|
+
return false
|
|
426
|
+
end
|
|
427
|
+
ProxyControl.spawn_daemon(store: ctx.store, port: port, tls: tls, sudo: true)
|
|
428
|
+
wait_for_ours(ctx, port, tls: tls)
|
|
429
|
+
rescue Ask::Local::ProxyNotRunningError, SystemCallError => e
|
|
430
|
+
warn " daemon start failed: #{e.message.lines.first&.strip}"
|
|
431
|
+
false
|
|
432
|
+
end
|
|
433
|
+
|
|
434
|
+
def wait_for_ours(ctx, port, tls:, timeout: 20)
|
|
435
|
+
deadline = Process.clock_gettime(Process::CLOCK_MONOTONIC) + timeout
|
|
436
|
+
until ProxyControl.ours?(port, tls: tls)
|
|
437
|
+
return false if Process.clock_gettime(Process::CLOCK_MONOTONIC) > deadline
|
|
438
|
+
|
|
439
|
+
sleep 0.5
|
|
440
|
+
end
|
|
441
|
+
true
|
|
442
|
+
end
|
|
443
|
+
|
|
444
|
+
# ask-local start — one-setup-and-go: idempotent workstation setup
|
|
445
|
+
# (trust, 443, hosts) when doctor fails, then boots the app in the
|
|
446
|
+
# current directory. The single command you run day-to-day; existing
|
|
447
|
+
# bare `ask-local` keeps working via BootCommand.run_inferred, and
|
|
448
|
+
# `setup` stays for explicit re-setup.
|
|
449
|
+
def start(ctx, args)
|
|
450
|
+
if args.include?("--help") || args.include?("-h")
|
|
451
|
+
puts <<~HELP
|
|
452
|
+
Usage: ask-local start [name] [cmd...] [options]
|
|
453
|
+
|
|
454
|
+
One-setup-and-go: if the workstation isn't ready (CA, proxy,
|
|
455
|
+
hosts), runs the minimal needed setup first, then boots the
|
|
456
|
+
app in the current directory.
|
|
457
|
+
|
|
458
|
+
ask-local start # infer name, boot -> https://<app>.localhost
|
|
459
|
+
ask-local start myapp # explicit name
|
|
460
|
+
ask-local start -- --help # pass --help to the app, not here
|
|
461
|
+
|
|
462
|
+
Options are passed through to the boot path:
|
|
463
|
+
--name <name> --service <svc> --variant <v> --tld <tld> --branch
|
|
464
|
+
|
|
465
|
+
Setup failures become hard errors pointing at `ask-local setup`;
|
|
466
|
+
non-interactive CI without a running proxy exits immediately.
|
|
467
|
+
HELP
|
|
468
|
+
return
|
|
469
|
+
end
|
|
470
|
+
|
|
471
|
+
# Fast path: every doctor check passes => skip setup entirely.
|
|
472
|
+
# This makes `start` as fast as `ask-local` on a ready machine.
|
|
473
|
+
if needs_workstation_setup?(ctx)
|
|
474
|
+
ensure_workstation!(ctx)
|
|
475
|
+
end
|
|
476
|
+
BootCommand.run_inferred(ctx, args)
|
|
477
|
+
end
|
|
478
|
+
|
|
479
|
+
def needs_workstation_setup?(ctx)
|
|
480
|
+
Ask::Local::Doctor.run(store: ctx.store).any? { |c| !c.ok }
|
|
481
|
+
end
|
|
482
|
+
|
|
483
|
+
|
|
484
|
+
# Quiet workstation setup for `start`: trust the CA, ensure a proxy
|
|
485
|
+
# on 443 (root service, sudo daemon fallback), and sync hosts.
|
|
486
|
+
# Each step is idempotent; only missing pieces run. Non-interactive
|
|
487
|
+
# CI without a proxy fails fast rather than prompting for sudo.
|
|
488
|
+
def ensure_workstation!(ctx)
|
|
489
|
+
# 1. CA
|
|
490
|
+
unless Ask::Local::Certs.trusted?(ctx.store.dir)
|
|
491
|
+
result = Ask::Local::Trust.trust
|
|
492
|
+
unless result[:trusted]
|
|
493
|
+
abort_setup("CA trust failed: #{result[:error]}",
|
|
494
|
+
"Run `ask-local setup` in a terminal (handles trust + service),",
|
|
495
|
+
"then re-run `ask-local start`.")
|
|
496
|
+
end
|
|
497
|
+
end
|
|
498
|
+
|
|
499
|
+
# 2. Proxy on 443
|
|
500
|
+
port = 443
|
|
501
|
+
tls = true
|
|
502
|
+
unless Ask::Local::ProxyControl.listening?(port) && ProxyControl.ours?(port, tls: tls)
|
|
503
|
+
if port < 1024 && !Ask::Local::ProxyControl.root? && !ctx.interactive?
|
|
504
|
+
abort_setup("Proxy is not running and port 443 needs root to bind.",
|
|
505
|
+
"Run this once in a terminal: ask-local setup")
|
|
506
|
+
end
|
|
507
|
+
ok =
|
|
508
|
+
if ProxyControl.root?
|
|
509
|
+
Ask::Local::CLI::SystemCommand.ensure_root_service(ctx)
|
|
510
|
+
elsif ctx.interactive?
|
|
511
|
+
begin
|
|
512
|
+
Ask::Local::ProxyControl.spawn_daemon(store: ctx.store, port: port, tls: tls, sudo: true)
|
|
513
|
+
wait_for_ours(ctx, port, tls: tls)
|
|
514
|
+
rescue Ask::Local::ProxyNotRunningError, SystemCallError => e
|
|
515
|
+
warn " daemon start failed: #{e.message.lines.first&.strip}"
|
|
516
|
+
false
|
|
517
|
+
end
|
|
518
|
+
else
|
|
519
|
+
false
|
|
520
|
+
end
|
|
521
|
+
unless ok
|
|
522
|
+
abort_setup("Proxy is not running and could not be started on port 443.",
|
|
523
|
+
"Run `ask-local setup` in a terminal (it handles trust + service + hosts),",
|
|
524
|
+
"then re-run `ask-local start`.")
|
|
525
|
+
end
|
|
526
|
+
end
|
|
527
|
+
|
|
528
|
+
# 3. Hosts (best-effort: only needed for Safari; warn, don't fail)
|
|
529
|
+
unless Hosts.sync(ctx.store.load_routes.map { |r| r["hostname"] })
|
|
530
|
+
warn "Warning: could not write /etc/hosts (try sudo ask-local hosts sync)."
|
|
531
|
+
end
|
|
532
|
+
end
|
|
266
533
|
# ask-local kamal <variant> [--app myapp] [--domain preview.example.com]
|
|
267
534
|
def kamal(_ctx, args)
|
|
268
535
|
opts = {}
|
|
@@ -273,13 +540,15 @@ module Ask
|
|
|
273
540
|
case a[i]
|
|
274
541
|
when "--app" then opts[:app] = a.fetch(i + 1); i += 2
|
|
275
542
|
when "--domain" then opts[:domain] = a.fetch(i + 1); i += 2
|
|
543
|
+
when "--tld" then opts[:tld] = a.fetch(i + 1); i += 2
|
|
276
544
|
else rest << a[i]; i += 1
|
|
277
545
|
end
|
|
278
546
|
end
|
|
279
547
|
variant = rest.first
|
|
280
|
-
raise Error, "Usage: ask-local kamal <variant> [--app myapp] [--domain preview.example.com]" unless variant
|
|
548
|
+
raise Error, "Usage: ask-local kamal <variant> [--app myapp] [--domain preview.example.com] [--tld <tld>]" unless variant
|
|
549
|
+
tld = opts[:tld] || ENV["ASK_LOCAL_TLD"]&.split(",")&.first || "localhost"
|
|
281
550
|
|
|
282
|
-
app = opts[:app] || Resolver.resolve(Dir.pwd
|
|
551
|
+
app = opts[:app] || Resolver.resolve(Dir.pwd).app
|
|
283
552
|
domain = opts[:domain] || ENV["ASK_LOCAL_KAMAL_DOMAIN"] || "preview.example.com"
|
|
284
553
|
slug = Sanitize.hostname_label(variant)
|
|
285
554
|
puts "# Paste into deploy.yml proxy section for a preview of variant #{slug}:"
|
|
@@ -287,6 +556,7 @@ module Ask
|
|
|
287
556
|
puts " ssl: true"
|
|
288
557
|
puts " hosts:"
|
|
289
558
|
puts " - #{app}-#{slug}.#{domain}"
|
|
559
|
+
puts " # Prefer Kamal multi-host for production; single-host preview above is fine for ephemeral branches."
|
|
290
560
|
end
|
|
291
561
|
end
|
|
292
562
|
end
|
data/lib/ask/local/cli.rb
CHANGED
|
@@ -11,7 +11,7 @@ module Ask
|
|
|
11
11
|
# In non-interactive environments (no TTY or CI=1) we fail early with
|
|
12
12
|
# a clear message instead of prompting (portless lesson).
|
|
13
13
|
class CLI
|
|
14
|
-
SUBCOMMANDS = %w[run get alias hosts list doctor trust clean prune proxy service kamal stop restart log status open].freeze
|
|
14
|
+
SUBCOMMANDS = %w[run get alias hosts list doctor trust clean prune proxy service kamal stop restart log status open setup start init].freeze
|
|
15
15
|
|
|
16
16
|
def self.run(argv)
|
|
17
17
|
new.run(argv)
|
|
@@ -44,6 +44,9 @@ module Ask
|
|
|
44
44
|
when "prune" then RoutesCommand.prune(ctx, args)
|
|
45
45
|
when "proxy" then SystemCommand.proxy(ctx, args)
|
|
46
46
|
when "service" then SystemCommand.service(ctx, args)
|
|
47
|
+
when "setup" then SystemCommand.setup(ctx, args)
|
|
48
|
+
when "init" then SystemCommand.init(ctx, args)
|
|
49
|
+
when "start" then SystemCommand.start(ctx, args)
|
|
47
50
|
when "kamal" then SystemCommand.kamal(ctx, args)
|
|
48
51
|
when "stop"
|
|
49
52
|
exit RoutesCommand.stop(ctx, args)
|
|
@@ -64,7 +67,9 @@ module Ask
|
|
|
64
67
|
ask-local - Stable named .localhost URLs for Ruby development.
|
|
65
68
|
|
|
66
69
|
Usage:
|
|
67
|
-
ask-local
|
|
70
|
+
ask-local start [name] [cmd...] One-setup-and-go: setup if needed, then boot -> https://<app>.localhost
|
|
71
|
+
ask-local setup One-shot workstation setup without booting (run once)
|
|
72
|
+
ask-local Bare form of `start` -> https://<app>.localhost
|
|
68
73
|
ask-local run [cmd] Same, with explicit command
|
|
69
74
|
ask-local <name> <cmd> Run with explicit name
|
|
70
75
|
ask-local get <name> Print URL for a service
|
|
@@ -95,7 +100,6 @@ module Ask
|
|
|
95
100
|
def inject_port_flags(command, port)
|
|
96
101
|
BootCommand.inject_port_flags(command, port)
|
|
97
102
|
end
|
|
98
|
-
|
|
99
103
|
def procfile_command(process = nil)
|
|
100
104
|
BootCommand.procfile_command(process)
|
|
101
105
|
end
|