| description | Review a Bioconda recipe PR and post inline comments (requires human confirmation before posting) |
|---|
Review the Bioconda recipe pull request at the given URL following this systematic process.
Input: $ARGUMENTS (a GitHub PR URL like https://github.com/bioconda/bioconda-recipes/pull/XXXXX)
gh pr view <number> --repo bioconda/bioconda-recipes --json title,body,state,author,labels,additions,deletions,changedFiles,commits,filesgh pr diff <number> --repo bioconda/bioconda-recipesgh pr checks <number> --repo bioconda/bioconda-recipes
From the files list in Phase 1, count how many distinct meta.yaml files are changed. If the PR touches more than one recipe (multiple recipes/*/meta.yaml paths), stop and ask the user for confirmation before posting a comment asking the author to split into separate PRs — Bioconda policy is one recipe per PR.
Determine whether this PR adds a new recipe or updates an existing one. From gh pr diff, a new recipe shows new file mode for its meta.yaml; an update shows a normal diff. Then check for duplicates:
New recipe (adds recipes/<name>/meta.yaml):
- Confirm the recipe does not already exist on the main branch:
gh api repos/bioconda/bioconda-recipes/contents/recipes/<name>/meta.yaml --jq .name 2>/dev/null \ && echo "ALREADY EXISTS on main — this should be an Update, not an Add"
- Search for another open PR adding the same recipe:
gh pr list --repo bioconda/bioconda-recipes --state open --search "<name> in:title" \ --json number,title,author,createdAt
Update of an existing recipe to a given version:
- Search for any other open PR touching the same recipe — including bot-authored autobump PRs (author
BiocondaBot/bioconda-bot/ other autobump bots) that may already bump to the same version:gh pr list --repo bioconda/bioconda-recipes --state open --search "<name> in:title" \ --json number,title,author,createdAt - If another PR (human or bot) already targets the same recipe at the same version, this PR is a duplicate.
If a duplicate is found (recipe already on main, or another open PR adds/bumps the same recipe to the same version): do NOT approve. Always post a COMMENT (never APPROVE) that links the duplicate (PR number or existing recipe path) and asks the author to close this PR in favor of the existing one, or coordinate. Show the finding to the user for confirmation before posting, per Phase 4.
- Verify sha256: Download the source tarball and compute
shasum -a 256to confirm it matches the recipe - Check upstream build config: Fetch the upstream project's
pyproject.toml,setup.py,setup.cfg, orCargo.tomlto extract:- Actual runtime dependencies (
install_requires,[project] dependencies) - Build system requirements (
[build-system] requires) - Python/Rust version constraints
- Entry points / console scripts
- License
- Actual runtime dependencies (
- If CI fails: Fetch failed logs with
gh run view <run_id> --repo bioconda/bioconda-recipes --log-failedand diagnose the root cause. Note that Linux builds use--docker --mulled-test, so failures can occur in two phases:- Docker build phase: compilation or pip install errors inside the CentOS 7 container
- Mulled test phase: test commands fail in a minimal BioContainer (catches build-time deps leaking into runtime)
- Variant mismatch:
BUILD FAILED: ... pypyh*_0.conda cannot be foundmeans the build matrix expects a PyPy variant — usually caused bynoarch: python+extra.additional-platforms
Check every item below against the recipe. Only flag actual issues — do not nitpick style if it follows existing conventions.
- CI status: All checks pass (Lint, Linux, OSX-64, ARM)
- sha256 hash: Verified against actual download
- Source URL: Stable, resolves correctly, not using
git_url/git_rev - License: Correct SPDX identifier,
license_filepresent (GPL requires it) - License for Rust packages:
cargo-bundle-licensesused,THIRDPARTY.ymlinlicense_file
- Dependencies match upstream: Compare recipe
rundeps against upstream's declared runtime dependencies. Flag extras or missing deps. -
run_exportspinning:max_pin="x"for semver 1.x+,max_pin="x.x"for 0.x.x,max_pin=Nonefor calendar versioning. Value must be a pattern of x's, never a literal version. -
about.home: Should point to project homepage/GitHub, not PyPI - Build script:
--no-deps --no-build-isolationpresent for pip installs. Nopip installof dependencies in build scripts. Rust packages should use--locked. -
noarch: python: Used for pure Python packages. Must NOT haveextra.additional-platforms— noarch packages are platform-independent by definition, and addingadditional-platformscan cause PyPy variant build failures (pypyh*_0artifact not found). - Build number: 0 for new versions, increment only for rebuilds
- Python version constraints:
python >=X.Y(not barepython X.Ywhich pins to a single minor) - Folder name matches package name
- PR title: Starts with "Add" or "Update" as appropriate
- Jinja variables:
{% set name %}and{% set version %}used at top -
dev_url/doc_url: Present if project has GitHub repo and docs - No redundant
entry_pointsin meta.yaml if upstream already declares them - No
pipin run dependencies (only intest: requires) - No debug output left in build scripts
IMPORTANT: Before posting anything to GitHub, you MUST show the full review (summary + all inline comments) to the user and ask for explicit confirmation. Do NOT post until the user approves. The user may want to edit, remove, or add comments before posting.
Post a GitHub PR review with inline comments using this pattern:
cat <<'PAYLOAD' | gh api repos/bioconda/bioconda-recipes/pulls/<number>/reviews --method POST --input -
{
"commit_id": "<head_sha>",
"event": "<COMMENT|APPROVE>",
"body": "<summary>",
"comments": [
{
"path": "<file_path>",
"line": <end_line_in_new_file>,
"body": "<comment with markdown, code suggestions, and rationale>"
}
]
}
PAYLOADMulti-line suggestions: When a suggestion block replaces multiple lines, you MUST specify start_line to define the range. GitHub replaces lines start_line through line (inclusive) with the suggestion content. Omitting start_line means only the single line gets replaced -- which will break the recipe if the suggestion content spans more lines.
{
"path": "<file_path>",
"start_line": <first_line_to_replace>,
"line": <last_line_to_replace>,
"side": "RIGHT",
"body": "Explanation:\n\n```suggestion\nreplacement line 1\nreplacement line 2\n```"
}Rule: Always count the exact lines your suggestion replaces in the diff. If replacing N lines (N > 1), use start_line + line. If replacing exactly 1 line, line alone is sufficient.
Verification step (MANDATORY for multi-line suggestions): Before posting, for each suggestion that uses start_line + line, list every line number in the range with its content from the diff to confirm:
- The FIRST line in the range (
start_line) is the first line that needs to change - The LAST line in the range (
line) is the last line that needs to change - No lines OUTSIDE the range should be modified (check
start_line - 1andline + 1) - The suggestion content correctly replaces the entire range
Example pitfall: To remove an entry_points block (key + values) while keeping the script line below it, the range must include the entry_points: key line as start_line and stop BEFORE the script line. An off-by-one error here will silently delete the wrong lines when the author accepts the suggestion.
Decision logic:
- COMMENT (never APPROVE) if: a duplicate was found in Phase 1.5b — the recipe already exists on main, or another open PR (human or autobump bot) adds/bumps the same recipe to the same version. Link the duplicate and ask the author to close in favor of it.
- APPROVE if: CI passes, no duplicate, no critical/important issues, only optional nits
- COMMENT if: Any critical or important issues need addressing
Keep the review body concise. Put details in inline comments, not the summary.
Key Bioconda rules to remember:
- PyPI source URLs:
https://pypi.org/packages/source/{{ name[0] }}/{{ name }}/{{ name }}-{{ version }}.tar.gz - GitHub source URLs:
https://github.com/<org>/<repo>/archive/v{{ version }}.tar.gz(verify tag name matches) - Rust packages: need
{{ compiler('rust') }},cargo-bundle-licenses,THIRDPARTY.yml,--lockedflag - GPL license: must have
license_fileincluding the license text --no-build-isolation: required for pip installs in conda build environmentspin_subpackagedoes NOT auto-lowercase. Ifnameis mixed-case (e.g."ntSynt-viz") and the package usesname|lower, thenpin_subpackagemust also usename|lower:{{ pin_subpackage(name|lower, max_pin="x") }}noarch: pythonpackages should not have architecture-specific constraints- External CLI tools (not Python imports) as run deps are fine in bioconda even if not in upstream's install_requires
matplotlib-baseis the correct conda equivalent ofmatplotlib(avoids Qt backend)
noarch: pythonpackages must NOT haveextra.additional-platforms— noarch is platform-independent by definition. Combining them causes bioconda-utils to generate a PyPy build variant (pypyh*_0) thatnoarchwon't produce, leading toBUILD FAILED: ... pypyh*_0.conda cannot be found.- Fix: remove
additional-platformsfromextra, or addconda_build_config.yamlwithpython_impl: [cpython](but removingadditional-platformsis the correct solution for noarch). - Linux CI builds use
--docker --mulled-test: failures can be in the docker build phase (compilation) or the mulled test phase (runtime test in minimal container without build deps).
pkg_resourceswas removed from setuptools >=78. Packages usingfrom pkg_resources import ...needsetuptools <78as a runtime dep.setuptools >=78still providessetuptools.build_meta(build backend) but NOTpkg_resources- If a recipe uses setuptools as a build backend (
[build-system] requires = ["setuptools"]), it must be inhostdeps AND the build script needs--no-build-isolation - Common failure pattern: build succeeds but test fails because the test env gets a newer setuptools without
pkg_resources