wokku-cli 0.5.2 → 0.6.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 51db3894481aec24701a7fc0172ca81910c1f6967c6a00decbf5d0dadb6454eb
4
- data.tar.gz: 7e3151f3a81c249667e3a1128eaaa58763b40d453c401504f53b35783f5da96c
3
+ metadata.gz: 7ca5f8ef74b0d7c4991fde4d09e2fe741bd2925152b20a1337ca34b20f4a54cd
4
+ data.tar.gz: 661e85e21edc58b048532701bc32f5e9ef062d5fc610898d9d18718d051b39b5
5
5
  SHA512:
6
- metadata.gz: c021749b27b73a5c325c1f268de70c329b9aff10c325e67cfe6ef9b2a274f656967e2bb51732393a1548ea19d193255715e8bab66bafd50b7f92de650cbc570a
7
- data.tar.gz: e8c0fe5d6878a6105247fa049153a784885906decdc8cc92f8d3385ef357655f2d54c827658f30e6b6853a2e8eefe853ae37829f6ec92794294e0ec63c62f4c7
6
+ metadata.gz: 73e67bb05dbbba4f756a0fc69312d8bebd626d826779a5496404363786b45121046a0cc4e669a635d51e527932d19bfb92aa70bdc376b1df7b864fc75a3f404b
7
+ data.tar.gz: b08a864fce04de1fc2435c0aa071d4ce343aee48214b963f4cc3859c67e14fef3a08250631ab343aaee155ab1c5406613f990f2fc30fd5cc79f5cfe0454bd0d9
data/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.6.0 — 2026-07-30
4
+
5
+ ### Removed
6
+
7
+ - Removed `do`, `buildpacks`, `storage`, and log-drain commands — the underlying wokku-cloud endpoints were retired when the platform moved off Dokku (Track B). `wokku logs` is unchanged.
8
+
9
+ ## 0.5.3 — 2026-06-26
10
+
11
+ ### Fixed
12
+
13
+ - `databases:create` and `addons:shared:enable` no longer report success optimistically. The CLI used to print "Created"/"Enabled" on any 2xx — including a `202 Accepted` ("queued") — so a database or shared engine that was still provisioning, or that failed afterward, looked done. Both commands now poll until the resource is actually `running` (then "Created"/"ready"), `error` (a clear failure + nonzero exit), or a timeout ("still provisioning — check `wokku addons APP` / `databases:info`"). Poll cadence is tunable via `WOKKU_POLL_ATTEMPTS`/`WOKKU_POLL_INTERVAL`.
14
+
3
15
  ## 0.5.2 — 2026-06-06
4
16
 
5
17
  ### Changed
data/README.md CHANGED
@@ -28,7 +28,7 @@ wokku logs myapp -f
28
28
 
29
29
  ## Commands
30
30
 
31
- Run `wokku --help` for the full list. ~39 first-class commands today, grouped:
31
+ Run `wokku --help` for the full list. ~30 first-class commands today, grouped:
32
32
 
33
33
  - **Apps:** `apps`, `apps:create`, `apps:destroy`, `apps:info`, `apps:transfer`
34
34
  - **Process:** `ps`, `ps:start/stop/restart/scale/rebuild`, `redeploy`
@@ -39,13 +39,7 @@ Run `wokku --help` for the full list. ~39 first-class commands today, grouped:
39
39
  - **Servers:** `servers`, `servers:info`, `servers:default` (multi-Dokku-host installs)
40
40
  - **Teams:** `teams`, `teams:members`, `teams:invite`, `teams:remove`
41
41
  - **Logs:** `wokku logs APP --follow [--tail N]`
42
- - **Run / passthrough:** `wokku run APP -- COMMAND` (one-off in a fresh container), `wokku do APP -- DOKKU_ARGS` (arbitrary `dokku` CLI passthrough)
43
-
44
- ### Parity with Dokku
45
-
46
- `wokku do` is the bridge for the ~110 Dokku subcommands not yet first-classed here. Anything `dokku <something> APP` does, `wokku do APP -- <something>` does too — same exit code, same stdout.
47
-
48
- If you find yourself running the same `wokku do` invocation repeatedly, open an issue — that's the queue for promoting commands to first-class status.
42
+ - **Run:** `wokku run APP -- COMMAND` (one-off in a fresh container)
49
43
 
