jatai 1.4.0 → 1.5.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: 648533180b943e80129355c3308d594f1867fde7946470361f3897ad3cdd06eb
4
- data.tar.gz: 2cfe1d3481ef074b1c1e257f0cd2eea369193a6681694d5d2da6a8e6131f4c42
3
+ metadata.gz: cce550fef38fbddcb460eaa4d1d5e2631dfc8b43e33d035c607680e24ea6fee6
4
+ data.tar.gz: 75eaffc25c616af78578cd43b077ef62604852b90a4319d961bc50b4190f067e
5
5
  SHA512:
6
- metadata.gz: 82d898f647e4109c8842d35d203fd818a170f013f174865ef91e5934a6afc93083e2dd4aa7dd305ef2922d7ab2897c0257f9dbd9fba8c763a021b4707851a7be
7
- data.tar.gz: 444b6824a85488f361916f126e34bc68e574c7248bef66d35c51430cdaa9056ad0b18d6880a17ea856d2ef98ca1483f7df71ad991d08729c19630f6b053b99a8
6
+ metadata.gz: b16035861874a14cadbafff211bfb8517d30652187a0d428e377b5e3fff59a2f98aa0a04588bb8819e98aa1114429ea856e89432c7ef40657a987019eb63f4dc
7
+ data.tar.gz: 8a50d74a29189c5f21f62c1d16a73030fad4031be48a70957c6350c35d4935b14f6e76fd8b0fdb54365cc2f4a667519699e534e66913855293dfa6ac61fb1b29
data/lib/jatai/cli.rb CHANGED
@@ -88,7 +88,7 @@ module Jatai
88
88
 
89
89
  desc "scale", "Escalar a aplicação"
90
90
  option :replicas, type: :numeric, desc: "Número de réplicas"
91
- option :preset, type: :string, desc: "Preset (micro, hobby, pro)"
91
+ option :preset, type: :string, desc: "Hex (base, escala_1, escala_2)"
92
92
  def scale
93
93
  Commands::Scale.new.execute(options)
94
94
  end
@@ -48,6 +48,22 @@ module Jatai
48
48
  abort pastel.red("Não autenticado. Execute `jatai login` primeiro.")
49
49
  end
50
50
 
51
+ # Rótulo do cliente para a unidade computacional. O slug é interno
52
+ # (`base`, `escala_1`); quem lê a tela lê "Hex Escala 1" — o mesmo texto
53
+ # que o painel e a fatura mostram (AppsHelper#hex_label no servidor).
54
+ def hex_label(tier)
55
+ return "-" if tier.to_s.empty?
56
+
57
+ "Hex #{tier.to_s.tr("_", " ").capitalize}"
58
+ end
59
+
60
+ # O banco é um addon; `database_plan` vem nulo no app que não tem nenhum.
61
+ def pg_label(plan)
62
+ return "sem banco" if plan.to_s.empty?
63
+
64
+ "PG #{plan.to_s.tr("_", " ").capitalize}"
65
+ end
66
+
51
67
  def success(msg)
52
68
  puts pastel.green(msg)
53
69
  end
@@ -59,6 +75,12 @@ module Jatai
59
75
  def info(msg)
60
76
  puts pastel.cyan(msg)
61
77
  end
78
+
79
+ # Nem verde nem vermelho: o deploy parcial subiu o site e deixou processo
80
+ # para trás — chamar de sucesso ou de falha mentiria nas duas direções.
81
+ def warning(msg)
82
+ puts pastel.yellow(msg)
83
+ end
62
84
  end
63
85
  end
64
86
  end
@@ -14,7 +14,7 @@ module Jatai
14
14
  db = helper.api.get("/api/v1/apps/#{slug}/database")
15
15
 
16
16
  puts helper.pastel.bold("Banco de dados de #{slug}")
17
- puts " Tier: #{db["database_tier"] || db["mode"]}"
17
+ puts " Modo: #{db["mode"]}"
18
18
  puts " Host: #{db["host"]}"
19
19
  puts " Porta: #{db["port"]}"
20
20
  puts " Nome: #{db["name"]}"
@@ -44,12 +44,15 @@ module Jatai
44
44
 
45
45
  status = ws.wait_for("DeployStatusChannel",
46
46
  params: { deploy_id: deploy_id },
47
- until_status: %w[live failed],
47
+ until_status: %w[live failed partially_live],
48
48
  timeout: DEPLOY_TIMEOUT)
49
49
 
50
50
  elapsed = (Time.now - started).to_i
51
- if status == "live"
51
+ case status
52
+ when "live"
52
53
  success("\nDeploy concluído com sucesso! (#{elapsed}s)")
54
+ when "partially_live"
55
+ print_partial_summary(slug, deploy_id, elapsed)
53
56
  else
54
57
  error("\nDeploy falhou.")
55
58
  end
@@ -74,6 +77,9 @@ module Jatai
74
77
  when "live"
75
78
  success("\nDeploy concluído com sucesso! (#{(Time.now - started).to_i}s)")
76
79
  return
80
+ when "partially_live"
81
+ print_partial_summary(slug, deploy_id, (Time.now - started).to_i)
82
+ return
77
83
  when "failed"
