Files

87 lines
2.6 KiB
Python

"""Uji V31: tiap image stack mandiri — daftar COPY memuat semua impor intra-proyek.
`./venv/bin/python test_images.py` harus exit 0. Memindai `Dockerfile`,
`Dockerfile.scan`, `Dockerfile.dist`, `Dockerfile.claim`: untuk tiap modul .py
yang di-COPY, kumpulkan impor intra-proyek (top-level `import X` / `from X
import`) lalu pastikan semuanya ada di daftar COPY image itu. Menangkap kelas
bug B4: modul yang diimpor modul yang di-COPY tapi ⊥ ikut disalin →
ModuleNotFoundError di container (image ⊥ mandiri).
"""
import os
import re
import sys
ROOT = os.path.dirname(os.path.abspath(__file__))
PROJECT = {'config', 'db', 'get_voters', 'dashboard', 'gunicorn_conf',
'scan_loop', 'distribute', 'distribute_loop', 'telegram',
'claim', 'claim_loop'}
IMAGES = {
'web': 'Dockerfile',
'scan': 'Dockerfile.scan',
'dist': 'Dockerfile.dist',
'claim': 'Dockerfile.claim',
}
def copied_modules(dfile):
"""Modul .py yang di-COPY oleh `COPY <files...> ./`."""
mods = set()
with open(os.path.join(ROOT, dfile)) as fh:
for line in fh:
if not line.startswith('COPY '):
continue
tokens = line.split()
if tokens[-1] != './':
continue
for tok in tokens[1:-1]:
if tok.endswith('.py'):
mods.add(tok[:-3])
return mods
def project_imports(mod):
"""Impor intra-proyek dari sumber modul `mod`."""
imports = set()
path = os.path.join(ROOT, mod + '.py')
if not os.path.exists(path):
return imports
with open(path) as fh:
src = fh.read()
for m in re.finditer(r'^\s*import\s+([\w.]+)', src, re.M):
name = m.group(1).split('.')[0]
if name in PROJECT:
imports.add(name)
for m in re.finditer(r'^\s*from\s+([\w.]+)\s+import', src, re.M):
name = m.group(1).split('.')[0]
if name in PROJECT:
imports.add(name)
return imports
def check(tag, cond):
if not cond:
raise AssertionError(f'GAGAL: {tag}')
print(f'ok: {tag}')
def main():
for image, dfile in IMAGES.items():
copied = copied_modules(dfile)
check(f'V31 {image}: ada modul .py di COPY',
bool(copied) and 'config' in copied)
for mod in sorted(copied):
missing = project_imports(mod) - copied
if missing:
raise AssertionError(
f'GAGAL: V31 {image}: {mod} impor {sorted(missing)} '
f'yang ⊥ di-COPY')
print(f'ok: V31 {image}: {mod} impor ⊆ COPY')
print('\nSEMUA UJI PASS')
if __name__ == '__main__':
sys.exit(main())