Skip to content

Refine pre-commit #11665

Refine pre-commit

Refine pre-commit #11665

Workflow file for this run

name: build
permissions: # Principle of least privilege
contents: read
actions: read
on:
push:
branches: [master, nightly, develop, test-ci]
pull_request:
branches: ['*']
jobs:
pre-commit:
runs-on: ubuntu-22.04 # (glibc 2.35) wider runtime range than 24.04/glibc 2.39
steps:
# https://github.com/step-security/harden-runner
- uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1
with:
egress-policy: audit
- name: Checkout repository
# https://github.com/actions/checkout
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
- name: Common setup
uses: ./.github/actions/common-setup
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
python-version: "3.13"
free-disk-space: "true"
build-type: "pre-commit"
- name: Run pre-commit
run: pre-commit run --all-files
cargo-deny:
runs-on: ubuntu-22.04
steps:
# https://github.com/step-security/harden-runner
- uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1
with:
egress-policy: audit
- name: Checkout repository
# https://github.com/actions/checkout
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
# https://github.com/EmbarkStudios/cargo-deny-action
- name: Run cargo-deny (advisories, licenses, sources, bans)
uses: EmbarkStudios/cargo-deny-action@f9cc7aa250dec5698b425dc01fbf0d745fcd1b78 # v2.0.13
with:
command: check advisories licenses sources bans
arguments: --all-features
build-linux-x86:
strategy:
fail-fast: false
matrix:
os:
- ubuntu-22.04 # (glibc 2.35) wider runtime range than 24.04/glibc 2.39
python-version:
- "3.12"
- "3.13"
- "3.14"
# REMINDER: Update EXPECTED_WHEEL_COUNT below when changing Python versions
defaults:
run:
shell: bash
name: build - python ${{ matrix.python-version }} (${{ matrix.os }})
runs-on: ${{ matrix.os }}
needs:
- cargo-deny
- pre-commit
env:
BUILD_MODE: release
RUST_BACKTRACE: 1
services:
redis:
image: public.ecr.aws/docker/library/redis:7.4.5-alpine3.21
ports:
- 6379:6379
options: >-
--health-cmd "redis-cli ping"
--health-interval 10s
--health-timeout 5s
--health-retries 5
postgres:
image: public.ecr.aws/docker/library/postgres:16.4-alpine
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: pass
POSTGRES_DB: nautilus
ports:
- 5432:5432
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
steps:
# https://github.com/step-security/harden-runner
- uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1
with:
egress-policy: audit
- name: Checkout repository
# https://github.com/actions/checkout
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
- name: Common setup
uses: ./.github/actions/common-setup
with:
python-version: ${{ matrix.python-version }}
free-disk-space: "true"
- name: Install Nautilus CLI
env:
NAUTILUS_CLI_FORCE_SOURCE: ${{ github.ref == 'refs/heads/nightly' && '1' || '0' }}
run: bash scripts/ci/install-nautilus-cli.sh
- name: Init postgres schema
run: nautilus database init --schema ${{ github.workspace }}/schema/sql
env:
POSTGRES_HOST: localhost
POSTGRES_PORT: 5432
POSTGRES_USERNAME: postgres
POSTGRES_PASSWORD: pass
POSTGRES_DATABASE: nautilus
- name: Cached test data
uses: ./.github/actions/common-test-data
- name: Run Rust tests
run: make cargo-test-hypersync
- name: Build and install wheel
uses: ./.github/actions/common-wheel-build
with:
python-version: ${{ matrix.python-version }}
github_ref: ${{ github.ref }}
- name: Run tests
run: |
uv run --no-sync pytest --ignore=tests/performance_tests -n logical --dist=loadgroup
- name: Upload wheel artifact
uses: ./.github/actions/upload-artifact-wheel
build-linux-arm:
strategy:
fail-fast: false
matrix:
os:
- ubuntu-22.04-arm
python-version:
- "3.12"
- "3.13"
- "3.14"
# REMINDER: Update EXPECTED_WHEEL_COUNT below when changing Python versions
defaults:
run:
shell: bash
name: build - python ${{ matrix.python-version }} (${{ matrix.os }})
runs-on: ${{ matrix.os }}
# Pause job on develop branch for now (takes ~1-1.5 hrs)
if: >
!( (github.event_name == 'push' && github.ref_name == 'develop')
|| (github.event_name == 'pull_request' && github.base_ref == 'develop') )
needs:
- pre-commit
- cargo-deny
env:
BUILD_MODE: release
RUST_BACKTRACE: 1
services:
redis:
image: public.ecr.aws/docker/library/redis:7.4.5-alpine3.21
ports:
- 6379:6379
options: >-
--health-cmd "redis-cli ping"
--health-interval 10s
--health-timeout 5s
--health-retries 5
postgres:
image: public.ecr.aws/docker/library/postgres:16.4-alpine
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: pass
POSTGRES_DB: nautilus
ports:
- 5432:5432
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
steps:
# https://github.com/step-security/harden-runner
- uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1
with:
egress-policy: audit
- name: Checkout repository
# https://github.com/actions/checkout
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
- name: Common setup
uses: ./.github/actions/common-setup
with:
python-version: ${{ matrix.python-version }}
free-disk-space: "true"
- name: Install Nautilus CLI
env:
NAUTILUS_CLI_FORCE_SOURCE: ${{ github.ref == 'refs/heads/nightly' && '1' || '0' }}
run: bash scripts/ci/install-nautilus-cli.sh
- name: Init postgres schema
run: nautilus database init --schema ${{ github.workspace }}/schema/sql
env:
POSTGRES_HOST: localhost
POSTGRES_PORT: 5432
POSTGRES_USERNAME: postgres
POSTGRES_PASSWORD: pass
POSTGRES_DATABASE: nautilus
- name: Cached test data
uses: ./.github/actions/common-test-data
- name: Run Rust tests
run: make cargo-test-hypersync
- name: Build and install wheel
uses: ./.github/actions/common-wheel-build
with:
python-version: ${{ matrix.python-version }}
github_ref: ${{ github.ref }}
- name: Run tests
run: |
uv run --no-sync pytest --ignore=tests/performance_tests
- name: Upload wheel artifact
uses: ./.github/actions/upload-artifact-wheel
build-macos:
strategy:
fail-fast: false
matrix:
os:
- macos-latest
python-version:
- "3.12"
- "3.13"
- "3.14"
# REMINDER: Update EXPECTED_WHEEL_COUNT below when changing Python versions
defaults:
run:
shell: bash
name: build - python ${{ matrix.python-version }} (${{ matrix.os }})
runs-on: ${{ matrix.os }}
needs:
- pre-commit
- cargo-deny
env:
BUILD_MODE: release
RUST_BACKTRACE: 1
steps:
# https://github.com/step-security/harden-runner
- uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1
with:
egress-policy: audit
- name: Checkout repository
# https://github.com/actions/checkout
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
- name: Common setup
uses: ./.github/actions/common-setup
with:
python-version: ${{ matrix.python-version }}
- name: Cached test data
uses: ./.github/actions/common-test-data
- name: Run Rust tests
run: make cargo-test-hypersync
- name: Build and install wheel
uses: ./.github/actions/common-wheel-build
with:
python-version: ${{ matrix.python-version }}
github_ref: ${{ github.ref }}
- name: Run tests
run: |
uv run --no-sync pytest --ignore=tests/performance_tests
- name: Upload wheel artifact
uses: ./.github/actions/upload-artifact-wheel
build-windows:
strategy:
fail-fast: false
matrix:
os:
- windows-latest
python-version:
- "3.12"
- "3.13"
# REMINDER: Update EXPECTED_WHEEL_COUNT below when changing Python versions
defaults:
run:
shell: bash
name: build - python ${{ matrix.python-version }} (${{ matrix.os }})
runs-on: ${{ matrix.os }}
needs:
- pre-commit
- cargo-deny
env:
BUILD_MODE: release
HIGH_PRECISION: false
PARALLEL_BUILD: false
RUST_BACKTRACE: 1
steps:
# https://github.com/step-security/harden-runner
- uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1
with:
egress-policy: audit
- name: Checkout repository
# https://github.com/actions/checkout
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
- name: Common setup
uses: ./.github/actions/common-setup
with:
python-version: ${{ matrix.python-version }}
free-disk-space: "true"
- name: Build and install wheel
uses: ./.github/actions/common-wheel-build
with:
python-version: ${{ matrix.python-version }}
github_ref: ${{ github.ref }}
- name: Cached test data
uses: ./.github/actions/common-test-data
- name: Install test dependencies
run: make install-just-deps
- name: Run tests
run: |
uv run --no-sync python -m pytest --ignore=tests/performance_tests
- name: Upload wheel artifact
uses: ./.github/actions/upload-artifact-wheel
publish-wheels-develop:
name: publish-wheels-develop
runs-on: ubuntu-latest
environment: r2-develop
permissions:
actions: write # Required for deleting artifacts
contents: read
id-token: write # Required for attestations
attestations: write # Required for attestations
needs:
- build-linux-x86
- build-macos
- build-windows
# - build-linux-arm # Keep for nightly only (slow build)
if: >
github.event_name == 'push' && github.ref == 'refs/heads/develop'
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
CLOUDFLARE_R2_URL: ${{ secrets.CLOUDFLARE_R2_URL }}
CLOUDFLARE_R2_REGION: "auto"
CLOUDFLARE_R2_BUCKET_NAME: "packages"
EXPECTED_WHEEL_COUNT: 8 # Linux x86 (3) + macOS (3) + Windows (2, no 3.14 support)
steps:
# https://github.com/step-security/harden-runner
- uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1
with:
egress-policy: audit
allowed-endpoints: |
${{ vars.COMMON_ALLOWED_ENDPOINTS }}
${{ secrets.CLOUDFLARE_R2_ALLOWED_HOST }}:443
- name: Checkout repository
# https://github.com/actions/checkout
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
- name: Download built wheels
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0
with:
path: dist
pattern: "nautilus_trader-*.whl"
merge-multiple: true
- name: Validate wheel count
run: bash ./scripts/ci/validate-wheel-count.sh "$EXPECTED_WHEEL_COUNT"
# https://github.com/actions/attest-build-provenance
- name: Attest wheel provenance
uses: actions/attest-build-provenance@977bb373ede98d70efdf65b84cb5f73e068dcc2a # v3.0.0
with:
subject-path: 'dist/nautilus_trader-*.whl'
- name: Publish wheels to Cloudflare R2
uses: ./.github/actions/publish-wheels
- name: Fetch and delete artifacts for current run
shell: bash
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
bash ./scripts/ci/publish-wheels-delete-artifacts.sh
publish-wheels-nightly:
name: publish-wheels-nightly
runs-on: ubuntu-latest
environment: r2-nightly
permissions:
actions: write # Required for deleting artifacts
contents: read
id-token: write # Required for attestations
attestations: write # Required for attestations
needs:
- build-linux-x86
- build-linux-arm
- build-macos
- build-windows
if: >
github.event_name == 'push' && github.ref == 'refs/heads/nightly'
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
CLOUDFLARE_R2_URL: ${{ secrets.CLOUDFLARE_R2_URL }}
CLOUDFLARE_R2_REGION: "auto"
CLOUDFLARE_R2_BUCKET_NAME: "packages"
EXPECTED_WHEEL_COUNT: 11 # Linux x86 (3) + Linux ARM (3) + macOS (3) + Windows (2, no 3.14 support)
steps:
# https://github.com/step-security/harden-runner
- uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1
with:
egress-policy: audit
allowed-endpoints: |
${{ vars.COMMON_ALLOWED_ENDPOINTS }}
${{ secrets.CLOUDFLARE_R2_ALLOWED_HOST }}:443
- name: Checkout repository
# https://github.com/actions/checkout
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
- name: Download built wheels
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0
with:
path: dist
pattern: "nautilus_trader-*.whl"
merge-multiple: true
- name: Validate wheel count
run: bash ./scripts/ci/validate-wheel-count.sh "$EXPECTED_WHEEL_COUNT"
# https://github.com/actions/attest-build-provenance
- name: Attest wheel provenance
uses: actions/attest-build-provenance@977bb373ede98d70efdf65b84cb5f73e068dcc2a # v3.0.0
with:
subject-path: 'dist/nautilus_trader-*.whl'
- name: Publish wheels to Cloudflare R2
uses: ./.github/actions/publish-wheels
- name: Fetch and delete artifacts for current run
shell: bash
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
bash ./scripts/ci/publish-wheels-delete-artifacts.sh
publish-wheels-master:
runs-on: ubuntu-latest
environment: release
permissions:
contents: write # Required for uploading release assets
actions: write # Required for deleting artifacts
id-token: write # Required for attestations
attestations: write # Required for attestations
needs:
- build-linux-x86
- build-linux-arm
- build-macos
- build-windows
- tag-release
if: >
github.event_name == 'push' && github.ref == 'refs/heads/master'
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
CLOUDFLARE_R2_URL: ${{ secrets.CLOUDFLARE_R2_URL }}
CLOUDFLARE_R2_REGION: "auto"
CLOUDFLARE_R2_BUCKET_NAME: "packages"
EXPECTED_WHEEL_COUNT: 11 # Linux x86 (3) + Linux ARM (3) + macOS (3) + Windows (2, no 3.14 support)
steps:
# https://github.com/step-security/harden-runner
- uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1
with:
egress-policy: audit
allowed-endpoints: |
${{ vars.COMMON_ALLOWED_ENDPOINTS }}
pypi.org:443
files.pythonhosted.org:443
upload.pypi.org:443
${{ secrets.CLOUDFLARE_R2_ALLOWED_HOST }}:443
- name: Checkout repository
# https://github.com/actions/checkout
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
- name: Get uv version
shell: bash
run: |
echo "UV_VERSION=$(cat uv-version)" >> "$GITHUB_ENV"
- name: Install uv
uses: astral-sh/setup-uv@3259c6206f993105e3a61b142c2d97bf4b9ef83d # v7.1.0
with:
version: ${{ env.UV_VERSION }}
- name: Download built wheels for PyPI and GitHub release
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0
with:
path: dist
pattern: "nautilus_trader-*.whl"
merge-multiple: true
- name: Validate wheel count
run: bash ./scripts/ci/validate-wheel-count.sh "$EXPECTED_WHEEL_COUNT"
# https://github.com/actions/attest-build-provenance
- name: Attest wheel provenance
uses: actions/attest-build-provenance@977bb373ede98d70efdf65b84cb5f73e068dcc2a # v3.0.0
with:
subject-path: 'dist/nautilus_trader-*.whl'
- name: Publish wheels to Cloudflare R2
uses: ./.github/actions/publish-wheels
- name: Upload wheels to GitHub release
# https://cli.github.com/manual/gh_release_upload
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAG_NAME: ${{ needs.tag-release.outputs.tag_name }}
run: |
set +e
success=false
for i in {1..5}; do
gh release upload "$TAG_NAME" dist/nautilus_trader-*.whl --clobber --repo "$GITHUB_REPOSITORY"
status=$?
if [ $status -eq 0 ]; then
success=true
break
else
echo "gh upload (wheels) failed (exit=$status), retry ($i/5)"
sleep $((2**i))
fi
done
set -e
if [ "$success" = false ]; then echo "Failed to upload wheels to release after retries"; exit 1; fi
- name: Publish to PyPI
if: success()
env:
UV_PUBLISH_USERNAME: ${{ secrets.PYPI_USERNAME }}
UV_PUBLISH_PASSWORD: ${{ secrets.PYPI_TOKEN }}
run: |
# Use --check-url to skip files that already exist on PyPI
set +e
success=false
for i in {1..5}; do
uv publish --check-url https://pypi.org/simple/
status=$?
if [ $status -eq 0 ]; then
success=true
break
else
echo "uv publish failed (exit=$status), retry ($i/5)"
sleep $((2**i))
fi
done
set -e
if [ "$success" = false ]; then
echo "Failed to publish wheels to PyPI after retries"
exit 1
fi
- name: Fetch and delete artifacts for current run
shell: bash
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
bash ./scripts/ci/publish-wheels-delete-artifacts.sh
tag-release:
needs:
- build-linux-x86
- build-linux-arm
- build-macos
- build-windows
permissions:
contents: write # Required for pushing tags and upload release assets
actions: write # Required for creating releases
if: github.event_name == 'push' && github.ref == 'refs/heads/master'
runs-on: ubuntu-latest
outputs:
upload_url: ${{ steps.create-release.outputs.upload_url }}
tag_name: ${{ env.TAG_NAME }}
steps:
# Security hardening
- uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1
with:
egress-policy: audit
allowed-endpoints: |
${{ vars.COMMON_ALLOWED_ENDPOINTS }}
- name: Checkout repository
# https://github.com/actions/checkout
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: true # Required for salsify action to push git tags
fetch-depth: 2
- name: Set up Python environment
# https://github.com/actions/setup-python
uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
with:
python-version: "3.13"
- name: Create git tag
# https://github.com/salsify/action-detect-and-tag-new-version
uses: salsify/action-detect-and-tag-new-version@b1778166f13188a9d478e2d1198f993011ba9864 # v2.0.3
with:
version-command: ./scripts/package-version.sh
- name: Set output
id: vars
run: |
echo "TAG_NAME=v$(./scripts/package-version.sh)" >> "$GITHUB_ENV"
echo "RELEASE_NAME=NautilusTrader $(./scripts/package-version.sh) Beta" >> "$GITHUB_ENV"
sed -n '/^#/,$ {p;/^---/q}; w RELEASE.md' RELEASES.md
- name: Create GitHub release
id: create-release
# https://github.com/actions/create-release # v1.1.4
uses: actions/create-release@0cb9c9b65d5d1901c1f53e5e66eaf4afd303e70e
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ env.TAG_NAME }}
release_name: ${{ env.RELEASE_NAME }}
draft: false
prerelease: false
body_path: RELEASE.md
publish-sdist:
needs: [tag-release]
if: github.event_name == 'push' && github.ref == 'refs/heads/master'
runs-on: ubuntu-latest
environment: release
permissions:
contents: write # Required for uploading release assets
id-token: write # Required for attestations
attestations: write # Required for attestations
env:
COPY_TO_SOURCE: false # Do not copy built *.so files back into source tree
steps:
# https://github.com/step-security/harden-runner
- uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1
with:
egress-policy: audit
allowed-endpoints: |
${{ vars.COMMON_ALLOWED_ENDPOINTS }}
pypi.org:443
files.pythonhosted.org:443
upload.pypi.org:443
- name: Checkout repository
# https://github.com/actions/checkout
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
- name: Common setup
uses: ./.github/actions/common-setup
with:
python-version: "3.13"
free-disk-space: "true"
- name: Build sdist
run: |
uv build --sdist
# https://github.com/actions/attest-build-provenance
- name: Attest sdist provenance
uses: actions/attest-build-provenance@977bb373ede98d70efdf65b84cb5f73e068dcc2a # v3.0.0
with:
subject-path: 'dist/*.tar.gz'
- name: Set release output
id: vars
run: |
if [ ! -d "./dist" ]; then
echo "Error: dist directory not found"
exit 1
fi
ASSET_PATH=$(find ./dist -name "*.tar.gz" -type f -print0 | xargs -0 ls -t 2>/dev/null | head -n 1)
if [ -z "$ASSET_PATH" ]; then
echo "Error: No .tar.gz files found in dist directory"
exit 1
fi
echo "ASSET_PATH=$ASSET_PATH" >> "$GITHUB_ENV"
echo "ASSET_NAME=$(basename "$ASSET_PATH")" >> "$GITHUB_ENV"
- name: Upload release asset
# https://cli.github.com/manual/gh_release_upload
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAG_NAME: ${{ needs.tag-release.outputs.tag_name }}
ASSET_PATH: ${{ env.ASSET_PATH }}
run: |
set +e
success=false
for i in {1..5}; do
gh release upload "$TAG_NAME" "$ASSET_PATH" --clobber --repo "$GITHUB_REPOSITORY"
status=$?
if [ $status -eq 0 ]; then
success=true
break
else
echo "gh upload (sdist) failed (exit=$status), retry ($i/5)"
sleep $((2**i))
fi
done
set -e
if [ "$success" = false ]; then echo "Failed to upload sdist to release after retries"; exit 1; fi
- name: Publish to PyPI
if: success()
env:
UV_PUBLISH_USERNAME: ${{ secrets.PYPI_USERNAME }}
UV_PUBLISH_PASSWORD: ${{ secrets.PYPI_TOKEN }}
run: |
# Use --check-url to skip files that already exist on PyPI
set +e
success=false
for i in {1..5}; do
uv publish --check-url https://pypi.org/simple/
status=$?
if [ $status -eq 0 ]; then
success=true
break
else
echo "uv publish failed (exit=$status), retry ($i/5)"
sleep $((2**i))
fi
done
set -e
if [ "$success" = false ]; then
echo "Failed to publish sdist to PyPI after retries"
exit 1
fi