Skip to content

fix: make QA deployment cleanup import reliably - #60

Merged
v-Kaefer merged 3 commits into
developfrom
fix/issue-59-qa-cleanup-import
Aug 11, 2026
Merged

fix: make QA deployment cleanup import reliably#60
v-Kaefer merged 3 commits into
developfrom
fix/issue-59-qa-cleanup-import

Conversation

@v-Kaefer

@v-Kaefer v-Kaefer commented Aug 11, 2026

Copy link
Copy Markdown
Owner

Linked Issue

Milestone

  • M0

Related PRs

Summary

  • Fixes the Q.A deployment cleanup crash caused by project_setup not being importable when the script is executed directly from tests/qa.
  • Bootstraps the repository root in the cleanup CLI before importing the GPA package.
  • Adds a regression test that executes the exact cleanup CLI from outside the repository with PYTHONPATH removed.

How to test

  • Test type: automated regression
  • Steps: run Repository quality; the new subprocess test must execute python tests/qa/cleanup_deployments.py --help from an external working directory without an installed package/PYTHONPATH dependency.

Evidence

  • Regression test added for the previously failing import path
  • Existing cleanup API behavior unchanged
  • No permission expansion

Known risks

  • None. The change only fixes Python module discovery before the existing cleanup code runs.

DoD checklist

  • Scope implemented as defined
  • Tests added
  • Previous failure mode reproduced by contract
  • No known critical breakage introduced

Summary by CodeRabbit

  • Bug Fixes

    • Improved the cleanup command’s reliability when launched directly from outside the project directory.
    • The command now works without requiring manual environment-path configuration.
    • Added validation to ensure the command displays its help information successfully in this scenario.
  • Tests

    • Expanded automated coverage for running cleanup workflows from different locations and environments.

@github-actions

github-actions Bot commented Aug 11, 2026

Copy link
Copy Markdown

PR Sync

@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The cleanup CLI now adds the repository root to sys.path before project-local imports. Tests verify that cleanup_deployments.py --help succeeds from outside the repository without PYTHONPATH.

Changes

QA cleanup CLI import resolution

Layer / File(s) Summary
Repository-root import bootstrap
tests/qa/cleanup_deployments.py
The script derives the repository root from its location and adds it to sys.path before importing project-local modules.
Isolated CLI regression coverage
tests/test_qa_workflows.py
The tests run the cleanup CLI from a temporary directory without PYTHONPATH and verify successful help output. Existing cleanup contract assertions remain covered.

