"""Offline architecture/PATH regressions for Gitea Linux runners.""" import os from pathlib import Path import subprocess import tempfile import unittest SCRIPTS = Path(__file__).resolve().parent class ToolSetup(unittest.TestCase): def setUp(self): self.temp = tempfile.TemporaryDirectory() self.addCleanup(self.temp.cleanup) self.root = Path(self.temp.name) self.bin = self.root / 'bin' self.bin.mkdir() self.env = dict(os.environ, PATH=f'{self.bin}:{os.environ["PATH"]}', FAKE_ARCH='aarch64', FAKE_OS='Linux', RUNNER_TEMP=str(self.root), RUNNER_TOOL_CACHE=str(self.root / 'cache'), GITHUB_PATH=str(self.root / 'path'), GITHUB_ENV=str(self.root / 'env')) self.mock(self.bin / 'uname', 'if [ "$1" = -s ]; then echo "$FAKE_OS"; else echo "$FAKE_ARCH"; fi\n') def mock(self, path, body): path.parent.mkdir(parents=True, exist_ok=True) path.write_text('#!/bin/sh\n' + body) path.chmod(0o755) def run_script(self, script, *args): return subprocess.run(['bash', str(SCRIPTS / script), *args], env=self.env, capture_output=True, text=True) def test_all_native_tool_urls_and_checksums(self): for arch, suffix in [('aarch64', 'arm64'), ('x86_64', 'amd64')]: self.env['FAKE_ARCH'] = arch for tool in ['trivy', 'actionlint', 'hcloud']: with self.subTest(arch=arch, tool=tool): result = self.run_script('install-tool.sh', tool, '--print-source') self.assertEqual(result.returncode, 0, result.stderr) url, checksum = result.stdout.splitlines() expected = 'Linux-ARM64' if tool == 'trivy' and suffix == 'arm64' else ( 'Linux-64bit' if tool == 'trivy' else suffix) self.assertIn(expected, url) self.assertRegex(checksum, r'^[0-9a-f]{64}$') def test_unsupported_architecture_rejected_before_download(self): self.env['FAKE_ARCH'] = 'armv7l' self.assertNotEqual(self.run_script('install-tool.sh', 'trivy', '--print-source').returncode, 0) def test_non_linux_rejected(self): self.env['FAKE_OS'] = 'Darwin' self.assertNotEqual(self.run_script('install-tool.sh', 'trivy', '--print-source').returncode, 0) def test_wrong_checksum_cannot_install_or_activate_binary(self): self.mock(self.bin / 'curl', 'exit 0\n') self.mock(self.bin / 'sha256sum', 'exit 1\n') self.mock(self.bin / 'tar', f'touch "{self.root}/extracted"\n') self.assertNotEqual(self.run_script('install-tool.sh', 'trivy').returncode, 0) self.assertFalse((self.root / 'extracted').exists()) self.assertFalse((self.root / 'path').exists()) def test_node22_takes_precedence_over_preinstalled_node18(self): self.mock(self.bin / 'node', 'echo v18.20.8\nexit 1\n') self.mock(self.bin / 'npm', 'echo old-npm\nexit 1\n') for version in ['22.9.0', '22.23.2']: node_bin = self.root / 'cache' / 'node' / version / 'arm64' / 'bin' self.mock(node_bin / 'node', f'if [ "$1" = -e ]; then exit 0; fi\necho v{version}\n') self.mock(node_bin / 'npm', 'echo 10.9.0\n') result = self.run_script('activate-node.sh') self.assertEqual(result.returncode, 0, result.stderr) self.assertIn('v22.23.2', result.stdout) self.assertNotIn('v18.', result.stdout) self.assertIn('/22.23.2/arm64/bin', (self.root / 'path').read_text()) self.assertTrue((self.root / 'env').read_text().startswith('PATH=')) def test_missing_native_node22_fails(self): wrong_arch = self.root / 'cache' / 'node' / '22.23.2' / 'x64' / 'bin' self.mock(wrong_arch / 'node', 'exit 0\n') self.assertNotEqual(self.run_script('activate-node.sh').returncode, 0) if __name__ == '__main__': unittest.main()