Visitar URL original
File a cycle's changelogs as a pull request by stonebig · Pull Request #2103 · winpython/winpython · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Find wppm from the checkout, not from luck
changelog_files.py imported wppm and CI answered ModuleNotFoundError.
Running a script puts the script's own directory on sys.path -- here
.github/scripts -- and not the checkout root, so the import fell through
to whatever else was reachable. On a developer machine that is the
installed wppm and everything looks fine; a runner installs none, so the
step died. The workflow job would have died the same way, not just the
tests.

The path is now derived from __file__ and inserted first, so the wppm
imported is the one being released.

The tests missed it for the same reason CI caught it, so they now run the
script by absolute path from an unrelated directory, with a decoy wppm
on PYTHONPATH. The insert puts the checkout ahead of that decoy; without
it the decoy wins and the test fails, on any machine.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MBk5k7WdpPvx4SUFyEk3U3
  • Loading branch information
stonebig and claude committed Sep 6, 2026
commit dab14efd1bd26c85a1def2454c140ab6c64f83cf
13 changes: 9 additions & 4 deletions .github/scripts/changelog_files.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,21 @@
only a checkout of the repository has that to compare against. Ordering is
`wppm.diff`'s job; this decides what to hand it.
Run from the checkout root, so `from wppm import diff` finds the wppm being
released rather than an installed one.
"""
import re
import shutil
import sys
from pathlib import Path

from wppm import diff
from wppm.diff import version # packaging, or pip's vendored copy
# Running a script puts the script's own directory on sys.path, not the
# checkout root, so wppm has to be found deliberately: this file is
# .github/scripts/changelog_files.py, hence two levels up. Without it the
# import finds whatever wppm happens to be installed, or -- as on a CI runner,
# which installs none -- nothing at all.
sys.path.insert(0, str(Path(__file__).resolve().parents[2]))

from wppm import diff # noqa: E402 the path above has to be set first
from wppm.diff import version # noqa: E402 packaging, or pip's vendored copy

# WinPythonslim-64bit-3.15.0.5b1.md -- the flavor may be empty, and the version
# may carry a release level, which is why the parser decides and not the regex
Expand Down
59 changes: 41 additions & 18 deletions tests/test_changelog_files.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
only the repository has.
"""
import importlib.util
import os
import shutil
import subprocess
import sys
Expand Down Expand Up @@ -87,6 +88,19 @@ def test_directories_are_skipped(self, changelog_files, tmp_path):
]


def run_script(*args, cwd, env=None):
"""The script by absolute path, from an unrelated directory.
It must not need to be run from the checkout: the workflow's own step and
these tests both invoke it as a path, which puts .github/scripts on
sys.path rather than the repository root.
"""
return subprocess.run(
[sys.executable, str(SCRIPT), *map(str, args)],
cwd=str(cwd), capture_output=True, text=True, env=env,
)


@needs_script
class TestEndToEnd:
"""Run the script the way the workflow runs it."""
Expand All @@ -111,12 +125,30 @@ def cycle(self, tmp_path):
(source / "hashes_3.15.0.5slimb1.md").write_text("x", encoding="utf-8")
return source, changelogs

def test_files_the_three_and_writes_the_history(self, cycle):
def test_wppm_comes_from_the_checkout(self, cycle, tmp_path):
"""A decoy wppm on PYTHONPATH must lose to the one being released.
Running a script puts the script's directory on sys.path, not the
checkout root, so without a deliberate insert the import falls through
to whatever else is reachable. On a machine with wppm installed that
looks fine -- which is how it once slipped past a green local run --
and on a CI runner, which installs none, it is ModuleNotFoundError.
The decoy makes the difference visible either way.
"""
source, changelogs = cycle
proc = subprocess.run(
[sys.executable, ".github/scripts/changelog_files.py", str(source), str(changelogs)],
cwd=REPO, capture_output=True, text=True,
decoy = tmp_path / "decoy"
(decoy / "wppm").mkdir(parents=True)
(decoy / "wppm" / "__init__.py").write_text(
"raise RuntimeError('decoy wppm imported')", encoding="utf-8"
)
env = {**os.environ, "PYTHONPATH": str(decoy)}
proc = run_script(source, changelogs, cwd=tmp_path, env=env)
assert proc.returncode == 0, proc.stderr
assert "decoy" not in proc.stderr

def test_files_the_three_and_writes_the_history(self, cycle, tmp_path):
source, changelogs = cycle
proc = run_script(source, changelogs, cwd=tmp_path)
assert proc.returncode == 0, proc.stderr
landed = sorted(p.name for p in changelogs.iterdir())
assert landed == [
Expand All @@ -127,12 +159,10 @@ def test_files_the_three_and_writes_the_history(self, cycle):
"requir.64-3_15_0_5slimb1.txt",
]

def test_the_history_names_the_release_it_compares_against(self, cycle):
def test_the_history_names_the_release_it_compares_against(self, cycle, tmp_path):
source, changelogs = cycle
subprocess.run(
[sys.executable, ".github/scripts/changelog_files.py", str(source), str(changelogs)],
cwd=REPO, capture_output=True, text=True, check=True,
)
proc = run_script(source, changelogs, cwd=tmp_path)
assert proc.returncode == 0, proc.stderr
history = (changelogs / "WinPythonslim-64bit-3.15.0.5b1_History.md").read_text(
encoding="utf-8"
)
Expand All @@ -144,18 +174,11 @@ def test_an_empty_metadata_directory_is_an_error(self, tmp_path):
source, changelogs = tmp_path / "src", tmp_path / "changelogs"
source.mkdir()
changelogs.mkdir()
proc = subprocess.run(
[sys.executable, ".github/scripts/changelog_files.py", str(source), str(changelogs)],
cwd=REPO, capture_output=True, text=True,
)
proc = run_script(source, changelogs, cwd=tmp_path)
assert proc.returncode != 0
assert "held no changelog" in proc.stdout + proc.stderr

def test_a_missing_directory_is_an_error(self, tmp_path):
proc = subprocess.run(
[sys.executable, ".github/scripts/changelog_files.py",
str(tmp_path / "nope"), str(tmp_path)],
cwd=REPO, capture_output=True, text=True,
)
proc = run_script(tmp_path / "nope", tmp_path, cwd=tmp_path)
assert proc.returncode != 0
assert "no such directory" in proc.stdout + proc.stderr
Loading