Conversation
Signed-off-by: Sodawyx <sodawyx@126.com>
There was a problem hiding this comment.
Pull request overview
Fixes the Unix installer’s “latest release tag” extraction so it works on macOS/BSD sed, and adds an integration test that runs scripts/install.sh end-to-end using mocked curl/uname/tar/shasum.
Changes:
- Update
scripts/install.shto use POSIX[[:space:]]*instead of non-portable\s*in thesedexpression that extractstag_name. - Add
tests/integration/test_install_script.pyto validate that the installer resolvesv0.1.0and constructs the expected download URL when run undershwith mocked tooling.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| scripts/install.sh | Makes latest GitHub release tag parsing portable across GNU/BSD sed. |
| tests/integration/test_install_script.py | Adds an integration test harness for install.sh with mocked external commands and API output. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| fake_bin / "curl", | ||
| f"""#!/usr/bin/env sh | ||
| set -eu | ||
| url="${{@:$#}}" |
Sodawyx
left a comment
There was a problem hiding this comment.
Thanks for the fix — the \s → [[:space:]] swap in install.sh is correct, minimal, and addresses a real macOS/BSD sed portability bug. Good problem statement in the PR description.
The integration test, however, has two issues that I think warrant changes before merge:
-
The mock
curluses bash-only syntax (${@:$#}) in a#!/usr/bin/env shscript. This will break on any Linux CI runner whereshis dash or busybox ash — i.e., almost all of them. The test would only happen to pass on macOS, which is an awkward outcome for a test guarding cross-platform installer behavior. See inline. -
The test does not actually exercise the bug it's named after. On a Linux runner with GNU
sed,\sworks, so the test would pass against the old regex too. As written it's a happy-path smoke test, not a regression pin. A small direct-sedunit test withLC_ALL=POSIX(or shadowingsedin the fake-bin) would lock the portability invariant down properly. See inline.
Two minor nits inline as well (executable bits, PATH lookup). Nothing else stands out — DCO sign-off is present, the install-script change is in scope, no docs impact.
(Submitting as a comment review since GitHub doesn't allow request-changes on one's own PR.)
| fake_bin / "curl", | ||
| f"""#!/usr/bin/env sh | ||
| set -eu | ||
| url="${{@:$#}}" |
There was a problem hiding this comment.
Portability bug — bash-only syntax in a sh mock.
${@:$#} is a bash array-slice extension and is not POSIX. The shebang here is #!/usr/bin/env sh, so on most Linux distros (Debian/Ubuntu where sh → dash, Alpine where sh → busybox ash) this will be a syntax error and the test will fail with curl: 1: Bad substitution (or similar) — even though the very point of this PR is to harden a script that has to run under non-bash sh.
It happens to work on macOS only because /bin/sh is bash-in-POSIX-mode there, which still recognizes the bash slice syntax. Ironic for a regression test targeting BSD sed portability.
A POSIX-clean way to grab the last positional parameter:
for arg in "$@"; do url="$arg"; doneor
shift $(( $# - 1 ))
url="$1"Same applies if/when other mocks need this idiom.
| path.chmod(path.stat().st_mode | stat.S_IXUSR) | ||
|
|
||
|
|
||
| def test_install_sh_parses_latest_release_tag_on_posix_sed(tmp_path): |
There was a problem hiding this comment.
The test name promises more than it delivers — it doesn't actually demonstrate the bug under POSIX sed.
The function is named test_install_sh_parses_latest_release_tag_on_posix_sed, but it just runs the script with the host's sed. On a Linux CI runner with GNU sed, \s already works, so this test passes against the old regex too — meaning it would not have caught the original bug, and it won't catch a regression of the same shape.
If you want this test to pin the portability fix, force POSIX behavior. Two options:
- Run
sedwithLC_ALL=POSIXplus--posix(GNU only) in a wrapper, or shadowsedvia the same fake-bin trick used forcurl/tarand have the mock invokegsed --posix/busybox sed. - Add a tiny unit test that calls
sed -E 's/.*"tag_name":[[:space:]]*"([^"]+)".*/\1/'directly withLC_ALL=POSIXagainst a sample line, asserting it returnsv0.1.0— much smaller surface, and it'd actually fail before the fix.
Without that, this PR's biggest claim (the regression test) is mostly a smoke test for the happy path. Worth keeping the integration test, but please add something that actually locks down the portability invariant.
|
|
||
| def _write_executable(path: Path, content: str) -> None: | ||
| path.write_text(content) | ||
| path.chmod(path.stat().st_mode | stat.S_IXUSR) |
There was a problem hiding this comment.
Nit: only S_IXUSR is set, not group/other.
If anything ever runs the test under a different uid (containerized CI with a mismatched user, sudo nuances), execution fails confusingly. Cheap fix: | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH, mirroring what chmod +x does. Not blocking, but trivial to harden.
| ["sh", str(repo_root / "scripts" / "install.sh")], | ||
| env=env, | ||
| text=True, | ||
| stdout=subprocess.PIPE, |
There was a problem hiding this comment.
Nit: os.environ['PATH'] will KeyError if PATH is unset.
Edge case in stripped-down sandboxes / minimal CI containers. os.environ.get("PATH", "") is safer and reads identically. Minor.
| info "Resolving latest release from github.com/${REPO}" | ||
| VERSION=$($DOWNLOAD "https://api.github.com/repos/${REPO}/releases/latest" \ | ||
| | grep '"tag_name"' | head -1 | sed -E 's/.*"tag_name":\s*"([^"]+)".*/\1/') | ||
| | grep '"tag_name"' | head -1 | sed -E 's/.*"tag_name":[[:space:]]*"([^"]+)".*/\1/') |
There was a problem hiding this comment.
The fix itself is correct: [[:space:]] is POSIX and works on both GNU and BSD sed, while \s is a GNU extension that BSD/macOS sed does not interpret as whitespace. Diff is appropriately surgical — good.
One adjacent thought (not blocking): the parsing pipeline grep | head -1 | sed -E '...' is still a bit fragile — e.g., it would break if the assets section somewhere happened to contain a line whose substring matched "tag_name" (rare, but trivially possible if a release name itself contains that string). If you ever want to harden this further, a small python3 -c "import json,sys; print(json.load(sys.stdin)['tag_name'])" (gated on command -v python3) is one option. Out of scope for this PR.
Signed-off-by: Sodawyx <sodawyx@126.com>
Signed-off-by: Sodawyx <sodawyx@126.com>
Signed-off-by: Sodawyx <sodawyx@126.com>
Summary
scripts/install.shby replacing non-portable\s*with POSIX[[:space:]]*Problem
On macOS/BSD
sed,\sis not treated as a whitespace matcher. The installer therefore kept the full JSON line as the version, producing an invalid download URL such as:Tests
sh -n scripts/install.sh.venv/bin/pytest tests/integration/test_install_script.py -v.venv/bin/ruff check tests/integration/test_install_script.py