Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,19 @@ jobs:
python -m pip install --upgrade pip
python -m pip install -r tools/requirements.txt
python -m pip install -r tools/server/requirements.txt
python -m pip install pyinstaller
- name: Build with PyInstaller
run: pyinstaller --noconfirm --clean tools/server/citeseal_server.spec
run: python -m PyInstaller --noconfirm --clean --distpath tools/dist --workpath tools/build tools/server/citeseal_server.spec
- name: Verify exe
run: |
tools\dist\citeseal_server.exe &
$env:CITESEAL_PORT = "8765"
Start-Sleep -Seconds 5
(Invoke-WebRequest -UseBasicParsing "http://127.0.0.1:8765/api/health").Content
$server = Start-Process -FilePath "tools/dist/citeseal_server.exe" -PassThru
try {
Start-Sleep -Seconds 5
(Invoke-WebRequest -UseBasicParsing "http://127.0.0.1:8765/api/health").Content
} finally {
Stop-Process -Id $server.Id -Force -ErrorAction SilentlyContinue
}
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
Expand Down
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [v0.7.1] - 2026-07-20

Maintenance release restoring cross-platform demo execution and documenting the repository maintenance process.
Maintenance release restoring cross-platform demo execution and release artifact builds, while documenting the repository maintenance process.

### Fixed
- Made the live demo's terminal output ASCII-safe and its JSON file I/O explicitly UTF-8, preventing `UnicodeEncodeError` failures on legacy Windows console encodings.
- Added a `cp1252` subprocess regression test so Windows console compatibility remains covered by the full test suite.
- Repaired the Windows release job by installing and invoking PyInstaller explicitly, fixing its output path, and setting the health-check port before server startup.
- Corrected the Linux tarball source layout so bundled scripts remain directly importable; added release packaging contract tests for both platforms.

### Added
- ADR (Architecture Decision Records): `docs/adr/0001-agent-bundle-as-agent-consumption-layer.md`, `docs/adr/0002-local-first-boundary.md`
Expand Down
69 changes: 69 additions & 0 deletions tests/test_release_contract.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
"""Release packaging contracts for the distributable server artifacts."""
from __future__ import annotations

import os
import re
import subprocess
from pathlib import Path

import pytest


ROOT = Path(__file__).resolve().parents[1]


def test_windows_release_installs_pyinstaller_before_build() -> None:
workflow = (ROOT / ".github" / "workflows" / "release.yml").read_text(
encoding="utf-8"
)
windows_job = workflow.split(" windows-exe:", 1)[1].split(
" linux-tarball:", 1
)[0]
before_build, after_build = windows_job.split(
" - name: Build with PyInstaller", 1
)

assert re.search(
r"python -m pip install(?:[^\n]*\s)?pyinstaller(?:[<>=!~].*)?$",
before_build,
flags=re.IGNORECASE | re.MULTILINE,
), "Windows release job must install PyInstaller before invoking it"
assert "python -m PyInstaller" in after_build
assert "--distpath tools/dist" in after_build


def test_windows_release_sets_health_port_before_starting_server() -> None:
workflow = (ROOT / ".github" / "workflows" / "release.yml").read_text(
encoding="utf-8"
)
windows_job = workflow.split(" windows-exe:", 1)[1].split(
" linux-tarball:", 1
)[0]
verify_step = windows_job.split(" - name: Verify exe", 1)[1].split(
" - name: Upload artifact", 1
)[0]

port_index = verify_step.find('$env:CITESEAL_PORT = "8765"')
start_index = verify_step.find("Start-Process")
health_index = verify_step.find("http://127.0.0.1:8765/api/health")
assert min(port_index, start_index, health_index) >= 0
assert port_index < start_index < health_index


@pytest.mark.skipif(os.name == "nt", reason="Linux bundle contract runs on POSIX")
def test_linux_bundle_copies_scripts_into_flat_import_path(tmp_path: Path) -> None:
build_script = ROOT / "tools" / "server" / "build_linux.sh"
copy_line = next(
line.strip()
for line in build_script.read_text(encoding="utf-8").splitlines()
if line.strip().startswith('cp -r "$TOOLS/scripts')
)
stage_root = tmp_path / "citeseal_server-linux-x64"
(stage_root / "scripts").mkdir(parents=True)
env = os.environ.copy()
env.update({"TOOLS": str(ROOT / "tools"), "ROOT": str(stage_root)})

subprocess.run(["bash", "-c", copy_line], env=env, check=True)

assert (stage_root / "scripts" / "ci_common.py").is_file()
assert not (stage_root / "scripts" / "scripts").exists()
4 changes: 2 additions & 2 deletions tools/server/build_linux.sh
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ mkdir -p "$ROOT/bin" "$ROOT/venv" "$ROOT/scripts"

echo "[1/5] Copying source..."
cp -r "$TOOLS/server" "$ROOT/server"
cp -r "$TOOLS/scripts" "$ROOT/scripts"
cp "$TOOLS/citeseal.py" "$ROOT/"
cp -r "$TOOLS/scripts/." "$ROOT/scripts/"
cp "$TOOLS/citeseal.py" "$ROOT/"
cp "$TOOLS/requirements.txt" "$ROOT/"
cp "$TOOLS/server/requirements.txt" "$ROOT/server-requirements.txt"

Expand Down
Loading