ci: make the CI workflow portable across hosts - #12
Conversation
There was a problem hiding this comment.
Pull request overview
This PR aims to make the GitHub Actions CI workflow behave consistently across different CI hosts/runners (including root-run containers) and to prevent GitHub-hosted publishing/artifact steps from running on non-GitHub hosts.
Changes:
- Updated CI workflow to install
uvvia its upstream installer and to letuvfetch the requested Python version via--python, plus addedworkflow_dispatchand broadened PR triggering. - Gated GitHub-hosted publishing workflows (PyPI trusted publishing / GHCR) and the CI coverage artifact upload to run only on
https://github.com. - Updated file tool tests to exercise error paths reliably even when the test suite runs as root by using an
ENOTDIR-style failure setup.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| tests/test_file_tools.py | Makes error-handling tests root-safe by forcing ENOTDIR via “parent is a file” setup. |
| .github/workflows/ci.yml | Reworks CI to install uv via script + --python, adds workflow_dispatch, broadens PR coverage, gates artifact upload on GitHub. |
| .github/workflows/pypi-publish.yml | Gates PyPI publish job to GitHub-hosted environment via github.server_url. |
| .github/workflows/docker-publish.yml | Gates GHCR publish job to GitHub-hosted environment via github.server_url. |
Suppressed comments (1)
.github/workflows/ci.yml:53
- Same silent-failure risk here: without
pipefail, a failedcurlin the installer pipeline may not fail the step. Enableset -euo pipefail(and bash) so the installation step fails immediately on download errors.
- name: Install uv
run: |
curl -LsSf https://astral.sh/uv/install.sh | sh
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
| - name: Install uv | ||
| uses: astral-sh/setup-uv@v5 | ||
| with: | ||
| enable-cache: true | ||
| run: | | ||
| curl -LsSf https://astral.sh/uv/install.sh | sh | ||
| echo "$HOME/.local/bin" >> "$GITHUB_PATH" |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 6 out of 6 changed files in this pull request and generated 1 comment.
Suppressed comments (2)
.github/workflows/ci.yml:53
- Same supply-chain concern as the lint job:
curl ... | shexecutes a remote installer script without version pinning or verification, which reduces reproducibility and increases risk across CI hosts.
- name: Install uv
run: |
curl -LsSf https://astral.sh/uv/install.sh | sh
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
plugins/tsugite-daemon/tsugite_daemon/auth.py:158
- TokenStore.validate() only holds the lock while reading the in-memory ephemeral token, then releases it before consulting SQLite and before returning. That allows a revoked persistent token to be accepted if revoke() interleaves between the DB read and the return, and it also weakens the concurrency guarantee this change is trying to enforce. Consider holding the lock for the entire validation path (including the SQLite read) so validate() and revoke() are mutually exclusive.
def validate(self, token: str) -> tuple[bool, str]:
"""Validate a token. Returns (valid, identity)."""
h = self._hash(token)
with self._lock:
t = self._tokens.get(h)
t = t or self._persistent_token(h)
| - name: Install uv | ||
| uses: astral-sh/setup-uv@v5 | ||
| with: | ||
| enable-cache: true | ||
| run: | | ||
| curl -LsSf https://astral.sh/uv/install.sh | sh | ||
| echo "$HOME/.local/bin" >> "$GITHUB_PATH" |
256785a to
9721025
Compare
9721025 to
f68da69
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 8 out of 8 changed files in this pull request and generated 1 comment.
Suppressed comments (1)
plugins/tsugite-daemon/tsugite_daemon/auth.py:165
- Locking in
validate()is currently split across multiple sections, which allows a concrete race withrevoke(): a thread can read an ephemeral token under the lock (line 156–157), release the lock, then another thread revokes the token, and the first thread still returnsTruebased on the staletreference. To make validation linearizable for the in-memory token map, holdself._lockfor the whole validation path that depends on_tokens(lookup + expiry check + potential removal), and (since this is anRLock) you can also perform the persistent lookup within the same critical section if desired.
def validate(self, token: str) -> tuple[bool, str]:
"""Validate a token. Returns (valid, identity)."""
h = self._hash(token)
with self._lock:
t = self._tokens.get(h)
t = t or self._persistent_token(h)
if not t:
return False, ""
if t.expires_at and t.expires_at < datetime.now(timezone.utc).isoformat():
with self._lock:
self._tokens.pop(h, None)
return False, ""
return True, t.identity
| run: | | ||
| curl -LsSf https://astral.sh/uv/install.sh | sh | ||
| echo "$HOME/.local/bin" >> "$GITHUB_PATH" |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 8 out of 8 changed files in this pull request and generated 1 comment.
Suppressed comments (2)
.github/workflows/ci.yml:28
- The
curl | shpipeline can mask installer failures because the default bash-edoesn’t enablepipefail. Ifcurlfails,shmay still exit 0 and the step can proceed with uv missing, producing confusing downstream errors. Addset -o pipefail(orset -euo pipefail) before the pipeline so the step fails reliably on download errors.
run: |
curl -LsSf https://astral.sh/uv/install.sh | sh
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
.github/workflows/ci.yml:53
- Same
curl | shpipeline issue here: withoutpipefail, a failed download can still result in a successful step status, making later failures harder to diagnose. Addset -o pipefail(orset -euo pipefail) before the pipeline.
run: |
curl -LsSf https://astral.sh/uv/install.sh | sh
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
| def validate(self, token: str) -> tuple[bool, str]: | ||
| """Validate a token. Returns (valid, identity).""" | ||
| h = self._hash(token) | ||
| t = self._tokens.get(h) or self._persistent_token(h) | ||
| with self._lock: | ||
| t = self._tokens.get(h) |
Makes the CI workflow portable across CI hosts, and fixes the test failure that exposed.
ci:astral-sh/setup-uvaction, and let uv fetch the interpreter (--python X.Y). That also removes thesetup-pythondependency, leavingcheckout,setup-nodeandupload-artifactas the only actions.uses:refs bare so the file is not pinned to one host.github.server_url, so they run only where those services exist. Tag-triggered publishing on GitHub is unchanged.workflow_dispatch, and stop restrictingpull_requestto master so branch PRs get CI.test:test_write_file_error_handlingandtest_create_directory_errorasserted that writing to/invalid/path/...and/root/cannot_create_hereraises. That only holds for an unprivileged user: run the suite as root, as CI containers commonly do, and both calls succeed, so the tests fail withDID NOT RAISE.Both now nest the target under a regular file, which fails with
ENOTDIRfor every uid. The error path stays covered instead of being skipped under root, and the suite becomes safe to run in a root container.