Pular para o conteúdo

Atualizando um projeto que já nasceu do kit

O kit é um ponto de partida, não uma dependência. Depois do create-project o projeto é seu: você renomeia painéis, muda canAccessPanel(), edita seeders. Por isso não existe um kit:update que sobrescreve arquivos — ele reescreveria justamente o que você personalizou, e um starter kit que estraga o projeto do usuário não serve para nada.

O que muda separa-se em três camadas, e cada uma tem um caminho próprio:

Camada O que é Como atualizar
Dependências Filament, plugins, Laravel composer update — é a maior parte das melhorias e chega sozinha
Cola do kit providers, traits, widgets, views de erro diff manual contra a tag nova (abaixo)
Seu negócio tudo que você escreveu nunca é tocado

O jeito fácil: php artisan kit:update

O comando automatiza a etapa do git inteira e não aplica nada sem sua aprovação:

Janela do terminal
php artisan kit:update --dry-run # só mostra o que mudou
php artisan kit:update # revisa e aplica, arquivo a arquivo

O que ele faz, em ordem:

  1. Confere o terreno — exige repositório git com a árvore limpa. Sem isso não haveria como reverter, e ele recusa rodar (mostrando os comandos para versionar o projeto).

  2. Vincula o kit temporariamente — adiciona o remote kit com push bloqueado e busca as tags num namespace próprio (kit-v*), para não colidirem com as versões do seu projeto.

  3. Compara — da versão em config('kit.version') até a tag escolhida, restrito aos caminhos que pertencem ao kit. Seu código de negócio nunca entra na conta.

  4. Oferece um branch temporário (kit-update/v0.16.0) para não sujar o seu.

  5. Pergunta arquivo a arquivo — ver o diff, aplicar, pular ou parar. Dá para mudar de ideia no meio e aplicar o resto em lote. Arquivo removido do kit nunca é apagado automaticamente: ele só avisa.

  6. Desfaz o vínculo — remove o remote e as tags kit-* ao sair, mesmo se você interromper no meio. O projeto não fica com nada de terceiros pendurado.

  7. Marca a versão aplicada em config/kit.php — só aquela linha, sem tocar no resto do arquivo. É o ponto de partida da próxima comparação.

O fluxo é não interativo quando não há terminal (CI, --no-interaction) — nunca “sem TTY”: ele vira relatório e sai sem aplicar nada, a menos que --all ou --only-new já tenham dado a aprovação na linha de comando (app/Console/Commands/KitUpdate.php:isInteractive:457).

flowchart TD
%% DG-16
accTitle: Fluxo do kit:update
accDescr: O kit:update confere o terreno, vincula o kit como remote temporário, calcula o diff restrito aos caminhos do kit e mostra o resumo; sem terminal ou sem --all/--only-new ele sai sem aplicar; aplicando, revisa arquivo a arquivo e marca a versão; o remote é sempre desfeito no fim, inclusive quando algo falha.
  pre_voo{"Pré-voo: git limpo?"} -->|"não"| falha["Falha: recusa rodar"]
  pre_voo -->|"sim"| remote_kit["Remote temporário do kit (kit-v*)"]
  remote_kit --> diff_filtrado["Diff filtrado por CAMINHOS_DO_KIT"]
  diff_filtrado -->|"nada mudou"| nada_a_atualizar["Nada a atualizar"]
  diff_filtrado -->|"há mudanças"| resumo["Resumo do que mudou"]
  resumo -->|"--dry-run"| saida_relatorio["Sai: só relatório"]
  resumo --> nao_interativo{"Não interativo, sem --all nem --only-new?"}
  nao_interativo -->|"sim"| saida_sem_aprovacao["Sai sem aplicar"]
  nao_interativo -->|"não"| branch_update["Branch temporário kit-update/<tag>"]
  branch_update --> revisar["Revisa e aplica, arquivo a arquivo"]
  revisar --> so_relatorio["composer.json: só relatório"]
  so_relatorio --> marcar_versao["marcarVersao() em config/kit.php"]
  subgraph sempre ["finally — sempre roda"]
    encerramento["Desfaz o remote e as tags kit-*"]
  end
  nada_a_atualizar -.-> encerramento
  saida_relatorio -.-> encerramento
  saida_sem_aprovacao -.-> encerramento
  marcar_versao -.-> encerramento

