1#!/usr/bin/env python3
2import json
3import os
4from pathlib import Path
5import shutil
6import subprocess
7import tempfile
8import unittest
9from unittest.mock import patch
10
11import deploy
12import release
13
14
15class MainExport(unittest.TestCase):
16 def test_production_exports_main_while_staging_exports_working_files(self):
17 with tempfile.TemporaryDirectory(prefix="studio-main-export-") as temporary:
18 root = Path(temporary)
19 repo = root / "repo"
20 repo.mkdir()
21 remote = root / "remote"
22 subprocess.run(["jj", "git", "init", str(repo)], check=True, capture_output=True)
23 for name in release.SOURCES:
24 path = repo / name
25 if "." in name:
26 path.write_text("fixture")
27 else:
28 path.mkdir()
29 (path / "fixture").write_text("committed")
30 (repo / "config/excluded-services.json").write_text('["retired"]\n')
31 (repo / "service/retired").mkdir()
32 (repo / "service/retired/private").write_text("must not be exported")
33 executable = repo / "tools/fixture"
34 executable.chmod(0o755)
35 for args in [["describe", "-m", "Test main"], ["bookmark", "set", "main"]]:
36 subprocess.run(["jj", *args], cwd=repo, check=True, capture_output=True)
37 commit = subprocess.run(["jj", "--ignore-working-copy", "log", "-r", "main", "--no-graph", "-T", "commit_id"], cwd=repo, check=True, capture_output=True, text=True).stdout
38 subprocess.run(["jj", "new"], cwd=repo, check=True, capture_output=True)
39 (repo / "service/fixture").write_text("unfinished")
40 (repo / "service/new").write_text("new working file")
41 original = subprocess.run
42
43 def transport(argv, **kwargs):
44 if argv[0] == "jj" and "show" in argv and argv[-1].startswith("service/retired/"):
45 raise AssertionError("read excluded source")
46 if argv[0] == "ssh":
47 return subprocess.CompletedProcess(argv, 1)
48 if argv[0] == "rsync" and "-e" in argv:
49 destination = Path(argv[-1].partition(":")[2])
50 destination.mkdir(parents=True)
51 for name in argv[4:-1]:
52 source = Path(name)
53 if source.is_dir():
54 shutil.copytree(source, destination / source.name)
55 else:
56 shutil.copy2(source, destination / source.name)
57 return subprocess.CompletedProcess(argv, 0)
58 return original(argv, **kwargs)
59
60 with patch.object(deploy, "REPO", repo), patch.object(deploy, "REMOTE", remote), patch.object(deploy, "ssh"), patch.object(deploy.subprocess, "run", side_effect=transport):
61 main = remote / "releases" / deploy.upload(main=True)
62 stage = remote / "releases" / deploy.upload()
63 self.assertEqual((main / "service/fixture").read_text(), "committed")
64 self.assertFalse((main / "service/new").exists())
65 self.assertTrue(os.access(main / "tools/fixture", os.X_OK))
66 self.assertEqual(json.loads((main / ".studio-release.json").read_text())["main"], {"commit": commit, "description": "Test main\n"})
67 self.assertEqual((stage / "service/fixture").read_text(), "unfinished")
68 self.assertEqual((stage / "service/new").read_text(), "new working file")
69 self.assertNotIn("main", json.loads((stage / ".studio-release.json").read_text()))
70 self.assertFalse((main / "service/retired").exists())
71 self.assertFalse((stage / "service/retired").exists())
72 self.assertFalse(any("retired" in relative.parts for _, relative in release.files(repo)))
73 subprocess.run(["jj", "bookmark", "set", "main"], cwd=repo, check=True, capture_output=True)
74 with self.assertRaisesRegex(ValueError, "commit description"):
75 deploy.upload(main=True)
76
77
78if __name__ == "__main__":
79 unittest.main()