feat: Dagu DAGと配置手順を追加

This commit is contained in:
2026-10-12 18:15:26 +09:00
parent aa11347fa3
commit ba145d801a
3 changed files with 271 additions and 7 deletions
+162
View File
@@ -0,0 +1,162 @@
from pathlib import Path
import re
import unittest
import unifydrive2nas as app
ROOT = Path(__file__).resolve().parents[1]
DAG_PATH = ROOT / "deploy" / "dagu" / "unifydrive2nas.yaml"
PROJECT_PATH = ROOT / "PROJECT.md"
INSTALL_DIRECTORY = "/volume1/homes/kazu634/bin/unifydrive2nas"
SCRIPT_PATH = INSTALL_DIRECTORY + "/unifydrive2nas.py"
DAG_STAGING_PATH = "/tmp/unifydrive2nas.yaml"
DAGU_COMMAND = "DAGU_HOME=/var/lib/dagu /usr/local/bin/dagu"
class DaguDeploymentContractTests(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.dag = DAG_PATH.read_text(encoding="utf-8")
cls.project = PROJECT_PATH.read_text(encoding="utf-8")
def dag_value(self, key):
pattern = r"^\s*" + re.escape(key) + r":\s*(.*?)\s*$"
values = re.findall(pattern, self.dag, flags=re.MULTILINE)
self.assertEqual(1, len(values), "DAG の {0} は一意であること".format(key))
return values[0].strip('"\'')
def test_schedule_and_execution_guards(self):
self.assertEqual("0 4 * * *", self.dag_value("schedule"))
self.assertEqual("skip", self.dag_value("overlap_policy"))
self.assertEqual("7200", self.dag_value("timeout_sec"))
self.assertIn("毎日 04:00(Asia/Tokyo)", self.project)
self.assertIn('schedule: "0 4 * * *"', self.project)
def test_ssh_target_and_single_command(self):
expected = {
"action": "ssh.run",
"host": "192.168.10.201",
"port": "22",
"user": "kazu634",
"key": "/home/kazu634/.ssh/id_ed25519_nas",
"strict_host_key": "true",
"known_host_file": "/home/kazu634/.ssh/known_hosts",
"command": "python3 " + SCRIPT_PATH,
}
for key, value in expected.items():
with self.subTest(key=key):
self.assertEqual(value, self.dag_value(key))
self.assertIn("| Synology `ds923p` | `192.168.10.201`", self.project)
self.assertIn("ssh -i ~/.ssh/id_ed25519_nas kazu634@192.168.10.201", self.project)
self.assertIn("`" + INSTALL_DIRECTORY + "/`", self.project)
def test_ssh_settings_are_nested_in_one_step_with_block(self):
match = re.search(
r"^steps:\n"
r" - id: copy_unifydrive_to_nas\n"
r" action: ssh\.run\n"
r" with:\n"
r"((?: [^\n]+\n?)+)$",
self.dag,
flags=re.MULTILINE,
)
self.assertIsNotNone(match)
with_block = match.group(1)
for key in (
"host",
"port",
"user",
"key",
"strict_host_key",
"known_host_file",
"command",
):
with self.subTest(key=key):
self.assertRegex(with_block, r"(?m)^ " + re.escape(key) + r":")
self.assertEqual(1, self.dag.count("command:"))
def test_project_documents_deployment_contract(self):
required_values = (
"毎日 04:00",
"/volume1/homes/kazu634/bin/unifydrive2nas/",
"deploy/dagu/unifydrive2nas.yaml",
"/var/lib/dagu/dags/",
"kazu634/UnifyDrive2NAS",
"--init",
)
for value in required_values:
with self.subTest(value=value):
self.assertIn(value, self.project)
def test_default_database_is_shared_by_implementation_project_and_dag(self):
expected = Path.home() / ".local" / "state" / "unifydrive2nas" / "copied.sqlite3"
self.assertEqual(expected, app.DEFAULT_DATABASE)
self.assertIn("`~/.local/state/unifydrive2nas/copied.sqlite3`", self.project)
self.assertNotIn("--database", self.dag_value("command"))
def test_deployment_commands_use_consistent_paths_and_exclude_tests(self):
section = self.project.split("### 配置手順", 1)[1].split("## 外部とのつながり", 1)[0]
blocks = re.findall(r"```bash\n(.*?)\n[ \t]*```", section, flags=re.DOTALL)
self.assertEqual(8, len(blocks))
(
runtime_copy,
runtime_dry_run,
initialize,
staging_copy,
validate,
dag_dry_run,
manual_start,
schedule_enable,
) = blocks
self.assertIn("--exclude='__pycache__' unifydrive2nas.py vendor", runtime_copy)
self.assertNotIn("tests/", runtime_copy)
self.assertIn("tar -C " + INSTALL_DIRECTORY + " -xf -", runtime_copy)
self.assertIn("python3 " + SCRIPT_PATH + " --dry-run", runtime_dry_run)
self.assertIn("python3 " + SCRIPT_PATH + " --init", initialize)
self.assertNotIn("--database", initialize)
self.assertIn("-C deploy/dagu unifydrive2nas.yaml", staging_copy)
self.assertIn("tar -C /tmp -xf -", staging_copy)
self.assertNotIn("tests/", staging_copy)
self.assertIn(DAGU_COMMAND + " validate " + DAG_STAGING_PATH, validate)
self.assertIn(DAGU_COMMAND + " dry " + DAG_STAGING_PATH, dag_dry_run)
self.assertIn(DAGU_COMMAND + " start " + DAG_STAGING_PATH, manual_start)
self.assertIn("-C deploy/dagu unifydrive2nas.yaml", schedule_enable)
self.assertIn("tar -C /var/lib/dagu/dags -xf -", schedule_enable)
self.assertIn("rm -f " + DAG_STAGING_PATH, schedule_enable)
def test_verification_uses_temporary_tests_on_nas(self):
section = self.project.split("## 確かめ方", 1)[1]
blocks = re.findall(r"```bash\n(.*?)\n[ \t]*```", section, flags=re.DOTALL)
verification = next(block for block in blocks if "unifydrive2nas.py vendor tests" in block)
unpack = "tar -C /tmp/unifydrive2nas-test -xf -"
run_tests = (
"cd /tmp/unifydrive2nas-test && "
"python3 -W error::ResourceWarning -m unittest discover -s tests -v"
)
cleanup = "rm -rf /tmp/unifydrive2nas-test"
production_dry_run = "python3 " + SCRIPT_PATH + " --dry-run"
self.assertIn("--exclude='__pycache__' unifydrive2nas.py vendor tests", verification)
self.assertIn(unpack, verification)
self.assertIn(run_tests, verification)
self.assertEqual(2, verification.count(cleanup))
self.assertIn(production_dry_run, verification)
self.assertLess(verification.index(unpack), verification.index(run_tests))
self.assertLess(verification.index(run_tests), verification.rindex(cleanup))
self.assertLess(verification.rindex(cleanup), verification.index(production_dry_run))
def test_all_documented_dagu_commands_use_service_home(self):
commands = re.findall(r"/usr/local/bin/dagu (?:validate|dry|start) [^\s\"']+", self.project)
self.assertEqual(5, len(commands))
self.assertEqual(5, self.project.count(DAGU_COMMAND))
self.assertNotIn("/usr/local/bin/dagu", self.project.replace(DAGU_COMMAND, ""))
if __name__ == "__main__":
unittest.main()