A ordem vem direto de KitUpdate::handle() (app/Console/Commands/KitUpdate.php:handle:405): pré-voo (:preVoo:486), remote temporário (:vincularKit:545), diff restrito (:arquivosAlterados:651), resumo (:mostrarResumo:827), a checagem de terminal (:isInteractive:449), o branch temporário (:prepararBranch:847), a revisão por arquivo (:revisarEAplicar:896), o relatório do composer.json (:relatarComposerJson:1081, :CAMINHOS_SO_RELATORIO:388) e marcarVersao() (:marcarVersao:1190, chamada dentro de :encerrar:1115). O finally que desfaz o remote roda em todo caminho de saída, inclusive erro (:desvincularKit:554).

Dois detalhes que aparecem na prática:

  • config/kit.php sempre consta como “modificado” (ele carrega a marca de versão). Aplicá-lo traz as chaves novas do kit, mas substitui o arquivo inteiro — se você mudou credenciais do seeder ou adicionou chaves próprias ali, veja o diff e copie só o que interessa em vez de aplicar.
  • O próprio kit:update se atualiza. Como o PHP já carregou a classe em memória, o comportamento novo (e as mensagens novas) só valem a partir da execução seguinte. O comando avisa quando isso acontece. A lista de caminhos que filtra o diff é lida da versão destino (a partir da v0.30.1), então diretório que só a versão nova cobre chega na mesma rodada — o aviso “rode o comando de novo” só aparece quando essa leitura falhou. Instalação anterior à v0.30.1 ainda roda a lista antiga na primeira rodada: rode a segunda com o comando que o aviso imprime. O caso conhecido é v0.22.x → v0.23.0 ou posterior, que deixava View [svg.arte-do-login] not found entre as duas rodadas; a segunda rodada resolve, ou copie resources/views/svg/arte-do-login.blade.php do repositório do kit.

Dependência nova do kit: o composer.json nunca é aplicado

O kit:update não sobrescreve o seu composer.json — ele carrega as dependências do SEU projeto, e aplicá-lo apagaria tudo que você instalou depois do kit. Em vez disso, o comando relata o que mudou ali (pacote novo, script novo) e você copia à mão:

Janela do terminal
git diff kit-v0.35.0 kit-v0.36.0 -- composer.json
composer update
php artisan filament:assets # obrigatório quando o pacote novo publica CSS/JS

O relatório só aparece quando o comando sabe de onde você partiu. Ele lê config('kit.version'); se essa marca não casar com uma tag do kit, passe --from=vX.Y.Z.

Na v0.36.0 isso vale para mortalkiller/filament-page-header, que traz o cabeçalho rico das telas de registro e publica CSS e JS próprios. Sem o composer require + filament:assets, as telas de View/Edit de usuário e organização respondem normalmente, só que sem o cabeçalho.

Na v0.48.0 isso vale para hoceineel/filament-quick-action-dock, o pacote do dock de atalhos:

Janela do terminal
composer require hoceineel/filament-quick-action-dock:^0.2
php artisan filament:assets
php artisan migrate --force

Quem atualiza para a v0.48.0 precisa rodar o composer require antes de qualquer outro comando: nessa versão o código do dock exige o pacote, e sem ele todo php artisan falha com Class "HoceineEl\QuickActionDock\QuickActionDockPlugin" not found. A partir da versão seguinte o projeto sobe sem o pacote, só sem o dock, e a seção “Dock de atalhos” da aba Aplicação, em /admin/configuracoes-da-aplicacao, diz o que falta instalar.

Na v0.49.0 isso vale para sehsah/filament-remember-table e asignua/filament-infinite-scroll, os pacotes das visões salvas e do carregamento contínuo, e para o happenv-com/filament-phpstan-macros (só desenvolvimento):

Janela do terminal
composer require sehsah/filament-remember-table:^1.0 asignua/filament-infinite-scroll:^1.0
composer require --dev happenv-com/filament-phpstan-macros
php artisan filament:assets
php artisan migrate --force
php artisan db:seed --class=Database\Seeders\ShieldPermissionsSeeder
php artisan db:seed --class=Database\Seeders\PapeisSeeder

Quem atualiza para a v0.49.0 roda o composer require antes de qualquer outro comando: as dez listagens do kit usam o trait do remember-table, e sem ele todo php artisan falha com Trait "Sehsah\FilamentRememberTable\Concerns\HasRememberTable" not found. Os dois recursos nascem desligados; ligam-se na aba Tabelas de /admin/configuracoes-da-aplicacao.

