Skip to content
Francesco Bruzzesi edited this page Oct 20, 2026 · 30 revisions

Release process

Pre-requisites

In order for this process to work, you should clone Narwhals locally using ssh.

Then, run verify that you see the following output if you run git remote get-url upstream:

$ git remote get-url upstream
git@github.com:narwhals-dev/narwhals.git

If not, then you should first do

git remote add upstream git@github.com:narwhals-dev/narwhals.git

Fetch the latest changes

After you've merged everything you'd like to include, check out the latest upstream/main so the local tests below run against it:

git checkout main
git fetch upstream
git reset --hard upstream/main

Commit or stash any local changes first: the release script refuses to run while tracked files have uncommitted changes. Untracked files are fine.

Release process

Let's suppose we're doing a minor release, and are releasing Narwhals version 1.30.0. Examples will refer to that version.

  1. Check that the cuDF tests pass (this takes ~3 minutes). You can do this by running this Kaggle notebook: https://www.kaggle.com/code/marcogorelli/narwhals-pre-release-test-cudf and verifying that the tests are all green. If they are not, please post in Narwhals Discord so they can be fixed up.

  2. Check that the PySpark tests pass: pytest --constructors=pyspark. If they don't, please post in Narwhals Discord or open an issue so they can be fixed up.

  3. Copy/fixup the release notes: go to https://github.com/narwhals-dev/narwhals/releases, click "edit" on the latest (draft) release notes. If anything needs fixing up (e.g. adding missing labels to closed pull requests), you can do so now. Once you're done, copy all the content, and paste it somewhere. You'll need this later.

  4. Run python utils/bump_version.py minor. Replace minor with patch or major, depending on how much you want to bump the version. In general:

    • If you're doing a scheduled Monday release, use minor
    • If you're doing an emergency mid-week release to patch a serious issue, use patch

    The script fetches upstream, resets your local bump-version branch to upstream/main, bumps the version in pyproject.toml and uv.lock, and commits release: Bump version to 1.30.0. It then prints the commit and asks:

    Tag and push v1.30.0 to upstream? This publishes to PyPI. [y/N]
    

    Check the version number, then answer y. This pushes bump-version and the v1.30.0 tag together, and the tag starts the PyPI release. Any other answer pushes nothing, and you can rerun the script later.

  5. Open a pull request from bump-version to main. To do this, go to https://github.com/narwhals-dev/narwhals/pulls, click "new pull request", and open a pull request from bump-version to main. Set the "release" label. Alternatively, using the github CLI:

    gh pr create --repo narwhals-dev/narwhals --base main --head bump-version --label release --fill
    
  6. Wait a few minutes, until a new release appears in https://github.com/narwhals-dev/narwhals/releases with empty release notes. Its title will be something like v1.30.0.

  7. Go to the latest release in https://github.com/narwhals-dev/narwhals/releases (the one without any release notes). You should see something like "Narwhals Unreleased" in draft (with full release notes), and something like v1.30.0 as published (without any release notes). Copy the "Narwhals Unreleased" release notes into the v1.30.0 release notes, and amend the title to be of the form "Narwhals v<version goes here>", e.g. Narwhals v1.30.0. Do not click "publish" on the "Narwhals Unreleased" notes. You should instead see a button that says "Update release notes" - press that.

  8. After you've updated the release notes, squash-merge the PR from bump-version to main. Note: if you see a message like bypassed rule violations, don't worry about it - we block people from creating tags (as they trigger releases) so if you see it, it's because you have permission to bypass that rule 😇

Do not amend, force-push or re-tag bump-version after step 4: the tag and the PyPI release point at that commit. If something needs fixing, open a separate PR.

If the script stops

If the script fails, it undoes its own changes to pyproject.toml and uv.lock and deletes the local tag, so you can rerun it as soon as the cause is fixed. Nothing is pushed unless you answered y.

  • Commit or stash your changes to tracked files first.: clean your working tree and rerun.
  • Tag v1.30.0 already exists on upstream.: that version is already released. Check the releases page before picking a different bump.
  • The push is rejected with Updates were rejected because the tip of your current branch is behind: a bump-version branch is left on GitHub from an abandoned release. Nothing was published. Delete it at https://github.com/narwhals-dev/narwhals/branches and rerun.
  • Expected one narwhals ... entry in uv.lock, Expected a one-line change in pyproject.toml and uv.lock, or a failing uv lock --check: update uv and rerun. If it still fails, pyproject.toml and uv.lock disagree on main. Fix that in a normal PR first.

Note: the script deletes local tags that don't exist on upstream.