50
44
  ## Global flags
51
45
 
@@ -40,8 +40,26 @@ end
40
40
  register "addons:shared:enable", "Enable a shared engine on an app (usage: wokku addons:shared:enable APP ENGINE). ENGINE: postgres|redis|memcached|rabbitmq|meilisearch" do
41
41
  id = ARGV.shift || abort("Usage: wokku addons:shared:enable APP ENGINE")
42
42
  engine = ARGV.shift || abort("Missing engine")
43
- data = api(:post, "/apps/#{id}/addons/shared", { engine: engine })
44
- Wokku::Output.status(data["message"] || "Enabled shared #{engine} on #{id}")
43
+ api(:post, "/apps/#{id}/addons/shared", { engine: engine })
44
+
45
+ attempts = (ENV["WOKKU_POLL_ATTEMPTS"] || "30").to_i
46
+ interval = (ENV["WOKKU_POLL_INTERVAL"] || "2").to_f
47
+
48
+ Wokku::Output.status "Enabling shared #{engine} on #{id} — provisioning…" unless Wokku.quiet
49
+ final = poll_until("/apps/#{id}/addons", attempts: attempts, interval: interval) do |list|
50
+ row = Array(list).find { |a| a["service_type"] == engine && a["shared"] }
51
+ row && %w[running error].include?(row["status"])
52
+ end
53
+
54
+ row = Array(final).find { |a| a["service_type"] == engine && a["shared"] }
55
+ case row && row["status"]
56
+ when "running"
57
+ Wokku::Output.status "Shared #{engine} ready on #{id}."
58
+ when "error"
59
+ abort "Shared #{engine} failed to provision on #{id}. See the dashboard or re-run to retry."
60
+ else
61
+ Wokku::Output.status "Shared #{engine} still provisioning on #{id}. Check: wokku addons #{id}"
62
+ end
45
63
  end
46
64
 
47
65
  register "addons:shared:disable", "Disable a shared engine on an app (usage: wokku addons:shared:disable APP ENGINE)" do
@@ -32,7 +32,23 @@ register "databases:create", "Create a database (usage: wokku databases:create N
32
32
  abort "Missing --type (postgres, mysql, redis, mongo)" unless type
33
33
  server = resolve_server(explicit: server)
34
34
  data = api(:post, "/databases", { name: name, service_type: type, server_id: server })
35
- Wokku::Output.status "Created #{data['service_type']} database: #{data['name']}"
35
+
36
+ if data["status"] != "running"
37
+ attempts = (ENV["WOKKU_POLL_ATTEMPTS"] || "15").to_i
38
+ interval = (ENV["WOKKU_POLL_INTERVAL"] || "2").to_f
39
+ data = poll_until("/databases/#{data['name']}", attempts: attempts, interval: interval) do |d|
40
+ %w[running error].include?(d["status"])
41
+ end || { "name" => name, "service_type" => type, "status" => "creating" }
42
+ end
43
+
44
+ case data["status"]
45
+ when "running"
46
+ Wokku::Output.status "Created #{data['service_type']} database: #{data['name']}"
47
+ when "error"
48
+ abort "Database '#{data['name']}' failed to provision."
49
+ else
50
+ Wokku::Output.status "Database '#{data['name']}' is still provisioning. Check: wokku databases:info #{data['name']}"
51
+ end
36
52
  end
37
53
 
38
54
  register "databases:destroy", "Destroy a database (usage: wokku databases:destroy NAME)" do
data/lib/wokku/version.rb CHANGED
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module Wokku
3
- VERSION = "0.5.2"
3
+ VERSION = "0.6.0"
4
4
  end
data/lib/wokku.rb CHANGED
@@ -80,6 +80,19 @@ rescue Wokku::ApiClient::NotAuthenticated, Wokku::ApiClient::Timeout,
80
80
  abort e.message
