fix: ship updatable 1.1.1 source archives

Package a clean shallow main checkout with credential-free Gitea remote, localize the China launcher, and set the new-install Responses endpoint.
This commit is contained in:
chenmingxuan committed 2026-09-24 10:49:57 +08:00
1 parent bfcd828fa8
commit 8522fa3ca3
22 files changed
+445 -140

No files matched your search

@@ -59,6 +59,17 @@ def test_save_reload_and_blank_key_preserves_secret(isolated_settings):
assert dotenv_values(isolated_settings)["OPENAI_API_KEY"] == "new-test-secret"
def test_new_install_uses_playones_endpoint_without_overriding_saved_url(monkeypatch):
monkeypatch.delenv("OPENAI_BASE_URL", raising=False)
expected = "https://api-n-cd.playones.com/v1"
assert Settings(_env_file=None).OPENAI_BASE_URL == expected
assert service.ModelSettingsUpdate(model="test").base_url == expected
monkeypatch.setattr(settings, "OPENAI_BASE_URL", None)
assert service.get_model_settings()["base_url"] == expected
monkeypatch.setattr(settings, "OPENAI_BASE_URL", "https://saved.example/v1")
assert service.get_model_settings()["base_url"] == "https://saved.example/v1"
@pytest.mark.parametrize(
"url",
[
+8 -15
View File
@@ -12,7 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
"""Keep bootstrap scripts ASCII for Windows PowerShell 5.1 compatibility.
"""Require ASCII or UTF-8 with BOM for Windows PowerShell 5.1 scripts.
BOM-less scripts are decoded using the system ANSI code page. UTF-8 symbols
can become quote delimiters under cp1252 and cause parse errors.
@@ -31,18 +31,11 @@ def test_powershell_scripts_are_discovered():
@pytest.mark.parametrize("script", POWERSHELL_SCRIPTS, ids=lambda p: p.name)
def test_powershell_script_is_pure_ascii(script):
def test_powershell_script_has_windows_compatible_encoding(script):
raw = script.read_bytes()
offending = []
line_no = 1
for index, byte in enumerate(raw):
if byte == 0x0A:
line_no += 1
elif byte > 0x7F:
offending.append((line_no, index, hex(byte)))
assert not offending, (
f"{script.name} contains non-ASCII bytes at (line, offset, byte) "
f"{offending[:10]}; Windows PowerShell 5.1 decodes BOM-less .ps1 files "
"with the system ANSI code page. Use ASCII status labels."
)
if any(byte > 0x7F for byte in raw):
assert raw.startswith(b"\xef\xbb\xbf"), (
f"{script.name} contains non-ASCII bytes without a UTF-8 BOM; "
"Windows PowerShell 5.1 would use the system ANSI code page."
)
raw.decode("utf-8-sig")
+112 -14
View File
@@ -1,36 +1,134 @@
import importlib.util
from pathlib import Path
import shutil
import subprocess
import zipfile
import pytest
def test_archive_keeps_source_and_excludes_local_builds_and_secrets(tmp_path):
script = Path(__file__).resolve().parents[2] / "scripts/package_source.py"
spec = importlib.util.spec_from_file_location("package_source", script)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
SCRIPT = Path(__file__).resolve().parents[2] / "scripts/package_source.py"
spec = importlib.util.spec_from_file_location("package_source", SCRIPT)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
def git(root, *args):
result = subprocess.run(
["git", "-C", str(root), *args], capture_output=True, text=True, timeout=30
)
assert result.returncode == 0, result.stderr
return result.stdout.strip()
@pytest.fixture
def repository(tmp_path):
if not shutil.which("git"):
pytest.skip("Git required")
root = tmp_path / "source"
root.mkdir()
git(root, "init", "-b", "main")
git(root, "config", "user.name", "Fixture")
git(root, "config", "user.email", "fixture@example.test")
keep = ["start.bat", ".env.example", "uv.lock", "apps/showcase_ui/src/main.ts"]
tracked_builds = [
"artemis/resources/showcase_ui/main.js",
"packages/artemis-accessibility-helper/ArtemisAccessibilityHelper.apk",
"packages/artemis-accessibility-helper/debug.keystore",
]
omit = [
".env",
".env.local",
".venv/pyvenv.cfg",
"nested/node_modules/foo.js",
"apps/showcase_ui/dist/main.js",
"artemis/resources/showcase_ui/main.js",
"__pycache__/a.pyc",
"traces/data.db",
"helper.apk",
"debug.keystore",
".git/config",
".roo/mcp.json",
]
for name in keep + omit:
target = tmp_path / name
for name in keep + tracked_builds + omit:
target = root / name
target.parent.mkdir(parents=True, exist_ok=True)
target.write_text("fixture")
(root / ".gitignore").write_text(
".env*\n!.env.example\n.venv/\nnode_modules/\ndist/\ntraces/\n.roo/\n__pycache__/\n"
)
git(root, "add", "--", ".gitignore", *keep, *tracked_builds)
git(root, "commit", "-m", "initial")
git(root, "remote", "add", "eapil", "https://git.example.invalid/team/project.git")
git(root, "config", "branch.main.remote", "eapil")
git(root, "config", "branch.main.merge", "refs/heads/main")
return root, keep, tracked_builds, omit
def test_archive_is_updatable_clean_checkout_without_local_data(repository, tmp_path):
root, keep, tracked_builds, omit = repository
output = tmp_path / "artifacts/source.zip"
count, digest = module.package_source(tmp_path, output)
assert count == len(keep)
count, digest = module.package_source(root, output)
assert count > len(keep)
assert len(digest) == 64
with zipfile.ZipFile(output) as archive:
assert archive.testzip() is None
assert set(archive.namelist()) == {f"artemis/{name}" for name in keep}
names = set(archive.namelist())
assert {f"artemis/{name}" for name in keep}.issubset(names)
assert not {f"artemis/{name}" for name in tracked_builds + omit} & names
assert "artemis/.git/config" in names
assert "artemis/.git/index" in names
assert not any(name.startswith("artemis/.git/logs/") for name in names)
assert not any(name.startswith("artemis/.git/hooks/") for name in names)
archive.extractall(tmp_path / "unpacked")
extracted = tmp_path / "unpacked/artemis"
assert git(extracted, "symbolic-ref", "--short", "HEAD") == "main"
assert git(extracted, "rev-parse", "HEAD") == git(root, "rev-parse", "HEAD")
assert git(extracted, "status", "--porcelain") == ""
assert git(extracted, "remote", "get-url", "origin") == (
"https://git.example.invalid/team/project.git"
)
assert git(extracted, "config", "--get", "branch.main.remote") == "origin"
(extracted / tracked_builds[0]).parent.mkdir(parents=True)
(extracted / tracked_builds[0]).write_text("new bundle")
assert git(extracted, "status", "--porcelain") == ""
def test_unpacked_archive_fast_forwards(repository, tmp_path):
root, _, tracked_builds, _ = repository
bare = tmp_path / "upstream.git"
bare.mkdir()
git(bare, "init", "--bare")
git(root, "remote", "add", "test-upstream", str(bare))
git(root, "push", "test-upstream", "main")
archive = tmp_path / "source.zip"
module.package_source(root, archive)
with zipfile.ZipFile(archive) as contents:
contents.extractall(tmp_path / "unpacked")
extracted = tmp_path / "unpacked/artemis"
git(extracted, "remote", "set-url", "origin", str(bare))
built_asset = extracted / tracked_builds[0]
built_asset.parent.mkdir(parents=True)
built_asset.write_text("generated after setup")
(root / "start.bat").write_text("updated")
git(root, "add", "--", "start.bat")
git(root, "commit", "-m", "update launcher")
git(root, "push", "test-upstream", "main")
from scripts.update_installation import update_source
assert update_source(extracted, port=1) == 10
assert (extracted / "start.bat").read_text() == "updated"
assert git(extracted, "status", "--porcelain") == ""
def test_packaging_rejects_uncommitted_changes_and_credentials(repository, tmp_path):
root, _, _, _ = repository
(root / "start.bat").write_text("local change")
with pytest.raises(ValueError, match="Commit tracked changes"):
module.package_source(root, tmp_path / "source.zip")
(root / "start.bat").write_text("fixture")
git(
root,
"remote",
"set-url",
"eapil",
"https://user:secret@git.example.invalid/team/project.git",
)
with pytest.raises(ValueError, match="credential-free HTTPS"):
module.package_source(root, tmp_path / "source.zip")