78
84
  error("\nDeploy falhou.")
79
85
  return
@@ -92,6 +98,20 @@ module Jatai
92
98
  error("\nTimeout (#{(Time.now - started).to_i}s). Verifique com: jatai status")
93
99
  end
94
100
 
101
+ # O deploy parcial é terminal: o site subiu, mas processo(s) declarados
102
+ # ficaram no release anterior. Sem este ramo a CLI penduraria 20min
103
+ # esperando um "live" que não vem.
104
+ def print_partial_summary(slug, deploy_id, elapsed)
105
+ warning("\nDeploy parcialmente concluído (#{elapsed}s)")
106
+ deploy = api.get("/api/v1/apps/#{slug}/deploys/#{deploy_id}")
107
+ Array(deploy["process_failures"]).each do |failure|
108
+ warning(" #{failure['name']}: #{failure['reason']}")
109
+ end
110
+ warning("O site está no ar na versão nova; os processos acima seguem na anterior.")
111
+ rescue StandardError
112
+ warning(" (detalhe indisponível — ver o deploy no painel)")
113
+ end
114
+
95
115
  def status_label(status)
96
116
  { "queued" => "Na fila...", "building" => "Construindo imagem...",
97
117
  "deploying" => "Deployando..." }[status] || status
@@ -5,10 +5,17 @@ require "yaml"
5
5
  module Jatai
6
6
  module Commands
7
7
  class Init < Base
8
- PRESETS = {
9
- "micro" => "Micro — 256MB RAM, 1 réplica (Grátis)",
10
- "hobby" => "Hobby — 1GB RAM, réplicas ilimitadas (R$ 19/mês)",
11
- "pro" => "Pro — 2GB RAM, réplicas ilimitadas (R$ 69/mês)"
8
+ # O Hex é a unidade computacional da plataforma — web e worker custam o
9
+ # mesmo degrau. Os preços aqui são os que a fatura cobra (Pricing::HEX):
10
+ # esta lista já anunciou "hobby R$ 19" enquanto a plataforma debitava
11
+ # R$ 29, e o cliente só descobria na primeira fatura.
12
+ #
13
+ # O `Hex Dedicado` (R$ 299) fica de fora: ele reserva 2Gi, 51% do nó k3s
14
+ # atual, e só fica vendável após o retype da VM.
15
+ HEX = {
16
+ "base" => "Hex Base — R$ 29/mês (1 grátis por time)",
17
+ "escala_1" => "Hex Escala 1 — R$ 59/mês",
18
+ "escala_2" => "Hex Escala 2 — R$ 119/mês"
12
19
  }.freeze
13
20
 
14
21
  # Região única por enquanto (Sudeste). Quando houver multi-região, volta a ser um prompt.
@@ -19,11 +26,6 @@ module Jatai
19
26
  "static" => "Estático (HTML/CSS/JS, sem build)",
20
27
  "docker" => "Docker (custom Dockerfile)"
21
28
  }.freeze
22
- DATABASE_TIERS = {
23
- "mini" => "Mini — 512MB, 20 conexões (Grátis)",
24
- "essencial" => "Essencial — 1GB, 50 conexões (R$ 19/mês)",
25
- "padrao" => "Padrão — 5GB, 100 conexões (R$ 49/mês)"
26
- }.freeze
27
29
 
28
30
  def execute(options = {})
29
31
  prompt = TTY::Prompt.new
@@ -45,9 +47,7 @@ module Jatai
45
47
  end
46
48
 
47
49
  name, slug = ask_name_and_slug(prompt)
48
- preset = prompt.select("Preset:", PRESETS.map { |k, v| { name: v, value: k } })
49
- # Sites estáticos não têm banco — não pergunta o tier.
50
- db_tier = stack == "static" ? nil : prompt.select("Banco de dados:", DATABASE_TIERS.map { |k, v| { name: v, value: k } })
50
+ preset = prompt.select("Hex:", HEX.map { |k, v| { name: v, value: k } })
51
51
  branch = detect_git_branch || "main"
52
52
 
53
53
  # `app` é o IDENTIFICADOR (slug) usado nas URLs da API; `name` é o nome de
@@ -61,12 +61,16 @@ module Jatai
61
61
  "region" => DEFAULT_REGION,
62
62
  "branch" => branch
63
63
  }
64
- config["database_tier"] = db_tier if db_tier
65
64
 
66
65
  File.write(config_path, YAML.dump(config))
67
66
  success("Arquivo .jatai.yml criado com sucesso!")
68
67
  info("Identificador: #{slug}") if slug != name
69
68
  info("Branch: #{branch}")
69
+ # O banco deixou de ser uma pergunta: o Provision::Orchestrator dá um
70
+ # Postgres a todo app não-estático, e o PG Base do app de cortesia do
71
+ # time é isento. Escolher tier aqui só antecipava uma decisão que a aba
72
+ # "Banco de dados" refaz melhor, com o app já rodando.
73
+ info("Banco: PG Base provisionado automaticamente no `jatai up`.") unless stack == "static"
70
74
  info("Execute `jatai up` para criar e fazer deploy do app.")
