Skip to content

Commit f880b38

Browse files
sklinglernvclaude
andcommitted
ci: address review — script injection, dispatch safety, python bound
Three fixes from review of #49. **Script injection in attach-to-release** (found by Codex, via @alessiodevoto). `gh release upload "${{ github.event.release.tag_name }}"` interpolated the tag into the shell script. GitHub expands `${{ }}` textually before bash parses the line, so a tag containing `$(...)` or backticks executes — the double quotes do not help, because substitution happens before quoting applies. The job holds `contents: write`. The tag now goes through `env:`, so bash sees it as data. Audited the rest of the file for the same class: this was the only instance; every other interpolation already used the env pattern. **Manual dispatch could reach real PyPI** (@alessiodevoto). The dispatch input offered `pypi` as well as `testpypi`, and publish-pypi ran on `inputs.target == 'pypi'`. Rather than reducing the choice list to one item, the input is removed entirely: a manual run is always a TestPyPI dry run, and real PyPI is reachable only by publishing a GitHub Release. A single-option selector would have been a knob that cannot be turned. **nooa-bench requires-python** (@alessiodevoto). It declared `>=3.12` with no upper bound while depending on nooa and nooa-cli, both `>=3.12,<3.14`. On 3.14 that surfaces as "could not find a version that satisfies nooa" rather than a clean "requires a different Python". Now consistent across all four packages. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 8fccbd7 commit f880b38

3 files changed

Lines changed: 26 additions & 13 deletions

File tree

.github/workflows/publish.yml

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,10 @@ name: Publish
1717
on:
1818
release:
1919
types: [published]
20+
# Manual runs are ALWAYS a TestPyPI dry run. There is deliberately no input
21+
# to select the index: real PyPI is reachable only by publishing a GitHub
22+
# Release, so a mis-click here cannot burn a version number on PyPI.
2023
workflow_dispatch:
21-
inputs:
22-
target:
23-
description: "Index to upload to"
24-
type: choice
25-
options: [testpypi, pypi]
26-
default: testpypi
2724

2825
permissions: {}
2926

@@ -110,7 +107,7 @@ jobs:
110107
# but a distinct environment is also what gives per-package approval gates.)
111108
publish-testpypi:
112109
needs: build
113-
if: github.event_name == 'workflow_dispatch' && inputs.target == 'testpypi'
110+
if: github.event_name == 'workflow_dispatch'
114111
runs-on: ubuntu-latest
115112
strategy:
116113
fail-fast: false # a partial publish is recoverable; a cancelled one is messier
@@ -155,7 +152,8 @@ jobs:
155152
156153
publish-pypi:
157154
needs: build
158-
if: github.event_name == 'release' || inputs.target == 'pypi'
155+
# Real PyPI is reachable ONLY from a published GitHub Release.
156+
if: github.event_name == 'release'
159157
runs-on: ubuntu-latest
160158
strategy:
161159
fail-fast: false
@@ -214,6 +212,13 @@ jobs:
214212
with:
215213
name: dist
216214
path: dist/
217-
- env:
215+
# The tag name goes through `env:`, not `${{ }}` inside the script.
216+
# GitHub expands `${{ }}` textually *before* bash parses the line, so a
217+
# tag containing `$(...)` or backticks would execute — double quotes do
218+
# not help, because the substitution happens before quoting is applied.
219+
# This job holds `contents: write`. Via env, bash sees the value as data.
220+
- name: Attach artifacts to the release
221+
env:
218222
GH_TOKEN: ${{ github.token }}
219-
run: gh release upload "${{ github.event.release.tag_name }}" dist/* --repo "$GITHUB_REPOSITORY"
223+
RELEASE_TAG: ${{ github.event.release.tag_name }}
224+
run: gh release upload "$RELEASE_TAG" dist/* --repo "$GITHUB_REPOSITORY"

RELEASING.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,12 @@ reviewer there gives a second pair of eyes before the irreversible step.
7272
7373
### Dry run against TestPyPI
7474

75-
Run the **Publish** workflow manually (Actions → Publish → Run workflow) with
76-
target `testpypi`. This exercises the identical build and smoke test.
75+
Run the **Publish** workflow manually (Actions → Publish → Run workflow). This
76+
exercises the identical build, version check, and smoke test.
77+
78+
A manual run always targets TestPyPI — there is no index selector. Real PyPI is
79+
reachable only by publishing a GitHub Release, so a mis-click here cannot burn
80+
a version number on PyPI.
7781

7882
### Doing it by hand
7983

packages/nooa-bench/pyproject.toml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,11 @@ dynamic = ["version"]
55
description = "Benchmark agent (BenchAgent) and Harbor runner for the NOOA framework — reproduces the tech report's SWE-bench and Terminal-Bench results"
66
license = {text = "Apache-2.0"}
77
readme = "README.md"
8-
requires-python = ">=3.12"
8+
# Matches nooa and nooa-cli (both >=3.12,<3.14), which this depends on.
9+
# Without the upper bound, installing on 3.14 fails with a confusing
10+
# "could not find a version that satisfies nooa" instead of a clean
11+
# "requires a different Python".
12+
requires-python = ">=3.12,<3.14"
913
dependencies = [
1014
"nooa",
1115
"nooa-cli",

0 commit comments

Comments
 (0)