Recognize macOS extraction helper staging and verify release resources
This commit is contained in:
1 parent
f1028e2689
commit
d41c5b5e98
4 files changed
+44
-4
No files matched your search
@@ -29,7 +29,7 @@ fn main() {
|
||||
);
|
||||
}
|
||||
if std::env::var("PROFILE").as_deref() == Ok("release")
|
||||
&& matches!(target.as_str(), "windows" | "linux")
|
||||
&& matches!(target.as_str(), "windows" | "linux" | "macos")
|
||||
{
|
||||
let root = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("../../..");
|
||||
let status = std::process::Command::new(if cfg!(windows) { "python" } else { "python3" })
|
||||
|
||||
@@ -215,3 +215,16 @@ Windows throughput, cold filesystem caches or steady-state search latency.
|
||||
No persistent parser/cache optimization is justified by this small observation.
|
||||
Raw ignored report: `.searchi/qa/document-throughput-013.json`, SHA-256
|
||||
`b86df0bf2ca0953317033b35b4642e6e0fadb93e50c499a7a28b1ae82512acd1`.
|
||||
|
||||
## macOS staging preparation
|
||||
|
||||
The helper staging tool now detects Darwin as macOS, recognizes native thin
|
||||
64-bit little-endian Mach-O executables for Intel/Apple Silicon, and preserves
|
||||
executable mode. Desktop release verification now covers macOS too. Header
|
||||
layout and constants follow Apple's [loader header](https://github.com/apple-oss-distributions/xnu/blob/main/EXTERNAL_HEADERS/mach-o/loader.h)
|
||||
and [CPU definitions](https://github.com/apple-oss-distributions/xnu/blob/main/osfmk/mach/machine.h).
|
||||
Six staging tests passed normally and under optimized Python, including mocked
|
||||
Darwin defaults and synthetic header rejection. The actual staged Windows
|
||||
helper still verifies. This is preparation only: native macOS build, loader
|
||||
closure, resource-limit behavior, signing and application execution remain open.
|
||||
Universal/fat extraction-helper binaries are not supported by this staging tool.
|
||||
@@ -53,10 +53,17 @@ def identity(path):
|
||||
machine = struct.unpack_from('<H', signature, 4)[0] if signature[:4] == b'PE\0\0' else 0
|
||||
arch = {0x8664: 'x86_64', 0xAA64: 'aarch64'}.get(machine)
|
||||
platform = 'windows'
|
||||
elif header[:6] == b'\x7fELF\x02\x01':
|
||||
elif len(header) == 64 and header[:6] == b'\x7fELF\x02\x01':
|
||||
machine = struct.unpack_from('<H', header, 18)[0]
|
||||
arch = {62: 'x86_64', 183: 'aarch64'}.get(machine)
|
||||
platform = 'linux'
|
||||
elif len(header) >= 32 and header[:4] == b'\xcf\xfa\xed\xfe':
|
||||
# Apple's mach_header_64: thin little-endian native executable.
|
||||
cpu, _, filetype, commands, command_bytes = struct.unpack_from('<IIIII', header, 4)
|
||||
if filetype != 2 or not 0 < commands <= 8192 or not commands * 8 <= command_bytes <= path.stat().st_size - 32:
|
||||
raise ValueError('Invalid extraction Mach-O executable header')
|
||||
arch = {0x01000007: 'x86_64', 0x0100000c: 'aarch64'}.get(cpu)
|
||||
platform = 'macos'
|
||||
else:
|
||||
raise ValueError('Unsupported extraction executable format')
|
||||
if arch is None:
|
||||
@@ -106,7 +113,7 @@ def stage(binary, provenance, destination, root=ROOT):
|
||||
try:
|
||||
if target == name and hashlib.sha256(data).hexdigest() != record['sha256']:
|
||||
raise ValueError('Extraction binary changed while staging')
|
||||
if target == name and platform == 'linux':
|
||||
if target == name and platform != 'windows':
|
||||
temporary.chmod(0o755)
|
||||
os.replace(temporary, destination / target)
|
||||
finally:
|
||||
@@ -119,7 +126,7 @@ def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument('--destination', type=Path, default=ROOT / 'apps/desktop/src-tauri/resources')
|
||||
parser.add_argument('--verify-only', action='store_true')
|
||||
parser.add_argument('--platform', choices=['windows', 'linux'], default='windows' if os.name == 'nt' else 'linux')
|
||||
parser.add_argument('--platform', choices=['windows', 'linux', 'macos'], default='windows' if os.name == 'nt' else ('macos' if host_platform.system() == 'Darwin' else 'linux'))
|
||||
parser.add_argument('--architecture', choices=['x86_64', 'aarch64'], default='aarch64' if host_platform.machine().lower() in {'aarch64', 'arm64'} else 'x86_64')
|
||||
args = parser.parse_args()
|
||||
if args.verify_only:
|
||||
|
||||
@@ -56,6 +56,26 @@ class ExtractionStageTests(unittest.TestCase):
|
||||
worker.stage(binary, before, resources, root)
|
||||
self.assertEqual((resources / 'searchi-extract-worker.exe').read_bytes(), binary.read_bytes())
|
||||
|
||||
def test_macos_thin_headers_and_staging(self):
|
||||
import struct
|
||||
for cpu, arch in [(0x01000007, 'x86_64'), (0x0100000c, 'aarch64')]:
|
||||
with self.subTest(architecture=arch), tempfile.TemporaryDirectory() as directory:
|
||||
root, binary, resources = self.fixture(directory)
|
||||
data = struct.pack('<8I', 0xfeedfacf, cpu, 0, 2, 1, 8, 0, 0) + struct.pack('<2I', 0, 8)
|
||||
binary.write_bytes(data)
|
||||
worker.stage(binary, worker.fingerprint(root), resources, root)
|
||||
records = worker.verify(resources, root, 'macos', arch)
|
||||
self.assertIn('searchi-extract-worker', records)
|
||||
for invalid in [data[:31], data[:12] + struct.pack('<I', 6) + data[16:], data[:4] + struct.pack('<I', 7) + data[8:], data[:20] + struct.pack('<I', 9999) + data[24:]]:
|
||||
binary.write_bytes(invalid)
|
||||
with self.assertRaises(ValueError):
|
||||
worker.identity(binary)
|
||||
|
||||
def test_darwin_default_uses_macos_and_arm64(self):
|
||||
with patch.object(worker.os, 'name', 'posix'), patch.object(worker.host_platform, 'system', return_value='Darwin'), patch.object(worker.host_platform, 'machine', return_value='arm64'), patch.object(worker, 'verify') as verify, patch('sys.argv', ['stage_extraction_worker.py', '--verify-only']):
|
||||
worker.main()
|
||||
self.assertEqual(verify.call_args.args[2:], ('macos', 'aarch64'))
|
||||
|
||||
def test_failed_build_never_stages_existing_binary(self):
|
||||
with patch.object(worker.subprocess, 'run', side_effect=RuntimeError('cargo failed')), patch.object(worker, 'stage') as stage, patch('sys.argv', ['stage_extraction_worker.py']):
|
||||
with self.assertRaises(RuntimeError):
|
||||
|
||||
Reference in new issue
Block a user