CICD to PyPi with Bitbucket Pipelines - V3

Jun 18, 2025 min read

Hello again! There have been a number of new additions to the Python universe since I wrote the article CICD to PyPI with Bitbucket Pipelines - V2. Because of these updates, I’ve decided to re-publish my Pipelines configuration along with how I’m now using Pipelines to test and automatically publish my Python Modules and Packages to PyPi.

Here is my Bitbucket Pipeline configuration for an example project:

image: python:latest

pipelines:
  default:
    - step: &lint
        name: Lint using Ruff
        script:
          - pip install uv
          - uv run —group test ruff check —exit-zero ./
        caches:
            - pip
    - step: &py39
        name: Python 3.9 Tests
        image: python:3.9
        script:
          - pip install uv
          - uv run —group test python -m unittest discover test/
        caches:
          - pip
    - step: &py310
        name: Python 3.10 Tests
        image: python:3.10
        script:
          - pip install uv
          - uv run —group test python -m unittest discover test/
        caches:
          - pip
    - step: &py311
        name: Python 3.11 Tests
        image: python:3.11
        script:
          - pip install uv
          - uv run —group test python -m unittest discover test/
        caches:
          - pip
    - step: &py312
        name: Python 3.12 Tests
        image: python:3.12
        script:
          - pip install uv
          - uv run —group test python -m unittest discover test/
        caches:
          - pip
    - step: &py313
        name: Python 3.13 Tests
        image: python:3.13
        script:
          - pip install uv
          - uv run —group test python -m unittest discover test/
        caches:
          - pip
  branches:
    release/*:
      - step: *lint
      - step: *py39
      - step: *py310
      - step: *py311
      - step: *py312
      - step: *py313
      - step:
          name: Deploy To Test PyPi
          trigger: manual
          script:
            - pip install uv
            - uv build && uv publish —index test-pypi —username __token__ —password ${TEST_PYPI_PASSWORD}
          caches:
            - pip
    master:
      - step: *lint
      - step: *py310
      - step: *py311
      - step: *py312
      - step: *py313
      - step:
          name: Deploy To PyPi
          script:
            - pip install uv
            - uv build && uv publish —username __token__ —password ${PYPI_PASSWORD}
          caches:
            - pip

This configuration runs tests for Python3.9 and later on all branches. The only difference on master and any release/* branch is the additional step to actually do the deploy to PyPI. Of course, the release/* branches are also doing a test publish to the testpypi instance.

The biggest changes to mention here are the use of Ruff and UV as my new Python linter and package manager. Both are written in Rust which makes them extremely fast and, as you can see, easy to configure. Because of these changes, I’ve see a 25% improvement in time spent running various pipelines in this fashion.

Keep in mind, you must configure token-based authentication as well as create a secure environment variable to store it in so that not just anyone can read the token used to authenticate with PyPi. Once you’ve done this, the above “recipe” will work and your secure token will stay secure within Bitbucket Pipelines logs.

And that is it. That is the update! Hope you enjoy using Bitbucket Pipelines to make your testing and publishing of Python Modules and Packages easier and automated!