Skip to content

Commit ea24b47

Browse files
codexByron
authored andcommitted
ci(downstream): test releases with CLI and Gix backends
The downstream runner always installed plain `GitPython` into a fresh virtual environment. Starting it from a Gix-enabled environment therefore still exercised the CLI backend and left downstream Gix compatibility untested. Add `--backend {cli,gix}`, retaining CLI as the default. Gix runs install this editable checkout with `.[gix]` and verify the selected backend before running downstream tests. Include the backend in `result.json`, status output and retained directory names. Expand the five-project CI matrix to ten independent jobs and document local backend selection. Validated all five selected suites with official `GixPython` 0.1.0 first, then CLI, using the existing Python 3.12.14 interpreter and Git 2.54.0: 72 passed and one credential-dependent DataHub skip per backend. Tested LangChain Community 0.4.2, MLflow 3.17.0, Bandit 1.9.4, SWE-bench 5.0.2, and DataHub 1.7.0.14 with matching release sources on both backends. Verified the default CLI installation and backend mismatch rejection in both directions, frozen dependencies, result metadata, and workflow matrix wiring. `ruff check`, `ruff format --check`, runner `--help`, and `git diff --check` passed. The ten local runs took 69.75 seconds for Gix and 80.80 seconds for CLI including source and environment setup; these are downstream validation timings, not controlled performance benchmarks.
1 parent 105114d commit ea24b47

3 files changed

Lines changed: 37 additions & 15 deletions

File tree

‎.github/workflows/downstream.yml‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ jobs:
1717
fail-fast: false
1818
matrix:
1919
project: [langchain, mlflow, bandit, swebench, datahub]
20+
backend: [gix, cli]
2021
steps:
2122
- uses: actions/checkout@v7
2223
with:
@@ -34,4 +35,4 @@ jobs:
3435
version: '0.12.13'
3536
enable-cache: false
3637
- name: Test latest release against this checkout
37-
run: uv run test/downstream/run.py "${{ matrix.project }}"
38+
run: uv run test/downstream/run.py "${{ matrix.project }}" --backend "${{ matrix.backend }}"

‎test/downstream/README.md‎

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,24 @@ Run released projects' GitPython-related tests against this checkout with
55

66
```sh
77
uv run test/downstream/run.py bandit
8+
uv run test/downstream/run.py bandit --backend gix
89
uv run test/downstream/run.py bandit --version 1.9.4
9-
for project in langchain mlflow bandit swebench datahub; do
10-
uv run test/downstream/run.py "$project"
10+
for backend in gix cli; do
11+
for project in langchain mlflow bandit swebench datahub; do
12+
uv run test/downstream/run.py "$project" --backend "$backend"
13+
done
1114
done
1215
```
1316

1417
The default resolves the latest release from PyPI each time. `--version` reproduces
15-
a release; `--python` selects the test interpreter (default: 3.12). The runner
16-
creates a fresh environment, replaces GitPython with this editable checkout, and checks
17-
the imported module before testing. Upstream test files are not modified.
18+
a release; `--python` selects the test interpreter (default: 3.12), including an
19+
existing interpreter's path. `--backend` selects `cli` (the default) or `gix`.
20+
The runner creates a fresh environment and installs this editable checkout,
21+
adding the official GixPython release through `.[gix]` for Gix runs. It verifies
22+
both the imported checkout and the selected backend before testing, and records
23+
the backend in `result.json` and the default work-directory name. Starting the
24+
runner from a Gix environment alone does not select Gix in the isolated test
25+
environment. Upstream test files are not modified.
1826

1927
Source, the environment, frozen requirements, JUnit results, and release provenance
2028
are retained under `.cache/downstream/`. `--work-dir PATH` uses a new directory
@@ -35,7 +43,7 @@ and test-only dependencies are excluded. Downloads are not unique installations.
3543
| Project | Distribution downloads | Last tested release | Selected coverage |
3644
| --- | ---: | --- | --- |
3745
| LangChain Community | 27,882,881 | 0.4.2 | 2 upstream GitLoader tests: real clones, commits, checkout, tree traversal, ignored paths, and remote validation |
38-
| MLflow (`mlflow-skinny`) | 25,850,354 | 3.16.1 | 47 upstream tests: 31 repository/project/model-versioning cases plus 16 Git context and credential-redaction contract cases |
46+
| MLflow (`mlflow-skinny`) | 25,850,354 | 3.17.0 | 47 upstream tests: 31 repository/project/model-versioning cases plus 16 Git context and credential-redaction contract cases |
3947
| Bandit | 24,935,372 | 1.9.4 | 12 upstream baseline CLI tests: real repository creation, commits, branches, resets, discovery, and dirty state |
4048
| SWE-bench | 22,942,741 | 5.0.2 | 4 supplemental integration cases for `AutoContextManager`; no upstream tests cover its GitPython callers |
4149
| DataHub (`acryl-datahub`) | 5,019,402 | 1.7.0.14 | 7 upstream tests passed, 1 credential-dependent skip: public clone/checkout, SSH timeout, exception/redaction contracts, and configuration |
@@ -94,6 +102,8 @@ The private SSH-clone test retains its upstream skip: the runner removes its
94102
credential variable and needs no private credentials. `ssh`, public GitLab
95103
access, and local TCP sockets are needed for the selected tests.
96104

97-
CI runs the same command against the latest release and fails when no test passes,
98-
including when all selected tests are skipped. Test dependency ranges only supply
99-
the upstream test harness; they do not pin the dependent's release.
105+
CI runs all five projects against the latest release with both CLI and Gix
106+
backends, for ten independent jobs. A backend mismatch or a run in which no
107+
test passes fails the job, including when all selected tests are skipped.
108+
Test dependency ranges only supply the upstream test harness; they do not pin
109+
the dependent's release.

‎test/downstream/run.py‎

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ def main():
8888
profiles = json.loads((HERE / "projects.json").read_text())
8989
parser = argparse.ArgumentParser(description=__doc__)
9090
parser.add_argument("project", choices=profiles)
91+
parser.add_argument("--backend", choices=("cli", "gix"), default="cli", help="Backend to install and verify")
9192
parser.add_argument("--version", help="Release to reproduce; defaults to PyPI's latest release")
9293
parser.add_argument("--python", default="3.12", help="Python used by the isolated test environment")
9394
parser.add_argument("--work-dir", type=Path, help="New directory for retained source, environment, and results")
@@ -100,8 +101,8 @@ def main():
100101
else:
101102
cache = CHECKOUT / ".cache" / "downstream"
102103
cache.mkdir(parents=True, exist_ok=True)
103-
work = Path(tempfile.mkdtemp(prefix=f"{args.project}-{version}-", dir=cache))
104-
print(f"Testing {args.project} {version}; retained work directory: {work}", flush=True)
104+
work = Path(tempfile.mkdtemp(prefix=f"{args.project}-{version}-{args.backend}-", dir=cache))
105+
print(f"Testing {args.project} {version} with {args.backend}; retained work directory: {work}", flush=True)
105106

106107
config = work / "gitconfig"
107108
config.write_text(
@@ -134,25 +135,35 @@ def expand(value):
134135
if profile.get("no_deps"):
135136
run(install + ["--no-deps"] + list(map(expand, profile["no_deps"])), env=env)
136137
# Replace the released dependency even if the downstream pins another version.
137-
run(install + ["--reinstall-package", "gitpython", "-e", str(CHECKOUT)], env=env)
138+
requirement = str(CHECKOUT) + ("[gix]" if args.backend == "gix" else "")
139+
run(install + ["--reinstall-package", "gitpython", "-e", requirement], env=env)
138140

139141
# Check the import used by tests and by downstream Python subprocesses.
140142
run(
141143
[
142144
str(python),
143145
"-c",
144-
"import os, pathlib, git; "
146+
"import os, pathlib, sys, git; from git import _backend; "
145147
"expected = pathlib.Path(os.environ['GITPYTHON_CHECKOUT']) / 'git' / '__init__.py'; "
146148
"assert pathlib.Path(git.__file__).resolve() == expected.resolve(), git.__file__; "
149+
"print('GitPython backend:', _backend.name); "
150+
"assert _backend.name == sys.argv[1], f'Expected {sys.argv[1]} backend, got {_backend.name}'; "
147151
"print('GitPython:', git.__file__); print(git.Git().version()); "
148152
"assert git.Git().version_info >= (2, 52), 'Git 2.52 or newer is required'",
153+
args.backend,
149154
],
150155
cwd=source,
151156
env=env,
152157
)
153158
with (work / "requirements-frozen.txt").open("w") as output:
154159
run(["uv", "pip", "freeze", "--python", str(python)], env=env, stdout=output)
155-
result = {"project": args.project, "version": version, "source": provenance, "gitpython": str(CHECKOUT)}
160+
result = {
161+
"project": args.project,
162+
"version": version,
163+
"backend": args.backend,
164+
"source": provenance,
165+
"gitpython": str(CHECKOUT),
166+
}
156167
result_path = work / "result.json"
157168
result_path.write_text(json.dumps(result, indent=2) + "\n")
158169
report = work / "junit.xml"

0 commit comments

Comments
 (0)