Add SSM deployment and reliable Maven setup
This commit is contained in:
@@ -182,6 +182,30 @@ class DeployerTests(unittest.TestCase):
|
||||
self.assertEqual(exit_code, 1)
|
||||
self.assertEqual(terminated, [27544])
|
||||
|
||||
def test_tomcat_restart_accepts_port_owned_by_batch_child_process(self):
|
||||
class RunningProcess:
|
||||
pid = 27544
|
||||
|
||||
def poll(self):
|
||||
return None
|
||||
|
||||
with (
|
||||
TemporaryDirectory() as temp,
|
||||
patch.object(deployer_module, "_is_port_open", return_value=True),
|
||||
patch.object(deployer_module, "_find_listening_pids", return_value={31000}),
|
||||
):
|
||||
exit_code = deployer_module._restart_backend(
|
||||
["catalina.bat", "run"],
|
||||
Path(temp),
|
||||
lambda _value: None,
|
||||
port=8080,
|
||||
require_service_pid=False,
|
||||
replace_listeners=lambda _port, _output: True,
|
||||
popen=lambda *args, **kwargs: RunningProcess(),
|
||||
)
|
||||
|
||||
self.assertEqual(exit_code, 0)
|
||||
|
||||
def test_backend_restart_terminates_child_after_startup_timeout(self):
|
||||
restart_backend = getattr(deployer_module, "_restart_backend", lambda *args, **kwargs: 0)
|
||||
terminated = []
|
||||
@@ -496,6 +520,64 @@ class DeployerTests(unittest.TestCase):
|
||||
|
||||
self.assertEqual(plan[0].command, [str(maven), "clean", "package", "-DskipTests"])
|
||||
|
||||
def test_deploys_ssm_war_to_configured_tomcat(self):
|
||||
with TemporaryDirectory() as temp:
|
||||
root = Path(temp)
|
||||
backend = root / "ssm-server"
|
||||
target = backend / "target"
|
||||
tomcat = root / "apache-tomcat" / "bin" / "catalina.bat"
|
||||
target.mkdir(parents=True)
|
||||
tomcat.parent.mkdir(parents=True)
|
||||
tomcat.write_text("@echo off", encoding="utf-8")
|
||||
source_war = target / "movies.war"
|
||||
source_war.write_text("war-content", encoding="utf-8")
|
||||
scan = ScanResult(
|
||||
root=root,
|
||||
backend=ProjectComponent(
|
||||
"SSM", backend, "Maven SSM WAR", {"backend_type": "ssm", "context_path": "/movies"}
|
||||
),
|
||||
frontend=None,
|
||||
sql_files=[],
|
||||
)
|
||||
config = DeploymentConfig(
|
||||
output_dir=root / "deploy",
|
||||
tool_paths={"Tomcat": tomcat},
|
||||
tool_search_roots=[root / "missing"],
|
||||
)
|
||||
|
||||
plan = build_deployment_plan(scan, config)
|
||||
|
||||
self.assertEqual([step.kind for step in plan], ["backend_build_skip", "ssm_start", "deployment_summary"])
|
||||
start_step = plan[1]
|
||||
self.assertEqual(start_step.command, [str(tomcat.resolve()), "run"])
|
||||
self.assertEqual(start_step.output_path, tomcat.parent.parent / "webapps" / "movies.war")
|
||||
self.assertEqual(start_step.url, "http://localhost:8080/movies")
|
||||
|
||||
calls = []
|
||||
|
||||
def fake_runner(command, cwd, on_output, input_path=None, background=False):
|
||||
calls.append((command, cwd, background))
|
||||
return 0
|
||||
|
||||
self.assertEqual(run_steps(plan, lambda _line: None, process_runner=fake_runner), 0)
|
||||
self.assertEqual((tomcat.parent.parent / "webapps" / "movies.war").read_text(), "war-content")
|
||||
self.assertIn(([str(tomcat.resolve()), "run"], root / "deploy", True), calls)
|
||||
|
||||
def test_ssm_plan_requires_tomcat(self):
|
||||
with TemporaryDirectory() as temp:
|
||||
root = Path(temp)
|
||||
backend = root / "ssm-server"
|
||||
backend.mkdir()
|
||||
scan = ScanResult(
|
||||
root=root,
|
||||
backend=ProjectComponent("SSM", backend, "SSM", {"backend_type": "ssm", "context_path": "/app"}),
|
||||
frontend=None,
|
||||
sql_files=[],
|
||||
)
|
||||
|
||||
with self.assertRaisesRegex(FileNotFoundError, "需要 Tomcat"):
|
||||
build_deployment_plan(scan, DeploymentConfig(tool_search_roots=[root / "missing"]))
|
||||
|
||||
def test_plan_uses_discovered_mysql_path_for_sql_import(self):
|
||||
with TemporaryDirectory() as temp:
|
||||
root = Path(temp)
|
||||
|
||||
@@ -2,7 +2,9 @@ import subprocess
|
||||
import tempfile
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
from unittest.mock import patch
|
||||
|
||||
import deploy_tool.environment as environment_module
|
||||
from deploy_tool.environment import check_environment, load_tool_paths, save_tool_paths
|
||||
|
||||
|
||||
@@ -98,6 +100,18 @@ class EnvironmentTests(unittest.TestCase):
|
||||
|
||||
self.assertEqual(loaded, {"MySQL": mysql})
|
||||
|
||||
def test_tomcat_command_receives_catalina_home(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
catalina = Path(tmp) / "apache-tomcat" / "bin" / "catalina.bat"
|
||||
catalina.parent.mkdir(parents=True)
|
||||
catalina.write_text("@echo off", encoding="utf-8")
|
||||
completed = subprocess.CompletedProcess([str(catalina), "version"], 0, "Tomcat", "")
|
||||
|
||||
with patch("deploy_tool.environment.subprocess.run", return_value=completed) as run:
|
||||
environment_module._run_command([str(catalina), "version"])
|
||||
|
||||
self.assertEqual(run.call_args.kwargs["env"]["CATALINA_HOME"], str(catalina.parent.parent))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
@@ -1,8 +1,20 @@
|
||||
import hashlib
|
||||
import io
|
||||
import tempfile
|
||||
import unittest
|
||||
import zipfile
|
||||
from pathlib import Path
|
||||
|
||||
from deploy_tool.installer import build_install_command, explain_install_failure, format_install_output, recommend_jdk_version
|
||||
from deploy_tool.installer import (
|
||||
MAVEN_ARCHIVE_NAME,
|
||||
TOMCAT_VERSION,
|
||||
install_maven,
|
||||
install_tomcat,
|
||||
build_install_command,
|
||||
explain_install_failure,
|
||||
format_install_output,
|
||||
recommend_jdk_version,
|
||||
)
|
||||
from deploy_tool.scanner import scan_project
|
||||
|
||||
|
||||
@@ -73,6 +85,54 @@ class InstallerTests(unittest.TestCase):
|
||||
|
||||
self.assertEqual(command, ["winget", "install", "-e", "--id", "OpenJS.NodeJS.LTS"])
|
||||
|
||||
def test_maven_is_not_offered_as_an_invalid_winget_package(self):
|
||||
with self.assertRaises(ValueError):
|
||||
build_install_command("Maven")
|
||||
|
||||
def test_installs_maven_from_verified_apache_archive(self):
|
||||
archive_buffer = io.BytesIO()
|
||||
with zipfile.ZipFile(archive_buffer, "w") as archive:
|
||||
archive.writestr("apache-maven-3.10.0/bin/mvn.cmd", "@echo off")
|
||||
archive_bytes = archive_buffer.getvalue()
|
||||
checksum = hashlib.sha512(archive_bytes).hexdigest().encode("ascii")
|
||||
|
||||
def opener(url, timeout):
|
||||
return io.BytesIO(checksum if url.endswith(".sha512") else archive_bytes)
|
||||
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
messages = []
|
||||
executable = install_maven(messages.append, Path(tmp), opener)
|
||||
|
||||
self.assertTrue(executable.exists())
|
||||
self.assertEqual(executable.read_text(), "@echo off")
|
||||
self.assertTrue(any("校验通过" in message for message in messages))
|
||||
|
||||
def test_rejects_maven_archive_with_wrong_checksum(self):
|
||||
def opener(url, timeout):
|
||||
payload = ("0" * 128).encode("ascii") if url.endswith(".sha512") else b"not-a-zip"
|
||||
return io.BytesIO(payload)
|
||||
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
with self.assertRaisesRegex(RuntimeError, "SHA-512"):
|
||||
install_maven(lambda _message: None, Path(tmp), opener)
|
||||
self.assertFalse((Path(tmp) / MAVEN_ARCHIVE_NAME.removesuffix("-bin.zip")).exists())
|
||||
|
||||
def test_installs_tomcat_from_verified_apache_archive(self):
|
||||
archive_buffer = io.BytesIO()
|
||||
with zipfile.ZipFile(archive_buffer, "w") as archive:
|
||||
archive.writestr(f"apache-tomcat-{TOMCAT_VERSION}/bin/catalina.bat", "@echo off")
|
||||
archive_bytes = archive_buffer.getvalue()
|
||||
checksum = hashlib.sha512(archive_bytes).hexdigest().encode("ascii")
|
||||
|
||||
def opener(url, timeout):
|
||||
return io.BytesIO(checksum if url.endswith(".sha512") else archive_bytes)
|
||||
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
executable = install_tomcat(lambda _message: None, Path(tmp), opener)
|
||||
|
||||
self.assertTrue(executable.exists())
|
||||
self.assertEqual(executable.name, "catalina.bat")
|
||||
|
||||
def test_formats_winget_progress_without_garbled_bar(self):
|
||||
raw = "████████████▒▒▒▒▒▒ 63%\r".encode("utf-8")
|
||||
|
||||
|
||||
@@ -32,6 +32,12 @@ class PathManagerTests(unittest.TestCase):
|
||||
self.assertEqual(update.path_entries, [Path(r"C:\Program Files\Eclipse Adoptium\jdk-8\bin")])
|
||||
self.assertEqual(update.env_vars["JAVA_HOME"], r"C:\Program Files\Eclipse Adoptium\jdk-8")
|
||||
|
||||
def test_build_environment_update_sets_catalina_home_from_bin(self):
|
||||
update = build_environment_update("Tomcat", Path(r"C:\Tools\apache-tomcat-9\bin\catalina.bat"))
|
||||
|
||||
self.assertEqual(update.path_entries, [Path(r"C:\Tools\apache-tomcat-9\bin")])
|
||||
self.assertEqual(update.env_vars["CATALINA_HOME"], r"C:\Tools\apache-tomcat-9")
|
||||
|
||||
def test_apply_user_environment_writes_path_and_env_vars(self):
|
||||
registry = FakeRegistry()
|
||||
registry.values["Path"] = r"C:\Windows"
|
||||
|
||||
@@ -99,6 +99,37 @@ class ScannerTests(unittest.TestCase):
|
||||
self.assertEqual(result.frontend.path, frontend)
|
||||
self.assertEqual(result.sql_files, [sql_dir / "schema.sql"])
|
||||
|
||||
def test_detects_maven_ssm_war_project_and_context_path(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
root = Path(tmp)
|
||||
backend = root / "ssm-server"
|
||||
(backend / "src" / "main" / "java").mkdir(parents=True)
|
||||
web_inf = backend / "src" / "main" / "webapp" / "WEB-INF"
|
||||
web_inf.mkdir(parents=True)
|
||||
(web_inf / "web.xml").write_text("<web-app />", encoding="utf-8")
|
||||
(backend / "pom.xml").write_text(
|
||||
"""
|
||||
<project>
|
||||
<artifactId>movie-system</artifactId>
|
||||
<packaging>war</packaging>
|
||||
<dependencies>
|
||||
<dependency><artifactId>spring-webmvc</artifactId></dependency>
|
||||
<dependency><artifactId>mybatis</artifactId></dependency>
|
||||
</dependencies>
|
||||
<build><finalName>movies</finalName></build>
|
||||
</project>
|
||||
""",
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
result = scan_project(root)
|
||||
|
||||
self.assertIsNotNone(result.backend)
|
||||
self.assertEqual(result.backend.kind, "SSM")
|
||||
self.assertEqual(result.backend.metadata["backend_type"], "ssm")
|
||||
self.assertEqual(result.backend.metadata["packaging"], "war")
|
||||
self.assertEqual(result.backend.metadata["context_path"], "/movies")
|
||||
|
||||
def test_empty_folder_reports_missing_parts(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
result = scan_project(Path(tmp))
|
||||
|
||||
Reference in New Issue
Block a user