publish

Automate the nbdev edit->ship loop over the git/gh CLIs: nbdev-clean/export, a reviewed commit, push, wait for CI, and (on a feature branch) open and merge a PR to main. One confirm gate doubles as an interactive prompt and an agent read-and-authorize handoff. Only the ‘github’ destination is wired up so far (pypi/conda later).

Building blocks

Three small helpers the loop is assembled from: _run (run an argv list – never a shell string, so no quoting/injection surface), _confirm (the proceed gate – see below), and _wait_for_ci (block on the GitHub Actions run for a pushed commit, via gh).

_confirm is the interesting one: it prints git status and then, on an interactive terminal, prompts [Y/n]; on a non-TTY (a background or agent-driven call) it instead stops and returns without prompting, so the caller reads the printed status and re-invokes with yes=True. The one gate thus serves as both a human confirmation and an agent’s read-and-authorize handoff.

The publish loop

publish strings the pieces together: run prebuild (nbdev-clean + nbdev-export by default), show status and gate on it, commit and push, wait for CI, and – on a feature branch with a green run – open and merge a PR to main, leaving you on an updated local main. Every step is blocking and stop-on-error; run publish itself in the background if you don’t want to wait on CI. It knows nothing about nbdev specifically – that’s just the default prebuild, so the same loop works for a plain Python project.


source

publish

def publish(
    msg:str=None, # commit message; required only if there are changes to commit (prompted for on an interactive TTY if omitted). Ignored when the branch is already committed and just being shipped.
    prebuild:list=None, # shell commands to run before committing (regenerate build artifacts, run local checks, ...). Defaults to nbdev's ['nbdev-clean', 'nbdev-export', 'nbdev-readme'] (the last regenerates README.md from nbs/index.ipynb -- without it, index edits never reach the README); pass [] to skip, or e.g. ['ruff check .', 'pytest -q'] for a non-nbdev project. This venv's bin/ is prepended to PATH so bare tool names resolve to the project's own venv.
    dests:list=None, # publish destinations; only 'github' is implemented so far (pypi/conda later). Defaults to ['github'].
    merge:bool=True, # when on a non-main branch and CI passes, open a PR to main and merge it
    wait_ci:bool=True, # after pushing, wait for GitHub Actions to finish (forced on when a merge is needed -- a merge can't be gated without it)
    ci_timeout:int=120, # seconds to wait for the CI run to REGISTER after pushing before giving up (see _wait_for_ci); the run itself is then waited on for as long as it takes
    merge_method:str='merge', # how gh merges the PR: 'merge' | 'squash' | 'rebase'
    yes:bool=False, # skip the proceed prompt (the -y override); on a non-TTY without it, publish stops after showing git status so the caller can review and re-invoke
    cwd:str=None, # repo directory (default: current directory)
)->str:

*Automate the edit->ship loop. In order: run prebuild (default nbdev-clean + nbdev-export + nbdev-readme), show git status and gate on it (see _confirm – interactive prompt, or read-and-authorize for an agent), git add -u, commit (skipped if nothing staged), push; then for a ‘github’ dest wait for CI (see _wait_for_ci), and if it passes while you’re on a non-main branch with merge=True, open a PR to main and merge it (merge_method), then leave you on an updated local main. Everything runs blocking/stop-on-error (don’t proceed if a step failed) – run publish itself in the background if you don’t want to wait on CI. Progress is printed live; the returned string is a short final outcome. Only tracked files are staged (git add -u); new/untracked files show in the status review but aren’t auto-added. If nothing is staged but the branch already has commits to ship, the commit step is skipped and it still pushes/CIs/merges. Knows nothing about nbdev specifically – that’s just the default prebuild.*