dovetail_cli 0.1.1
dovetail_cli: ^0.1.1 copied to clipboard
One entry point for the dovetail toolkit: report which platform tools are usable, bundle an installer, sign what was bundled, and write an update manifest.
Changelog #
0.1.1 — 2026-09-09 #
Nada no código mudou. A 0.1.0 foi publicada com o README em português e com avisos que deixaram de ser verdade no momento em que o pacote saiu — "parte do dovetail, versionado só localmente" era um deles. A página do pub.dev é congelada na versão publicada, então esta versão existe para substituir o que ela mostra.
O que muda: README em inglês no caminho canônico, com o português ao lado em
README.pt-BR.md e um seletor de idioma no topo dos dois. A instalação passa a
mostrar a dependência publicada em vez de um caminho para dentro do monorepo.
0.1.0 — 2026-09-08 #
A porta única do toolkit, distribuída como binário: init, doctor, dev,
build, ship, bundle, sign, icon, inspect, keygen, manifest,
probe, new e release.
-
newcria um projeto Flutter mínimo já consumindo a biblioteca dovetail:pubspec.yamlcom dependência de caminho,main.dart, um teste de widget e umdovetail.yamlquedovetail doctoraceita. O caminho para a biblioteca é calculado a partir do repositório quando o CLI roda dentro dele, e pode ser sobrescrito com--dovetail-path. -
devvigia um crate deflutter_rust_bridgee recusa iniciar quando o Dart gerado está atrás do Rust, nomeando cada método que falta — a armadilha em que tudo compila e o método não existe do lado Dart.--checksó reporta,--onceregenera e diz o que entrou. -
shippercorre a esteira inteira a partir dodovetail.yaml, com o plano como dado —--dry-runimprime os passos sem rodar nenhum. -
doctorsem--targetrelata o projeto antes das ferramentas, e distingue o que falta do que só não foi configurado. -
bundle --target windowsproduz MSI pelowixle NSIS pelomakensisem qualquer host;sign --target windowsusaosslsigncodefora do Windows. -
--versioninforma versão, alvo, o Dart que construiu o binário e o commit de que ele saiu (commit sourcequando roda pordart run). O semver está parado em 0.1.0, e sem o commit um binário de três semanas e a árvore de hoje respondiam a mesma linha. -
shipno macOS ganha o passoarchive: o.appassinado vira<binário>_<versão>_universal.app.tar.gz, e é ESSE arquivo que o manifesto publica — o updater extrai tar.gz com um.appdentro e não monta dmg. Publicar o dmg no manifesto fazia a primeira atualização automática morrer em "could not extract the update archive" depois de baixar e verificar 33 MB. O dmg continua saindo, assinado e notarizado, como download de instalação inicial; oshipimprime os dois no fim, separados.bundle --macos-format tarproduz o arquivo avulso. -
sign --target macos --file <dmg>: oshipplanejava o passosign dmge o comando o recusava com "macos needs --bundle" — a esteira morria no quarto de cinco passos. O.dmgé assinado como arquivo plano e, com--notarize, notarizado e grampeado ele mesmo. -
sign --bundleaplica por padrão o plist que o projeto Xcode assina em Release (CODE_SIGN_ENTITLEMENTSdomacos/Runner.xcodeproj/project.pbxproj, resolvido contra a raiz do projeto;macos/Runner/Release.entitlementscomo reserva) e diz qual aplicou:codesign --forcesem--entitlementssubstitui a assinatura do Xcode e deixa cair os entitlements que o build tinha — oapp-sandbox— e é também o que remove oget-task-allowque o Xcode deixa no Release e que a notarização recusa. Oshiprepassasign.macos.entitlementsquando declarado. -
shipcomsign.macos.notarize: truepassa--require-signaturee--notarizenos dois passos de assinatura do macOS — o.appantes do dmg, para o ticket ficar grampeado nos dois — e recusa ANTES do build, no--dry-runinclusive, quando a identidade não está exportada ou nenhum grupo de credenciais de notarização está completo. Comnotarize: false, que é o que oinitescreve, o build local sem Developer ID segue sem assinar e diz isso, como sempre.sign --notarize --require-signaturesem credenciais de notarização recusa nomeando os dois grupos, em vez de sair 0 dizendo "nothing was submitted". -
sign.macos.identity-envpassa a valer: a variável que a config nomeia é lida e alimenta a identidade que a política conhece, quandoAPPLE_SIGNING_IDENTITYnão está definida ou está vazia (um secret ausente no CI vira string vazia). Estava documentada, escrita peloinit, impressa pelodoctor— e nunca lida. Osignsó consulta odovetail.yamlnos alvos macOS e Windows (o Linux nunca o lê), e um yaml inválido vira nota, não recusa;--filerecusa diretório. -
doctordizmissing updatequandoupdate.public-keynão está declarada, emissing signingquandonotarize: truenão tem identidade ou credenciais exportadas — em vez deokpara o mesmo yaml que oshiprecusa. -
buildembute no app o que odovetail.yamldecide, como--dart-define:dovetail.update.public_key(base64 de uma linha),dovetail.update.endpoint,dovetail.update.base_url,dovetail.identifieredovetail.version(dopubspec.yaml) — e diz o que embutiu. Um só arquivo decide em quem o app confia, com o que oreleaseassina e o que oprobeverifica; um build de staging aponta para um host de staging trocando o yaml, não o app. -
update.endpointnodovetail.yaml: onde o app instalado pergunta pelo manifesto (https, com os placeholders do updater). Não é obase-url. -
probe --ca <pem>: uma raiz A MAIS para esta corrida, nunca um jeito de pular o TLS — cadeia e hostname continuam verificados. É o que permite provar transporte e assinatura contra um host local com CA privada (tool/serve_dist.dart,tool/ci/prove_update.sh). Um PEM inválido é erro de uso, não defeito. Sem--public-key, oprobeverifica com aupdate.public-keydodovetail.yamlao lado, e diz isso. -
HttpArtifactFetcherrecusa um redirecionamento que saia do https: oHttpClientsegue redirecionamentos por padrão, e um CDN respondendo 302 para um espelho http entregaria o manifesto em texto claro com a cara de https. Saltar é permitido; sair do https não. -
release --public-keyvenceupdate.public-key, como--version,--keye--outjá venciam, e diz quando vence (os dois key ids). Era o contrário, e no dia em que o yaml de produção declara a chave do parque nenhum release de staging com par descartável seria possível sem editar o yaml de produção.--public-keye--outrelativos resolvem contra--root, como--artifacteupdate.key— da raiz do repo,--public-key keys/update.pubnão achava o arquivo e tratava o texto do caminho como a chave. -
Windows tinha os dois defeitos que o macOS tinha:
sign.windows.*era lido do yaml e ignorado pelo signer (passa a alimentarWINDOWS_CERTIFICATE_FILE,WINDOWS_CERTIFICATE_PASSWORDeWINDOWS_TIMESTAMP_URLquando essas não estão definidas), e oshippedia--windows-format msisem o--upgrade-codeque obundleexige — um ship Windows morria no passo de bundle. O UpgradeCode passa a ser derivado doidentifier(UUID v5, estável entre releases; dois produtos nunca compartilham um). -
self-install,self-updateeupdateganham limite de tempo, 30 s entre dois pedaços do download (oprobee odoctorusam 15 s, por serem sondas): um host que parou de responder não deixa mais o comando mudo. -
probe --timeout <s>(padrão 15) limita conexão, resposta e intervalo entre pedaços; um endpoint morto viraFAILED — did not answer within 15sem vez de minutos de silêncio. Zero devolve a espera do SO. Odoctor --check-updatesusa o mesmo limite. -
buildmostra a saída doflutter buildconforme ela sai, não no fim: um comando que só fala ao terminar não se distingue de um travado. Osign --notarizefaz o mesmo com onotarytool submit --wait. -
newlocaliza as quatro coisas que o scaffold aponta — barril,weave_di, template do app e template do bridge — antes de escrever qualquer byte, e recusa junto: uma corrida lista tudo o que falta com a sua saída, em vez de mostrar uma falta por corrida. Com uma só, a mensagem é a de sempre. -
doctorganha a seçãobinary: compara odovetailque responde no PATH com o processo atual, por commit e por conjunto de comandos, e nomeia os comandos que o instalado não tem — o sinal que antes só aparecia na hora de usar um comando que não existia. -
doctor: sonda se a ferramenta funciona, não se está no PATH, e reporta se o alvo Rust do par SO/arquitetura está instalado. -
icon: de um PNG saem.ico,.icnse os PNGs do tema do Linux. -
bundle: nsis, msi, dmg, deb, rpm. Nunca assina. -
sign: codesign com notarização, ou Authenticode. Nunca empacota. -
release: assina cada artefato e escreve o manifesto com o conteúdo da assinatura, não com um caminho. -
inspect: lê o artefato e recusa quando a arquitetura no nome discorda do conteúdo. -
manifest: o manifesto sozinho, quando a assinatura já existe. -
keygen: gera o par minisign e recusa sobrescrever — a recuperação de uma privada perdida é reinstalar em todas as máquinas. -
probe: pergunta a um endpoint, de fora e pela rede, se ele serve o que o cliente lê, com o mesmo parser e o mesmo verificador que o app carrega. -
shipno Windows assina o payload antes de empacotar e o instalador depois: assinar só o instalador passa pelo SmartScreen e deposita executáveis sem assinatura no disco de quem instalou. -
bundlerecusa AppImage quando a config declara serviço — o formato não instala nada, então não há unit, não há helper e não há kill switch.