omaship 0.2.2 → 0.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/CHANGELOG.md +14 -0
- data/README.md +3 -4
- data/lib/omaship/api_client.rb +7 -8
- data/lib/omaship/cli.rb +162 -92
- data/lib/omaship/color_picker.rb +129 -0
- data/lib/omaship/version.rb +1 -1
- data/lib/omaship.rb +1 -0
- metadata +3 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: eb563c444317757eeb0a2a8907d0c6b6b26bd1dc03e2373124270430e8565949
|
|
4
|
+
data.tar.gz: cbee20a1cd5b4e9e7c3e6af65246f632e4c024522d0caff7a68027bc8c43a0c5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 70ac4ca7b97b49bdfd46b0ac34d9193f183c57cad9c72a695434c705f92e342246d28ff2cf27a5d5b321bad22fe2ada28102c9277de888b131689c39bf66f147
|
|
7
|
+
data.tar.gz: 0432cb652ead9e22d92898bb17bd8d9babf45525b6f2e273248fdd55d47ce2deadf54a014002c9184dce302c024d4bad59abc4d7f2c371aa73217b964e981534
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.4.0](https://github.com/bloomedai/omaship/compare/omaship/v0.3.0...omaship/v0.4.0) (2026-03-14)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* ✨ redesign onboarding flow with progressive CLI terminal path ([#324](https://github.com/bloomedai/omaship/issues/324)) ([872b145](https://github.com/bloomedai/omaship/commit/872b145a2f72775448375557246b4b31fe194459))
|
|
9
|
+
|
|
10
|
+
## [0.3.0](https://github.com/bloomedai/omaship/compare/omaship/v0.2.2...omaship/v0.3.0) (2026-03-09)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Features
|
|
14
|
+
|
|
15
|
+
* ✨ ship the free landing-page flow end to end ([#290](https://github.com/bloomedai/omaship/issues/290)) ([352c088](https://github.com/bloomedai/omaship/commit/352c088219acff3a24a72cda965d12bf1767af1e))
|
|
16
|
+
|
|
3
17
|
## [0.2.2](https://github.com/bloomedai/omaship/compare/omaship/v0.2.1...omaship/v0.2.2) (2026-03-06)
|
|
4
18
|
|
|
5
19
|
|
data/README.md
CHANGED
|
@@ -18,7 +18,7 @@ brew install omaship
|
|
|
18
18
|
## Quick Start
|
|
19
19
|
|
|
20
20
|
```bash
|
|
21
|
-
omaship login --token <token>
|
|
21
|
+
omaship login --token <token>
|
|
22
22
|
omaship list
|
|
23
23
|
omaship use <ship-ref>
|
|
24
24
|
omaship info
|
|
@@ -33,7 +33,7 @@ omaship use 17
|
|
|
33
33
|
|
|
34
34
|
## Ship Selection
|
|
35
35
|
|
|
36
|
-
`info
|
|
36
|
+
`info` and `deploy` resolve the target ship in this order:
|
|
37
37
|
|
|
38
38
|
1. `--ship <ship-ref>`
|
|
39
39
|
2. saved default from `omaship use <ship-ref>`
|
|
@@ -51,7 +51,6 @@ Numeric ids are also accepted.
|
|
|
51
51
|
- `omaship use <ship-ref>`
|
|
52
52
|
- `omaship info [--ship <ship-ref>]` (`status` and `ship` are aliases)
|
|
53
53
|
- `omaship new <name>` (requires Full CLI access)
|
|
54
|
-
- `omaship configure --payments <provider> [--ship <ship-ref>]` (requires Full CLI access)
|
|
55
54
|
- `omaship deploy [--ship <ship-ref>]` (requires Full CLI access)
|
|
56
55
|
- `omaship complete <bash|zsh|fish>` (print shell completion script)
|
|
57
56
|
- `omaship logout`
|
|
@@ -94,7 +93,7 @@ omaship complete fish > ~/.config/fish/completions/omaship.fish
|
|
|
94
93
|
|
|
95
94
|
## Environment
|
|
96
95
|
|
|
97
|
-
- `OMASHIP_HOST` (default: `
|
|
96
|
+
- `OMASHIP_HOST` (default: `https://omaship.com`)
|
|
98
97
|
- `OMASHIP_TOKEN` (optional alternative for `login`)
|
|
99
98
|
|
|
100
99
|
## Credentials
|
data/lib/omaship/api_client.rb
CHANGED
|
@@ -26,16 +26,15 @@ module Omaship
|
|
|
26
26
|
post_json("/api/v1/cli/ships", { ship: { root_domain: root_domain, visibility: visibility } })
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
-
def
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
get_json("/api/v1/cli/ships/#{ship_id}/logs")
|
|
29
|
+
def create_landing_page(name:, color_scheme: "mono-dark", purpose_profile: {})
|
|
30
|
+
post_json("/api/v1/cli/ships", {
|
|
31
|
+
ship: { name: name, landing_page: true, color_scheme: color_scheme },
|
|
32
|
+
purpose_profile: purpose_profile
|
|
33
|
+
})
|
|
35
34
|
end
|
|
36
35
|
|
|
37
|
-
def
|
|
38
|
-
|
|
36
|
+
def ship(ship_id:)
|
|
37
|
+
get_json("/api/v1/cli/ships/#{ship_id}")
|
|
39
38
|
end
|
|
40
39
|
|
|
41
40
|
def create_deploy(ship_id:)
|
data/lib/omaship/cli.rb
CHANGED
|
@@ -51,8 +51,8 @@ module Omaship
|
|
|
51
51
|
shell.say " use Set default ship from `omaship list` (for example: `omaship use omaship/acme`)"
|
|
52
52
|
shell.say " info Show details for a ship (aliases: ship, status)"
|
|
53
53
|
shell.say " new Create and provision a new ship"
|
|
54
|
-
shell.say " configure Configure a ship (requires Full CLI access)"
|
|
55
54
|
shell.say " deploy Deploy a ship (requires Full CLI access)"
|
|
55
|
+
shell.say " upgrade Open browser to upgrade your plan"
|
|
56
56
|
shell.say " complete Print shell completion script (bash, zsh, fish)"
|
|
57
57
|
shell.say " logout Remove local CLI credentials"
|
|
58
58
|
shell.say
|
|
@@ -206,43 +206,20 @@ module Omaship
|
|
|
206
206
|
|
|
207
207
|
desc "new NAME", "Create and provision a new ship"
|
|
208
208
|
method_option :domain, type: :string, desc: "Root domain (defaults to NAME.com)"
|
|
209
|
+
method_option :skip_purpose, type: :boolean, default: false, desc: "Skip purpose profile questions"
|
|
209
210
|
def new_ship(name)
|
|
210
211
|
with_api_error_handling(command: :new_ship) do
|
|
211
212
|
token = current_token
|
|
212
|
-
|
|
213
|
-
payload = build_api_client(token: token).create_ship(root_domain: root_domain)
|
|
214
|
-
ship = payload.fetch("ship")
|
|
213
|
+
plan = fetch_plan(token: token)
|
|
215
214
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
if final_ship.fetch("status") == "live"
|
|
220
|
-
progress_renderer.step("Ready. Customers can sign up and pay at #{final_ship.fetch("root_domain")}")
|
|
215
|
+
if plan == "free"
|
|
216
|
+
create_free_ship(name: name, token: token)
|
|
221
217
|
else
|
|
222
|
-
|
|
223
|
-
raise Thor::Error, message
|
|
218
|
+
create_paid_ship(name: name, token: token)
|
|
224
219
|
end
|
|
225
220
|
end
|
|
226
221
|
end
|
|
227
222
|
|
|
228
|
-
desc "configure --payments PROVIDER", "Configure a ship"
|
|
229
|
-
method_option :payments, type: :string, required: true
|
|
230
|
-
method_option :ship, type: :string, desc: "Ship from `omaship list` (preferred: omaship/acme; id also works)"
|
|
231
|
-
def configure
|
|
232
|
-
with_api_error_handling(command: :configure) do
|
|
233
|
-
payments_provider = options[:payments].to_s.strip.downcase
|
|
234
|
-
|
|
235
|
-
token = current_token
|
|
236
|
-
ship = resolve_ship(token: token)
|
|
237
|
-
build_api_client(token: token).create_configuration(ship_id: ship.fetch("id"), package: payments_provider)
|
|
238
|
-
|
|
239
|
-
progress_renderer.step("Configuration queued.")
|
|
240
|
-
|
|
241
|
-
poll_until_configuration_complete(ship_id: ship.fetch("id"), token: token)
|
|
242
|
-
progress_renderer.step("Configuration applied.")
|
|
243
|
-
end
|
|
244
|
-
end
|
|
245
|
-
|
|
246
223
|
desc "deploy", "Deploy a ship (requires Full CLI access)"
|
|
247
224
|
method_option :ship, type: :string, desc: "Ship from `omaship list` (preferred: omaship/acme; id also works)"
|
|
248
225
|
def deploy
|
|
@@ -260,6 +237,17 @@ module Omaship
|
|
|
260
237
|
end
|
|
261
238
|
end
|
|
262
239
|
|
|
240
|
+
desc "upgrade [SHIP]", "Open browser to upgrade your plan"
|
|
241
|
+
def upgrade(ship_name = nil)
|
|
242
|
+
url = "#{resolved_host}/settings/license"
|
|
243
|
+
if ship_name
|
|
244
|
+
url = "#{url}?ship=#{ship_name}"
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
say "Opening #{url}"
|
|
248
|
+
open_browser(url)
|
|
249
|
+
end
|
|
250
|
+
|
|
263
251
|
desc "complete SHELL", "Print shell completion script (bash, zsh, fish)"
|
|
264
252
|
long_desc <<~DESC
|
|
265
253
|
Print a shell completion script to stdout.
|
|
@@ -307,7 +295,7 @@ module Omaship
|
|
|
307
295
|
fi
|
|
308
296
|
|
|
309
297
|
if [[ ${COMP_CWORD} -eq 1 ]]; then
|
|
310
|
-
COMPREPLY=( $(compgen -W "login whoami list use info status ship new
|
|
298
|
+
COMPREPLY=( $(compgen -W "login whoami list use info status ship new deploy upgrade logout complete help" -- "${cur}") )
|
|
311
299
|
return 0
|
|
312
300
|
fi
|
|
313
301
|
|
|
@@ -316,11 +304,6 @@ module Omaship
|
|
|
316
304
|
return 0
|
|
317
305
|
fi
|
|
318
306
|
|
|
319
|
-
if [[ "${prev}" == "--payments" ]]; then
|
|
320
|
-
COMPREPLY=( $(compgen -W "stripe" -- "${cur}") )
|
|
321
|
-
return 0
|
|
322
|
-
fi
|
|
323
|
-
|
|
324
307
|
if [[ "${prev}" == "--ship" ]]; then
|
|
325
308
|
COMPREPLY=( $(compgen -W "$(omaship list 2>/dev/null | awk '/^[* ] [0-9]+ / { print $3 }')" -- "${cur}") )
|
|
326
309
|
return 0
|
|
@@ -345,10 +328,7 @@ module Omaship
|
|
|
345
328
|
COMPREPLY=( $(compgen -W "--ship --host -h --help" -- "${cur}") )
|
|
346
329
|
;;
|
|
347
330
|
new)
|
|
348
|
-
COMPREPLY=( $(compgen -W "--domain --host -h --help" -- "${cur}") )
|
|
349
|
-
;;
|
|
350
|
-
configure)
|
|
351
|
-
COMPREPLY=( $(compgen -W "--payments --ship --host -h --help" -- "${cur}") )
|
|
331
|
+
COMPREPLY=( $(compgen -W "--domain --skip-purpose --host -h --help" -- "${cur}") )
|
|
352
332
|
;;
|
|
353
333
|
complete)
|
|
354
334
|
COMPREPLY=( $(compgen -W "bash zsh fish" -- "${cur}") )
|
|
@@ -386,8 +366,8 @@ module Omaship
|
|
|
386
366
|
'status:Alias for info'
|
|
387
367
|
'ship:Alias for info'
|
|
388
368
|
'new:Create and provision a new ship'
|
|
389
|
-
'configure:Configure a ship'
|
|
390
369
|
'deploy:Deploy a ship'
|
|
370
|
+
'upgrade:Open browser to upgrade your plan'
|
|
391
371
|
'logout:Remove local CLI credentials'
|
|
392
372
|
'complete:Print shell completion script'
|
|
393
373
|
'help:Describe available commands'
|
|
@@ -417,10 +397,7 @@ module Omaship
|
|
|
417
397
|
_arguments '--ship[Ship from omaship list]:ship:_omaship_ship_refs' '--host[API host]:host:'
|
|
418
398
|
;;
|
|
419
399
|
new)
|
|
420
|
-
_arguments '--domain[Root domain]:domain:' '--host[API host]:host:' '1:name:'
|
|
421
|
-
;;
|
|
422
|
-
configure)
|
|
423
|
-
_arguments '--payments[Payments provider]:provider:(stripe)' '--ship[Ship from omaship list]:ship:_omaship_ship_refs' '--host[API host]:host:'
|
|
400
|
+
_arguments '--domain[Root domain]:domain:' '--skip-purpose[Skip purpose questions]' '--host[API host]:host:' '1:name:'
|
|
424
401
|
;;
|
|
425
402
|
complete)
|
|
426
403
|
_arguments '1:shell:(bash zsh fish)'
|
|
@@ -444,19 +421,149 @@ module Omaship
|
|
|
444
421
|
end
|
|
445
422
|
|
|
446
423
|
complete -c omaship -f
|
|
447
|
-
complete -c omaship -n '__fish_use_subcommand' -a 'login whoami list use info status ship new
|
|
424
|
+
complete -c omaship -n '__fish_use_subcommand' -a 'login whoami list use info status ship new deploy upgrade logout complete help'
|
|
448
425
|
complete -c omaship -l host -d 'API host'
|
|
449
426
|
|
|
450
427
|
complete -c omaship -n '__fish_seen_subcommand_from login' -l token -d 'API token from omaship settings'
|
|
451
428
|
complete -c omaship -n '__fish_seen_subcommand_from new' -l domain -d 'Root domain'
|
|
452
|
-
complete -c omaship -n '__fish_seen_subcommand_from
|
|
453
|
-
complete -c omaship -n '__fish_seen_subcommand_from configure' -l ship -d 'Ship from omaship list' -a '(__fish_omaship_ship_refs)'
|
|
429
|
+
complete -c omaship -n '__fish_seen_subcommand_from new' -l skip-purpose -d 'Skip purpose questions'
|
|
454
430
|
complete -c omaship -n '__fish_seen_subcommand_from info status ship deploy' -l ship -d 'Ship from omaship list' -a '(__fish_omaship_ship_refs)'
|
|
455
431
|
complete -c omaship -n '__fish_seen_subcommand_from use' -f -a '(__fish_omaship_ship_refs)'
|
|
456
432
|
complete -c omaship -n '__fish_seen_subcommand_from complete' -f -a 'bash zsh fish'
|
|
457
433
|
FISH
|
|
458
434
|
end
|
|
459
435
|
|
|
436
|
+
def fetch_plan(token:)
|
|
437
|
+
auth = build_api_client(token: token).authenticate
|
|
438
|
+
auth.dig("user", "plan") || "free"
|
|
439
|
+
end
|
|
440
|
+
|
|
441
|
+
def create_paid_ship(name:, token:)
|
|
442
|
+
root_domain = options[:domain] || "#{name}.com"
|
|
443
|
+
payload = build_api_client(token: token).create_ship(root_domain: root_domain)
|
|
444
|
+
ship = payload.fetch("ship")
|
|
445
|
+
|
|
446
|
+
progress_renderer.step("Setting up your codebase")
|
|
447
|
+
final_ship = poll_until_terminal(ship_id: ship.fetch("id"), token: token)
|
|
448
|
+
|
|
449
|
+
if final_ship.fetch("status") == "live"
|
|
450
|
+
progress_renderer.step("Ready. Customers can sign up and pay at #{final_ship.fetch("root_domain")}")
|
|
451
|
+
else
|
|
452
|
+
message = final_ship["error_message"] || "Provisioning failed"
|
|
453
|
+
raise Thor::Error, message
|
|
454
|
+
end
|
|
455
|
+
end
|
|
456
|
+
|
|
457
|
+
def create_free_ship(name:, token:)
|
|
458
|
+
print_free_banner
|
|
459
|
+
|
|
460
|
+
purpose_profile = {}
|
|
461
|
+
color_scheme = "mono-dark"
|
|
462
|
+
|
|
463
|
+
unless options[:skip_purpose]
|
|
464
|
+
if ask_purpose_profile?
|
|
465
|
+
purpose_profile = collect_purpose_profile
|
|
466
|
+
end
|
|
467
|
+
color_scheme = pick_color_scheme
|
|
468
|
+
end
|
|
469
|
+
|
|
470
|
+
say
|
|
471
|
+
progress_renderer.step("Creating your landing page...")
|
|
472
|
+
|
|
473
|
+
payload = build_api_client(token: token).create_landing_page(
|
|
474
|
+
name: name,
|
|
475
|
+
color_scheme: color_scheme,
|
|
476
|
+
purpose_profile: purpose_profile
|
|
477
|
+
)
|
|
478
|
+
ship = payload.fetch("ship")
|
|
479
|
+
|
|
480
|
+
if ship["distillation_status"] == "pending"
|
|
481
|
+
progress_renderer.step("Sokrates is distilling your answers...")
|
|
482
|
+
poll_until_distilled(ship_id: ship.fetch("id"), token: token)
|
|
483
|
+
end
|
|
484
|
+
|
|
485
|
+
say
|
|
486
|
+
say "Live at: https://#{ship.fetch("root_domain")}"
|
|
487
|
+
say "Review and refine at: #{resolved_host}/ships/#{ship.fetch("id")}/purpose_profile"
|
|
488
|
+
end
|
|
489
|
+
|
|
490
|
+
def print_free_banner
|
|
491
|
+
lines = [
|
|
492
|
+
"FREE PLAN",
|
|
493
|
+
"",
|
|
494
|
+
"You'll get a static landing page with a waitlist",
|
|
495
|
+
"on *.omaship.app",
|
|
496
|
+
"",
|
|
497
|
+
"Want the full Rails app on your own server?",
|
|
498
|
+
"Run: omaship upgrade"
|
|
499
|
+
]
|
|
500
|
+
width = lines.map(&:length).max + 4
|
|
501
|
+
bar = "+#{"-" * (width + 2)}+"
|
|
502
|
+
|
|
503
|
+
say
|
|
504
|
+
say bar
|
|
505
|
+
lines.each do |line|
|
|
506
|
+
say "| #{line.ljust(width)}|"
|
|
507
|
+
end
|
|
508
|
+
say bar
|
|
509
|
+
say
|
|
510
|
+
end
|
|
511
|
+
|
|
512
|
+
def ask_purpose_profile?
|
|
513
|
+
say "Would you like to create a purpose profile?"
|
|
514
|
+
say "This helps generate better landing page copy. (5 questions, ~2 min)"
|
|
515
|
+
say
|
|
516
|
+
answer = ask("Continue with purpose profile? (Y/n):")
|
|
517
|
+
answer.to_s.strip.downcase != "n"
|
|
518
|
+
end
|
|
519
|
+
|
|
520
|
+
def collect_purpose_profile
|
|
521
|
+
say
|
|
522
|
+
say "--- Purpose Profile ---"
|
|
523
|
+
say
|
|
524
|
+
|
|
525
|
+
raw_problem = ask("What problem does your product solve?\n>")
|
|
526
|
+
raw_audience = ask("Who is most affected by this problem?\n>")
|
|
527
|
+
raw_belief = ask("Why is this worth solving? What's your core belief?\n>")
|
|
528
|
+
raw_contribution = ask("What's your contribution to the solution?\n>")
|
|
529
|
+
raw_outcome = ask("What does the world look like when you succeed?\n>")
|
|
530
|
+
|
|
531
|
+
{
|
|
532
|
+
raw_problem: raw_problem,
|
|
533
|
+
raw_audience: raw_audience,
|
|
534
|
+
raw_belief: raw_belief,
|
|
535
|
+
raw_contribution: raw_contribution,
|
|
536
|
+
raw_outcome: raw_outcome
|
|
537
|
+
}.reject { |_k, v| v.to_s.strip.empty? }
|
|
538
|
+
end
|
|
539
|
+
|
|
540
|
+
def pick_color_scheme
|
|
541
|
+
say
|
|
542
|
+
say "--- Color Scheme ---"
|
|
543
|
+
selected = color_picker.pick
|
|
544
|
+
say "Selected: #{selected}"
|
|
545
|
+
selected
|
|
546
|
+
end
|
|
547
|
+
|
|
548
|
+
def color_picker
|
|
549
|
+
@color_picker ||= Omaship::ColorPicker.new
|
|
550
|
+
end
|
|
551
|
+
|
|
552
|
+
def poll_until_distilled(ship_id:, token:)
|
|
553
|
+
client = build_api_client(token: token)
|
|
554
|
+
30.times do
|
|
555
|
+
ship_payload = client.ship(ship_id: ship_id).fetch("ship")
|
|
556
|
+
status = ship_payload["distillation_status"]
|
|
557
|
+
|
|
558
|
+
if status == "complete" || status == "skipped" || status == "none"
|
|
559
|
+
return ship_payload
|
|
560
|
+
end
|
|
561
|
+
|
|
562
|
+
sleep 2
|
|
563
|
+
end
|
|
564
|
+
say "Distillation timed out. You can refine later in the dashboard."
|
|
565
|
+
end
|
|
566
|
+
|
|
460
567
|
def current_token
|
|
461
568
|
value = credentials.token
|
|
462
569
|
if value.to_s.strip.empty?
|
|
@@ -560,8 +667,6 @@ module Omaship
|
|
|
560
667
|
def permission_denied_message(command:)
|
|
561
668
|
if command == :new_ship
|
|
562
669
|
"`omaship new` requires a full-access token. Create a token with Full CLI access and run `omaship login` again."
|
|
563
|
-
elsif command == :configure
|
|
564
|
-
"`omaship configure` requires a full-access token. Create a token with Full CLI access and run `omaship login` again."
|
|
565
670
|
elsif command == :deploy
|
|
566
671
|
"`omaship deploy` requires a full-access token. Create a token with Full CLI access and run `omaship login` again."
|
|
567
672
|
else
|
|
@@ -581,31 +686,10 @@ module Omaship
|
|
|
581
686
|
@progress_renderer ||= Omaship::ProgressRenderer.new(out: $stdout)
|
|
582
687
|
end
|
|
583
688
|
|
|
584
|
-
def emit_logs(log_entries)
|
|
585
|
-
log_entries.each do |entry|
|
|
586
|
-
next unless displayable_log_entry?(entry)
|
|
587
|
-
|
|
588
|
-
message = entry.fetch("message")
|
|
589
|
-
progress_renderer.step(message)
|
|
590
|
-
end
|
|
591
|
-
end
|
|
592
|
-
|
|
593
|
-
def displayable_log_entry?(entry)
|
|
594
|
-
entry.fetch("level", "").to_s != "debug"
|
|
595
|
-
end
|
|
596
|
-
|
|
597
689
|
def poll_until_terminal(ship_id:, token:)
|
|
598
690
|
client = build_api_client(token: token)
|
|
599
|
-
last_log_id = 0
|
|
600
691
|
90.times do
|
|
601
692
|
ship_payload = client.ship(ship_id: ship_id).fetch("ship")
|
|
602
|
-
logs_payload = client.ship_logs(ship_id: ship_id)
|
|
603
|
-
logs = logs_payload.fetch("logs").select { |entry| entry.fetch("id") > last_log_id }
|
|
604
|
-
if logs.any?
|
|
605
|
-
last_log_id = logs.last.fetch("id")
|
|
606
|
-
end
|
|
607
|
-
|
|
608
|
-
emit_logs(logs)
|
|
609
693
|
status = ship_payload.fetch("status")
|
|
610
694
|
if %w[live error].include?(status)
|
|
611
695
|
return ship_payload
|
|
@@ -616,27 +700,6 @@ module Omaship
|
|
|
616
700
|
raise Thor::Error, "Provisioning timed out."
|
|
617
701
|
end
|
|
618
702
|
|
|
619
|
-
def poll_until_configuration_complete(ship_id:, token:)
|
|
620
|
-
client = build_api_client(token: token)
|
|
621
|
-
60.times do
|
|
622
|
-
ship_payload = client.ship(ship_id: ship_id).fetch("ship")
|
|
623
|
-
pending = ship_payload.fetch("package_update_pending")
|
|
624
|
-
if pending
|
|
625
|
-
progress_renderer.step("Applying configuration changes.")
|
|
626
|
-
sleep 2
|
|
627
|
-
else
|
|
628
|
-
error_message = ship_payload["error_message"].to_s.strip
|
|
629
|
-
if error_message.empty?
|
|
630
|
-
return
|
|
631
|
-
end
|
|
632
|
-
|
|
633
|
-
raise Thor::Error, error_message
|
|
634
|
-
end
|
|
635
|
-
end
|
|
636
|
-
|
|
637
|
-
raise Thor::Error, "Configuration timed out."
|
|
638
|
-
end
|
|
639
|
-
|
|
640
703
|
def poll_until_deploy_finished(ship_id:, token:)
|
|
641
704
|
client = build_api_client(token: token)
|
|
642
705
|
last_status = nil
|
|
@@ -686,8 +749,15 @@ module Omaship
|
|
|
686
749
|
end
|
|
687
750
|
end
|
|
688
751
|
|
|
752
|
+
def open_browser(url)
|
|
753
|
+
opener = RUBY_PLATFORM.include?("darwin") ? "open" : "xdg-open"
|
|
754
|
+
system(opener, url)
|
|
755
|
+
rescue Errno::ENOENT
|
|
756
|
+
say "Could not open browser. Visit: #{url}"
|
|
757
|
+
end
|
|
758
|
+
|
|
689
759
|
def resolved_host
|
|
690
|
-
options[:host] || credentials.host || ENV["OMASHIP_HOST"] || "
|
|
760
|
+
options[:host] || credentials.host || ENV["OMASHIP_HOST"] || "https://omaship.com"
|
|
691
761
|
end
|
|
692
762
|
|
|
693
763
|
def existing_credentials_status_message
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
require "io/console"
|
|
2
|
+
|
|
3
|
+
module Omaship
|
|
4
|
+
class ColorPicker
|
|
5
|
+
SCHEMES = [
|
|
6
|
+
{ key: "flexoki-light", bg: "#FFFCF0", primary: "#205EA6", accent: "#AD8301", text: "#100F0F" },
|
|
7
|
+
{ key: "flexoki-dark", bg: "#100F0F", primary: "#4385BE", accent: "#D0A215", text: "#FFFCF0" },
|
|
8
|
+
{ key: "catppuccin-latte", bg: "#EFF1F5", primary: "#8839EF", accent: "#179299", text: "#4C4F69" },
|
|
9
|
+
{ key: "catppuccin-mocha", bg: "#1E1E2E", primary: "#CBA6F7", accent: "#94E2D5", text: "#CDD6F4" },
|
|
10
|
+
{ key: "rosepine-dawn", bg: "#FAF4ED", primary: "#907AA9", accent: "#D7827E", text: "#575279" },
|
|
11
|
+
{ key: "rosepine-moon", bg: "#232136", primary: "#C4A7E7", accent: "#EA9A97", text: "#E0DEF4" },
|
|
12
|
+
{ key: "nord-snow", bg: "#ECEFF4", primary: "#5E81AC", accent: "#88C0D0", text: "#2E3440" },
|
|
13
|
+
{ key: "nord-frost", bg: "#2E3440", primary: "#88C0D0", accent: "#5E81AC", text: "#ECEFF4" },
|
|
14
|
+
{ key: "mono-light", bg: "#FFFFFF", primary: "#18181B", accent: "#71717A", text: "#18181B" },
|
|
15
|
+
{ key: "mono-dark", bg: "#18181B", primary: "#FAFAFA", accent: "#A1A1AA", text: "#FAFAFA" },
|
|
16
|
+
{ key: "solarized-light", bg: "#FDF6E3", primary: "#268BD2", accent: "#2AA198", text: "#657B83" },
|
|
17
|
+
{ key: "solarized-dark", bg: "#002B36", primary: "#268BD2", accent: "#2AA198", text: "#839496" }
|
|
18
|
+
].freeze
|
|
19
|
+
|
|
20
|
+
DEFAULT_INDEX = 9
|
|
21
|
+
|
|
22
|
+
def initialize(out: $stdout, input: $stdin)
|
|
23
|
+
@out = out
|
|
24
|
+
@input = input
|
|
25
|
+
@index = DEFAULT_INDEX
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def pick
|
|
29
|
+
render
|
|
30
|
+
loop do
|
|
31
|
+
key = read_key
|
|
32
|
+
case key
|
|
33
|
+
when :left, "h"
|
|
34
|
+
@index = (@index - 1) % SCHEMES.size
|
|
35
|
+
render
|
|
36
|
+
when :right, "l"
|
|
37
|
+
@index = (@index + 1) % SCHEMES.size
|
|
38
|
+
render
|
|
39
|
+
when :enter, "q"
|
|
40
|
+
@out.print "\e[?25h"
|
|
41
|
+
@out.puts
|
|
42
|
+
return SCHEMES[@index][:key]
|
|
43
|
+
when :ctrl_c
|
|
44
|
+
@out.print "\e[?25h"
|
|
45
|
+
@out.puts
|
|
46
|
+
return SCHEMES[DEFAULT_INDEX][:key]
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
private
|
|
52
|
+
|
|
53
|
+
def render
|
|
54
|
+
scheme = SCHEMES[@index]
|
|
55
|
+
@out.print "\e[?25l"
|
|
56
|
+
@out.print "\r\e[K"
|
|
57
|
+
lines = build_preview(scheme)
|
|
58
|
+
move_up = @rendered_lines || 0
|
|
59
|
+
@out.print "\e[#{move_up}A" if move_up > 0
|
|
60
|
+
lines.each do |line|
|
|
61
|
+
@out.print "\r\e[K#{line}\n"
|
|
62
|
+
end
|
|
63
|
+
@rendered_lines = lines.size
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def build_preview(scheme)
|
|
67
|
+
name = scheme[:key]
|
|
68
|
+
bg_block = color_block(scheme[:bg])
|
|
69
|
+
primary_block = color_block(scheme[:primary])
|
|
70
|
+
accent_block = color_block(scheme[:accent])
|
|
71
|
+
text_block = color_block(scheme[:text])
|
|
72
|
+
|
|
73
|
+
nav = SCHEMES.map.with_index do |s, i|
|
|
74
|
+
if i == @index
|
|
75
|
+
"\e[1m[#{s[:key]}]\e[0m"
|
|
76
|
+
else
|
|
77
|
+
" #{s[:key]} "
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
[
|
|
82
|
+
"",
|
|
83
|
+
" #{left_arrow} #{name} #{right_arrow}",
|
|
84
|
+
"",
|
|
85
|
+
" #{bg_block} bg #{primary_block} primary #{accent_block} accent #{text_block} text",
|
|
86
|
+
"",
|
|
87
|
+
" Use \e[1m<-\e[0m / \e[1m->\e[0m or \e[1mh\e[0m / \e[1ml\e[0m to browse. \e[1mEnter\e[0m to select.",
|
|
88
|
+
""
|
|
89
|
+
]
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def left_arrow
|
|
93
|
+
@index > 0 ? "\e[1m<\e[0m" : " "
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def right_arrow
|
|
97
|
+
@index < SCHEMES.size - 1 ? "\e[1m>\e[0m" : " "
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def color_block(hex)
|
|
101
|
+
r, g, b = hex_to_rgb(hex)
|
|
102
|
+
"\e[48;2;#{r};#{g};#{b}m \e[0m"
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def hex_to_rgb(hex)
|
|
106
|
+
hex = hex.delete("#")
|
|
107
|
+
[ hex[0..1], hex[2..3], hex[4..5] ].map { |c| c.to_i(16) }
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def read_key
|
|
111
|
+
char = @input.raw { @input.getc }
|
|
112
|
+
case char
|
|
113
|
+
when "\r", "\n"
|
|
114
|
+
:enter
|
|
115
|
+
when "\e"
|
|
116
|
+
seq = @input.raw { @input.read_nonblock(2) rescue "" }
|
|
117
|
+
case seq
|
|
118
|
+
when "[D" then :left
|
|
119
|
+
when "[C" then :right
|
|
120
|
+
else :unknown
|
|
121
|
+
end
|
|
122
|
+
when "\x03"
|
|
123
|
+
:ctrl_c
|
|
124
|
+
else
|
|
125
|
+
char
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
end
|
data/lib/omaship/version.rb
CHANGED
data/lib/omaship.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: omaship
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Omaship
|
|
@@ -51,8 +51,7 @@ dependencies:
|
|
|
51
51
|
- - ">="
|
|
52
52
|
- !ruby/object:Gem::Version
|
|
53
53
|
version: '0'
|
|
54
|
-
description: CLI for login, ship provisioning,
|
|
55
|
-
against Omaship.
|
|
54
|
+
description: CLI for login, ship provisioning, and deploy operations against Omaship.
|
|
56
55
|
email:
|
|
57
56
|
- hello@omaship.com
|
|
58
57
|
executables:
|
|
@@ -66,6 +65,7 @@ files:
|
|
|
66
65
|
- lib/omaship.rb
|
|
67
66
|
- lib/omaship/api_client.rb
|
|
68
67
|
- lib/omaship/cli.rb
|
|
68
|
+
- lib/omaship/color_picker.rb
|
|
69
69
|
- lib/omaship/credentials.rb
|
|
70
70
|
- lib/omaship/progress_renderer.rb
|
|
71
71
|
- lib/omaship/ship_detector.rb
|