Estimated code review effort: 2 (Simple) | ~10 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: reliable imports for QA deployment cleanup.
Description check ✅ Passed The description covers the linked issue, summary, testing, evidence, risks, and completion checklist with relevant details.
Linked Issues check ✅ Passed The changes satisfy issue #59 by bootstrapping the repository root and adding the required external-directory regression test.
Out of Scope Changes check ✅ Passed All changes directly support the import fix and its regression coverage; no unrelated code changes are identified.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/issue-59-qa-cleanup-import

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@v-Kaefer
v-Kaefer merged commit 1cbd49e into develop Aug 11, 2026
5 of 6 checks passed

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/test_qa_workflows.py`:
- Around line 142-160: Update
test_cleanup_cli_imports_from_outside_repository_without_pythonpath to invoke
the child interpreter with the -S flag, ensuring an isolated environment without
site-packages while retaining the existing PYTHONPATH removal and assertions.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 5b96e93b-c72c-444f-b949-ab158cf338c5

📥 Commits

Reviewing files that changed from the base of the PR and between 7f9a560 and c07532b.

📒 Files selected for processing (2)
  • tests/qa/cleanup_deployments.py
  • tests/test_qa_workflows.py

Comment on lines +142 to +160
def test_cleanup_cli_imports_from_outside_repository_without_pythonpath(self):
script = ROOT / "tests/qa/cleanup_deployments.py"
env = os.environ.copy()
env.pop("PYTHONPATH", None)

with tempfile.TemporaryDirectory() as temporary_directory:
result = subprocess.run(
[sys.executable, str(script), "--help"],
cwd=temporary_directory,
env=env,
capture_output=True,
text=True,
timeout=30,
check=False,
)

self.assertEqual(result.returncode, 0, msg=result.stderr or result.stdout)
self.assertIn("Clean historical GitHub deployments", result.stdout)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -eu
printf '%s\n' '--- candidate files ---'
git ls-files | rg '(^|/)(tests/test_qa_workflows\.py|cleanup_deployments\.py|project_setup|pyproject\.toml|setup\.py|setup\.cfg|tox\.ini|pytest\.ini|conftest\.py)$' || true
printf '%s\n' '--- test context ---'
sed -n '110,175p' tests/test_qa_workflows.py
printf '%s\n' '--- cleanup script outline ---'
if command -v ast-grep >/dev/null 2>&1; then ast-grep outline tests/qa/cleanup_deployments.py; fi
printf '%s\n' '--- cleanup script imports and bootstrap ---'
sed -n '1,120p' tests/qa/cleanup_deployments.py
printf '%s\n' '--- project_setup references ---'
rg -n --glob '*.py' 'project_setup|REPOSITORY_ROOT|PYTHONPATH|sys\.path|site-packages' .
printf '%s\n' '--- packaging/test configuration ---'
for f in pyproject.toml setup.py setup.cfg tox.ini pytest.ini tests/conftest.py conftest.py; do
  if [ -f "$f" ]; then
    echo "### $f"
    sed -n '1,220p' "$f"
  fi
done

Repository: v-Kaefer/Github-Project-Automation

Length of output: 23208


🏁 Script executed:

#!/bin/bash
set -eu
printf '%s\n' '--- imported module and package imports ---'
sed -n '1,100p' project_setup/github.py
if [ -f project_setup/__init__.py ]; then sed -n '1,80p' project_setup/__init__.py; fi
printf '%s\n' '--- standalone -S masking probe ---'
python3 - <<'PY'
import os
import pathlib
import subprocess
import sys
import tempfile

with tempfile.TemporaryDirectory() as td:
    root = pathlib.Path(td)
    checkout = root / "checkout"
    installed = root / "installed"
    outside = root / "outside"
    for package_root, marker in ((checkout, "checkout"), (installed, "installed")):
        package = package_root / "project_setup"
        package.mkdir(parents=True)
        (package / "__init__.py").write_text("")
        (package / "github.py").write_text(f"MARKER = {marker!r}\n")
    script = outside / "probe.py"
    outside.mkdir()
    script.write_text(
        "import sys\n"
        f"sys.path.insert(0, {str(checkout)!r})\n"
        "from project_setup.github import MARKER\n"
        "print(MARKER)\n"
    )
    env = os.environ.copy()
    env["PYTHONPATH"] = str(installed)
    env.pop("PYTHONHOME", None)
    normal = subprocess.run(
        [sys.executable, str(script)],
        env=env, capture_output=True, text=True, check=False,
    )
    isolated = subprocess.run(
        [sys.executable, "-S", str(script)],
        env=env, capture_output=True, text=True, check=False,
    )
    print("normal:", normal.returncode, normal.stdout.strip(), normal.stderr.strip())
    print("-S:", isolated.returncode, isolated.stdout.strip(), isolated.stderr.strip())
    assert normal.returncode == 0 and isolated.returncode == 0
    assert normal.stdout.strip() == "checkout"
    assert isolated.stdout.strip() == "checkout"
PY

Repository: v-Kaefer/Github-Project-Automation

Length of output: 3749


🏁 Script executed:

#!/bin/bash
set -eu
python3 - <<'PY'
import os
import pathlib
import subprocess
import sys
import tempfile

with tempfile.TemporaryDirectory() as td:
    root = pathlib.Path(td)
    startup = root / "startup"
    installed = root / "installed"
    script = root / "probe.py"
    startup.mkdir()
    package = installed / "project_setup"
    package.mkdir(parents=True)
    (package / "__init__.py").write_text("")
    (package / "github.py").write_text("MARKER = 'installed'\n")
    (startup / "sitecustomize.py").write_text(
        f"import sys\nsys.path.insert(0, {str(installed)!r})\n"
    )
    script.write_text(
        "from project_setup.github import MARKER\n"
        "print(MARKER)\n"
    )

    env = os.environ.copy()
    env["PYTHONPATH"] = str(startup)
    normal = subprocess.run(
        [sys.executable, str(script)],
        env=env, capture_output=True, text=True, check=False,
    )
    isolated = subprocess.run(
        [sys.executable, "-S", str(script)],
        env=env, capture_output=True, text=True, check=False,
    )
    print("normal:", normal.returncode, normal.stdout.strip(), normal.stderr.strip())
    print("-S:", isolated.returncode, isolated.stdout.strip(), isolated.stderr.strip())
    assert normal.returncode == 0 and normal.stdout.strip() == "installed"
    assert isolated.returncode != 0
PY

Repository: v-Kaefer/Github-Project-Automation

Length of output: 388


Use an isolated child interpreter for this regression test.

Removing PYTHONPATH does not remove project_setup from site-packages. Run the child with sys.executable -S; project_setup.github uses only standard-library modules.

🧰 Tools
🪛 ast-grep (0.45.1)

[error] 147-155: Command coming from incoming request
Context: subprocess.run(
[sys.executable, str(script), "--help"],
cwd=temporary_directory,
env=env,
capture_output=True,
text=True,
timeout=30,
check=False,
)
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(subprocess-from-request)

🪛 Ruff (0.16.1)

[error] 148-148: subprocess call: check for execution of untrusted input

(S603)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/test_qa_workflows.py` around lines 142 - 160, Update
test_cleanup_cli_imports_from_outside_repository_without_pythonpath to invoke
the child interpreter with the -S flag, ensuring an isolated environment without
site-packages while retaining the existing PYTHONPATH removal and assertions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant