Cleanup
publish / publish (push) Successful in 10m36s

This commit is contained in:
khodowany
2026-09-24 19:40:03 -05:00
parent 097cb61d2d
commit 0c9ba1bff4
15 changed files with 628 additions and 53 deletions
+125
View File
@@ -0,0 +1,125 @@
import ast
import json
import os
from pathlib import Path
import subprocess
import tempfile
import unittest
from unittest.mock import patch
import modulus.bootstrap
import modulus.cli
import modulus.config
import modulus.export
class BootstrapTests(unittest.TestCase):
def test_recipe_exports_prune_generated_trees(self):
workspace = Path(modulus.bootstrap.__file__).parents[2]
for name in ('modulus', 'ligo', 'noto', 'luma', 'sonus', 'product-vamp'):
recipe = workspace / name / 'conanfile.py'
if not recipe.exists():
continue
fields = {target.id: ast.literal_eval(node.value) for node in ast.walk(ast.parse(recipe.read_text())) if isinstance(node, ast.Assign) for target in node.targets if isinstance(target, ast.Name) and target.id in {'exports', 'exports_sources'}}
for field in ('exports', 'exports_sources'):
self.assertTrue({'!build', '!.git', '!test_package/build'}.issubset(fields[field]), f'{name}: {field}')
def test_consumers_use_canonical_bootstrap(self):
canonical = Path(modulus.bootstrap.__file__)
for name in ('ligo', 'noto', 'luma', 'sonus', 'product-vamp'):
consumer = canonical.parents[2] / name / 'modulus.py'
if consumer.exists():
self.assertEqual(canonical.read_bytes(), consumer.read_bytes(), name)
def test_read_environments_strip_all_credential_forms(self):
credentials = dict.fromkeys(('CONAN_USER', 'CONAN_TOKEN', 'CONAN_LOGIN_USERNAME', 'CONAN_PASSWORD', 'CONAN_LOGIN_USERNAME_KHODO', 'CONAN_PASSWORD_KHODO'), 'secret')
root = Path('/tmp/project')
context = modulus.cli.build_context(root, modulus.config.normalize({'package': {'name': 'sample'}}))
with patch.dict(os.environ, credentials):
for env in (modulus.bootstrap.fetch_env(root), modulus.cli.subprocess_environment(context, ['conan', 'install'])):
self.assertFalse(set(credentials) & env.keys())
self.assertEqual(env['CONAN_NON_INTERACTIVE'], '1')
self.assertEqual(env['CONAN_HOME'], str(root / 'build/build_common/conan_cache'))
def test_update_refreshes_runtime_and_offline_stays_offline(self):
root = Path('/tmp/project')
with patch.object(modulus.bootstrap, 'runtime_ready', return_value=True), patch.object(modulus.bootstrap, 'materialize_runtime') as refresh:
modulus.bootstrap.ensure_runtime(root)
refresh.assert_not_called()
modulus.bootstrap.ensure_runtime(root, update=True, no_remote=True)
refresh.assert_called_once_with(root, no_remote=True)
with patch.object(modulus.bootstrap, 'run_fetch', return_value=subprocess.CompletedProcess([], 0, '{}')) as fetch:
with self.assertRaisesRegex(SystemExit, 'local Conan cache'):
modulus.bootstrap.find_modulus(root)
self.assertNotIn('-r', fetch.call_args.args[1])
def test_fetch_failure_preserves_runtime(self):
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
runtime = root / modulus.bootstrap.RUNTIME_DIR / 'modulus/cli.py'
runtime.parent.mkdir(parents=True)
runtime.write_text('old runtime')
with patch.object(modulus.bootstrap, 'remote_config', return_value=('khodo', 'https://example.test')), patch.object(modulus.bootstrap, 'configure_remote'), patch.object(modulus.bootstrap, 'find_modulus', side_effect=SystemExit(1)):
with self.assertRaises(SystemExit):
modulus.bootstrap.materialize_runtime(root)
self.assertEqual(runtime.read_text(), 'old runtime')
def test_offline_bootstrap_never_builds_a_fetch_image(self):
with patch.object(modulus.bootstrap.subprocess, 'run', return_value=subprocess.CompletedProcess([], 1)), patch.object(modulus.bootstrap, 'run') as build:
with self.assertRaisesRegex(SystemExit, 'offline bootstrap requires'):
modulus.bootstrap.ensure_fetch_image(offline=True)
build.assert_not_called()
def test_export_rolls_back_when_editor_refresh_fails(self):
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
(root / 'modulus.json').write_text(json.dumps({'package': {'name': 'sample'}}))
runtime = root / modulus.export.runtime_dir / 'modulus/cli.py'
runtime.parent.mkdir(parents=True)
runtime.write_text('old runtime')
package = root / 'package'
(package / 'modulus').mkdir(parents=True)
(package / 'modulus/cli.py').write_text('new runtime')
(package / 'tools').mkdir()
with patch.object(modulus.export, 'sync_editor_files', side_effect=OSError('editor is read-only')):
with self.assertRaisesRegex(OSError, 'read-only'):
modulus.export.sync_project(root, package)
self.assertEqual(runtime.read_text(), 'old runtime')
self.assertFalse((root / modulus.export.tools_dir).exists())
def test_public_remote_error_is_noninteractive_and_does_not_delete_packages(self):
completed = subprocess.CompletedProcess([], 1, '', '401 Unauthorized')
with patch.object(modulus.bootstrap, 'direct_mode', return_value=True), patch.object(modulus.bootstrap.subprocess, 'run', return_value=completed) as run:
with self.assertRaises(SystemExit):
modulus.bootstrap.find_modulus(Path('/tmp/project'), 'khodo')
self.assertEqual(run.call_args.args[0][:2], ['conan', 'list'])
self.assertEqual(run.call_args.kwargs['env']['CONAN_NON_INTERACTIVE'], '1')
def test_export_stages_all_payloads_before_replacing_runtime(self):
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
(root / 'modulus.json').write_text(json.dumps({'package': {'name': 'sample'}}))
runtime = root / modulus.export.runtime_dir / 'modulus/cli.py'
runtime.parent.mkdir(parents=True)
runtime.write_text('old runtime')
package = root / 'package'
(package / 'modulus').mkdir(parents=True)
(package / 'modulus/cli.py').write_text('new runtime')
with self.assertRaisesRegex(RuntimeError, 'missing tools'):
modulus.export.sync_project(root, package)
self.assertEqual(runtime.read_text(), 'old runtime')
def test_upload_exception_still_logs_out(self):
context = modulus.cli.build_context(Path('/tmp/project'), modulus.config.normalize({'package': {'name': 'sample'}}))
with patch.object(modulus.cli, 'configure_conan_remote', return_value=0), patch.object(modulus.cli, 'upload_all_packages', side_effect=RuntimeError('upload failed')), patch.object(modulus.cli, 'run', return_value=0) as run:
with self.assertRaisesRegex(RuntimeError, 'upload failed'):
modulus.cli.upload_packages(context)
run.assert_called_once_with(context, ['conan', 'remote', 'logout', 'khodo'])
def test_socket_is_scoped_and_docker_inputs_change_fingerprint(self):
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
config = modulus.config.normalize({'package': {'name': 'sample'}, 'tooling': {'docker': {'dockerfile': 'package/build/Dockerfile', 'inputs': ['requirements.txt'], 'socket_commands': ['build', 'package']}}})
context = modulus.cli.build_context(root, config)
context.dockerfile.parent.mkdir(parents=True)
context.dockerfile.write_text('FROM scratch')
context.docker_inputs[0].parent.mkdir(parents=True)
for path in context.docker_inputs:
path.write_text('old')
before = modulus.clangd.recipe_digest(context)
context.docker_inputs[-1].write_text('new')
self.assertNotEqual(before, modulus.clangd.recipe_digest(context))
with patch.object(Path, 'is_socket', return_value=True), patch.object(Path, 'stat') as stat:
stat.return_value.st_gid = 123
for command in ('configure', 'test', 'lsp', 'clangd', 'format'):
self.assertEqual(modulus.cli.docker_socket_args(context, command), [])
self.assertIn('/var/run/docker.sock:/var/run/docker.sock', modulus.cli.docker_socket_args(context, 'package'))