Files
Interview-Notebook/website/builder/verify_updates.py

85 lines
4.4 KiB
Python

"""Exercise repeat builds, article/image updates, and failed-build preservation locally."""
import hashlib
import json
import shutil
import subprocess
import sys
import tempfile
from pathlib import Path
from urllib.parse import unquote
from check import check
BASE = Path(__file__).resolve().parents[1]
SOURCE = BASE / 'upstream'
OUTPUT = BASE / 'dist'
def digest(path):
return hashlib.sha256(path.read_bytes()).hexdigest()
def build(source, output, *extra):
return subprocess.run([sys.executable, str(BASE / 'builder/build.py'),
'--source', str(source), '--output', str(output), *extra], capture_output=True, text=True)
first_manifest = digest(OUTPUT / 'asset-manifest.json')
result = build(SOURCE, OUTPUT)
assert result.returncode == 0, result.stderr
assert digest(OUTPUT / 'asset-manifest.json') == first_manifest, '相同输入的两次构建不一致'
report = json.loads((BASE / 'dist-report.json').read_text())
url, image = next((url, path) for url, path in report['imageReplacements'].items()
if path.startswith('/notes/pics/') and path.endswith('.png'))
image_name = unquote(image).lstrip('/')
note = next(path for path in (SOURCE / 'notes').glob('*.md') if url in path.read_text())
note_name = note.relative_to(SOURCE)
original_note = digest(note)
original_image = digest(SOURCE / image_name)
with tempfile.TemporaryDirectory(prefix='.update-check-', dir=BASE) as temp:
root = Path(temp)
source, output = root / 'source', root / 'dist'
shutil.copytree(SOURCE, source)
with (source / note_name).open('a') as handle:
handle.write('\n\n## 本地验证更新\n\n本地同步验证示例词:这是未上传的更新测试。\n')
replacement = next(path for path in (SOURCE / 'notes/pics').glob('*.png') if digest(path) != original_image)
shutil.copyfile(replacement, source / image_name)
shutil.copyfile(replacement, source / 'notes/pics/local-validation-new.png')
(source / 'notes/本地新增验证.md').write_text('# 本地新增验证\n\n仅在测试副本中生成。\n\n![新图片](pics/local-validation-new.png)\n')
result = build(source, output, '--no-optimize-images', '--source-commit', 'local-update-check')
assert result.returncode == 0, result.stderr
assert '本地同步验证示例词' in (output / note_name).read_text()
assert digest(output / image_name) == digest(replacement)
assert digest(output / image_name) != original_image
assert (output / 'notes/本地新增验证.md').is_file()
assert (output / 'notes/pics/local-validation-new.png').is_file()
validation = check(source, output)
assert not validation['errors'], validation['errors']
subprocess.run(['node', str(BASE / 'builder/test_search.cjs'), str(output), '--updated'], check=True)
successful_manifest = digest(output / 'asset-manifest.json')
with (source / note_name).open('a') as handle:
handle.write('\n\n![故障模拟](pics/local-validation-missing.png)\n')
failed = build(source, output, '--no-optimize-images', '--source-commit', 'local-failure-check')
assert failed.returncode != 0, '缺少必须图片时构建应失败'
assert digest(output / 'asset-manifest.json') == successful_manifest, '失败的构建覆盖了上次产物'
text = (source / note_name).read_text()
(source / note_name).write_text(text.replace('![故障模拟](pics/local-validation-missing.png)',
'[故障模拟](/notes/local-validation-missing.md)'))
failed = build(source, output, '--no-optimize-images', '--source-commit', 'local-link-failure-check')
assert failed.returncode != 0, '站内链接校验失败时构建应失败'
assert digest(output / 'asset-manifest.json') == successful_manifest, '校验失败覆盖了上次产物'
assert digest(note) == original_note and digest(SOURCE / image_name) == original_image
result = {'sameInputRebuildIdentical': True, 'articleUpdatePickedUp': True,
'repositoryImageUpdatePickedUp': True, 'newArticleAndImageIncluded': True,
'updatedSearchIndexPassed': True, 'failedBuildPreservesPreviousOutput': True,
'failedValidationPreservesPreviousOutput': True,
'upstreamSnapshotUnchanged': True, 'testFixtureRemoved': True,
'sourceCommit': report['sourceCommit'], 'manifestSha256': first_manifest}
(BASE / 'update-validation.json').write_text(json.dumps(result, ensure_ascii=False, indent=2) + '\n')
print(json.dumps(result, ensure_ascii=False, indent=2))