Pular para o conteúdo

Referência de CLI

merql inclui uma CLI para capturar snapshots, calcular diferenças e executar mesclagens diretamente pelo terminal. Composer a instala como vendor/bin/merql; em um checkout do repositório merql, ela é bin/merql.

vendor/bin/merql funciona a partir de uma instalação de dependência porque merql 0.4.1. Versões anteriores procuravam o autoloader Composer apenas no diretório vendor/ do próprio pacote e falhavam quando merql era instalado como dependência.

Início rápido

Shell
# Capture a baseline from your database.
export MERQL_DB_DSN="sqlite:/path/to/db.sqlite"
vendor/bin/merql snapshot base

# Make some changes, then capture again.
vendor/bin/merql snapshot current

# See what changed.
vendor/bin/merql diff base current

Uma mesclagem de três vias exige que ours e theirs sejam capturados de dois bancos de dados que divergiram da base. Aponte MERQL_DB_DSN para o banco de dados correto para cada snapshot:

Shell
# Base: live before the sandbox was copied from it.
MERQL_DB_DSN="sqlite:/data/live.sqlite" vendor/bin/merql snapshot base

# ... copy live.sqlite to sandbox.sqlite, then both databases change ...

MERQL_DB_DSN="sqlite:/data/sandbox.sqlite" vendor/bin/merql snapshot ours
MERQL_DB_DSN="sqlite:/data/live.sqlite" vendor/bin/merql snapshot theirs

# Three-way merge with dry-run preview.
vendor/bin/merql merge base ours theirs --dry-run

Capturar os três snapshots do mesmo banco de dados, um após o outro, não funciona como mesclagem: theirs já conteria as alterações de ours, então cada linha inserida por ours seria relatada como um conflito insert_insert.

Variáveis de ambiente

O CLI lê os detalhes da conexão do banco de dados a partir de variáveis de ambiente. Você pode conectar usando uma string DSN do PDO ou parâmetros MySQL individuais.

Conexão baseada em DSN

Use MERQL_DB_DSN para conexões baseadas em PDO. Essa é a abordagem recomendada para SQLite e para configurações não padrão. O DSN deve apontar para um banco de dados MySQL ou SQLite: o CLI não tem como registrar um driver para outros mecanismos.

Variável Descrição Exemplo
MERQL_DB_DSN String DSN completa do PDO sqlite:/path/to/db.sqlite
MERQL_DB_USER Nome de usuário (opcional com DSN) root
MERQL_DB_PASS Senha (opcional com DSN) secret
Shell
# SQLite file.
export MERQL_DB_DSN="sqlite:/var/data/myapp.sqlite"

# MySQL via DSN.
export MERQL_DB_DSN="mysql:host=127.0.0.1;dbname=mydb;charset=utf8mb4"
export MERQL_DB_USER="root"
export MERQL_DB_PASS="secret"

Use um banco de dados SQLite baseado em arquivo. Cada comando CLI é executado em seu próprio processo, portanto sqlite::memory: sempre abre um banco de dados novo e vazio, e um snapshot dele não contém tabelas.

Atalho de MySQL

Quando MERQL_DB_DSN não está definido, o CLI recorre às variáveis específicas de MySQL.

Variável Padrão Descrição
MERQL_DB_NAME (obrigatório) Nome do banco de dados
MERQL_DB_HOST 127.0.0.1 Host de MySQL
MERQL_DB_PORT 3306 Porta de MySQL
MERQL_DB_USER root Nome de usuário de MySQL
MERQL_DB_PASS (vazio) Senha de MySQL
MERQL_DB_ADAPTER pdo Defina como mysqli para usar o adaptador mysqli
Shell
export MERQL_DB_NAME="wordpress"
export MERQL_DB_HOST="127.0.0.1"
export MERQL_DB_PORT="3306"
export MERQL_DB_USER="root"
export MERQL_DB_PASS="password"

Comandos

merql snapshot <name>

Capture o estado atual do banco de dados indicado pelas variáveis de ambiente e salve-o como um snapshot nomeado. Este é o único comando que se conecta a um banco de dados.

Shell
vendor/bin/merql snapshot baseline

Saída:

Código
Snapshot 'baseline' saved
Tables: posts, comments, users, settings

Os snapshots são persistidos como arquivos JSON em .merql/snapshots/ no diretório de trabalho (consulte Armazenamento de snapshots). O nome deve conter apenas caracteres alfanuméricos, hífens e sublinhados.

Fluxo de trabalho típico: capture um snapshot antes de fazer alterações e depois capture outro. Os dois snapshots formam a base de uma comparação ou mesclagem.