Na v0.50.0 não há nada a instalar nem migrar: a mudança é só na tela de configurações (seções colapsáveis, abas carregadas ao abrir, aba “Kit” renomeada “Aplicação” — ver Configurações do kit). Quem customizou a Page revisa o diff que o kit:update mostra: os campos passaram para dentro de secao() e as abas para dentro de aba(). As abas ganharam chave fixa na URL (?tab=identidade, ?tab=tabelas…); link ou favorito antigo com ?tab=…::data::tab passa a abrir na primeira aba.

Na v0.52.0 há uma migration de settings e o tutorial guiado chega desligado. Rode php artisan migrate depois do kit:update: a nova migration de settings acrescenta onboarding_habilitado, com o valor de KIT_ONBOARDING (false por padrão). O pacote wallacemartinss/filament-onboarding já era dependência do kit. Para usar, rode php artisan db:seed --class=OnboardingSeeder e ligue Usar o tutorial guiado na aba Aplicação de /admin/configuracoes-da-aplicacao. Semear não liga o interruptor. Ver Tutorial guiado. O config/logging.php é seu e o kit:update não o entrega: o canal de log onboarding é declarado pelo kit, no molde do seu canal ai, quando o seu arquivo não o tem. As views de resources/views/vendor/filament-onboarding passam a ser do kit (a página “Primeiros passos” foi corrigida): quem as customizou revisa o diff.

Na v0.51.2 não há nada a instalar nem migrar: o botão do dock deixa de descer para fora da tela no desktop. O kit:update traz public/js/kit/quick-action-dock.js, uma cópia corrigida do script do pacote do dock servida no lugar do original — não edite esse arquivo: depois de um composer update do hoceineel/filament-quick-action-dock, rode a suíte do kit, que avisa se a cópia ficou defasada.

Na v0.51.1 não há nada a instalar nem migrar: o desbloqueio da sessão passa a voltar à página em que a pessoa estava (ver Autenticação). Quem customizou a TelaBloqueio ou registrou o próprio middleware de bloqueio revisa o diff que o kit:update mostra: o AppServiceProvider ganha o bind do Locker do pacote para App\Http\Middleware\BloquearSessaoComRetorno.

Na v0.51.0 há uma migration de settings e o chat do Assistente nasce desligado. Rode php artisan migrate depois do kit:update: ela semeia o interruptor Usar agentes de IA e a lista Painéis onde o chat do Assistente aparece a partir de KIT_AGENTES_IA e KIT_AGENTES_IA_PAINEIS — desligado e vazia por padrão. Quem quer o catálogo /admin/agentes-ia e o chat do /app como antes liga os dois na aba Aplicação de /admin/configuracoes-da-aplicacao (ou põe KIT_AGENTES_IA=true e KIT_AGENTES_IA_PAINEIS=app no .env antes de rodar a migration). Enquanto o uso está desligado, o catálogo some do menu e responde 404, o grupo de widgets Agentes de IA some do dashboard do /admin e o chat não aparece em painel nenhum; nada é apagado. Ver Configurações do kit.

Tela nova: ressemeie os dois seeders

Os “próximos passos” que o comando imprime citam filament:assets e os testes, não os seeders — e tela nova do kit costuma trazer permissão nova, que nasce sem dono no seu banco:

Janela do terminal
php artisan db:seed --class=Database\Seeders\ShieldPermissionsSeeder
php artisan db:seed --class=Database\Seeders\PapeisSeeder

Os dois são idempotentes — rodar de novo não duplica nada.

Na v0.36.0, especificamente, eles são no-op — e vale saber por quê, para não procurar defeito onde não há. A tela ViewUser que essa versão traz consome a permissão View:User, e essa permissão já era gerada e distribuía desde sempre: view está em config('filament-shield.policies.methods'), então o ShieldPermissionsSeeder sempre a criou e o PapeisSeeder sempre a entregou aos papéis. Entre a v0.35.0 e a v0.36.0 nem os seeders nem o config/filament-shield.php mudaram uma linha (git diff v0.35.0 v0.36.0 -- database/seeders config/filament-shield.php volta vazio). O que faltava era a tela, não a permissão: o checkbox em /admin/shield/roles existia e não decidia nada.

Rode os dois assim mesmo. O hábito custa dois comandos idempotentes e paga na versão que trouxer um Resource ou uma Page de fato novos — aí a permissão nasce mesmo sem dono no seu banco, e o sintoma é uma tela que ninguém enxerga.

Ao final nada está commitado: você revisa com git diff, roda php artisan migrate se chegou migration nova (a partir da v0.31.0 o comando entrega também database/settings/, e a tela de configurações quebra enquanto a propriedade nova não tiver linha no banco), roda composer test:kit (a fundação) e commita. Deu errado? git checkout -- . desfaz, ou apague o branch e volte para o seu.

  • A URL da tela de configurações mudou na v0.32.0. Ela passou a responder em /admin/configuracoes-da-aplicacao, o nome que já exibia no menu. O slug antigo responde 301 para o novo, então favorito e link antigo continuam chegando. Se o seu projeto escreveu o endereço antigo à mão em algum lugar — um teste, um link numa view sua, um bookmarklet —, atualize; a permissão (View:ConfiguracoesDoKit) e a classe não mudaram, só o slug. Não precisa aprovar 30 arquivos um a um. Durante a revisão, o menu oferece “Aplicar todos os arquivos NOVOS daqui em diante” e “Aplicar TUDO daqui em diante” — uma confirmação vale para o conjunto. E dá para começar já em lote:
Janela do terminal
php artisan kit:update --only-new # só o que ainda não existe no projeto
php artisan kit:update --all # tudo, inclusive o que sobrescreve

A distinção é o ponto: arquivo novo não tem o que sobrescrever, então aplicá-los em massa é seguro — é o caso dos widgets, do Spotlight, das concerns, do CSS do kit (resources/css/filament/ e public/css/kit/, entregues a partir da v0.30.0) e das migrations de Settings (database/settings/, a partir da v0.31.0). O .env.example também passou a ser entregue (a partir da v0.32.2): ele é arquivo de sugestão, é onde o kit documenta cada chave nova, e o seu .env continua intocado — mas ele entra como modificado, então revise o diff se você acrescentou chaves próprias ali. Já um modificado substitui o conteúdo atual, e se você editou aquele arquivo a sua versão se perde (recuperável com git checkout -- <arquivo>, já que nada é commitado). Por isso --only-new é o lote recomendado para a primeira passada, deixando os modificados para revisar com calma.

Opção Para quê
--only-new aplica de uma vez só os arquivos novos (não sobrescreve nada)
--all aplica tudo de uma vez, com uma confirmação para o conjunto
--dry-run só o relatório, não altera nada
--tag=v0.16.0 comparar com uma versão específica
--from=v0.15.0 dizer de qual versão o projeto partiu (quando config/kit.php não sabe)
--branch=nome escolher o nome do branch temporário
--no-branch aplicar no branch atual
--keep-remote manter o remote e as tags do kit ao final
--repo=URL comparar com outro repositório do kit (um fork, por exemplo); o padrão é config('kit.repository'), que lê KIT_REPOSITORY do .env

Sem terminal (CI, --no-interaction) o comando vira relatório e não altera nada — a menos que você passe --only-new ou --all, que são a aprovação, dada na linha de comando.

Atualizar pedindo a um agente. Toda versão do kit, a partir da v0.52.0, tem um roteiro de atualização que um agente segue sozinho, usando o kit:update como ferramenta e perguntando a você antes de cada decisão que é sua. O link segue o padrão https://raw.githubusercontent.com/gsferro/filament-starter-kit-easy/v{versão}/wikis/atualizacoes/v{versão}.md, com a versão destino e o v da tag nas duas posições (a v0.52.0 é a exceção: o link dela aponta para a main). O agente lê também o mesmo arquivo na main e prefere o da main quando ele trouxer a linha Roteiro revisado em, a marca de uma correção publicada depois da tag; na dúvida, ele para e pergunta. O pedido-modelo, num agente em sessão nova: “Atualize este projeto seguindo o roteiro em” seguido do link. A seção “Atualizar o projeto pelo agente” de wikis/agentes-e-skills.md traz o mesmo padrão e a cópia do roteiro da versão instalada, a partir de projetos na v0.53.0: os que estão na v0.52.0 ou antes ainda não têm a cópia, e o pedido precisa trazer o link.

As duas rotas de entrega

O kit chega ao seu projeto de dois jeitos, e cada um entrega um recorte diferente da árvore. O composer create-project traz tudo que o .gitattributes não exclui (.gitattributes:/docs export-ignore:40, .gitattributes:/site export-ignore:46, .gitattributes:/wikis/specs export-ignore:32, .gitattributes:/.github export-ignore:20); o kit:update traz só os caminhos de CAMINHOS_DO_KIT (app/Console/Commands/KitUpdate.php:CAMINHOS_DO_KIT:93), com o composer.json como exceção — ele viaja no create-project, mas no kit:update é só relatório, nunca aplicado (a seção acima, “Dependência nova do kit”).

Caminho que entrou em CAMINHOS_DO_KIT depois da sua versão — como as pastas autorais de resources/views/vendor — é comparado com a sua árvore, e não só tag com tag (app/Console/Commands/KitUpdate.php:caminhosNovosNaLista:702): o arquivo que você não tem aparece como “novo no kit” mesmo que o kit não o tenha mudado desde a sua versão, e arquivo que o seu projeto não tem aparece como “novo no kit” ainda que o kit o tenha mudado entre as tags — é o rótulo que o --only-new aplica. Esse comportamento vale a partir da versão em que ele entrou (a classe que roda é a instalada): na primeira rodada a partir de uma versão anterior, a view que você não tem pode aparecer como “modificado” — aplique pelo modo interativo ou com --all, não com --only-new, e rode de novo.

flowchart LR
%% DG-17
accTitle: As duas rotas de entrega
accDescr: O composer create-project entrega tudo que o .gitattributes não exclui; o kit:update entrega só os caminhos de CAMINHOS_DO_KIT, e composer.json entra só como relatório, nunca aplicado.
  subgraph rota_create ["composer create-project"]
    gitattributes[".gitattributes (exclusão)"]
    fora_create["Fora: .github, CHANGELOG.md, .styleci.yml, wikis/specs, docs, site, site-vitepress"]
    gitattributes --> fora_create
  end
  subgraph rota_update ["kit:update"]
    caminhos_do_kit["CAMINHOS_DO_KIT (inclusão)"]
    so_relatorio["composer.json: só relatório"]
    fora_update["Fora: art, stubs, bootstrap, lang/pt_BR, public/fonts, public/js, resources/js, tests/Feature, tests/Unit, README.md"]
    caminhos_do_kit --> so_relatorio
    caminhos_do_kit --> fora_update
  end

Um diretório listado em parte não é desenhado como entregue inteiro: app/Models no kit:update é só os 7 arquivos do kit, um a um (app/Console/Commands/KitUpdate.php:'app/Models/User.php':118), nunca o model que você acrescentar depois — e config/ é só cinco arquivos nomeados (app/Console/Commands/KitUpdate.php:'config/kit.php':153). E wikis/ não é export-ignore inteiro — só wikis/specs: os documentos de topo de wikis/ viajam no create-project e o kit:update os entrega um a um (app/Console/Commands/KitUpdate.php:'wikis/README.md':322), assim como tests/Kit e tests/Pest.php (app/Console/Commands/KitUpdate.php:'tests/Kit':245, app/Console/Commands/KitUpdate.php:'tests/Pest.php':264) — tests/ não é “intocado pelo kit:update”.

O jeito manual

Se preferir controlar cada passo — ou entender o que o comando faz por baixo:

Adicione o kit como um segundo remote, uma única vez. Seu origin continua sendo o seu projeto; o kit é só uma fonte de leitura:

Janela do terminal
git remote add kit https://github.com/gsferro/filament-starter-kit-easy.git
# o remote do kit é somente-leitura: evita um `git push kit main` acidental
# mandar o SEU projeto para dentro do repositório do kit
git remote set-url --push kit no_push

As tags do kit vão para um namespace próprio (kit-v*). Isso importa: um git fetch kit --tags traria v0.15.0, v0.16.0… para o seu projeto e colidiria com as suas versões depois.

Janela do terminal
git fetch --no-tags kit 'refs/tags/*:refs/tags/kit-*'
git tag -l 'kit-*' # kit-v0.15.0, kit-v0.16.0, ...

Depois, a cada versão, veja o que mudou e traga só o que interessa:

Janela do terminal
# 1. panorama entre a sua versão e a nova
git diff kit-v0.15.0..kit-v0.16.0 --stat
# 2. o diff da "cola" do kit (ignore o que você já reescreveu)
git diff kit-v0.15.0..kit-v0.16.0 -- app/Providers app/Filament/Concerns \
app/Filament/Spotlight app/Traits resources/views/errors config/kit.php
# 3. traga arquivo a arquivo, revisando
git checkout kit-v0.16.0 -- resources/views/errors
git checkout kit-v0.16.0 -- app/Filament/Concerns/BadgeContagemNavegacao.php

Faça isso num branch (git switch -c atualiza-kit) e rode composer test antes do merge. Arquivos que você reescreveu: leia o diff e aplique à mão — é o único caminho seguro.

💡 TODO / rumo do projeto: extrair a “cola” para um pacote Composer próprio (gsferro/kit-core) com os providers, traits, widgets e páginas de infra. Aí a camada do meio vira composer update gsferro/kit-core e o skeleton fica mínimo — só o que é mesmo ponto de partida. É a evolução natural deste kit.