Skip to content

📝 ReadTheDocs for Bindings using TestPyPI#833

Open
wlambooy wants to merge 21 commits into
mainfrom
readthedocs-testpypi
Open

📝 ReadTheDocs for Bindings using TestPyPI#833
wlambooy wants to merge 21 commits into
mainfrom
readthedocs-testpypi

Conversation

@wlambooy

@wlambooy wlambooy commented Sep 18, 2025

Copy link
Copy Markdown
Collaborator

Description

Currently, ReadTheDocs builds fiction within the github action, which uses limited resources. This can cause OOM issues, which can be prevented by simply installing the bindings through pulling them from TestPyPI. This PR implements this.

Checklist:

  • The pull request only contains commits that are related to it.
  • I have added appropriate tests and documentation.
  • I have added a changelog entry.
  • I have created/adjusted the Python bindings for any new or updated functionality.
  • I have made sure that all CI jobs on GitHub pass.
  • The pull request introduces no new warnings and follows the project's style guidelines.

Summary by CodeRabbit

  • New Features
    • Published packages to Test PyPI for easier pre-release access.
    • Updated default Python binding name for charge_distribution_surface (implicit default now uses a different suffix); explicit lattice variants unchanged.
  • Documentation
    • Improved docs build reliability with automatic retries; builds now fetch dependencies from Test PyPI when needed.
  • Chores
    • Added CI job to upload built artifacts to Test PyPI.
    • Included mnt.pyfiction in docs dependencies.

@coderabbitai

coderabbitai Bot commented Sep 18, 2025

Copy link
Copy Markdown
Contributor

Walkthrough

Adds a TestPyPI publish job to the CI workflow, introduces a retrying Read the Docs build step that pulls from TestPyPI, updates docs dependencies in pyproject to include mnt.pyfiction, and changes a C++ binding template’s default lattice parameter from "" to "TRIGGER".

Changes

