Skip to content

Addition to allow Bitbucket Pipelines to work with cibuildwheel #2372

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 12 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ cibuildwheel

Python wheels are great. Building them across **Mac, Linux, Windows**, on **multiple versions of Python**, is not.

`cibuildwheel` is here to help. `cibuildwheel` runs on your CI server - currently it supports GitHub Actions, Azure Pipelines, Travis CI, AppVeyor, CircleCI, and GitLab CI - and it builds and tests your wheels across all of your platforms.
`cibuildwheel` is here to help. `cibuildwheel` runs on your CI server - currently it supports GitHub Actions, Azure Pipelines, Travis CI, AppVeyor, CircleCI, GitLab CI, Cirrus CI, and Bitbucket Pipelines - and it builds and tests your wheels across all of your platforms.


What does it do?
Expand Down Expand Up @@ -44,7 +44,7 @@ While cibuildwheel itself requires a recent Python version to run (we support th
<sup>⁵ manylinux armv7l support is experimental. As there are no RHEL based image for this architecture, it's using an Ubuntu based image instead.</sup><br>

- Builds manylinux, musllinux, macOS 10.9+ (10.13+ for Python 3.12+), and Windows wheels for CPython and PyPy
- Works on GitHub Actions, Azure Pipelines, Travis CI, AppVeyor, CircleCI, GitLab CI, and Cirrus CI
- Works on GitHub Actions, Azure Pipelines, Travis CI, AppVeyor, CircleCI, GitLab CI, Cirrus CI, and Bitbucket Pipelines
- Bundles shared library dependencies on Linux and macOS through [auditwheel](https://github.com/pypa/auditwheel) and [delocate](https://github.com/matthew-brett/delocate)
- Runs your library's tests against the wheel-installed version of your library

Expand All @@ -55,15 +55,16 @@ Usage

`cibuildwheel` runs inside a CI service. Supported platforms depend on which service you're using:

| | Linux | macOS | Windows | Linux ARM | macOS ARM | Windows ARM | iOS |
|-----------------|-------|-------|---------|-----------|-----------|-------------|-----|
| GitHub Actions | ✅ | ✅ | ✅ | ✅ | ✅ | ✅² | ✅³ |
| Azure Pipelines | ✅ | ✅ | ✅ | | ✅ | ✅² | ✅³ |
| Travis CI | ✅ | | ✅ | ✅ | | | |
| AppVeyor | ✅ | ✅ | ✅ | | ✅ | ✅² | ✅³ |
| CircleCI | ✅ | ✅ | | ✅ | ✅ | | ✅³ |
| Gitlab CI | ✅ | ✅ | ✅ | ✅¹ | ✅ | | ✅³ |
| Cirrus CI | ✅ | ✅ | ✅ | ✅ | ✅ | | ✅³ |
| | Linux | macOS | Windows | Linux ARM | macOS ARM | Windows ARM | iOS |
|----------------------|-------|-------|---------|-----------|-----------|-------------|-----|
| GitHub Actions | ✅ | ✅ | ✅ | ✅ | ✅ | ✅² | ✅³ |
| Azure Pipelines | ✅ | ✅ | ✅ | | ✅ | ✅² | ✅³ |
| Travis CI | ✅ | | ✅ | ✅ | | | |
| AppVeyor | ✅ | ✅ | ✅ | | ✅ | ✅² | ✅³ |
| CircleCI | ✅ | ✅ | | ✅ | ✅ | | ✅³ |
| Gitlab CI | ✅ | ✅ | ✅ | ✅¹ | ✅ | | ✅³ |
| Cirrus CI | ✅ | ✅ | ✅ | ✅ | ✅ | | ✅³ |
| Bitbucket Pipelines | ✅ | ✅ | ✅ | ✅¹ | ✅ | | ✅³ |

<sup>¹ [Requires emulation](https://cibuildwheel.pypa.io/en/stable/faq/#emulation), distributed separately. Other services may also support Linux ARM through emulation or third-party build hosts, but these are not tested in our CI.</sup><br>
<sup>² [Uses cross-compilation](https://cibuildwheel.pypa.io/en/stable/faq/#windows-arm64). It is not possible to test `arm64` on this CI platform.</sup><br>
Expand Down
3 changes: 3 additions & 0 deletions cibuildwheel/ci.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ class CIProvider(Enum):
github_actions = "github_actions"
gitlab = "gitlab"
cirrus_ci = "cirrus_ci"
bitbucket_pipelines = "bitbucket_pipelines"
other = "other"


Expand All @@ -31,6 +32,8 @@ def detect_ci_provider() -> CIProvider | None:
return CIProvider.gitlab
elif "CIRRUS_CI" in os.environ:
return CIProvider.cirrus_ci
elif "BITBUCKET_PIPELINE_UUID" in os.environ:
return CIProvider.bitbucket_pipelines
elif strtobool(os.environ.get("CI", "false")):
return CIProvider.other
else:
Expand Down
5 changes: 5 additions & 0 deletions cibuildwheel/logger.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"azure": ("##[group]{name}", "##[endgroup]"),
"travis": ("travis_fold:start:{identifier}\n{name}", "travis_fold:end:{identifier}"),
"github": ("::group::{name}", "::endgroup::{name}"),
"bitbucket": ("--- {name} ---", "--- End {name} ---"),
}

PLATFORM_IDENTIFIER_DESCRIPTIONS: Final[dict[str, str]] = {
Expand Down Expand Up @@ -98,6 +99,10 @@ def __init__(self) -> None:
self.fold_mode = "travis"
self.colors_enabled = True

elif ci_provider == CIProvider.bitbucket_pipelines:
self.fold_mode = "bitbucket"
self.colors_enabled = True

elif ci_provider == CIProvider.appveyor:
self.fold_mode = "disabled"
self.colors_enabled = True
Expand Down
19 changes: 19 additions & 0 deletions docs/setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,25 @@ Commit this file, enable building of your repo on Cirrus CI, and push.

Cirrus CI will store the built wheels for you - you can access them from the individual task view. Check out the Cirrus CI [docs](https://cirrus-ci.org/guide/writing-tasks/) for more info on this config file.

### Bitbucket Pipelines [linux/mac/windows] {: #bitbucket-pipelines}

To build Linux, Mac, and Windows wheels on Bitbucket Pipelines, create a `bitbucket-pipelines.yml` file in your repo,

> bitbucket-pipelines.yml

```yaml
{% include "../examples/bitbucket-pipelines-minimal.yml" %}
```

Commit this file, enable building of your repo on Bitbucket Pipelines, and push.

Bitbucket Pipelines will store the built wheels for you - you can access them from the artifacts tab. Check out the Bitbucket Pipelines [docs](https://support.atlassian.com/bitbucket-cloud/docs/configure-bitbucket-pipelinesyml/) for more info on this config file.

[`examples/bitbucket-pipelines-deploy.yml`](https://github.com/pypa/cibuildwheel/blob/main/examples/bitbucket-pipelines-deploy.yml) extends this minimal example to include iOS and Pyodide builds, and a demonstration of how to automatically upload the built wheels to PyPI when a tag is pushed.

!!! note
To enable Windows and macOS runners in Bitbucket Pipelines, you may need to enable these resources in your Bitbucket settings. See the [Bitbucket documentation](https://support.atlassian.com/bitbucket-cloud/docs/windows-and-macos-build-environments/) for more information.

> ⚠️ Got an error? Check the [FAQ](faq.md).

## Next steps
Expand Down
123 changes: 123 additions & 0 deletions examples/bitbucket-pipelines-deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
image: python:3.12

pipelines:
default:
- parallel:
- step:
name: Build Linux Wheels
services:
- docker
script:
- python -m pip install cibuildwheel==2.23.2
- cibuildwheel --output-dir wheelhouse
artifacts:
- wheelhouse/**

- step:
name: Build Windows Wheels
runs-on:
- windows
script:
- python -m pip install cibuildwheel==2.23.2
- cibuildwheel --output-dir wheelhouse --platform windows
artifacts:
- wheelhouse/**

- step:
name: Build macOS Wheels
runs-on:
- macos
script:
- python -m pip install cibuildwheel==2.23.2
- cibuildwheel --output-dir wheelhouse --platform macos
artifacts:
- wheelhouse/**

- step:
name: Build iOS Wheels
runs-on:
- macos
script:
- python -m pip install cibuildwheel==2.23.2
# iOS is only available on macOS runners
- cibuildwheel --output-dir wheelhouse --platform ios
artifacts:
- wheelhouse/**

- step:
name: Build Pyodide Wheels
services:
- docker
script:
- python -m pip install cibuildwheel==2.23.2
# Pyodide can build on any platform, but linux is typically used
- cibuildwheel --output-dir wheelhouse --platform pyodide
artifacts:
- wheelhouse/**

tags:
release-*:
- parallel:
- step:
name: Build Linux Wheels
services:
- docker
script:
- python -m pip install cibuildwheel==2.23.2
- cibuildwheel --output-dir wheelhouse
artifacts:
- wheelhouse/**

- step:
name: Build Windows Wheels
runs-on:
- windows
script:
- python -m pip install cibuildwheel==2.23.2
- cibuildwheel --output-dir wheelhouse --platform windows
artifacts:
- wheelhouse/**

- step:
name: Build macOS Wheels
runs-on:
- macos
script:
- python -m pip install cibuildwheel==2.23.2
- cibuildwheel --output-dir wheelhouse --platform macos
artifacts:
- wheelhouse/**

- step:
name: Build iOS Wheels
runs-on:
- macos
script:
- python -m pip install cibuildwheel==2.23.2
- cibuildwheel --output-dir wheelhouse --platform ios
artifacts:
- wheelhouse/**

- step:
name: Build Pyodide Wheels
services:
- docker
script:
- python -m pip install cibuildwheel==2.23.2
- cibuildwheel --output-dir wheelhouse --platform pyodide
artifacts:
- wheelhouse/**

- step:
name: Deploy to PyPI
trigger: manual # Requires manual approval
script:
- python -m pip install twine
# Download artifacts from previous parallel steps
- twine upload wheelhouse/*.whl
deployment: production

definitions:
services:
docker:
memory: 2048
42 changes: 42 additions & 0 deletions examples/bitbucket-pipelines-minimal.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
image: python:3.12

pipelines:
default:
- parallel:
- step:
name: Linux Wheels
services:
- docker
script:
- python -m pip install cibuildwheel==2.23.2
- cibuildwheel --output-dir wheelhouse
artifacts:
- wheelhouse/**

- step:
name: Windows Wheels
runs-on:
- windows
script:
- python -m pip install cibuildwheel==2.23.2
- cibuildwheel --output-dir wheelhouse --platform windows
artifacts:
- wheelhouse/**

- step:
name: macOS Wheels
runs-on:
- macos
script:
- python -m pip install cibuildwheel==2.23.2
- cibuildwheel --output-dir wheelhouse --platform macos
artifacts:
- wheelhouse/**

# To enable Windows and macOS builds, you need to enable these resources in your Bitbucket settings
# See: https://support.atlassian.com/bitbucket-cloud/docs/windows-and-macos-build-environments/

definitions:
services:
docker:
memory: 2048
88 changes: 88 additions & 0 deletions unit_test/ci_detection_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import os
from unittest import mock

import pytest

from cibuildwheel.ci import CIProvider, detect_ci_provider


def test_detect_ci_provider_none():
"""Test that None is returned when no CI environment variables are set."""
with mock.patch.dict(os.environ, {}, clear=True):
assert detect_ci_provider() is None


def test_detect_ci_provider_generic():
"""Test that generic CI is detected when only CI=true is set."""
with mock.patch.dict(os.environ, {"CI": "true"}, clear=True):
assert detect_ci_provider() == CIProvider.other


def test_detect_ci_provider_travis():
"""Test that Travis CI is detected."""
with mock.patch.dict(os.environ, {"TRAVIS": "true"}, clear=True):
assert detect_ci_provider() == CIProvider.travis_ci


def test_detect_ci_provider_appveyor():
"""Test that AppVeyor is detected."""
with mock.patch.dict(os.environ, {"APPVEYOR": "true"}, clear=True):
assert detect_ci_provider() == CIProvider.appveyor


def test_detect_ci_provider_circle_ci():
"""Test that Circle CI is detected."""
with mock.patch.dict(os.environ, {"CIRCLECI": "true"}, clear=True):
assert detect_ci_provider() == CIProvider.circle_ci


def test_detect_ci_provider_azure_pipelines():
"""Test that Azure Pipelines is detected."""
with mock.patch.dict(os.environ, {"AZURE_HTTP_USER_AGENT": "true"}, clear=True):
assert detect_ci_provider() == CIProvider.azure_pipelines


def test_detect_ci_provider_github_actions():
"""Test that GitHub Actions is detected."""
with mock.patch.dict(os.environ, {"GITHUB_ACTIONS": "true"}, clear=True):
assert detect_ci_provider() == CIProvider.github_actions


def test_detect_ci_provider_gitlab():
"""Test that GitLab CI is detected."""
with mock.patch.dict(os.environ, {"GITLAB_CI": "true"}, clear=True):
assert detect_ci_provider() == CIProvider.gitlab


def test_detect_ci_provider_cirrus_ci():
"""Test that Cirrus CI is detected."""
with mock.patch.dict(os.environ, {"CIRRUS_CI": "true"}, clear=True):
assert detect_ci_provider() == CIProvider.cirrus_ci


def test_detect_ci_provider_bitbucket_pipelines():
"""Test that Bitbucket Pipelines is detected."""
with mock.patch.dict(os.environ, {"BITBUCKET_PIPELINE_UUID": "true"}, clear=True):
assert detect_ci_provider() == CIProvider.bitbucket_pipelines


def test_detect_ci_provider_order():
"""Test that CI providers are detected in the correct order."""
# Setting multiple CI environment variables
with mock.patch.dict(
os.environ,
{
"TRAVIS": "true",
"APPVEYOR": "true",
"CIRCLECI": "true",
"AZURE_HTTP_USER_AGENT": "true",
"GITHUB_ACTIONS": "true",
"GITLAB_CI": "true",
"CIRRUS_CI": "true",
"BITBUCKET_PIPELINE_UUID": "true",
"CI": "true",
},
clear=True,
):
# Travis CI should be detected first based on the order in detect_ci_provider
assert detect_ci_provider() == CIProvider.travis_ci
25 changes: 25 additions & 0 deletions unit_test/logger_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import os
from unittest import mock

from cibuildwheel.ci import CIProvider
from cibuildwheel.logger import Logger


def test_logger_fold_pattern_for_bitbucket_pipelines():
"""Test that the logger uses the correct fold pattern for Bitbucket Pipelines."""
with mock.patch("cibuildwheel.logger.detect_ci_provider", return_value=CIProvider.bitbucket_pipelines):
# Set up a logger that thinks it's running on Bitbucket Pipelines
logger = Logger()

# Check that it has the expected fold mode
assert logger.fold_mode == "bitbucket"

# Check that colors are enabled
assert logger.colors_enabled is True


def test_logger_fold_mode_default():
"""Test that the logger defaults to disabled fold mode for unknown CI providers."""
with mock.patch("cibuildwheel.logger.detect_ci_provider", return_value=CIProvider.other):
logger = Logger()
assert logger.fold_mode == "disabled"