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
18 changes: 18 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,24 @@ jobs:
coverageFile: coverage.xml
token: ${{ secrets.GITHUB_TOKEN }}

test-content-python-versions:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: astral-sh/setup-uv@68559e99cc807c1eb7e9238fd43a15bba460eec2 # v10.3.3
with:
version: ">=0.9.0"
- run: uv sync --python 3.11 --group test
- name: Install content Pythons
run: |
mapfile -t versions < <(uv run --no-sync python scripts/content_python_versions.py)
test "${#versions[@]}" -gt 0
uv run --no-sync uv python install --no-bin "${versions[@]}"
- name: Test manifests
run: |
uv run --no-sync pytest tests/test_manifest_content_pythons.py --junitxml=content-python-results.xml
uv run --no-sync python -c 'import xml.etree.ElementTree as ET; root = ET.parse("content-python-results.xml"); assert not root.findall(".//skipped"), "Content Python cases were skipped"'

prerelease-test:
runs-on: ubuntu-latest
continue-on-error: true
Expand Down
28 changes: 28 additions & 0 deletions scripts/content_python_versions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import json
import subprocess

from packaging.version import Version


def content_python_versions():
result = subprocess.run(
["uv", "python", "list", "--only-downloads", "--output-format", "json"],
check=True,
capture_output=True,
text=True,
)
versions = {
(entry["version_parts"]["major"], entry["version_parts"]["minor"])
for entry in json.loads(result.stdout)
if entry["implementation"] == "cpython"
and entry["variant"] == "default"
and not Version(entry["version"]).is_prerelease
and (entry["version_parts"]["major"], entry["version_parts"]["minor"]) >= (3, 8)
}
if not versions:
raise RuntimeError("uv did not list any supported CPython downloads")
return ["%s.%s" % version for version in sorted(versions)]


if __name__ == "__main__":
print("\n".join(content_python_versions()))
42 changes: 42 additions & 0 deletions tests/test_manifest_content_pythons.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import json
import os
import subprocess

import pytest
from click.testing import CliRunner

from rsconnect.main import cli
from scripts.content_python_versions import content_python_versions


@pytest.mark.parametrize("version", content_python_versions())
def test_manifest_uses_content_python_environment(version, tmp_path):
found = subprocess.run(
["uv", "python", "find", "--no-project", "--no-python-downloads", "cpython@" + version],
capture_output=True,
text=True,
)
if found.returncode:
if "No interpreter found" in found.stderr:
pytest.skip("CPython %s is not installed" % version)
found.check_returncode()

venv = tmp_path / "venv"
subprocess.run(["uv", "venv", "--python", found.stdout.strip(), str(venv)], check=True)
python = venv / ("Scripts/python.exe" if os.name == "nt" else "bin/python")
subprocess.run(["uv", "pip", "install", "--python", str(python), "idna==3.10"], check=True)
expected_version = subprocess.check_output(
[str(python), "-c", "import platform; print(platform.python_version())"], text=True
).strip()

project = tmp_path / "project"
project.mkdir()
(project / "app.py").write_text("app = None\n")
result = CliRunner().invoke(
cli, ["write-manifest", "api", str(project), "--python", str(python), "--force-generate", "--exclude-renv"]
)
assert result.exit_code == 0, result.output
manifest = json.loads((project / "manifest.json").read_text())
assert manifest["python"]["version"] == expected_version
assert manifest["python"]["package_manager"]["package_file"] == "requirements.txt"
assert "idna==3.10" in (project / "requirements.txt").read_text().splitlines()
Loading