`runner adopt`

Recupera o registro de uma app que continua rodando mas cujo state o Runner perdeu (disco corrompido, unregister acidental, migracao de servidor). Em vez de recriar a app do zero — e arriscar subir com secrets errados — o adopt le o container vivo como fonte da verdade.

Resolve a issue #36.

O que ele faz

  1. Captura as variaveis de ambiente do container em execucao
  2. Faz diff contra os defaults da imagem — so o que foi injetado no deploy sobra
  3. Classifica cada valor em secret ou plain via secret_detect
  4. Registra a app (cold) ou reusa o registro existente (warm)
  5. Grava .env / .secrets e re-encripta o bundle

Sintaxe

runner adopt <app> --from-container <container> -r <owner/repo> -b <branch> --ckey   # cold
runner adopt <app> --from-container <container>                                       # warm

Cold vs warm

Modo Quando Exige
cold Nao ha registro nenhum da app --repo, --branch, --ckey
warm O registro existe, so o .env/.secrets se perdeu so --from-container

Argumentos

Argumento Tipo Obrigatorio Descricao
<app> string Sim Nome da app

Flags

Flag Obrigatorio Descricao
--from-container <NOME> Sim Container vivo de onde capturar as envs
-r, --repo <OWNER/REPO> So no cold Repositorio GitHub
-b, --branch <BRANCH> So no cold Branch de distribuicao
--ckey [<CKEY>] So no cold Content key do bundle de secrets. Sem valor (--ckey solto): gera uma aleatoria de 32 chars
-t, --token <TOKEN> Nao Token GitHub para repo privado (encriptado at rest)
--token-from <APP> Nao Reusa o token de outra app registrada, sem passar por plaintext
--github-app <APP_ID> Nao Credencial de GitHub App (2.141.0+; exige --repo). Ver credenciais
--corte-pendente Nao Adocao com corte (2.141.0+): registra como pendente e pausa a app ate o corte. Ver abaixo
--manifest-path <PATH> Nao Caminho do .deploy.yml dentro do repo (monorepos)
--instance <INSTANCE> Nao Instance alvo (production, staging, ...)
--yes Nao Nao pergunta nada: a faixa ambigua de classificacao vira secret (conservador)
-o, --output Nao human | json | yaml
-x, --exact Nao Exige match exato de identidade, sem fallback por prefixo
--insecure Nao Bypass dos guards. Emite banner e grava em insecure-events.jsonl

Exemplos

# cold — nenhum registro sobrou
runner adopt myapp --from-container myapp -r user/myapp -b main --ckey

# warm — o registro existe, reusa
runner adopt myapp --from-container myapp

# monorepo, com token emprestado de outra app
runner adopt api --from-container api_production_v1-2-0 \
  -r acme/mono -b dist --manifest-path services/api/.deploy.yml --token-from web

Adocao com corte (parada e substituicao) — 2.141.0+

Sem --corte-pendente, o primeiro deploy substitui o adotado do jeito de sempre (parando-o antes so quando compartilha volume ou porta). Com ele, a troca vira uma operacao explicita, com janela e volta:

# 1. registra; a app fica pausada (o cron nao deploya, deploy comum e recusado)
runner adopt loja --from-container loja -r acme/loja -b dist --corte-pendente

# 2. corte: para o adotado, sobe o novo, health dentro da janela
runner deploy loja --replace-adopted --janela 60 [--antigo-id <id>] [--dados compartilhar|copiar|mover|sem]

# 3a. desfazer (religa o antigo)        3b. concluir (destroi o antigo, despausa)
runner revert-adoption loja             runner finalize-adoption loja

runner adoption status loja --json      # pendente | cortado | revertido | finalizado

O que o corte faz: confere o adotado (--antigo-id), para-o, trata os dados, renomeia para <nome>__ccs_antes com restart policy no e sobe o novo. Se o health nao passa dentro de --janela (contada desde a parada), o novo sai e o antigo volta com nome e restart policy originais. Sucesso deixa o antigo parado e preservado e a app ainda pausada ate o finalize/revert. Depois de um revert-adoption (status revertido, app pausada) da para cortar de novo direto com deploy --replace-adopted (2.141.2+), sem refazer o adopt.

--dados Efeito
compartilhar (padrao) O novo monta os mesmos volumes/binds (o antigo esta parado: um escritor so)
copiar Copia o conteudo dos volumes do adotado para os do manifesto (cp -a)
mover Copia e, no finalize-adoption, remove os volumes de origem
sem Nao toca nos dados; recusa (colisao_de_volume) se o novo montar o mesmo volume

Codigos de erro (prefixo da mensagem / error no JSON): adocao_em_andamento (deploy comum numa adocao pendente/cortada/revertida), app_desconhecida, adocao_nao_pendente, container_mudou, corte_em_andamento (ja existe um __ccs_antes), colisao_de_volume, revertido, janela_estourada, reversao_falhou (estado manual: confira docker ps -a), nada_a_reverter, antes_ausente, nada_a_finalizar.

Sobre a classificacao automatica

O secret_detect divide os valores em tres faixas: claramente secreto, claramente publico, e ambiguo. Sem --yes o comando pergunta caso a caso. Com --yes, o ambiguo vira secret — o erro conservador (um valor publico tratado como secreto nao vaza nada; o contrario sim).

Ver tambem

By Borlot.com.br on 19/08/2026