Changelog
Todas as mudancas notaveis neste projeto sao documentadas neste arquivo.
O formato e baseado em Keep a Changelog, e este projeto adere ao Semantic Versioning.
[2.21.0] - 2026-05-07 — Multi-app follow-ups
Resumo
5 features que completam MVPs/slots deixados em v2.20.0 + 1 UX safety net + 1 doc fix. Construído sobre o feedback de um deploy multi-service complexo em produção.
Mudança comportamento notável: runner add em repo já registrado agora falha com duplicate_repo exit 2. Workaround compatível: --force-duplicate. Documentado abaixo.
Adicionado
A — Interpolation `{{::Var}}` em `build.args:`
v2.20.0 entregou MVP literal (passa valor cru pro --build-arg). v2.21.0 fecha: agora interpola o mesmo {{::Var}} que environment: usa, resolvendo de .env e .secrets em build time.
build:
args:
VITE_SUPABASE_URL: "{{::SUPABASE_URL}}"
VITE_LIVEKIT_WS_URL: "{{::LIVEKIT_WS_URL?wss://default.com}}"Order: .secrets primeiro (mais específico), .env fallback. Suporta {{::Key?default}}. Sem TTY no build → não pede prompt; se var não resolve, erro instrutivo apontando pra runner env set.
B — Healthcheck `depends_on:` cross-app
Resolve o caso multi-app comum: meu-agent não deveria subir antes do meu-livekit estar healthy. Antes era manual (deploys sequenciais com vista nos logs).
instances:
production:
depends_on:
- app: meu-livekit
condition: healthy # healthy | started
timeout: 300s
- app: meu-redis
instance: production # opcional, default = mesma instance
condition: startedComportamento: start_container polla docker inspect por cada dep até condition met OR timeout. Bypass: --insecure ignora. Erro instrutivo: dependency_timeout: <app>/<instance> not ready after Ns.
C — `traefik.external.{snippet, write_to}` — slot ocupado
v2.20.0 entregou só o bypass do write local. v2.21.0 enriquece o slot:
# /opt/runner/config.yml
traefik:
mode: external
external:
write_snippet_to: "/opt/traefik-snippets/{app}.yml"
print_snippet: trueOutput em modo external:
print_snippet: true(default) → snippet em stderr depois do deploy, copy-pasteablewrite_snippet_to: <path>→ também salva em arquivo (Ansible sync){app}placeholder no path expande pro project slug
D — `runner add` detecta repo duplicado
UX safety net pro caso real: operador registrou 2 vezes sob nomes diferentes (meu-app-livekit + meu-livekit). Runner aceitou silenciosamente, gerando state divergente.
runner add --repo usuario/meu-app --branch main --ckey "..." ...
Error: duplicate_repo: Repo usuario/meu-app já está registrado
como `meu-app-livekit` (branch: deploy/livekit, status: registered).
Opções:
--force-duplicate permite criar SEGUNDA app pro mesmo repo
runner unregister meu-app-livekit --force remove a anteriorBypass: runner add --force-duplicate ... (raro; testes A/B, multi-instance experimental).
E — `generate-config` template completo
Operador olhou runner generate-config e concluiu erradamente que ports:/secrets:/type: image não existiam. Template embedded estava incompleto. Agora mostra TODOS os campos válidos do schema, com comentários esclarecendo opcionalidade + version markers (v2.20.0+, v2.21.0+).
Validação
- 340 unit tests pass (era 328 v2.20.0; +12 do v2.21)
- Matriz CI/CD: 82 pass / 0 fail / 71 n/a em ~184s (9 estados × 16 ops)
- 3 ops novas no matrix:
build_args_interp(state B): valida{{::Var}}resolution end-to-enddepends_on(state B): valida wait timeout em dep não-existenteduplicate_repo(state A): validarunner add --repo Xre-aceito falha comduplicate_repo
traefik_external(já em v2.20) ganhou snippet asserts: confirma stderr emit em mode=external
Backwards compatibility
.deploy.ymlsemdepends_on:→ Vec vazio, comportamento idêntico v2.20.x.deploy.ymlcom{{::Var}}embuild.args:agora interpola (era literal em v2.20 — comportamento mais correto, sem quebra esperada)config.ymlsemtraefik.external:→ sub-block opcional, só usado em mode=externalrunner addem repo já registrado muda comportamento: era silently OK, agoraduplicate_repoexit 2. Workaround:--force-duplicate
Backlog v2.22+
- Sol 2 post_deploy_check schema (smoke pós-deploy configurável)
- Service discovery dinâmico (env var templating tipo
${app.endpoint}) - W6 smoke probe (rodar imagem brevemente + detect listen port real)
- UDP-aware allocator (random strategy só pensa TCP hoje)
runner list --orphans(states sem deploy_count > 0)
Backlog v3.0 (acumulando breaking)
- Compose-style multi-service em 1 manifesto
status --jsoncleanup — remover legacy top-level fields
[2.20.0] - 2026-05-06 — Multi-app polish + CCS schema
Resumo
5 features opt-in que fecham fricções identificadas em deploys multi-service reais (LiveKit + Python agent + Vite frontend) + schema split do runtime status --json pra CCS dashboard. Zero breaking changes — apps v2.19.x rodam sem mudança.
Adicionado
A — `build.args:` no `.deploy.yml` (Vite/Next/Angular zero-touch)
build:
context: frontend
dockerfile: Dockerfile
args: # ← NOVO
VITE_SUPABASE_URL: "https://..."
VITE_LIVEKIT_WS_URL: "wss://..."
BUILD_ENV: productionCada entry vira --build-arg KEY=VAL no docker build. Mata o workaround "build local + commit dist/" pra apps com env vars compile-time. MVP v2.20.0: valores literais. Interpolation {{::Var}} (igual environment:) é follow-up — resolve_value helper público ainda não existe.
B — Auto-create docker network
Quando .deploy.yml declara networks: [meet] e a network não existe, runner cria automaticamente antes do docker run. Idempotente. Built-ins (bridge, host, none) são skipped. Consolidado num único path em containers::ensure_network.
C — `traefik.mode: external | none` (bypass)
# /opt/runner/config.yml
traefik:
mode: external # none | local (default) | external| Mode | Comportamento |
|---|---|
local (default) |
Escreve em <traefik_dynamic_dir> (v2.19.x) |
external |
Skip do write local — operador trata roteamento externo |
none |
Mesmo bypass — apps internas sem HTTP público |
v2.20.0 = só bypass. Slot reservado pra ações futuras (snippet output, write_to). Default local preserva backcompat 100%.
D — `status --json` schema split (aditivo)
Payload do runner status <app> --json ganha 3 blocos lógicos:
{
"current_version": "abc1234",
"manifest": { "project": "...", "repo": "...", "branch": "...", "ckey_fingerprint": "ck_...", ... },
"config": { "type": "...", "port": ..., "ports": [...], "networks": [...], "hosts": {...}, ... },
"runtime": { "current_version": "...", "status": "...", "network": { ... v2.19.0 } },
"_deprecated_top_level": true, // marker pra CCS migrar
// legacy top-level fields preserved (project, system, app_type, instances, state)
}CCS dashboard pode migrar gradualmente: data.manifest !== undefined → caminho novo. Cleanup de legados em v3.0 (postergado).
E — `hosts:` map (DNS aliases via `--add-host`)
# .deploy.yml
hosts:
host.docker.internal: host-gateway
internal-api: 10.0.0.5
livekit-pub: 10.108.0.5Cada entry vira --add-host KEY:VAL no docker run. Resolve host.docker.internal em Linux Docker (que não tem por default). Map (não array) — last-wins em duplicatas, override fácil em re-deploy.
Refatorado
containers::ensure_networkganhou skip pra docker built-ins (bridge,host,none). Single source of truth — pipeline não duplica mais a lógica.
Validação
- 328 unit tests pass (era 309 v2.19.0; +19 do v2.20)
- Matriz CI/CD: 79 pass / 0 fail / 47 n/a em ~170s (9 estados × 13 ops)
- 4 ops novas no matrix:
build_args: valida injection em build-mode states (B/D/I)network_autocreate: valida criação automática de network novatraefik_external: valida que mode=external não escreve local filehosts_check: valida--add-hostnodocker inspect
status_schema.pyagora hard check (era permissivo) — falha semanifest/config/runtimefaltarem
Backwards compatibility
.deploy.ymlsembuild.args:→ BTreeMap vazio, zero--build-argflags.deploy.ymlsemhosts:→ BTreeMap vazio, zero--add-hostflagsconfig.ymlsemtraefik.mode:→ defaultlocal→ comportamento idêntico v2.19.x.deploy.ymlcomnetworks: [X]e X não existe → v2.19.x quebrava no docker run; v2.20.x cria- Consumer do
status --jsonlegado → continua lendo top-level (_deprecated_top_level: truemarker indica caminho novo)
Nenhuma quebra. Apps registradas em v2.19.x seguem rodando.
Backlog identificado pra v2.21.0
- Interpolation
{{::Var}}embuild.args:— precisa exporresolve_single_valuecomopubemsrc/environment/mod.rs - Healthcheck
depends_on:cross-app — multi-app ordering - Sol 2 post_deploy_check schema (smoke pós-deploy configurável)
- Service discovery dinâmico entre apps (env var templating)
- Sub-bloco
traefik.external.{snippet, write_to}— ações concretas em external mode - W6 smoke probe (rodar imagem brevemente pra detectar listen port real)
Migration path
Pra apps existentes opt-in:
# Adicionar build.args
echo " args: { VITE_X: ... }" >> .deploy.yml/build/
# Adicionar hosts
echo -e "hosts:\n host.docker.internal: host-gateway" >> .deploy.yml
# Mudar pra external mode
echo "traefik:\n mode: external" >> /opt/runner/config.ymlSem precisar runner unregister ou --force.
[2.19.0] - 2026-05-06 — Port allocation autônomo (Lote B)
Resumo
Single feature focused: eliminar a única intervenção manual restante no CI/CD do runner — escrever ports: ["VPC_IP:porta:porta"] no .deploy.yml. Runner agora aloca porta livre automaticamente, persiste sticky no state file (re-deploys reusam a mesma porta), e expõe via runner status --json pra CCS consumir.
Bonus: pre-flight de secrets (Sol 1 do feedback de prod) — deploy aborta cedo se secret declarado em .deploy.yml::secrets: tem valor vazio (a menos de --insecure). Encerra o ciclo "deploy aparentemente bem-sucedido mas funcionalmente quebrado" causado por healthchecks que retornam 200 mesmo com secrets faltando (ex: Laravel /up).
Adicionado
Port allocation (config + state)
network: block opcional em /opt/runner/config.yml:
network:
vpc_ip: 10.108.0.5 # opcional; sem isso bind cai pra 127.0.0.1
publish_strategy: random # random | sequential | explicit | none
port_range: [8000, 8999]
port_blocklist: []Estratégias:
- random (recomendado): sorteia porta livre na primeira deploy, persiste sticky pra re-deploys reusarem
- sequential: pega a primeira porta livre no range
- explicit: lê
ports:do.deploy.yml(comportamento v2.18.x) - none: container roda sem
-pno host
Sticky lifecycle:
- Primeira deploy: aloca porta → persiste em
state/<app>.yml::network.published_port - Re-deploys (commit novo, fetch --deploy): reusa a mesma porta — Traefik externo permanece válido
runner unregister X: libera porta de volta ao poolrunner reset --hard X: libera state → próximo deploy realocarunner ports realloc X: força nova alocação (operador atualiza Traefik)
Resolução de bind address:
network.vpc_ipconfigurado → bind nesse IP- Sem vpc_ip → bind em
127.0.0.1(safe default — Traefik no mesmo host alcança via loopback)
Comandos novos
runner ports list [--json] # lista portas alocadas em todas apps
runner ports release <app> # libera state.network sem parar container
runner ports realloc <app> # limpa published_port — próxima deploy realoca
runner deploy <app> --port N # override permanente (escreve no state, força reuso)
runner deploy <app> --no-port # desabilita port mapping (strategy=none) só desta deploy`runner init` interativo
Quando rodado em TTY, runner init agora pergunta:
- "Esse server fica em uma VPC interna? [s/N]"
- Se sim: oferece auto-detect via
ip -4 -o addr show, confirma o IP - "Strategy [random/sequential/explicit] (default: random)"
- Persiste o bloco
network:no config.yml
Em modo não-TTY (CI, piped) skipa silenciosamente — backcompat 100%.
`runner status --json` runtime.network
Bloco novo no payload pra CCS consumir:
"runtime": {
"network": {
"vpc_ip": "10.108.0.5",
"published_port": 8473,
"endpoint": "http://10.108.0.5:8473",
"allocation": {
"strategy": "random",
"allocated_at": "2026-05-06T12:30:00Z",
"sticky": true,
"source": "state"
}
}
}Apps legacy sem state.network → runtime.network: null (key presente, valor null).
Pre-flight de secrets (Sol 1 do feedback prod)
Antes de iniciar o container, valida que cada secret declarado em .deploy.yml::secrets: tem valor não-vazio em .runner/secrets.enc (após decifrar). Se vazio:
Error: deploy_blocked: secrets declared in .deploy.yml have empty values: ["APP_KEY"]
Bypass: rerun with --insecure (NOT recommended).
Or fill the values:
runner env set <app> --key APP_KEY --secret --value <value>Bypass via --insecure (operator opt-out). Resolve o caso onde Laravel /up retornava 200 mesmo com APP_KEY vazio, fazendo o runner declarar deploy bem-sucedido enquanto / retornava 500.
Corrigido
- Sticky port resolution não filtrava containers do próprio app:
sticky::resolveconsultavaports_in_use(ss + docker ps) e rejeitava porta ocupada por OUTRO processo — mas não distinguia containers da MESMA app (que estão sendo substituídos). Resultado: re-deploy do mesmo app sob random/sequential strategy hitavaport_in_use_by_other. Fix: pipeline filtra ports de containers{project_und}_{instance}_*antes de chamar sticky. Resolve "ou é nosso → reusa" prometido na docstring desde a v2.19.0 inicial.
Backwards compatibility
- Apps registradas em v2.18.x continuam rodando sem mudança
- Default de
publish_strategyéExplicitquandonetworkblock ausente — comportamento idêntico ao v2.18.x (lêports:do.deploy.yml) - State files antigos sem bloco
networkmigram silenciosamente — porta re-alocada no próximo deploy quando strategy != Explicit instances.X.domaincontinua aceito (sem mudança)
Validação
- 309 unit tests pass (era 304 + 5 do
secrets_preflight+ extras de network module) - Matriz CI/CD: 90 cells, 73 pass, 0 fail, 17 n/a em 114s
- 2 ops novas no matrix:
network_check: valida porta sticky em range 9000-9100secrets_preflight: valida deploy aborta com secret vazio
- 9 estados validados ponta-a-ponta sob random strategy: A B C D E F G H I
Backlog identificado (não bloqueia)
runner status --jsonschema ainda flat fora do novoruntime.network— refactor completo (manifest/config/runtimeblocks separados) fica pra v2.20.0- W6 smoke probe (rodar imagem brevemente pra detectar listen port real do container)
- Sol 2 do feedback prod (post-deploy smoke test configurável no
.deploy.yml) — v2.20.0
[2.18.2] - 2026-05-06 — Wizard "auxiliar inteligente"
Resumo
5 melhorias no wizard/generator que tornam o runner add realmente zero-touch
para a maioria dos perfis e auxiliam o operador quando a análise estática
não tem como saber a resposta. Construído sobre uma matriz de testes CI/CD
(72 cells, 9 estados × 7 ops) que cobre todos os cenários de uso do runner.
Adicionado (W1) — Warning explícito quando porta é incerta
Antes: se o Dockerfile não tinha EXPOSE, o generator silenciava com port: 8080.
Para compose, pegava o mapping host:container sem avisar que o container
internamente pode escutar em outra porta.
Agora:
- Dockerfile sem EXPOSE: warning
Port: Dockerfile sem EXPOSE; assumindo 8080. Se o container escuta em outra porta, ajuste port: no .deploy.yml - Compose com
ports:: warningPort: usando porta X do mapping no compose. ATENÇÃO: este é mapping host:container — se o container escuta internamente em outra porta (nginx :80, php-fpm :9000), ajuste port:
Adicionado (W2) — Healthcheck type-aware no generator
Antes: TCP cego em todos os deploys gerados.
Agora detect_default_healthcheck() escolhe baseado no nome da imagem:
| Imagem | Healthcheck gerado |
|---|---|
nginx*, caddy*, httpd*, apache*, traefik* |
mode: http path: / |
php-fpm*, qualquer *fpm* |
mode: tcp |
node*, python*, ruby*, openjdk*, rust* |
mode: tcp |
| Outros | mode: tcp (fallback seguro) |
Adicionado (W5) — Aviso quando Dockerfile + compose coexistem
Antes: silenciosamente usava o Dockerfile, ignorava compose service.
Agora: warning Source: repo tem Dockerfile + docker-compose.yml. Build do Dockerfile foi usado como source-of-truth (compose só pra env vars). Para usar a imagem do compose em vez de buildar, edite type:image + image:
Adicionado (W7) — Detecção de domínio placeholder no wizard
Após o prompt de domínio interativo, warning loud se o valor casa com:
*.example.com / .org / .net.test,.local,.localhost,.invalid- Markers:
placeholder,todo,fixme,xxx,changeme,yourdomain
Não bloqueia (operador pode realmente querer um staging em *.test), só nudge.
Corrigido (Bonus) — `--accept-defaults` rejeita perfil bare
Antes: runner wizard --accept-defaults em repo sem Dockerfile/compose/.env
gerava silenciosamente um .deploy.yml inútil.
Agora: bail com no_deploy_profile (mesmo comportamento do runner add).
Corrigido — TCP/HTTP probe IMAGE mode usa `bash -c` no fallback
Bug: probe usava sh -c '... /dev/tcp/... ...' no fallback. Como /dev/tcp
é builtin do bash (não do dash/sh), containers Debian-based sem nc falhavam
silenciosamente o healthcheck. Quebrava nginx:1, php-fpm:debian, etc.
Fix: o fallback agora invoca bash -c "exec 3<>/dev/tcp/127.0.0.1/PORT" —
funciona em qualquer imagem que tenha bash (a maioria das de produção).
Validação
- 284 unit tests pass (era 283; +1 do
placeholder_domain) - Matriz CI/CD: 72 cells, 63 pass, 0 fail, 9 n/a em 110s
- 9 estados validados: A (.deploy.yml), B (Dockerfile), C (compose), D (Laravel), E (bare → falha esperada), F (image-only), G (sem EXPOSE), H (IP-restricted), I (secrets em env)
Backlog identificado pela matriz (não bloqueia)
runner status --jsonainda flat — refactor para separarmanifest/config/runtimeblocks (input pra integração CCS)- W6 (smoke probe pra detectar porta real do container) — alta complexidade
- W3 (version source detection: file vs git-commit) — baixa prioridade
- W4 (
git ls-remote --headspra listar branches no wizard) — baixa prioridade - W8 (presets por stack: Laravel/React/Flask) — média prioridade
[2.18.1] - 2026-05-05 — Hotfix (6 bugs do loop e simulation)
Corrigido (P0)
- Pipeline lê
repodo state file (Bug E real em prod): migration v2.16.0 era parcial — pipeline ainda exigiagithub.repono.deploy.ymle quebrava deploys de apps migrados. Agora resolve do state primeiro, fallback pro manifest legacy. Erro novono_repo_configuredé instrutivo. - Wizard
--answers.instance_branchpropaga até o YAML: era parseado mas hardcoded comobranch: main. AgoraInstanceSection.branchchega ao render. - Wizard recusa sobrescrever
.deploy.ymlexistente (perfil A): bail commanifest_already_exists. Antes gerava defaults silenciosos. - Validator aceita
type:ORapp_type:: antes pedia sóapp_type:mas o parser canonical e o wizard usamtype:.
Corrigido (P1)
- Mensagem de erro
no_deploy_yml: no_deploy_profile:redundante: strip do prefixo interno do generator. runner status --jsontop-levelcurrent_version: campo era NULL no top, populado em 3 lugares diferentes do JSON. Agora atalho do state.current_version.crypto.rs/vault/ckeys.rsaceitamRUNNER_KEYS_DIRenv var: paths hardcoded/opt/runner/.keysexigiam root. Agora override opcional pra rootless test/CI; default inalterado pra prod.
Testes
36 unit tests no wizard module + 283 na suite total. Loop validation script: 6/6 pass do zero, incluindo cenário adversarial sem github: no manifest.
[2.18.0] - 2026-05-05 — Lote A (feedback de producao)
Atendendo feedback do deploy real do middleware-241 (Laravel 11). 6 itens.
Corrigido
--branchpropaga prainstances.X.source.branch(#4): antes,runner add --branch devclonava dedevmas a instance ficava commaindo.deploy.yml—runner fetchposterior puxava da branch erradarunner add --deployrealmente deploya (#5): antes era silenciosamente ignorado com mensagemAuto-deploy requested but disabled. Agora invocapipeline::deploy()na sequencia
Wizard polish
- Curadoria de
.env.example(#6): detectaAPP_ENV=local,APP_DEBUG=true,LOG_LEVEL=debug,NODE_ENV=development→ oferece "perfil de producao" (production,false,info); pruning automatico de placeholders comuns (AWS_*,MAIL_*,MEMCACHED_*,PUSHER_*) - Deteccao Laravel via
composer.json(#7): identificalaravel/framework, analisaroutes/api.php+resources/views/para distinguirsystem: apivssys; com Dockerfile ->type: docker-build runner wizard --answers <file>(#8): substitui prompts interativos por respostas em JSON. Habilita IA/script/CI gerar manifesto valido sem TTY. Suporta override de campos do manifesto,env_overrides,skip_env(glob),secret_keys/non_secret_keys(forca classificacao)- ckey display por fingerprint (#10): default agora mostra apenas
ck_xxxx…xxxx(ckey fica encriptada emstate/<app>.yml); flag--ckey-showopt-in para exibir plaintext
Adiado para Lote B/C/D
Itens P0 do feedback (architectural changes):
VPC port allocation (— entregue na v2.19.0network.{vpc_ip, publish_strategy, port_range})traefik.mode: external+ remocao de URL fantasia no output (planejado, não implementado)- Backup automatico no
unregister(planejado, não implementado)
Detalhes
- 7 unit tests novos (281 total na suite, 0 falhas)
- Sem breaking change
[2.17.5] - 2026-05-05
- gitops fix attempt #5: migra
extra_artifacts->scripts:v3.0 section. Tool web emrunner.ccs.systems/tools/secrets.htmlsegue aguardando atualizacao do gitops worker em producao
[2.17.4] - 2026-05-05
- gitops fix attempt #4: arquivo
tools/secrets/index.htmlna raiz do repo (igualinit.sh)
[2.17.3] - 2026-05-05
Corrigido (gitops)
- Source path em
extra_artifactsagora usaclient/tools/secrets/index.html(relativo a raiz do repo) — tool web finalmente publicada em https://runner.ccs.systems/tools/secrets.html
[2.17.2] - 2026-05-05
Corrigido
- gitops:
type: htmlem extra_artifacts era silenciosamente ignorado. Trocado paratype: script
[2.17.1] - 2026-05-05 — F3 Tool web
- Secrets Web Tool — gera
.runner/secrets.enc100% client-side (Web Crypto API) - URL: https://runner.ccs.systems/tools/secrets.html
- Mesmo formato AES-256-GCM + SHA-256(ckey) que o Runner ja decifra
- Modo encrypt-only (sem decode, decisao de seguranca)
[2.17.0] - 2026-05-05 — F1 Wizard CLI
- `runner wizard` — wizard interativo para gerar
.deploy.yml(e opcionalmente.runner/secrets.enc) - Auto-trigger no
runner addem modo TTY quando perfil E (cru) - Detecta linguagem (next/react/flask/django/celery/rust/php) → infere
system/type - Multi-service compose: pergunta qual e o app principal, marca redis/postgres/etc como assets
- Subcomando
runner templateinspeciona schema embutido (debug)
[2.16.0] - 2026-05-05 — Lote 5 (#6, #4)
- Generator de .deploy.yml — detecta perfil A-E e gera manifesto proporcional
AppState.repomigrado para state file (deprecatesgithub:no.deploy.yml)- Migration silenciosa para apps existentes
[2.15.0] - 2026-05-05 — Lote 4 (#7)
runner addnao clona mais o repo na raiz do app dir (causava duplicacao)- Apenas
.deploy.yml+.runner/state-markerna raiz; codigo vive empull/esrc/{instance}/{ver}/ runner cleanup --reset-root <app>opt-in para apps existentes
[2.14.0] - 2026-05-05 — Lote 3 (#8)
runner add/deployBLOQUEIA secrets emenvironment:sem ckey configurada- Flag global `--insecure` suprime TODAS as travas (banner vermelho + audit log JSONL)
[2.13.0] - 2026-05-05 — Lote 2 (#3)
- `runner tokens` — list/test agrupado por fingerprint (sem revelar plaintext)
runner add --token-from <other-app>reusa token encriptado entre apps
[2.12.0] - 2026-05-05 — Lote 1 (#1, #2, #5)
--ckeysem valor gera ckey random; com valor aceita literal--repoaceita URLs (https/ssh/git@) e normaliza parauser/repo--branchagora obrigatorio sempre (sem defaultdist)
[2.11.6] - 2026-05-04
- Logs vao para stderr (stdout limpo para piping/JSON)
- Handler SIGPIPE evita panic ao piping para
head/jq
[2.11.5] - 2026-05-04
- self-update lida com chattr +i (immutable bit) automaticamente
[2.11.0-2.11.4] - 2026-04-29 a 2026-05-03
- v2.11.4: docker run env injection via
--env-file - v2.11.3: provisionamento de
.env/.secretsno bootstrap - v2.11.2: bug fix
app_has_ckey(secrets nao injetados em redeploys) - v2.11.0: vault completo (mkey + ckeys + config-backups + secrets), Ed25519 minisign
[2.10.0] - 2026-04-25
runner debug test— validacao end-to-end (config, vault, deploys, validators)- Coverage 100% de
--jsonem todos os comandos
[2.9.0] - 2026-04-22
- CCS Data Contract — alinhamento de output
runner snapshot --jsonpara integracao com dashboard CCS
[2.8.0] - 2026-04-20
- Master key backup:
runner mkey export/importpara disaster recovery
[2.7.0] - 2026-04-19
init.sh— instalador unificado (arch-aware,--rcflag para release candidates)
[2.6.0] - 2026-04-18
mode: exitno healthcheck — apps CLI/scanners/jobs one-shot (sem container permanente)
[2.5.0] - 2026-04-18
runner initinterativo — wizard de setup inicial com--sete--mkey
[2.4.2] - 2026-04-17
- Deteccao automatica de secrets — Aho-Corasick multi-pattern matching com 3 niveis de confianca (HIGH 95%, MEDIUM 75%, LOW 50%)
runner env setinteligente — auto-detecta secrets pelo nome, encripta com ckey ou avisa se plaintext--forceno env set — forca como secret sem questionamento (requer ckey).secretsso com encriptacao — sem ckey o pipeline ignora.secrets,--secretsem ckey retorna erroregeneratefallback — procura key no.envse.secretsnao existe- Filtro de falsos positivos —
keyboard,primary_key,max_tokens,cache_key,public_key, etc.
[2.4.1] - 2026-04-17
runner cleanup --images— remove imagens Docker nao utilizadas de projetos gerenciados pelo runnerrunner cleanup --build-cache [FILTER]— prune do Docker buildkit cache com filtro de idade (default: 72h, aceita24h,168h,all)- Limpeza automatica de imagens no pipeline —
cleanup_old_versionsagora remove a imagem Docker de versoes que excedemkeep_versions docker image prune -f— executado automaticamente apos cleanup de imagens (dangling layers)
[2.3.0] - 2026-04-16
- mTLS com tokio-rustls — API HTTP requer client certificates quando habilitado
- Hot-reload de CA cert — mtime-based, CCS rotaciona cert via SCP sem restart
- TLS proxy architecture — tokio-rustls acceptor + warp backend ephemeral
[2.2.0] - 2026-04-14
runner service— lifecycle CLI para systemd (init, start, stop, restart, status, config, uninstall)runner signal— sinais externos para CI/CD: send (ok/warn/fail + action), clear, listrunner serve --service— service mode com API HTTP + scheduler generico- API HTTP autenticada — 5 endpoints REST com X-API-Key (/health, /api/v1/status, signal, deploy, apps)
- API Key com fingerprint — formato
rk_{SHA256(hostname+MAC+machine-id)[..8]}_{random} - Scheduler generico — trait-based: CanaryJob, TtlCleanupJob, SignalCheckJob
- service.yml — configuracao separada do service mode (API, scheduler, webhook, mTLS)
[2.1.0] - 2026-04-07
- Healthcheck honra HEALTHCHECK do Dockerfile — nao sobrescreve com --health-cmd
- Healthcheck modos
tcp:ecmd:— prioridade cmd > tcp > path depends_on:entre apps — topo sort emrunner deploy --all, cycle detection- Validacao semantica em
runner add— build context, Dockerfile, port, healthcheck image_mode: dockerfileaceito como alias de self-containedstart_periodconfiguravel no .deploy.yml (default 30s)- wait_for_healthy tolerante — nao aborta em Unhealthy transitorio
- Self-update multi-arch — detecta x86_64/aarch64 e baixa binario correto do CDN
- Lock file PID-alive — remove locks orfaos automaticamente
- Mensagens de erro melhoradas — YAML com linha/coluna, token expirado, rollback em 1a deploy
[1.5.0] - 2026-03-17
Adicionado
--instancenorunner add: Define qual instancia do.deploy.ymlesta maquina deve deployar--instancenorunner edit: Altera a instancia a qualquer momento, com validacao contra o.deploy.yml- Campo
instanceno state: Persistido no state file, usado pelo auto-deploy - Guia Multi-Ambiente: Documentacao completa sobre deploy em multiplas maquinas
Alterado
- Auto-deploy resolve instancia por prioridade:
state.instance>default_instance_name()>"production" runner edit --show: Agora mostra a instancia configurada
[1.4.1] - 2026-03-13
Adicionado
- max_deploy_retries: Limita re-tentativas de deploy para o mesmo commit que falhou (padrao: 3). Evita loops infinitos em builds quebrados
- Rastreamento de falhas no estado: Campos
last_failed_commitedeploy_retry_countno arquivo de estado da app - Rollback automatico em falha de start: Quando o container antigo e parado por conflito de porta e o novo falha ao iniciar, o anterior e reiniciado automaticamente
Melhorado
- Mensagens de erro nas notificacoes: Extrai a linha de erro real de outputs verbosos (Docker BuildKit, npm, Rust) em vez de truncar no inicio. Limite de 800 caracteres
- Notificacao de falha mostra rollback: Indica qual versao ficou ativa apos o rollback
[1.4.0] - 2026-03-12
Adicionado
runner notify setup: Setup interativo de notificacoes Telegram e Discordrunner notify test: Envia notificacao de teste para todos os canais configuradosrunner notify status: Mostra status dos canais- StructuredLogChannel: Audit trail em
deploy-events.jsonl - NotifyManager v2: Substitui funcoes legacy por multi-channel com suporte a Telegram, Discord e structured logging
Alterado
- Pipeline usa NotifyManager v2: Substitui chamadas legacy por
NotifyManager.send() - Config consolidado:
GlobalConfigremovido, tudo viaRunnerConfig
Removido
- Codigo legacy de notificacoes (
send_telegram,send_discord,notify_deploy_*)
[1.2.3] - 2026-03-01
Modificado
Modos de Deploy Simplificados
O Runner agora opera em dois modos exclusivos, determinados pela presenca de image: ou build: no .deploy.yml:
- Modo IMAGE (
image: xxx): usa imagem pronta do registry, monta source via volume - Modo BUILD (
build: {dockerfile: Dockerfile}):docker buildcom Dockerfile do repositorio
Isso substitui a logica anterior de image_mode (bind-mount/self-contained) e app_type: docker-build.
A geracao automatica de Dockerfile por stack (generate_dockerfile) foi deprecada. Projetos devem fornecer seu proprio Dockerfile.
Retrocompatibilidade
Configs antigas continuam funcionando:
image_mode: self-contained-> tratado como modo BUILD com Dockerfile defaultapp_type: docker-build-> tratado como modo BUILDimage_mode: bind-mount+image:-> modo IMAGE (sem mudanca)
Adicionado
- Deploy lock: Previne deploys concorrentes no mesmo app via
.deploy.lock - Audit log no pipeline: Deploy registra sucesso e falha no
deploy-audit.jsoncom versao, duracao e erro - Output estruturado: Modulo
output/com envelope JSON para integracao programatica - Campo
source.directory: Permite especificar subdiretorio do repo para deploy (ex:directory: .usa raiz inteira) - Deteccao de VOLUME em imagens: Override automatico com bind mount explicito para evitar overlay de anonymous volumes
- Metodo
is_build_mode()para deteccao do modo de deploy - Metodo
is_image_mode()para deteccao do modo de deploy - Metodo
validate_deploy_mode()para validar configuracao - Metodo
effective_build_config()para obter config de build efetiva - Auto-mount de
data/,logs/,keys/em ambos os modos
Corrigido
- Audit log backward compat: Campo
instanceemDeployAuditEntrycomserde(default)para logs antigos - source.directory ignorado: Campo nao existia em
InstanceSourceConfig, serde ignorava silenciosamente - Read-only filesystem: Mount de source como
:roimpedia Docker de criar mountpoints para VOLUMEs - Health check timeout: Usava
healthcheck.timeout(per-check) como tempo total de espera. Agora calcula corretamente:start_period + (interval + timeout) * retries + buffer - prepare() em build mode: Usa diretorio completo do pull/ ao inves de auto-detectar subdiretorios
Deprecado
generate_dockerfile()— projetos devem fornecer Dockerfilewrite_dockerfile()— nao utilizado pelo pipeline- Campo
image_mode— usarimage:oubuild:diretamente app_type: docker-build— usar secaobuild:
[1.2.2] - 2026-02-19
Adicionado
Comando `runner edit`
Novo comando para editar configuracao de aplicacoes registradas:
# Ver configuracao atual
runner edit runner-docs/front --show
# Mudar branch trackeada
runner edit runner-docs/front --branch devO comando inclui validacoes automaticas:
- Verifica se repositorio git existe
- Verifica se branch existe no remote
- Verifica se branch contem
.deploy.yml
Melhorias no Cleanup
- Output colorido com destaque para nomes de containers
- Flag
--verbosepara logs detalhados - Deteccao de conflitos quando multiplas configs matcham mesmo container
Self-Update com URL
Suporte para atualizar para versao especifica via URL:
runner self-update --url https://runner.ccs.systems/rc/v1.2.2-rc.8/runnerCLI Padronizado
Todos os comandos agora aceitam app como argumento posicional:
# Antes (verbose)
runner fetch --app runner-docs/front
# Agora (direto)
runner fetch runner-docs/front
runner health runner-docs/front
runner logs tail runner-docs/front
runner env status runner-docs/frontPreparacao de Conteudo Melhorada
A funcao prepare() agora suporta repositorios com conteudo na raiz:
- Detecta automaticamente diretorios:
dist/,guides/,content/,docs/,public/,src/ - Fallback para raiz do projeto quando nenhum diretorio especifico existe
- Identifica corretamente
src/como estrutura de versoes vs codigo fonte
Corrigido
- Duplicatas na deteccao de containers orfaos
- Comparacao de versao usando SHA256 quando versoes sao iguais
- Conteudo na raiz (ex:
guides/) nao era copiado para diretorio de versao - Comandos
fetch,health,logs,envexigiam flag--appdesnecessariamente
[1.2.0] - 2026-02-16
Adicionado
Novos Source Types
Tres novos tipos de fonte para instancias:
| Tipo | Descricao | Uso |
|---|---|---|
tag |
Deploy de tags Git | Releases (v*) |
local |
Deploy de diretorio local | Hotfixes, builds externos |
image |
Deploy de imagem Docker pronta | Imagens pre-built |
instances:
release:
source:
type: tag
tag_pattern: "v*"
hotfix:
source:
type: local
path: /builds/hotfix/
canary:
source:
type: image
image: ghcr.io/org/app:canaryCanary Scheduler (Auto-Promocao)
Sistema de promocao automatica de canary deployments:
instances:
production:
canary:
enabled: true
auto_promote:
enabled: true
initial_weight: 10
increment: 10
interval: 5m
final_weight: 100
health_check_before: true
abort_on:
- health_check_failed
- error_rate_above:5%
- latency_p99_above:500msNovos comandos CLI:
runner canary status <project> -i <instance>- Ver status do schedulerrunner canary pause <project> -i <instance>- Pausar promocaorunner canary resume <project> -i <instance>- Retomar promocaorunner canary abort <project> -i <instance>- Abortar e rollback
Notificacoes v2
Arquitetura extensivel com trait-based channels:
HTTP Webhook - Notificacoes para qualquer endpoint HTTP:
# config.yml
integrations:
flare:
type: http_webhook
url: https://flareapp.io/api/deploys
method: POST
auth:
type: api_key
key: "${FLARE_API_TOKEN}"
location: query
param_name: api_tokenTipos de autenticacao suportados:
none- Sem autenticacaoapi_key- Token em query param ou headerbearer- Header Authorization: Beareroauth2- Client credentials flow (auto-renew)basic- Username:passwordhmac- Assinatura HMAC-SHA256
Structured Log - Audit log em JSON Lines:
logs:
deploy_events:
type: structured_log
path: /opt/runner/logs/deploy-events.jsonl
format: json_linesComando `runner serve`
Servidor de triggers para webhooks e cron:
runner serve # Auto-detecta modo
runner serve --mode webhook # Apenas webhook server
runner serve --mode cron # Apenas cron scheduler
runner serve --mode both # Ambos
runner serve --port 9000 # Porta customizadaO webhook server:
- Recebe eventos GitHub/GitLab/Bitbucket
- Verifica assinatura HMAC-SHA256
- Aciona deploys automaticos
- Endpoint de health em
/health
CLI Filters por Labels CCS
Filtragem de containers por labels:
runner list --project=meu-app
runner list --instance=production
runner list --project=frontend-* --jsonLabels CCS usadas:
ccs.systems/projectccs.systems/instanceccs.systems/version
Alterado
Deploy Output Melhorado
Novo formato de output com badges de progresso:
[1/6] [PREPARE] Preparando deploy...
[2/6] [ARTIFACT] Copiando artefatos...
[3/6] [ENV] Gerando ambiente...
[4/6] [SECURITY] Verificando segurança...
[5/6] [CONTAINER] Subindo container...
[6/6] [PROMOTE] Promovendo versao...
═══════════════════════════════════════
✓ DEPLOY CONCLUIDO
App: projeto/production
Versao: v1.2.0
URL: https://app.projeto.com.br
═══════════════════════════════════════- deploy --verbose (-V): Modo verbose com logs tecnicos detalhados
- deploy --force (-f): Forca redeploy mesmo se versao ja implantada
- Box de resumo: Exibe app, versao e URL ao final do deploy
Fetch Output em Portugues
Status traduzidos para melhor legibilidade:
| Icone | Status | Significado |
|---|---|---|
| ✓ | atualizado | App atualizada |
| ↑ | desatualizado | Update disponivel |
| ↓ | baixado | Artefatos baixados |
| ✗ | erro | Erro no processo |
- fetch output ordenado: Resultados ordenados alfabeticamente por projeto/sistema
- fetch --force (-F): Nova flag para forcar git pull antes de carregar config
Corrigido
- Recursao infinita na copia: Skip de diretorios de instancia (production, staging, pr-*) e commit hashes
- app_type no Dockerfile: Usa
config.app_typeao inves de hardcoded "sys" - Duracao de healthcheck: Normaliza valores numericos para formato Docker (30 → 30s)
- Tipo "docs": Novo tipo para imagens de documentacao (MkDocs, PimDocs)
[1.1.13] - 2026-02-16
Alterado
- fetch output colorido: Cores ANSI para facilitar leitura
- Verde (✓) para sucesso e "up to date"
- Amarelo (↑) para updates/pulled
- Vermelho (✗) para erros
- Ciano para sugestoes de solucao
- Dim para informacoes secundarias (versoes)
[1.1.12] - 2026-02-16
Alterado
- fetch output limpo por padrao: Output mais amigavel para usuarios
- Logs INFO escondidos por padrao (usa nivel WARN)
- Formato limpo com alinhamento e indicadores visuais
- Sugestoes de fix para erros comuns
- Nova flag
--verbosepara output detalhado - Uso:
runner fetch(limpo) ourunner fetch --verbose(detalhado)
[1.1.11] - 2026-02-16
Adicionado
- fetch --force (-F): Nova flag para forcar git pull antes de carregar config
- Resolve problema de
.deploy.ymldesatualizado no servidor - Util para migracao de formato legado para v1.1.10
- Uso:
runner fetch --forceourunner fetch -F
- Resolve problema de
[1.1.10] - 2026-02-16
Corrigido
- Bug 11 - Auto-mount volumes: Respeita volumes explicitos do
.deploy.yml- Se usuario configura
keys:/app/keys:ro, usa essa config - Default para
keys/mudou de:rwpara:ro(seguranca) logs/edata/continuam como:rw- Evita erro de mount duplicado
- Se usuario configura
[1.1.9] - 2026-02-16
Corrigido
- app_type parsing: Alias
type/app_typepara compatibilidade YAML - env_files: Container carrega
.enve.secretsautomaticamente - volumes bind mount: Volumes relativos convertidos para paths absolutos
[1.1.8] - 2026-02-16
Corrigido
extract_version(): Suporte a campos nested em TOML (ex:
package.versionem Cargo.toml)- Parser agora detecta secoes
[section]e busca chaves dentro delas - Compativel com JSON e YAML via dot notation
- Parser agora detecta secoes
copy_dir_recursive(): Previne recursao infinita durante copia de diretorios
- Detecta quando o destino esta dentro da origem
- Pula diretorios de versao (
v*) e symlinks
docker-build: Suporte completo no pipeline de deploy
- Nova struct
DeployBuildConfigpara configuracao de build - Metodo
is_docker_build()para deteccao de tipo - Healthcheck sem curl (usa bash /dev/tcp)
- Build de imagem a partir do Dockerfile do projeto
- Nova struct
[1.1.7] - 2026-02-15
Alterado
Storage: Default
apps_pathalterado de/appspara/data/apps- Segue estrutura de storage do ecossistema CCS
- Compativel com migracao para Kubernetes
- Configs existentes com
apps_path: /appscontinuam funcionando
Self-Update: Novas flags
--rc: Atalho para--channel rc-C, --check: Apenas verifica se ha atualizacao (nao baixa)
[1.1.6] - 2026-02-14
Adicionado
- Canais GitOps - Suporte a canais
stableercno.gitops.yml - Releases estaveis na raiz:
/runner - Release candidates em:
/rc/runner - Versionamento automatico por tag pattern (
vX.Y.ZvsvX.Y.Z-rc.N)
Alterado
- Self-update agora suporta flag
--rcpara baixar release candidates - Estrutura de storage separada por canal
[1.1.5] - 2026-02-14
Corrigido
- Versao interna do binario corrigida para corresponder a tag
[1.1.2] - 2026-02-13
Corrigido
folder_nameno.gitops.ymlcorrigido para processar corretamente
[1.1.1] - 2026-02-13
Alterado
- URL de download alterada para
runner.ccs.systems .gitops.ymlatualizado para nova estrutura de storage
[1.1.0] - 2026-02-12
Adicionado
MCP Server (Model Context Protocol)
Implementacao completa do servidor MCP para integracao com Claude Code.
Comandos CLI:
runner mcp serve # Inicia servidor MCP
runner mcp install # Instala config MCP
runner mcp uninstall # Remove config MCP
runner mcp status # Mostra info do servidorTools disponiveis (25+):
| Categoria | Tools |
|---|---|
| Deploy | runner_deploy, runner_rollback |
| Apps | runner_list_apps, runner_app_status, runner_add_app |
| Instances | runner_instances, runner_versions, runner_destroy_instance |
| Canary | runner_weights, runner_set_weight, runner_promote |
| Staging | runner_stage_deploy, runner_stage_list, runner_stage_destroy, runner_stage_cleanup |
| Environment | runner_env_status, runner_env_set, runner_env_validate |
| Config | runner_validate_config, runner_generate_config |
| Maintenance | runner_fetch, runner_cleanup |
| Logs | runner_logs_history, runner_logs_tail |
Resources (9):
runner://instructions/*- Documentacao estaticarunner://status/*- Status dinamico (apps, staging, config)
Prompts (5):
setup_new_project- Setup de novo projetodiagnose_deploy- Diagnostico de falhascanary_workflow- Workflow de canarymigrate_config- Migracao para v1.0.0staging_pr- Deploy de PR
Self-Update
Modulo de auto-atualizacao do binario.
runner self-update # Atualizar para versao estavel
runner self-update --force # Forcar reinstalacao
runner self-update --channel rc # Usar release candidate
runner self-update --path /opt/bin # Caminho customizadoFeatures:
- Download automatico da versao mais recente
- Suporte a canais (stable/rc)
- Backup automatico do binario atual
- Verificacao de integridade
[1.0.0] - 2026-02-05
Alterado (Breaking Changes)
Esta versao remove completamente o codigo legado e padroniza a arquitetura baseada em instancias.
Novo Formato `.deploy.yml`
O novo formato usa instances{} ao inves de containers[]:
# Novo formato (v1.0.0)
project: meu-projeto
port: 8000
networks: [public, mysql]
environment:
KEY: value
instances:
production:
domain: app.site.com
source:
type: dist
keep_versions: 3Comparacao de mudancas:
| Legado | v1.0.0 |
|---|---|
containers[] |
instances{} |
type: front-static (enum) |
app_type: "front-static" (string) |
versioning.keep_versions |
instances.X.keep_versions |
environment.templates |
environment: {} (HashMap) |
deploy.strategy |
Traefik dynamic config |
Mantido
InstanceDeployConfig- Estrutura principalGlobalConfig- Config globalRunnerConfig- Config em/opt/runner/config.yml- Traefik module - Geracao de YAML dinamico
- Containers module - Docker run/build
[0.6.0] - 2026-02-04
Adicionado
Novos Modulos
runner/mod.rs- Configuracao global do Runnertraefik/mod.rs- Geracao de YAML dinamico para Traefikcontainers/mod.rs- Operacoes Docker viadocker run/build
Novos Comandos CLI
runner init- Inicializa ambiente Runnerrunner instances <project>- Lista instanciasrunner versions <project> -i <name>- Lista versoesrunner weights <project> -i <name>- Mostra pesosrunner weight <project> <inst> <ver> <weight>- Ajusta pesorunner promote <project> -i <name> <version>- Promove versaorunner destroy <project> -i <name>- Remove instanciarunner cleanup- Limpa versoes antigasrunner migrate --app <path>- Migra estrutura legada
[0.5.0] - 2026-01-28
Adicionado
- Comando
reset- Reset de deploy - Flag
--hard- Reset incluindo.enve.secrets - Flag
--skip-tests- Pula testes apos redeploy - Hook
post_healthy- Comandos apos health check - Variaveis em hooks -
${VERSION},${APP_PATH},${CONTAINER} - Comando
stage reset- Reset de staging
[0.4.0] - 2026-01-26
Adicionado
- Production Compose Generation - Geracao automatica de
docker-compose.yml - Staging de PRs - Ambientes efemeros para Pull Requests
- Comando
stage deploy- Deploy de PR - Comando
stage list- Lista stagings - Comando
stage destroy- Remove staging - Comando
stage cleanup- Cleanup automatico - Template variables -
${PR_NUMBER},${REPO},${PROJECT}, etc.
[0.3.0] - 2026-01-26
Adicionado
- Comando
add- Registra novas apps - Comando
fetch- Busca atualizacoes - Tipo
docker-build- Deploy com Dockerfile customizado - Auto Deploy - Configuracao per-app
- State Management - Persistencia de estado
- Audit Log - Log global de fetch
Alterado
- Binario renomeado de
cicd-runnerpararunner - Path padrao alterado para
/opt/runner/
Removido
- Webhook server - Substituido por add/fetch
[0.2.0] - 2025-01-25
Adicionado
- MANUAL.md - Documentacao para clients
- Webhook server - Endpoint para GitHub push events
- HMAC-SHA256 - Validacao de assinaturas
[0.1.0] - 2025-01-25
Adicionado
- CI/CD Runner em Rust - Implementacao inicial
- Blue-Green Deployment - Teste antes de promover
- Versioned Deploys - Diretorios versionados
- Environment Templates - Sistema de templates
- Custom Deploy Types - wordpress-plugin, mautic, custom
- Multi-Source Downloads - Download de multiplos repos
- Telegram/Discord Notifications - Notificacoes
- Playwright Integration - Testes E2E
- Automatic Rollback - Rollback em falha
- Health Checks - Verificacao de saude