71
75
  end
72
76
 
@@ -8,9 +8,9 @@ module Jatai
8
8
  namespace :redis
9
9
 
10
10
  MEMORY_TO_PLAN = {
11
- 256 => "starter",
12
- 512 => "pro",
13
- 1024 => "business"
11
+ 256 => "base",
12
+ 512 => "escala_1",
13
+ 1024 => "escala_2"
14
14
  }.freeze
15
15
 
16
16
  desc "status", "Ver status do Redis"
@@ -18,7 +18,7 @@ module Jatai
18
18
  end
19
19
 
20
20
  estimate = api.post("/api/v1/apps/#{slug}/scale/estimate", body: body)
21
- info("Preset: #{estimate["preset"]}")
21
+ info("Hex: #{hex_label(estimate["preset"])}")
22
22
  info("Réplicas: #{estimate["replicas"]}") if estimate["replicas"]
23
23
  info("CPU: #{estimate["cpu_limit"]} | RAM: #{estimate["memory_limit"]}") if estimate["cpu_limit"]
24
24
  info("Custo estimado: R$ #{estimate["monthly_cost"]}/mês")
@@ -14,10 +14,16 @@ module Jatai
14
14
  puts " Stack: #{app["stack"]}"
15
15
  puts " Status: #{colorize_status(app["status"])}"
16
16
  puts " Região: #{app["region"]}"
17
- puts " Preset: #{app["preset"]}"
18
- puts " DB: #{app["database_tier"]}"
19
- subdomain = app["subdomain"] || app["slug"]
20
- puts " URL: https://#{subdomain}.usebrasa.com.br"
17
+ puts " Hex: #{hex_label(app["preset"])}"
18
+ # `database_tier` deixou de existir: o tier do banco vive no plano do
19
+ # addon, e é `database_plan` que a API devolve. Enquanto a CLI lia a
20
+ # chave antiga, esta linha saía sempre vazia.
21
+ puts " Banco: #{pg_label(app["database_plan"])}"
22
+ # A URL vem da API (ClientAppSerializer#url), como no `jatai up`: montá-la
23
+ # aqui hardcodando usebrasa.com.br fazia a CLI anunciar a marca antiga
24
+ # enquanto a documentação e o dashboard já mostram jataiapp.com.br — e
25
+ # deixava a CLI errada de novo no próximo passo do rebrand.
26
+ puts " URL: #{app["url"]}" if app["url"]
21
27
 
22
28
  if app["last_deploy"]
23
29
  deploy = app["last_deploy"]
@@ -95,7 +95,6 @@ module Jatai
95
95
  name: app_name, stack: config[:stack], preset: config[:preset],
96
96
  region: config[:region], repo_url: detect_git_remote, repo_branch: config[:branch]
97
97
  }
98
- app_params[:database_tier] = config[:database_tier] if config[:database_tier]
99
98
  app_params[:release_command] = config.dig(:release, :command) if config.dig(:release, :command)
100
99
  api.post("/api/v1/apps", body: { app: app_params })
101
100
  end
@@ -167,11 +166,14 @@ module Jatai
167
166
 
168
167
  status = ws.wait_for("DeployStatusChannel",
169
168
  params: { deploy_id: deploy_id },
170
- until_status: %w[live failed],
169
+ until_status: %w[live failed partially_live],
171
170
  timeout: DEPLOY_TIMEOUT)
172
171
 
173
- if status == "live"
172
+ case status
173
+ when "live"
174
174
  true
175
+ when "partially_live"
176
+ partial_deploy_notice
175
177
  else
176
178
  error("\nDeploy falhou.")
177
179
  false
@@ -188,6 +190,14 @@ module Jatai
188
190
  Jatai::Websocket::CableClient.new.tap(&:connect!)
189
191
  end
190
192
 
193
+ # `true` porque, para o `up`, o app ESTÁ no ar: o fluxo seguinte (mostrar
194
+ # a URL) continua válido. O que ficou fora do release sai no painel e no
195
+ # `jatai deploy`, que tem o resumo completo.
196
+ def partial_deploy_notice
197
+ warning("\nDeploy parcialmente concluído — processo(s) fora do release; ver o deploy no painel.")
198
+ true
199
+ end
200
+
191
201
  def warn_fallback(err)
192
202
  $stderr.puts "\n [WebSocket indisponível: #{err.message}. Usando polling...]"
193
203
  end
@@ -227,6 +237,7 @@ module Jatai
227
237
  DEPLOY_MAX_POLLS.times do |i|
228
238
  deploy = api.get("/api/v1/apps/#{slug}/deploys/#{deploy_id}")
229
239
  return true if deploy["status"] == "live"
240
+ return partial_deploy_notice if deploy["status"] == "partially_live"
230
241
  if deploy["status"] == "failed"
231
242
  error("\nDeploy falhou.")
232
243
  return false
data/lib/jatai/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Jatai
2
- VERSION = "1.4.0"
2
+ VERSION = "1.5.0"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jatai
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.4.0
4
+ version: 1.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jataí Cloud