81
81
  end
82
82
 
83
+ # Poll GET <path> until the block returns truthy (returns that data) or the
84
+ # attempt budget is exhausted (returns nil). interval is injectable so specs
85
+ # can run with no real sleep.
86
+ def poll_until(path, attempts: 30, interval: 2)
87
+ attempts.times do |i|
88
+ data = api(:get, path)
89
+ return data if yield(data)
90
+
91
+ sleep(interval) if interval.positive? && i < attempts - 1
92
+ end
93
+ nil
94
+ end
95
+
83
96
  def load_config = Wokku::Config.load
84
97
  def save_config(data) = Wokku::Config.save(data)
85
98
  def api_url = Wokku::Config.api_url
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: wokku-cli
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.2
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Johannes Dwicahyo
@@ -46,7 +46,6 @@ files:
46
46
  - lib/wokku/commands/apps_lifecycle.rb
47
47
  - lib/wokku/commands/auth.rb
48
48
  - lib/wokku/commands/billing.rb
49
- - lib/wokku/commands/buildpacks.rb
50
49
  - lib/wokku/commands/cdn.rb
51
50
  - lib/wokku/commands/certs.rb
52
51
  - lib/wokku/commands/checks.rb
@@ -55,7 +54,6 @@ files:
55
54
  - lib/wokku/commands/databases.rb
56
55
  - lib/wokku/commands/db_monitor.rb
57
56
  - lib/wokku/commands/dev.rb
58
- - lib/wokku/commands/do.rb
59
57
  - lib/wokku/commands/domains.rb
60
58
  - lib/wokku/commands/github.rb
61
59
  - lib/wokku/commands/https.rb
@@ -72,7 +70,6 @@ files:
72
70
  - lib/wokku/commands/servers.rb
73
71
  - lib/wokku/commands/shell.rb
74
72
  - lib/wokku/commands/ssh_keys.rb
75
- - lib/wokku/commands/storage.rb
76
73
  - lib/wokku/commands/teams.rb
77
74
  - lib/wokku/commands/templates.rb
78
75
  - lib/wokku/commands/tunnel.rb
