Skip to content

Commit ee93e04

Browse files
authored
⚡️ Compress code archives to speed up uploads (#125)
1 parent 58f1f63 commit ee93e04

File tree

3 files changed

+62
-30
lines changed

3 files changed

+62
-30
lines changed

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ dependencies = [
3838
"rich-toolkit >= 0.14.5",
3939
"pydantic[email] >= 1.6.1",
4040
"sentry-sdk >= 2.20.0",
41-
"fastar >= 0.5.0",
41+
"fastar >= 0.8.0",
4242
]
4343

4444
[project.optional-dependencies]

src/fastapi_cloud_cli/commands/deploy.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ def archive(path: Path, tar_path: Path) -> Path:
6868
logger.debug("Archive will be created at: %s", tar_path)
6969

7070
file_count = 0
71-
with fastar.open(tar_path, "w") as tar:
71+
with fastar.open(tar_path, "w:zst") as tar:
7272
for filename in files:
7373
if filename.is_dir():
7474
continue

tests/test_archive.py

Lines changed: 60 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import tarfile
21
from pathlib import Path
32

3+
import fastar
44
import pytest
55

66
from fastapi_cloud_cli.commands.deploy import archive
@@ -13,6 +13,13 @@ def src_path(tmp_path: Path) -> Path:
1313
return path
1414

1515

16+
@pytest.fixture
17+
def dst_path(tmp_path: Path) -> Path:
18+
path = tmp_path / "destination"
19+
path.mkdir()
20+
return path
21+
22+
1623
@pytest.fixture
1724
def tar_path(tmp_path: Path) -> Path:
1825
return tmp_path / "archive.tar"
@@ -29,7 +36,7 @@ def test_archive_creates_tar_file(src_path: Path, tar_path: Path) -> None:
2936

3037

3138
def test_archive_excludes_venv_and_similar_folders(
32-
src_path: Path, tar_path: Path
39+
src_path: Path, tar_path: Path, dst_path: Path
3340
) -> None:
3441
"""Should exclude .venv directory from archive."""
3542
# the only files we want to include
@@ -53,24 +60,38 @@ def test_archive_excludes_venv_and_similar_folders(
5360

5461
archive(src_path, tar_path)
5562

56-
with tarfile.open(tar_path, "r") as tar:
57-
names = tar.getnames()
58-
assert set(names) == {"main.py", "static/index.html"}
63+
with fastar.open(tar_path, "r") as tar:
64+
tar.unpack(dst_path)
65+
66+
assert set(dst_path.glob("**/*")) == {
67+
dst_path / "main.py",
68+
dst_path / "static",
69+
dst_path / "static" / "index.html",
70+
}
5971

6072

61-
def test_archive_preserves_relative_paths(src_path: Path, tar_path: Path) -> None:
73+
def test_archive_preserves_relative_paths(
74+
src_path: Path, tar_path: Path, dst_path: Path
75+
) -> None:
6276
(src_path / "src").mkdir()
6377
(src_path / "src" / "app").mkdir()
6478
(src_path / "src" / "app" / "main.py").write_text("print('hello')")
6579

6680
archive(src_path, tar_path)
6781

68-
with tarfile.open(tar_path, "r") as tar:
69-
names = tar.getnames()
70-
assert names == ["src/app/main.py"]
82+
with fastar.open(tar_path, "r") as tar:
83+
tar.unpack(dst_path)
7184

85+
assert set(dst_path.glob("**/*")) == {
86+
dst_path / "src",
87+
dst_path / "src" / "app",
88+
dst_path / "src" / "app" / "main.py",
89+
}
7290

73-
def test_archive_respects_fastapicloudignore(src_path: Path, tar_path: Path) -> None:
91+
92+
def test_archive_respects_fastapicloudignore(
93+
src_path: Path, tar_path: Path, dst_path: Path
94+
) -> None:
7495
"""Should exclude files specified in .fastapicloudignore."""
7596
(src_path / "main.py").write_text("print('hello')")
7697
(src_path / "config.py").write_text("CONFIG = 'value'")
@@ -82,16 +103,17 @@ def test_archive_respects_fastapicloudignore(src_path: Path, tar_path: Path) ->
82103

83104
archive(src_path, tar_path)
84105

85-
with tarfile.open(tar_path, "r") as tar:
86-
names = tar.getnames()
87-
assert set(names) == {
88-
"main.py",
89-
"config.py",
90-
}
106+
with fastar.open(tar_path, "r") as tar:
107+
tar.unpack(dst_path)
108+
109+
assert set(dst_path.glob("**/*")) == {
110+
dst_path / "main.py",
111+
dst_path / "config.py",
112+
}
91113

92114

93115
def test_archive_respects_fastapicloudignore_unignore(
94-
src_path: Path, tar_path: Path
116+
src_path: Path, tar_path: Path, dst_path: Path
95117
) -> None:
96118
"""Test we can use .fastapicloudignore to unignore files inside .gitignore"""
97119
(src_path / "main.py").write_text("print('hello')")
@@ -109,12 +131,20 @@ def test_archive_respects_fastapicloudignore_unignore(
109131

110132
archive(src_path, tar_path)
111133

112-
with tarfile.open(tar_path, "r") as tar:
113-
names = tar.getnames()
114-
assert set(names) == {"main.py", "static/build/style.css"}
134+
with fastar.open(tar_path, "r") as tar:
135+
tar.unpack(dst_path)
115136

137+
assert set(dst_path.glob("**/*")) == {
138+
dst_path / "main.py",
139+
dst_path / "static",
140+
dst_path / "static" / "build",
141+
dst_path / "static" / "build" / "style.css",
142+
}
116143

117-
def test_archive_includes_hidden_files(src_path: Path, tar_path: Path) -> None:
144+
145+
def test_archive_includes_hidden_files(
146+
src_path: Path, tar_path: Path, dst_path: Path
147+
) -> None:
118148
"""Should include hidden files in the archive by default."""
119149
(src_path / "main.py").write_text("print('hello')")
120150
(src_path / ".env").write_text("SECRET_KEY=xyz")
@@ -123,10 +153,12 @@ def test_archive_includes_hidden_files(src_path: Path, tar_path: Path) -> None:
123153

124154
archive(src_path, tar_path)
125155

126-
with tarfile.open(tar_path, "r") as tar:
127-
names = tar.getnames()
128-
assert set(names) == {
129-
"main.py",
130-
".env",
131-
".config/settings.json",
132-
}
156+
with fastar.open(tar_path, "r") as tar:
157+
tar.unpack(dst_path)
158+
159+
assert set(dst_path.glob("**/*")) == {
160+
dst_path / "main.py",
161+
dst_path / ".env",
162+
dst_path / ".config",
163+
dst_path / ".config" / "settings.json",
164+
}

0 commit comments

Comments
 (0)