Files
syly2/ops/uat-build/verify-build.py
T

78 lines
4.1 KiB
Python

#!/usr/bin/env python3
"""Verify the WAR, record exact source changes, and compare shared UAT containers."""
import collections
import hashlib
import json
import os
from pathlib import Path
import shutil
import struct
import subprocess
import sys
import zipfile
os.umask(0o077)
run = Path(sys.argv[1]).resolve()
phase = sys.argv[2]
assert (run / 'logs' / (phase + '.exit-code')).read_text().strip() == '0'
log = (run / 'logs' / (phase + '.log')).read_text(errors='replace')
assert 'BUILD SUCCESS' in log
source = run / 'source'
war = source / 'target/syly.war'
required = [
'WEB-INF/web.xml',
'WEB-INF/classes/com/zzc/web/system/controller/core/LoginController.class',
'WEB-INF/classes/com/zzc/core/interceptors/AuthInterceptor.class',
'WEB-INF/classes/com/zzc/web/htoal/controller/HotelMonthlyController.class',
]
with zipfile.ZipFile(war) as archive:
bad = archive.testzip()
assert bad is None, bad
names = archive.namelist()
assert all(name in names for name in required)
classes = [name for name in names if name.startswith('WEB-INF/classes/') and name.endswith('.class')]
major_versions = collections.Counter(struct.unpack('>H', archive.read(name)[6:8])[0] for name in classes)
assert set(major_versions) == {52}, major_versions
libs = [name for name in names if name.startswith('WEB-INF/lib/') and name.endswith('.jar')]
manifest = json.loads((run / 'source-manifest.json').read_text())
changes = []
for relative, expected in manifest.items():
actual = hashlib.sha256((source / relative).read_bytes()).hexdigest()
if expected != actual:
changes.append({'path': relative, 'original_sha256': expected, 'build_sha256': actual})
expected_changes = {
'pom.xml',
'src/main/java/com/zzc/web/htoal/controller/HotelQuarterStatisticController.java',
'src/main/java/com/zzc/web/travel/controller/InlandStatisticPageController.java',
}
assert {change['path'] for change in changes} == expected_changes, changes
ids = subprocess.check_output(['docker', 'ps', '-aq'], text=True).split()
fmt = '{"id":{{json .Id}},"name":{{json .Name}},"startedAt":{{json .State.StartedAt}},"running":{{json .State.Running}},"restartCount":{{json .RestartCount}}}'
after = [json.loads(subprocess.check_output(['docker', 'inspect', '--format', fmt, cid], text=True)) for cid in ids]
before = json.loads((run / 'containers-before.json').read_text())
key = lambda values: {value['id']: value for value in values}
unchanged = key(before) == key(after)
(run / 'containers-after.json').write_text(json.dumps(after, indent=2) + '\n')
artifact = run / 'artifacts/syly.war'
shutil.copyfile(war, artifact)
artifact.chmod(0o600)
evidence = {
'host': '192.168.68.223', 'run': str(run), 'phase': phase, 'build_success': True,
'artifact': str(artifact), 'artifact_bytes': artifact.stat().st_size,
'artifact_sha256': hashlib.sha256(artifact.read_bytes()).hexdigest(),
'zip_crc_check': 'passed', 'required_entries_present': required,
'compiled_class_files': len(classes), 'class_major_versions': dict(major_versions), 'bundled_jars': len(libs),
'main_java_sources': len(list((source / 'src/main/java').rglob('*.java'))),
'extra_java_sources_outside_maven_root': len(list((source / 'src/main/com').rglob('*.java'))),
'source_manifest_files': len(manifest), 'changed_source_files': changes,
'tests_skipped': 'Tests are skipped' in log,
'containers_before': len(before), 'containers_after': len(after), 'existing_containers_unchanged': unchanged,
'built_at': next((line.split('Finished at:', 1)[1].strip() for line in log.splitlines() if 'Finished at:' in line), None),
'elapsed': next((line.split('Total time:', 1)[1].strip() for line in log.splitlines() if 'Total time:' in line), None),
'java_version': subprocess.check_output([str(Path.home() / '.local/opt/syly2-build/corretto-8.512.09.1/bin/java'), '-version'], stderr=subprocess.STDOUT, text=True).strip(),
}
(run / 'artifacts/verification.json').write_text(json.dumps(evidence, indent=2) + '\n')
print(json.dumps(evidence, indent=2))
if not unchanged:
print('NOTICE: Container inventory changed during the build; inspect before/after records.')