@@ -1,50 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # --- Buildpacks ---
4
- register "buildpacks", "List buildpacks (usage: wokku buildpacks APP)" do
5
- id = ARGV.shift || abort("Usage: wokku buildpacks APP")
6
- data = api(:get, "/apps/#{id}/buildpacks")
7
- Wokku::Output.render(data) do |d|
8
- list = d.is_a?(Hash) ? Array(d["buildpacks"]) : Array(d)
9
- if list.empty?
10
- puts "(no buildpacks configured — auto-detected from source)"
11
- else
12
- list.each_with_index { |url, i| puts "#{i + 1}. #{url}" }
13
- end
14
- end
15
- end
16
-
17
- register "buildpacks:add", "Append a buildpack (usage: wokku buildpacks:add APP URL [--index N])" do
18
- id = ARGV.shift || abort("Usage: wokku buildpacks:add APP URL [--index N]")
19
- url = ARGV.shift || abort("Missing buildpack URL")
20
- index = nil
21
- while arg = ARGV.shift
22
- index = ARGV.shift if arg == "--index"
23
- end
24
- body = { url: url }
25
- body[:index] = index if index
26
- api(:post, "/apps/#{id}/buildpacks", body)
27
- Wokku::Output.status "Added buildpack: #{url}"
28
- end
29
-
30
- register "buildpacks:remove", "Remove a buildpack (usage: wokku buildpacks:remove APP URL)" do
31
- id = ARGV.shift || abort("Usage: wokku buildpacks:remove APP URL")
32
- url = ARGV.shift || abort("Missing buildpack URL")
33
- api(:delete, "/apps/#{id}/buildpacks", { url: url })
34
- Wokku::Output.status "Removed buildpack: #{url}"
35
- end
36
-
37
- register "buildpacks:clear", "Remove every buildpack (usage: wokku buildpacks:clear APP)" do
38
- id = ARGV.shift || abort("Usage: wokku buildpacks:clear APP")
39
- api(:delete, "/apps/#{id}/buildpacks")
40
- Wokku::Output.status "All buildpacks removed."
41
- end
42
-
43
- register "buildpacks:set", "Replace the buildpack stack in order (usage: wokku buildpacks:set APP URL [URL...])" do
44
- id = ARGV.shift || abort("Usage: wokku buildpacks:set APP URL [URL...]")
45
- urls = ARGV.dup
46
- ARGV.clear
47
- abort "No buildpack URLs given." if urls.empty?
48
- api(:put, "/apps/#{id}/buildpacks", { urls: urls })
49
- Wokku::Output.status "Buildpacks set: #{urls.join(', ')}"
50
- end
@@ -1,32 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # --- Raw dokku passthrough ---
4
- register "do", "Run any dokku command (usage: wokku do APP -- DOKKU_ARGS | wokku do --server N -- DOKKU_ARGS)" do
5
- app = nil
6
- server = nil
7
- force = false
8
- while ARGV.any? && ARGV.first != "--"
9
- arg = ARGV.shift
10
- case arg
11
- when "--server"
12
- server = ARGV.shift
13
- abort "--server requires a server ID" if server.nil? || server == "--"
14
- when "--force" then force = true
15
- else app ||= arg
16
- end
17
- end
18
-
19
- abort "Missing `--` separator. Example: wokku do myapp -- ps:list" unless ARGV.first == "--"
20
- ARGV.shift # drop the --
21
- args = ARGV.dup
22
- ARGV.clear
23
- abort "Missing dokku command. Example: wokku do myapp -- ps:list" if args.empty?
24
- abort "Pass either APP or --server N, not both" if app && server
25
- abort "Pass APP or --server N before --" if app.nil? && server.nil?
26
-
27
- path = app ? "/apps/#{app}/dokku" : "/servers/#{server}/dokku"
28
- data = api(:post, path, { args: args, force: force })
29
- $stdout.write(data["stdout"]) if data["stdout"] && !data["stdout"].empty?
30
- $stderr.write(data["stderr"]) if data["stderr"] && !data["stderr"].empty?
31
- exit (data["exit_code"] || 0).to_i
32
- end
@@ -1,31 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # --- Storage (host-bind volumes) ---
4
- register "storage", "List storage mounts (usage: wokku storage APP)" do
5
- id = ARGV.shift || abort("Usage: wokku storage APP")
6
- data = api(:get, "/apps/#{id}/storage")
7
- Wokku::Output.render(data) do |d|
8
- list = d.is_a?(Hash) ? Array(d["mounts"]) : Array(d)
9
- if list.empty?
10
- puts "(no mounts configured)"
11
- else
12
- list.each { |m| puts m }
13
- end
14
- end
15
- end
16
-
17
- register "storage:mount", "Add a host-bind mount (usage: wokku storage:mount APP HOST_PATH:CONTAINER_PATH)" do
18
- id = ARGV.shift || abort("Usage: wokku storage:mount APP HOST_PATH:CONTAINER_PATH")
19
- spec = ARGV.shift || abort("Missing HOST_PATH:CONTAINER_PATH")
20
- abort "Mount must be HOST_PATH:CONTAINER_PATH" unless spec.include?(":")
21
- api(:post, "/apps/#{id}/storage", { mount: spec })
22
- Wokku::Output.status "Mounted: #{spec}"
23
- Wokku::Output.status "Restart the app to pick up the mount: wokku ps:restart #{id}"
24
- end
25
-
26
- register "storage:unmount", "Remove a host-bind mount (usage: wokku storage:unmount APP HOST_PATH:CONTAINER_PATH)" do
27
- id = ARGV.shift || abort("Usage: wokku storage:unmount APP HOST_PATH:CONTAINER_PATH")
28
- spec = ARGV.shift || abort("Missing HOST_PATH:CONTAINER_PATH")
29
- api(:delete, "/apps/#{id}/storage", { mount: spec })
30
- Wokku::Output.status "Unmounted: #{spec}"
31
- end