brasa 0.1.0 → 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/README.md +1 -1
- data/lib/brasa/cli.rb +25 -0
- data/lib/brasa/commands/domains.rb +1 -1
- data/lib/brasa/commands/logs_export.rb +28 -0
- data/lib/brasa/commands/logs_search.rb +47 -0
- data/lib/brasa/commands/redis.rb +74 -0
- data/lib/brasa/commands/status.rb +1 -1
- data/lib/brasa/commands/up.rb +12 -7
- data/lib/brasa/config.rb +1 -1
- data/lib/brasa/version.rb +1 -1
- metadata +6 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9f7ecb8d9dafb1f7fe0e297323e2e0b5c4a9bbf2f8bf2b34145bceb5deea51f3
|
|
4
|
+
data.tar.gz: 03ea9501f89e932e79a4951fb109e00200f89583453c516939610cb3ea2be6c4
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e8cc452b7bbacacd245ab2de59944379c76b455ac9aa1a69adac55b7b5b955106eb6c38d5b77e4437ca177749c384d222a90863c90473c32c1c1ca4a44bbcd95
|
|
7
|
+
data.tar.gz: ff72d9f447707ed7dab625884ac8ddc759b3d6895cc1775578a99ce46f57877a1370c3c051ad09711710982deef9431382d70661625d6fa82ad06ac5c813c339
|
data/README.md
CHANGED
data/lib/brasa/cli.rb
CHANGED
|
@@ -4,12 +4,15 @@ require "brasa/commands/init"
|
|
|
4
4
|
require "brasa/commands/up"
|
|
5
5
|
require "brasa/commands/deploy"
|
|
6
6
|
require "brasa/commands/logs"
|
|
7
|
+
require "brasa/commands/logs_search"
|
|
8
|
+
require "brasa/commands/logs_export"
|
|
7
9
|
require "brasa/commands/env"
|
|
8
10
|
require "brasa/commands/database"
|
|
9
11
|
require "brasa/commands/scale"
|
|
10
12
|
require "brasa/commands/status"
|
|
11
13
|
require "brasa/commands/destroy"
|
|
12
14
|
require "brasa/commands/domains"
|
|
15
|
+
require "brasa/commands/redis"
|
|
13
16
|
|
|
14
17
|
module Brasa
|
|
15
18
|
class CLI < Thor
|
|
@@ -44,6 +47,25 @@ module Brasa
|
|
|
44
47
|
Commands::Logs.new.execute(options)
|
|
45
48
|
end
|
|
46
49
|
|
|
50
|
+
desc "logs:search", "Buscar nos logs da aplicação"
|
|
51
|
+
option :query, type: :string, required: true, desc: "Termo de busca"
|
|
52
|
+
option :level, type: :string, desc: "Filtrar por nível (error, warn, info)"
|
|
53
|
+
option :source, type: :string, desc: "Filtrar por source"
|
|
54
|
+
option :since, type: :string, desc: "Período (ex: 1h, 24h, 7d)"
|
|
55
|
+
option :limit, type: :string, default: "100", desc: "Limite de resultados"
|
|
56
|
+
define_method("logs:search") do
|
|
57
|
+
Commands::LogsSearch.new.execute(options)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
desc "logs:export", "Exportar logs da aplicação"
|
|
61
|
+
option :format, type: :string, default: "json", desc: "Formato de exportação (json, csv)"
|
|
62
|
+
option :since, type: :string, desc: "Data/hora inicial"
|
|
63
|
+
option :until, type: :string, desc: "Data/hora final"
|
|
64
|
+
option :level, type: :string, desc: "Filtrar por nível (error, warn, info)"
|
|
65
|
+
define_method("logs:export") do
|
|
66
|
+
Commands::LogsExport.new.execute(options)
|
|
67
|
+
end
|
|
68
|
+
|
|
47
69
|
desc "env SUBCOMMAND", "Gerenciar variáveis de ambiente"
|
|
48
70
|
subcommand "env", Commands::Env
|
|
49
71
|
|
|
@@ -53,6 +75,9 @@ module Brasa
|
|
|
53
75
|
desc "domains SUBCOMMAND", "Gerenciar domínios customizados"
|
|
54
76
|
subcommand "domains", Commands::Domains
|
|
55
77
|
|
|
78
|
+
desc "redis SUBCOMMAND", "Gerenciar Redis cache"
|
|
79
|
+
subcommand "redis", Commands::Redis
|
|
80
|
+
|
|
56
81
|
desc "scale", "Escalar a aplicação"
|
|
57
82
|
option :web, type: :numeric, desc: "Número de instâncias web"
|
|
58
83
|
option :preset, type: :string, desc: "Preset (hobby, production, scale)"
|
|
@@ -37,7 +37,7 @@ module Brasa
|
|
|
37
37
|
result = helper.api.post("/api/v1/apps/#{slug}/domains", body: { domain: { hostname: hostname } })
|
|
38
38
|
helper.success("Domínio #{hostname} adicionado.")
|
|
39
39
|
helper.info("Token de verificação: #{result["verification_token"]}")
|
|
40
|
-
helper.info("Adicione um registro CNAME ou TXT apontando para #{slug}.
|
|
40
|
+
helper.info("Adicione um registro CNAME ou TXT apontando para #{slug}.usebrasa.com.br")
|
|
41
41
|
rescue Api::Client::ValidationError => e
|
|
42
42
|
DomainsHelper.new.error("Erro: #{e.message}")
|
|
43
43
|
rescue Api::Client::ApiError => e
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
require "brasa/commands/base"
|
|
2
|
+
|
|
3
|
+
module Brasa
|
|
4
|
+
module Commands
|
|
5
|
+
class LogsExport < Base
|
|
6
|
+
def execute(options = {})
|
|
7
|
+
require_auth!
|
|
8
|
+
slug = app_slug
|
|
9
|
+
|
|
10
|
+
format = options.fetch("format", "json")
|
|
11
|
+
params = { export_format: format }
|
|
12
|
+
params[:since] = options["since"] if options["since"]
|
|
13
|
+
params[:until] = options["until"] if options["until"]
|
|
14
|
+
params[:level] = options["level"] if options["level"]
|
|
15
|
+
|
|
16
|
+
result = api.get("/api/v1/apps/#{slug}/logs/export", params: params)
|
|
17
|
+
|
|
18
|
+
if result.is_a?(Array)
|
|
19
|
+
puts JSON.pretty_generate(result)
|
|
20
|
+
else
|
|
21
|
+
puts result
|
|
22
|
+
end
|
|
23
|
+
rescue Api::Client::ApiError => e
|
|
24
|
+
error("Erro: #{e.message}")
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
require "brasa/commands/base"
|
|
2
|
+
|
|
3
|
+
module Brasa
|
|
4
|
+
module Commands
|
|
5
|
+
class LogsSearch < Base
|
|
6
|
+
def execute(options = {})
|
|
7
|
+
require_auth!
|
|
8
|
+
slug = app_slug
|
|
9
|
+
query = options["query"]
|
|
10
|
+
|
|
11
|
+
unless query
|
|
12
|
+
info("Uso: brasa logs:search <query> [--level error] [--since 1h]")
|
|
13
|
+
return
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
params = { q: query, limit: options.fetch("limit", "100") }
|
|
17
|
+
params[:level] = options["level"] if options["level"]
|
|
18
|
+
params[:source] = options["source"] if options["source"]
|
|
19
|
+
params[:since] = options["since"] if options["since"]
|
|
20
|
+
|
|
21
|
+
results = api.get("/api/v1/apps/#{slug}/logs/search", params: params)
|
|
22
|
+
|
|
23
|
+
if results.nil? || results.empty?
|
|
24
|
+
info("Nenhum resultado encontrado.")
|
|
25
|
+
return
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
results.each do |entry|
|
|
29
|
+
puts "#{pastel.dim(entry["timestamp"])} #{pastel.cyan(entry["source"])} [#{colorize_level(entry["level"])}] #{entry["message"]}"
|
|
30
|
+
end
|
|
31
|
+
rescue Api::Client::ApiError => e
|
|
32
|
+
error("Erro: #{e.message}")
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
private
|
|
36
|
+
|
|
37
|
+
def colorize_level(level)
|
|
38
|
+
case level
|
|
39
|
+
when "error" then pastel.red(level)
|
|
40
|
+
when "warn" then pastel.yellow(level)
|
|
41
|
+
when "info" then pastel.green(level)
|
|
42
|
+
else pastel.dim(level)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
require "thor"
|
|
2
|
+
require "brasa/commands/base"
|
|
3
|
+
|
|
4
|
+
module Brasa
|
|
5
|
+
module Commands
|
|
6
|
+
class Redis < Thor
|
|
7
|
+
namespace :redis
|
|
8
|
+
|
|
9
|
+
MEMORY_TO_PLAN = {
|
|
10
|
+
256 => "starter",
|
|
11
|
+
512 => "pro",
|
|
12
|
+
1024 => "business"
|
|
13
|
+
}.freeze
|
|
14
|
+
|
|
15
|
+
desc "status", "Ver status do Redis"
|
|
16
|
+
def status
|
|
17
|
+
helper = RedisHelper.new
|
|
18
|
+
helper.require_auth!
|
|
19
|
+
slug = helper.app_slug
|
|
20
|
+
|
|
21
|
+
addon = helper.api.get("/api/v1/apps/#{slug}/addons/cache")
|
|
22
|
+
|
|
23
|
+
puts helper.pastel.bold("Redis de #{slug}")
|
|
24
|
+
puts " Status: #{addon["status"]}"
|
|
25
|
+
puts " Plano: #{addon["plan"]}"
|
|
26
|
+
puts " Memória: #{addon.dig("config", "memory_mb")}MB"
|
|
27
|
+
puts " Custo: R$ #{addon["monthly_cost_brl"]}/mês"
|
|
28
|
+
rescue Api::Client::ApiError => e
|
|
29
|
+
RedisHelper.new.error("Erro: #{e.message}")
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
desc "enable", "Habilitar Redis para o app"
|
|
33
|
+
option :memory, type: :numeric, default: 256, desc: "Memória em MB (256, 512, 1024)"
|
|
34
|
+
def enable
|
|
35
|
+
helper = RedisHelper.new
|
|
36
|
+
helper.require_auth!
|
|
37
|
+
slug = helper.app_slug
|
|
38
|
+
|
|
39
|
+
plan = MEMORY_TO_PLAN[options[:memory]]
|
|
40
|
+
unless plan
|
|
41
|
+
helper.error("Memória inválida. Use: 256, 512 ou 1024")
|
|
42
|
+
return
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
helper.info("Habilitando Redis com #{options[:memory]}MB (plano #{plan})...")
|
|
46
|
+
result = helper.api.post("/api/v1/apps/#{slug}/addons", { addon: { slug: "cache", plan: plan } })
|
|
47
|
+
helper.success("Redis habilitado! Status: #{result["status"]}")
|
|
48
|
+
rescue Api::Client::ApiError => e
|
|
49
|
+
RedisHelper.new.error("Erro: #{e.message}")
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
desc "disable", "Remover Redis do app"
|
|
53
|
+
def disable
|
|
54
|
+
helper = RedisHelper.new
|
|
55
|
+
helper.require_auth!
|
|
56
|
+
slug = helper.app_slug
|
|
57
|
+
|
|
58
|
+
prompt = TTY::Prompt.new
|
|
59
|
+
unless prompt.yes?("Remover Redis? Todos os dados em cache serão perdidos.")
|
|
60
|
+
helper.info("Operação cancelada.")
|
|
61
|
+
return
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
helper.info("Removendo Redis...")
|
|
65
|
+
helper.api.delete("/api/v1/apps/#{slug}/addons/cache")
|
|
66
|
+
helper.success("Redis removido com sucesso!")
|
|
67
|
+
rescue Api::Client::ApiError => e
|
|
68
|
+
RedisHelper.new.error("Erro: #{e.message}")
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
class RedisHelper < Base; end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
@@ -15,7 +15,7 @@ module Brasa
|
|
|
15
15
|
puts " Status: #{colorize_status(app["status"])}"
|
|
16
16
|
puts " Região: #{app["region"]}"
|
|
17
17
|
puts " Preset: #{app["preset"]}"
|
|
18
|
-
puts " URL: https://#{app["slug"]}.
|
|
18
|
+
puts " URL: https://#{app["slug"]}.usebrasa.com.br"
|
|
19
19
|
|
|
20
20
|
if app["last_deploy"]
|
|
21
21
|
deploy = app["last_deploy"]
|
data/lib/brasa/commands/up.rb
CHANGED
|
@@ -15,14 +15,17 @@ module Brasa
|
|
|
15
15
|
app = create_app(config)
|
|
16
16
|
info("App #{app["slug"]} criado. Provisionando infraestrutura...")
|
|
17
17
|
|
|
18
|
-
wait_for_provisioning(app["slug"])
|
|
18
|
+
unless wait_for_provisioning(app["slug"])
|
|
19
|
+
return
|
|
20
|
+
end
|
|
19
21
|
success("Infraestrutura provisionada!")
|
|
20
22
|
|
|
21
23
|
info("Iniciando primeiro deploy...")
|
|
22
24
|
deploy = trigger_deploy(app["slug"])
|
|
23
|
-
wait_for_deploy(app["slug"], deploy["id"])
|
|
24
25
|
|
|
25
|
-
|
|
26
|
+
if wait_for_deploy(app["slug"], deploy["id"])
|
|
27
|
+
success("Deploy concluído! App disponível em https://#{app["slug"]}.usebrasa.com.br")
|
|
28
|
+
end
|
|
26
29
|
rescue Api::Client::ValidationError => e
|
|
27
30
|
error("Erro ao criar app: #{e.message}")
|
|
28
31
|
rescue Api::Client::ApiError => e
|
|
@@ -48,29 +51,31 @@ module Brasa
|
|
|
48
51
|
def wait_for_provisioning(slug)
|
|
49
52
|
MAX_POLLS.times do
|
|
50
53
|
app = api.get("/api/v1/apps/#{slug}")
|
|
51
|
-
return if app["status"] == "active"
|
|
54
|
+
return true if app["status"] == "active"
|
|
52
55
|
if app["status"] == "error"
|
|
53
56
|
error("Erro no provisionamento.")
|
|
54
|
-
return
|
|
57
|
+
return false
|
|
55
58
|
end
|
|
56
59
|
print "."
|
|
57
60
|
sleep(POLL_INTERVAL)
|
|
58
61
|
end
|
|
59
62
|
error("Timeout aguardando provisionamento.")
|
|
63
|
+
false
|
|
60
64
|
end
|
|
61
65
|
|
|
62
66
|
def wait_for_deploy(slug, deploy_id)
|
|
63
67
|
MAX_POLLS.times do
|
|
64
68
|
deploy = api.get("/api/v1/apps/#{slug}/deploys/#{deploy_id}")
|
|
65
|
-
return if deploy["status"] == "live"
|
|
69
|
+
return true if deploy["status"] == "live"
|
|
66
70
|
if deploy["status"] == "failed"
|
|
67
71
|
error("Deploy falhou.")
|
|
68
|
-
return
|
|
72
|
+
return false
|
|
69
73
|
end
|
|
70
74
|
print "."
|
|
71
75
|
sleep(POLL_INTERVAL)
|
|
72
76
|
end
|
|
73
77
|
error("Timeout aguardando deploy.")
|
|
78
|
+
false
|
|
74
79
|
end
|
|
75
80
|
end
|
|
76
81
|
end
|
data/lib/brasa/config.rb
CHANGED
|
@@ -7,7 +7,7 @@ module Brasa
|
|
|
7
7
|
CREDENTIALS_FILE = File.join(CONFIG_DIR, "credentials.json").freeze
|
|
8
8
|
PROJECT_FILE = ".brasa.yml".freeze
|
|
9
9
|
|
|
10
|
-
DEFAULT_API_URL = "https://api.
|
|
10
|
+
DEFAULT_API_URL = "https://api.usebrasa.com.br".freeze
|
|
11
11
|
|
|
12
12
|
class << self
|
|
13
13
|
def api_url
|
data/lib/brasa/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: brasa
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1
|
|
4
|
+
version: 0.2.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Brasa
|
|
@@ -124,7 +124,7 @@ dependencies:
|
|
|
124
124
|
description: Deploy apps Rails e Node.js na nuvem brasileira com um comando. Soberania
|
|
125
125
|
de dados, faturamento em reais.
|
|
126
126
|
email:
|
|
127
|
-
- contato@
|
|
127
|
+
- contato@usebrasa.com.br
|
|
128
128
|
executables:
|
|
129
129
|
- brasa
|
|
130
130
|
extensions: []
|
|
@@ -145,12 +145,15 @@ files:
|
|
|
145
145
|
- lib/brasa/commands/init.rb
|
|
146
146
|
- lib/brasa/commands/login.rb
|
|
147
147
|
- lib/brasa/commands/logs.rb
|
|
148
|
+
- lib/brasa/commands/logs_export.rb
|
|
149
|
+
- lib/brasa/commands/logs_search.rb
|
|
150
|
+
- lib/brasa/commands/redis.rb
|
|
148
151
|
- lib/brasa/commands/scale.rb
|
|
149
152
|
- lib/brasa/commands/status.rb
|
|
150
153
|
- lib/brasa/commands/up.rb
|
|
151
154
|
- lib/brasa/config.rb
|
|
152
155
|
- lib/brasa/version.rb
|
|
153
|
-
homepage: https://
|
|
156
|
+
homepage: https://usebrasa.com.br
|
|
154
157
|
licenses:
|
|
155
158
|
- MIT
|
|
156
159
|
metadata: {}
|