Cohort / File(s) Summary
CI/CD: TestPyPI publish job
.github/workflows/pyfiction-pypi-deployment.yml
New job publish_to_testpypi runs after wheel/sdist builds, downloads cibw-* artifacts into dist, and publishes to Test PyPI via pypa/gh-action-pypi-publish@release/v1 with skip-existing and verbose output.
Docs build with retry
.readthedocs.yml
Replaces single-line build with a retry loop (up to 300 attempts, 30s sleep) that sets uv pip extra-index to TestPyPI and runs the original uv run command; fails with message after exhausting retries.
Docs dependencies
pyproject.toml
Adds mnt.pyfiction to the dependency-groups.docs. No other groups changed.
C++ Python bindings default
bindings/mnt/pyfiction/include/pyfiction/technology/charge_distribution_surface.hpp
Changes default parameter lattice from "" to "TRIGGER" in charge_distribution_surface_layout, altering the default-generated Python binding name.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor Dev as Developer
  participant GA as GitHub Actions
  participant Art as actions/download-artifact
  participant Pub as pypa/gh-action-pypi-publish
  participant TPyPI as Test PyPI

  Dev->>GA: Push/Tag triggers workflow
  GA->>GA: build_wheels, build_sdist
  GA->>Art: Download artifacts (pattern: cibw-*)
  Art-->>GA: Wheels in dist/
  GA->>Pub: Publish dist/* to TestPyPI (skip-existing, verbose)
  Pub->>TPyPI: Upload packages
  TPyPI-->>Pub: 200 OK or Already exists
  Pub-->>GA: Publish result
Loading
sequenceDiagram
  autonumber
  participant RTD as Read the Docs
  participant Bash as build command loop
  participant UV as uv
  participant TPyPI as Test PyPI

  RTD->>Bash: Start docs build
  loop Up to 300 attempts
    Bash->>UV: pip config set extra-index TestPyPI
    Bash->>UV: run ... build docs
    alt Success
      UV-->>Bash: 0 exit
      Bash-->>RTD: Proceed (break)
    else Failure
      UV-->>Bash: non-zero exit
      Bash-->>Bash: Sleep 30s then retry
    end
  end
  note over Bash: On exhaustion: print failure and exit 1
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

A bunny hops through CI’s night,
Packing wheels for a TestPyPI flight. ✈️🐇
Docs now retry beneath moon’s glow,
“Again!” says uv, “then off we go.”
A trigger whispers in binding’s name—
Dependencies join the docsward game.
Merge, and watch the carrots grow. 🥕

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Description Check ⚠️ Warning The PR description states the motivation and a brief summary that ReadTheDocs will install bindings from TestPyPI to avoid OOM issues, but it does not follow the repository template fully: it omits a "Fixes #" reference and does not list required dependency details or other required template information. The checklist is present but entirely unchecked, and the description does not call out specific changes that reviewers will need to verify (e.g., the added TestPyPI publish workflow and the pyproject dependency change). Because required template fields are missing, the description is incomplete against the repository's template. Update the PR description to include the issue number under "Fixes #", explicitly list dependencies changed (for example the addition of mnt.pyfiction to dependency-groups.docs), and summarize any other noteworthy changes (the new publish_to_testpypi workflow job and the .readthedocs.yml retry logic). Also complete or justify the checklist items, add a changelog note if applicable, and mention any binding API-impacting changes (for example the lattice default change in charge_distribution_surface) so reviewers have the necessary context.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (1 passed)
Check name Status Explanation
Title Check ✅ Passed The title "📝 ReadTheDocs for Bindings using TestPyPI" succinctly captures the primary change: switching ReadTheDocs to install prebuilt bindings from TestPyPI rather than building them in CI. It is concise, specific to the main intent described in the PR, and clear enough for a reviewer scanning history to understand the primary change. The emoji is non-essential but does not make the title misleading.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch readthedocs-testpypi

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
pyproject.toml (1)

236-243: Remove self-dependency from pyproject (docs + test); install package in CI/RTD instead.

Listing "mnt.pyfiction" in optional-dependencies causes circular resolution/TestPyPI pulls and lockfile drift. Occurrences: pyproject.toml:49 (test = ["pytest>=7.2", "mnt.pyfiction"]) and pyproject.toml:242 (docs); pyproject.toml:9–11 are the project name/version.

Apply this diff to drop it from docs:

 docs = [
   "setuptools-scm>=8.1",
   "sphinx==8.2.3; python_version >= '3.11'",
   "breathe==4.36.0",
   "sphinx-rtd-theme==3.0.2",
   "sphinx-tabs==3.4.7",
-  "mnt.pyfiction"
 ]

Also remove "mnt.pyfiction" from [project.optional-dependencies].test and instead install the built package in CI/test jobs; for RTD run:
uv pip install --extra-index-url https://test.pypi.org/simple/ mnt.pyfiction==${PROJECT_VERSION} before Sphinx.

🧹 Nitpick comments (3)
bindings/mnt/pyfiction/include/pyfiction/pybind11_mkdoc_docstrings.hpp (1)

18812-18812: Remove temporary trigger marker from user-facing docstring.

The string ">>>REMOVETHIS_ONLYFORTRIGGER<<<" will leak into published docs. Please revert before merge (or use a non-user-facing knob to trigger RTD rebuilds).

- FCN concept.>>>REMOVETHIS_ONLYFORTRIGGER<<<)doc";
+ FCN concept.)doc";
include/fiction/technology/cell_technologies.hpp (1)

14-15: Remove temporary trigger marker from user-facing docs.

The marker ">>>REMOVETHIS_ONLYFORTRIGGER<<<" should not ship in main. Drop it before merge.

- * Quantum-dot Cellular Automata (QCA) technology implementation of the FCN concept.>>>REMOVETHIS_ONLYFORTRIGGER<<<
+ * Quantum-dot Cellular Automata (QCA) technology implementation of the FCN concept.
.readthedocs.yml (1)

29-44: Retry loop: fix edge-case failure, reduce retries, and avoid per-iteration config.

  • If success happens exactly on attempt 300, the post-loop check still fails. Track success explicitly.
  • 300×30s ≈ 150 minutes > RTD timeouts; reduce attempts/backoff or cap total wait.
  • Set extra-index once before the loop.
       html:
-        - |
-          set -euo pipefail
-          for i in {1..300}; do
-            uv pip config set global.extra-index-url https://test.pypi.org/simple/
-            if uv run --frozen --no-dev --group docs -m sphinx -T -b html -d docs/_build/doctrees -D language=en docs "$READTHEDOCS_OUTPUT/html"; then
-              break
-            fi
-            echo "Retrying in 30s... ($i/300)"
-            sleep 30
-          done
-          # If we didn't break early, fail explicitly
-          if [ "${i:-300}" -eq 300 ]; then
-            echo "Obtaining bindings from TestPyPI failed after 300 attempts."
-            exit 1
-          fi
+        - |
+          set -euo pipefail
+          uv pip config set global.extra-index-url https://test.pypi.org/simple/
+          max=20
+          delay=15
+          ok=0
+          for i in $(seq 1 "$max"); do
+            if uv run --frozen --no-dev --group docs -m sphinx -T -b html -d docs/_build/doctrees -D language=en docs "$READTHEDOCS_OUTPUT/html"; then
+              ok=1; break
+            fi
+            echo "Retrying in ${delay}s... ($i/$max)"
+            sleep "$delay"
+          done
+          if [ "$ok" -ne 1 ]; then
+            echo "Obtaining bindings from TestPyPI failed after $max attempts."
+            exit 1
+          fi

Also, in post_checkout, you reference ".readthedocs.yaml" but the file is ".readthedocs.yml". Consider fixing that path to avoid skipping builds unintentionally.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6931faf and a14d935.

📒 Files selected for processing (5)
  • .github/workflows/pyfiction-pypi-deployment.yml (1 hunks)
  • .readthedocs.yml (1 hunks)
  • bindings/mnt/pyfiction/include/pyfiction/pybind11_mkdoc_docstrings.hpp (1 hunks)
  • include/fiction/technology/cell_technologies.hpp (1 hunks)
  • pyproject.toml (1 hunks)

Comment on lines +133 to +152
publish_to_testpypi:
runs-on: ubuntu-latest
name: 🧪 Publish to TestPyPI
needs: [build_wheels, build_sdist]
steps:
- name: Download the previously stored artifacts
uses: actions/download-artifact@v4
with:
pattern: cibw-*
path: dist
merge-multiple: true

- name: Deploy to TestPyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
repository-url: https://test.pypi.org/legacy/
password: ${{ secrets.TEST_PYPI_DEPLOY_TOKEN }}
skip-existing: true
verbose: true

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

🧩 Analysis chain

Harden TestPyPI publish for forks and align artifact action version.

  • Forked PRs don’t receive repository secrets; this job will fail and break RTD expectations. Gate the job on the presence of the secret.
  • Use the same download-artifact major version across jobs (v5 is used below).
 publish_to_testpypi:
   runs-on: ubuntu-latest
   name: 🧪 Publish to TestPyPI
-  needs: [build_wheels, build_sdist]
+  needs: [build_wheels, build_sdist]
+  if: ${{ secrets.TEST_PYPI_DEPLOY_TOKEN != '' }}
   steps:
     - name: Download the previously stored artifacts
-      uses: actions/download-artifact@v4
+      uses: actions/download-artifact@v5
       with:
         pattern: cibw-*
         path: dist
         merge-multiple: true

Optional: also add permissions: contents: read at the job level if your org uses restrictive defaults.

Run to confirm actions versions:


🏁 Script executed:

#!/bin/bash
rg -n 'uses:\s*actions/download-artifact@' -n -C1 .github/workflows

Length of output: 538


Gate TestPyPI publish for forks and align download-artifact to v5

Gate the job on TEST_PYPI_DEPLOY_TOKEN and standardize the download-artifact action to v5; optionally add job-level permissions: contents: read.

 publish_to_testpypi:
   runs-on: ubuntu-latest
   name: 🧪 Publish to TestPyPI
-  needs: [build_wheels, build_sdist]
+  needs: [build_wheels, build_sdist]
+  if: ${{ secrets.TEST_PYPI_DEPLOY_TOKEN != '' }}
   steps:
     - name: Download the previously stored artifacts
-      uses: actions/download-artifact@v4
+      uses: actions/download-artifact@v5
       with:
         pattern: cibw-*
         path: dist
         merge-multiple: true
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
publish_to_testpypi:
runs-on: ubuntu-latest
name: 🧪 Publish to TestPyPI
needs: [build_wheels, build_sdist]
steps:
- name: Download the previously stored artifacts
uses: actions/download-artifact@v4
with:
pattern: cibw-*
path: dist
merge-multiple: true
- name: Deploy to TestPyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
repository-url: https://test.pypi.org/legacy/
password: ${{ secrets.TEST_PYPI_DEPLOY_TOKEN }}
skip-existing: true
verbose: true
publish_to_testpypi:
runs-on: ubuntu-latest
name: 🧪 Publish to TestPyPI
needs: [build_wheels, build_sdist]
if: ${{ secrets.TEST_PYPI_DEPLOY_TOKEN != '' }}
steps:
- name: Download the previously stored artifacts
uses: actions/download-artifact@v5
with:
pattern: cibw-*
path: dist
merge-multiple: true
- name: Deploy to TestPyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
repository-url: https://test.pypi.org/legacy/
password: ${{ secrets.TEST_PYPI_DEPLOY_TOKEN }}
skip-existing: true
verbose: true
🤖 Prompt for AI Agents
.github/workflows/pyfiction-pypi-deployment.yml around lines 133 to 152: the
TestPyPI publish job should be gated on the presence of TEST_PYPI_DEPLOY_TOKEN,
the download-artifact action must be standardized to v5, and job-level read
permissions should be set; add an if condition to the job like if: ${{
secrets.TEST_PYPI_DEPLOY_TOKEN != '' }}, change uses:
actions/download-artifact@v4 to uses: actions/download-artifact@v5, and add a
permissions block for the job (permissions: contents: read) to restrict scope.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
bindings/mnt/pyfiction/include/pyfiction/technology/charge_distribution_surface.hpp (1)

282-285: If you must keep "TRIGGER", at least preserve the old default binding name.

Explicitly pass an empty suffix for the default instantiation so it still exports charge_distribution_surface:

-    detail::charge_distribution_surface_layout<py_sidb_layout>(m);
+    detail::charge_distribution_surface_layout<py_sidb_layout>(m, "");
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a14d935 and 6c36a08.

📒 Files selected for processing (1)
  • bindings/mnt/pyfiction/include/pyfiction/technology/charge_distribution_surface.hpp (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (20)
  • GitHub Check: 🐍 windows-latest
  • GitHub Check: 🐧 ubuntu-24.04 with g++-13
  • GitHub Check: 🐧 ubuntu-24.04-arm with clang++-18
  • GitHub Check: 🐧 ubuntu-24.04-arm with clang++-16
  • GitHub Check: 🐧 ubuntu-24.04 with g++-11
  • GitHub Check: 🐧 ubuntu-24.04 with clang++-17
  • GitHub Check: 🛞 Wheels for macos-13
  • GitHub Check: 🛞 Wheels for ubuntu-24.04
  • GitHub Check: 🛞 Wheels for macos-14
  • GitHub Check: 🛞 Wheels for windows-latest
  • GitHub Check: 🛞 Wheels for ubuntu-24.04-arm
  • GitHub Check: 🪟 windows-2022 with Visual Studio 17 2022 and v143 toolset
  • GitHub Check: 🪟 windows-2025 with Visual Studio 17 2022 and ClangCL toolset
  • GitHub Check: 🪟 windows-2025 with Visual Studio 17 2022 and v143 toolset
  • GitHub Check: 🪟 windows-2022 with Visual Studio 17 2022 and ClangCL toolset
  • GitHub Check: Analyze python
  • GitHub Check: Analyze cpp
  • GitHub Check: Coverage on ubuntu-22.04 with g++-11
  • GitHub Check: 🐳 Build and publish Docker image
  • GitHub Check: 🚨 Clang-Tidy

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants