feat: готовый бренд ZoomStage и минимальный доменный подвал (refs #12)
deploy-preland / deploy zoomstage.online (push) Successful in 44s
deploy-preland / deploy zoomstage.online (push) Successful in 44s
This commit is contained in:
1 parent
a2c3197a16
commit
ec087ea636
24 files changed
+365
-237
No files matched your search
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
$root = dirname(__DIR__);
|
||||
foreach (['light', 'dark'] as $theme) foreach ([
|
||||
'zoomstage.online' => 'zoomstage.online',
|
||||
'New-Preland.example:8080' => 'new-preland.example',
|
||||
'www.example.test.' => 'www.example.test',
|
||||
'<script>alert(1)</script>' => 'fixture.local',
|
||||
] as $host => $expected) {
|
||||
$html = shell_exec(PHP_BINARY . ' ' . escapeshellarg(__DIR__ . '/render-preland.php') . ' ' . escapeshellarg($host) . ' ' . escapeshellarg($theme));
|
||||
if (!preg_match('~<footer\b.*?</footer>~s', $html, $match)) throw new RuntimeException('Подвал не найден');
|
||||
$footer = $match[0];
|
||||
foreach (['Наши реквизиты', 'payment.svg', 'ИНН:', 'ОГРНИП:', 'ИП Иванов', 'ИП Кардаполова'] as $forbidden) {
|
||||
if (str_contains($footer, $forbidden)) throw new RuntimeException('В подвале остался ' . $forbidden);
|
||||
}
|
||||
if (!str_contains($footer, date('Y') . '© ' . $expected)) throw new RuntimeException('Неправильный домен копирайта: ' . $host);
|
||||
if (substr_count($footer, '<img ') !== 2) throw new RuntimeException('Нужны только два тематических SVG логотипа');
|
||||
if (str_contains($footer, '<script>')) throw new RuntimeException('Host попал в HTML без проверки');
|
||||
}
|
||||
echo "OK: минимальный подвал, две темы, перенос домена/порт/trailing-dot и invalid-host\n";
|
||||
@@ -2,6 +2,9 @@
|
||||
/** preland#11: старый бренд запрещён независимо от обновления manifest. */
|
||||
declare(strict_types=1);
|
||||
$root = dirname(__DIR__);
|
||||
require_once $root . '/components/asset-url.php';
|
||||
$assetsUriPrefix = 'https://zoomzilla.ru';
|
||||
if (!str_starts_with(prelandAssetUrl('docs/logo.svg'), '/docs/logo.svg?v=')) throw new RuntimeException('Доменный логотип не должен браться с общего assets/API хоста');
|
||||
$oldBlob = '91267982e1ad373b6ebd5fb2b761b34fe2a0c2a5';
|
||||
foreach (['docs/logo.svg', 'docs/logo-alt.svg'] as $file) {
|
||||
$body = str_replace("\r\n", "\n", file_get_contents($root . '/' . $file));
|
||||
@@ -13,11 +16,11 @@ foreach (['docs/logo.svg', 'docs/logo-alt.svg'] as $file) {
|
||||
require $root . '/bin/release-assets.php';
|
||||
validateReleaseAssets($root);
|
||||
$files = releaseFiles($root);
|
||||
foreach (['docs/logo.svg', 'docs/logo-alt.svg', 'docs/privacy_policy.pdf', 'template.php'] as $file) {
|
||||
foreach (['docs/privacy_policy.pdf', 'template.php'] as $file) {
|
||||
if (!in_array($file, $files, true)) throw new RuntimeException('Не включён ' . $file);
|
||||
}
|
||||
foreach ($files as $file) {
|
||||
if (preg_match('~^(tests/|bin/|release/|docs/superpowers/|config\.json$|[0-9]+/)~', $file)) {
|
||||
if (preg_match('~^(tests/|bin/|release/|docs/superpowers/|config\.json$|[0-9]+/|docs/logo(?:-alt)?\.svg$)~', $file)) {
|
||||
throw new RuntimeException('В кодовую поставку попал защищённый/служебный файл ' . $file);
|
||||
}
|
||||
}
|
||||
@@ -32,6 +35,17 @@ $copy = static function (string $relative) use ($root, $fixture): void {
|
||||
$manifest = json_decode(file_get_contents($root . '/release/assets.json'), true, 512, JSON_THROW_ON_ERROR);
|
||||
foreach (array_keys($manifest['files']) as $file) $copy($file);
|
||||
$copy('release/assets.json');
|
||||
$copy('release/brands.json');
|
||||
$brands = json_decode(file_get_contents($root . '/release/brands.json'), true, 512, JSON_THROW_ON_ERROR);
|
||||
foreach ($brands as $brand) foreach ($brand['logos'] as $record) $copy($record['file']);
|
||||
$brand = prelandBrand($root, 'zoomstage.online');
|
||||
$new = array_map(static fn(array $record): string => $record['sha256'], $brand['logos']);
|
||||
$old = $brand['replace_from'];
|
||||
foreach ([[$old, 10], [$new, 0], [['docs/logo.svg' => $new['docs/logo.svg'], 'docs/logo-alt.svg' => $old['docs/logo-alt.svg']], 10], [['docs/logo.svg' => $old['docs/logo.svg'], 'docs/logo-alt.svg' => $new['docs/logo-alt.svg']], 10], [['docs/logo.svg' => 'unknown', 'docs/logo-alt.svg' => $new['docs/logo-alt.svg']], 1]] as [$actual, $state]) {
|
||||
if (prelandBrandState($actual, $brand) !== $state) throw new RuntimeException('Миграция бренда не восстанавливается после частичного put');
|
||||
}
|
||||
try { prelandBrand($root, 'unregistered.example'); throw new LogicException('Неизвестная цель принята'); }
|
||||
catch (RuntimeException $expected) {}
|
||||
try {
|
||||
validateReleaseAssets($fixture);
|
||||
$mustFail = static function (callable $mutate) use ($fixture): void {
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
<?php
|
||||
/** preland#11: разрешено менять только согласованный ключ requisites. */
|
||||
declare(strict_types=1);
|
||||
$root = dirname(__DIR__);
|
||||
$expected = json_decode(file_get_contents($root . '/release/requisites.json'), true, 512, JSON_THROW_ON_ERROR);
|
||||
$saved = json_decode(file_get_contents($root . '/config.json'), true, 512, JSON_THROW_ON_ERROR);
|
||||
if (($saved['requisites'] ?? null) !== $expected) {
|
||||
fwrite(STDERR, "FAILED: Git config содержит старые реквизиты\n"); exit(1);
|
||||
}
|
||||
$raw = "{\n \"host\" : \"custom.example\", \"nested\":{\"requisites\":[\"do not change\"]},\n \"requisites\": [\"old\",\"escaped \\\"quote\"], \"utm\" : {\"focus\":\"custom\"}, \"unknown\":\"\\u0041\"\n}\n";
|
||||
$path = tempnam(sys_get_temp_dir(), 'requs-');
|
||||
try {
|
||||
foreach ([$raw, '{"host":"other.example","unknown":{"a":1}}'] as $fixture) {
|
||||
file_put_contents($path, $fixture);
|
||||
exec(PHP_BINARY . ' ' . escapeshellarg($root . '/bin/ensure-requisites-config.php') . ' ' . escapeshellarg($path), $output, $code);
|
||||
if ($code !== 10) throw new RuntimeException('Ожидалась точечная замена requisites');
|
||||
$after = file_get_contents($path);
|
||||
$beforeData = json_decode($fixture, true, 512, JSON_THROW_ON_ERROR);
|
||||
$afterData = json_decode($after, true, 512, JSON_THROW_ON_ERROR);
|
||||
if ($afterData['requisites'] !== $expected) throw new RuntimeException('Реквизиты не заменены');
|
||||
unset($beforeData['requisites'], $afterData['requisites']);
|
||||
if ($beforeData !== $afterData) throw new RuntimeException('Другие значения изменены');
|
||||
if (isset($afterData['nested']) && !str_contains($after, '"unknown":"\\u0041"')) throw new RuntimeException('Изменены байты чужих полей');
|
||||
exec(PHP_BINARY . ' ' . escapeshellarg($root . '/bin/ensure-requisites-config.php') . ' ' . escapeshellarg($path), $output, $code);
|
||||
if ($code !== 0 || file_get_contents($path) !== $after) throw new RuntimeException('Повторная операция не идемпотентна');
|
||||
}
|
||||
} finally { unlink($path); }
|
||||
echo "OK: requisites-only, чужие поля/байты сохранены, повторный запуск без изменений\n";
|
||||
@@ -0,0 +1,6 @@
|
||||
<?php
|
||||
if (PHP_SAPI !== 'cli') { http_response_code(404); exit; }
|
||||
$_SERVER['HTTP_HOST'] = $argv[1] ?? 'zoomstage.online';
|
||||
$_SERVER['SERVER_NAME'] = 'fixture.local';
|
||||
$_GET = ['theme' => $argv[2] ?? 'light', 'indpic' => '1'];
|
||||
require dirname(__DIR__) . '/index.php';
|
||||
Reference in new issue
Block a user