merql diff <base> <current>

Compare dois snapshots nomeados e exiba um resumo das diferenças.

Shell
vendor/bin/merql diff baseline current

Saída:

Código
Changeset: baseline -> current
  Inserts: 3
  Updates: 7
  Deletes: 1

Os dois snapshots já devem existir (capturados anteriormente com merql snapshot). A comparação não exige conexão com um banco de dados, pois opera em arquivos de snapshot salvos.

merql merge <base> <ours> <theirs>

Faça uma mesclagem de três vias de três snapshots nomeados.

Shell
vendor/bin/merql merge base ours theirs

Saída:

Código
Merge: base + ours + theirs
  Operations: 12
  Conflicts:  0
  Clean:      yes

Os três snapshots já devem existir. A mesclagem não exige conexão com um banco de dados, pois opera em arquivos de snapshot salvos, e nunca grava em um banco de dados: a CLI não tem comando de aplicação. Use o PHP API para aplicar um resultado de mesclagem.

O CLI mescla no nível da coluna. Ele não aplica mesclagem no nível da célula às colunas TEXT ou JSON, portanto duas edições diferentes na mesma coluna sempre geram um conflito aqui (#7). Use ThreeWayMerge com um CellMergeConfig em PHP para mesclagem no nível da célula.

--dry-run

Adicione a opção --dry-run para visualizar as instruções SQL que a mesclagem geraria, sem executar nada. As instruções descrevem como transformar o estado base no resultado mesclado, portanto incluem as alterações dos dois lados.

Shell
vendor/bin/merql merge base ours theirs --dry-run

Saída:

Código
Merge: base + ours + theirs
  Operations: 3
  Conflicts:  0
  Clean:      yes

Dry run SQL:
  INSERT INTO `posts` (`id`, `title`, `status`) VALUES ('10', 'New Post', 'draft');
  UPDATE `posts` SET `title` = 'Updated Title' WHERE `id` = '5';
  DELETE FROM `comments` WHERE `id` = '3';

Comandos de desenvolvimento

Um checkout do repositório merql tem mais scripts em bin/ para sua suíte de testes orientada por oráculo. Eles não fazem parte do pacote Composer, portanto uma instalação de dependência só tem vendor/bin/merql. Execute-os na raiz do repositório depois de composer install.

Comando Descrição
./bin/oracle <name> Calcula o resultado esperado da mesclagem para um cenário
./bin/actual <name> Executa merql em um cenário e captura a saída
./bin/compare <name> Compara a saída do oráculo com a saída real
./bin/test-scenario <name> Fluxo completo: configuração, oráculo, execução, comparação
./bin/test-regression Executa todos os cenários
./bin/test-regression --category clean Executa uma categoria: clean, conflict, identity, types, scale ou edge
./bin/test-regression --fast Somente aprovação/reprovação, sem relatórios
./bin/verify-compliance Relatório completo de conformidade
./bin/verify-all PHPStan + estilo de código + PHPUnit + regressão do oráculo

bin/test-regression também aceita --jobs, mas atualmente ignora o valor e executa os cenários um após o outro (#5). Uma opção --category desconhecida exibe No scenarios found e termina com sucesso.

Consulte Estratégia de testes para ver a estrutura dos cenários.

Código de saída

merql snapshot, diff e merge terminam com status 1 quando falta um argumento, assim como snapshot quando nenhuma conexão está configurada. A falta de um snapshot encerra o comando com uma exceção SnapshotException não capturada e status diferente de zero. Um comando desconhecido exibe Unknown command, mas atualmente termina com status 0 (#3).

Armazenamento de snapshots

Os snapshots são armazenados em .merql/snapshots/ em relação ao diretório de trabalho. Cada snapshot é um único arquivo JSON chamado <name>.json. Execute todos os comandos no mesmo diretório para que encontrem os snapshots uns dos outros.

A CLI não tem opção nem variável de ambiente para outro local (#8). Para usar outro local, mude para um diretório de trabalho diferente antes de executá-la. Em PHP, defina o diretório antes de capturar ou carregar snapshots:

PHP
use Merql\Snapshot\SnapshotStore;

SnapshotStore::setDirectory('/var/data/merql-snapshots');

Os arquivos de snapshot são portáteis. Você pode copiá-los entre máquinas, incluí-los no controle de versão ou compartilhá-los para depuração colaborativa. Um snapshot capturado pela CLI pode ser carregado em PHP com SnapshotStore::load(), e vice-versa, desde que ambos usem o mesmo diretório.