Vendor platform-tools: everything this site runs is now in one repo
shared/platform-tools was a git submodule. On this branch it is a plain
directory of tracked files, frozen at ea3616abae30106df283e73a3e2f9e5bb0e6ae90
— the same commit fotografia.andmore.pt pins. Contents are byte-identical to
that tree, verified blob by blob.
The submodule caused every deployment failure this site has had:
- .gitmodules named no `branch`, so the update fell back to
refs/remotes/origin/HEAD — a ref a plain server-side clone never writes.
"Atualizar submódulo" failed with "Unable to find refs/remotes/origin/HEAD
revision in submodule path".
- Its URL was http://, which the origin answers with 523.
- This install is deployed by uploading files, and a submodule is invisible
to that. The tree moved forward while shared/platform-tools stayed on the
old pin, config/functions_secrets.php died on a missing compat_secrets.php,
and the whole site — public pages included — went down.
Each of those was separately fixable. Together they mean a second repository
nobody can see from the deployment path, gating whether the first one boots. A
plain directory deploys like every other file and cannot fall out of step.
The cost is that platform-tools updates no longer arrive by bumping a pin; they
are a copy-over-and-commit. shared/platform-tools/VENDORED.md records the
upstream SHA, the procedure, and which four files differ from upstream in line
endings only (social/*.js, .css, functions_publications.php — CRLF upstream,
normalised to LF here; content otherwise identical).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
0e034232e9
commit
4d40adbf64
67 files changed
+17478
-5
No files matched your search
@@ -0,0 +1,141 @@
|
||||
<?php
|
||||
// Ações remotas do hub sobre esta plataforma: cópias de segurança, atualização
|
||||
// e migrações. Chave DIFERENTE da do api/health.php, e POST — o que muda alguma
|
||||
// coisa não entra por GET, para não ser disparado por um link ou um prefetch.
|
||||
//
|
||||
// POST JSON: {"action": "...", ...}
|
||||
//
|
||||
// **A atualização e as cópias são independentes de propósito.** Num alojamento
|
||||
// cPanel o `git pull` falha ou nem existe (sem shell, sem git, sem permissões
|
||||
// de escrita no tree) — e isso não pode impedir o hub de mandar fazer uma cópia
|
||||
// da base de dados, que é a operação que realmente protege o cliente. Por isso
|
||||
// `git.pull` devolve `ok:false` com a explicação em vez de rebentar, e as ações
|
||||
// de cópia não passam por git nenhum.
|
||||
//
|
||||
// O stub de cada site (public/api/hub_ops.php) é uma linha a incluir isto.
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../hub_bootstrap.php';
|
||||
|
||||
hub_ops_verify_or_die();
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
hub_json(['ok' => false, 'error' => 'usa POST'], 405);
|
||||
}
|
||||
|
||||
$raw = (string) file_get_contents('php://input');
|
||||
$req = json_decode($raw, true);
|
||||
$action = is_array($req) ? (string) ($req['action'] ?? '') : (string) ($_POST['action'] ?? '');
|
||||
|
||||
$root = $repo_root;
|
||||
|
||||
// Numa montra estática não há esquema a migrar nem base de dados a despejar
|
||||
// (ver a nota do hub_bootstrap.php). Recusa-se aqui, com a razão escrita, em
|
||||
// vez de deixar as ações chamarem métodos sobre um $pdo a null — um erro de PHP
|
||||
// apareceria no hub como plataforma avariada, que é uma coisa diferente.
|
||||
if (!$pdo instanceof PDO
|
||||
&& in_array($action, ['migrations.list', 'migrations.apply_pending'], true)) {
|
||||
hub_json(['ok' => false, 'error' => 'plataforma sem base de dados — não há migrações a aplicar'], 400);
|
||||
}
|
||||
|
||||
switch ($action) {
|
||||
|
||||
// ── Cópias de segurança ──────────────────────────────────────────────────
|
||||
case 'backup.list':
|
||||
hub_load_backup_helpers();
|
||||
hub_json(['ok' => true, 'backups' => function_exists('backup_list') ? backup_list() : [],
|
||||
'summary' => hub_backup_summary()]);
|
||||
|
||||
case 'backup.trigger':
|
||||
$type = is_array($req) ? (string) ($req['type'] ?? 'db') : 'db';
|
||||
if (!in_array($type, ['db', 'files'], true)) {
|
||||
hub_json(['ok' => false, 'error' => 'tipo inválido (db|files)'], 400);
|
||||
}
|
||||
hub_load_backup_helpers();
|
||||
if (!function_exists('backup_trigger')) {
|
||||
hub_json(['ok' => false, 'error' => 'backups não disponíveis nesta plataforma'], 503);
|
||||
}
|
||||
$ok = backup_trigger($type);
|
||||
// O backup_trigger() é «dispara e esquece»: o ficheiro pode ainda estar
|
||||
// a ser escrito quando isto responde. O resumo devolvido é o de ANTES,
|
||||
// e é o hub que volta a perguntar — dizer o contrário seria mentir
|
||||
// sobre uma cópia que ainda não existe.
|
||||
hub_json([
|
||||
'ok' => $ok,
|
||||
'type' => $type,
|
||||
'started' => $ok,
|
||||
'note' => $ok ? 'cópia iniciada; volta a consultar daqui a instantes' : 'não foi possível iniciar a cópia',
|
||||
'summary' => hub_backup_summary(),
|
||||
], $ok ? 200 : 500);
|
||||
|
||||
// ── Atualização ──────────────────────────────────────────────────────────
|
||||
case 'git.status':
|
||||
case 'git.pull':
|
||||
require_once __DIR__ . '/../functions_git.php';
|
||||
if (!function_exists('git_run') || !is_dir($root . '/.git')) {
|
||||
hub_json(['ok' => false, 'error' => 'esta instalação não é um repositório git (típico em cPanel) — as cópias continuam a funcionar'], 503);
|
||||
}
|
||||
|
||||
if ($action === 'git.status') {
|
||||
$branch = git_run(['rev-parse', '--abbrev-ref', 'HEAD'], $root);
|
||||
$head = git_run(['log', '-1', '--format=%h|%cd|%s', '--date=short'], $root);
|
||||
$dirty = git_run(['status', '--porcelain'], $root);
|
||||
hub_json(['ok' => true,
|
||||
'branch' => $branch['ok'] ? trim($branch['output']) : null,
|
||||
'head' => $head['ok'] ? trim($head['output']) : null,
|
||||
'dirty' => $dirty['ok'] ? (trim($dirty['output']) !== '') : null,
|
||||
]);
|
||||
}
|
||||
|
||||
$pull = git_run(['pull', '--ff-only'], $root);
|
||||
$output = $pull['output'] !== '' ? $pull['output'] : '(sem saída)';
|
||||
if ($pull['ok']) {
|
||||
// O ff-only recusa-se a fundir; um repositório divergente falha aqui
|
||||
// em vez de inventar um merge que ninguém pediu.
|
||||
$sub = git_run(['submodule', 'update', '--init', '--recursive'], $root);
|
||||
if (trim($sub['output']) !== '') $output .= "\n\n[submodules]\n" . $sub['output'];
|
||||
if (function_exists('opcache_reset')) @opcache_reset();
|
||||
if (function_exists('settings_cache_clear')) settings_cache_clear();
|
||||
if (function_exists('platform_settings_cache_clear')) platform_settings_cache_clear();
|
||||
$output .= "\n\n(OPcache + cache de definições invalidados)";
|
||||
}
|
||||
hub_json(['ok' => $pull['ok'], 'output' => $output]);
|
||||
|
||||
// ── Migrações ────────────────────────────────────────────────────────────
|
||||
// Um pull sem as migrações aplicadas deixa a app a correr contra um esquema
|
||||
// antigo, portanto o hub tem de conseguir fazer as duas coisas.
|
||||
//
|
||||
// Numa plataforma estática não há esquema nenhum a migrar (ver a nota do
|
||||
// hub_bootstrap.php). Diz-se assim, em vez de rebentar com um erro de PHP a
|
||||
// chamar métodos sobre null — que o hub mostraria como app avariada.
|
||||
case 'migrations.list':
|
||||
hub_load_migration_helpers();
|
||||
if (!function_exists('migrations_status')) hub_json(['ok' => false, 'error' => 'runner indisponível'], 503);
|
||||
hub_json(['ok' => true, 'migrations' => migrations_status($pdo)]);
|
||||
|
||||
case 'migrations.apply_pending':
|
||||
hub_load_migration_helpers();
|
||||
if (!function_exists('migrations_status') || !function_exists('migrations_apply_file')) {
|
||||
hub_json(['ok' => false, 'error' => 'runner indisponível'], 503);
|
||||
}
|
||||
$applied = [];
|
||||
$error = null;
|
||||
foreach (migrations_status($pdo) as $row) {
|
||||
if (!empty($row['applied_at'])) continue;
|
||||
$r = migrations_apply_file($pdo, $row['filename'], null);
|
||||
if (!empty($r['ok'])) {
|
||||
$applied[] = $row['filename'];
|
||||
} else {
|
||||
// Pára à primeira falha: a seguinte pode depender desta, e
|
||||
// continuar a aplicar às cegas é como um esquema fica a meio.
|
||||
$error = $row['filename'] . ': ' . ($r['error'] ?? 'erro');
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($applied && function_exists('settings_cache_clear')) settings_cache_clear();
|
||||
hub_json(['ok' => $error === null, 'applied' => $applied, 'error' => $error]);
|
||||
|
||||
default:
|
||||
hub_json(['ok' => false, 'error' => 'ação desconhecida: ' . $action,
|
||||
'actions' => hub_ops_actions()], 400);
|
||||
}
|
||||
Reference in new issue
Block a user