Skip to content

Unified Release (Rust + Python) #1

Unified Release (Rust + Python)

Unified Release (Rust + Python) #1

name: Unified Release (Rust + Python)
on:
workflow_dispatch:
inputs:
bump_type:
description: 'Version bump type'
required: false
type: choice
default: 'patch'
options:
- patch
- minor
- major
force_version:
description: 'Force a specific version (e.g., 0.1.2-beta.1)'
required: false
type: string
skip_tests:
description: 'Skip tests (use with caution)'
required: false
type: boolean
default: false
dry_run:
description: 'Dry run - build everything but skip commits and publishing'
required: false
type: boolean
default: false
permissions:
contents: write
packages: write
id-token: write
env:
CARGO_TERM_COLOR: always
jobs:
test:
name: Run tests
if: ${{ !inputs.skip_tests }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Install protoc
uses: arduino/setup-protoc@v2
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Run workspace tests
run: cargo test --workspace --all-features
bump-version:
name: Bump version
needs: [test]
if: ${{ always() && (needs.test.result == 'success' || needs.test.result == 'skipped') }}
runs-on: ubuntu-latest
outputs:
version: ${{ steps.new.outputs.version }}
py_version: ${{ steps.new.outputs.py_version }}
steps:
- uses: actions/checkout@v4
with:
token: ${{ secrets.PAT_TOKEN || secrets.GITHUB_TOKEN }}
fetch-depth: 0
submodules: recursive
- name: Get current version
id: current
run: |
CURRENT=$(grep '^version = ' Cargo.toml | head -1 | sed 's/version = "\(.*\)"/\1/')
echo "version=${CURRENT}" >> $GITHUB_OUTPUT
echo "Current version: ${CURRENT}"
- name: Calculate new version
id: new
run: |
CURRENT="${{ steps.current.outputs.version }}"
FORCE_VERSION="${{ inputs.force_version }}"
BUMP_TYPE="${{ inputs.bump_type }}"
if [[ -n "$FORCE_VERSION" ]]; then
NEW_VERSION="$FORCE_VERSION"
else
NEW_VERSION=$(python3 scripts/version_tools.py bump --current "$CURRENT" --kind "$BUMP_TYPE")
fi
PY_VERSION=$(python3 scripts/version_tools.py pep440 --version "$NEW_VERSION")
echo "version=${NEW_VERSION}" >> $GITHUB_OUTPUT
echo "py_version=${PY_VERSION}" >> $GITHUB_OUTPUT
echo "New version: ${NEW_VERSION} (Python: ${PY_VERSION})"
- name: Update all version files
run: |
VERSION="${{ steps.new.outputs.version }}"
# Update all version files using version_tools.py
python3 scripts/version_tools.py update --version "$VERSION"
# Refresh Cargo.lock
cargo update --workspace
- name: Commit version bump
if: ${{ !inputs.dry_run }}
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add Cargo.toml Cargo.lock bindings/python/pyproject.toml
git commit -m "chore: bump version to ${{ steps.new.outputs.version }} [skip ci]"
git tag "v${{ steps.new.outputs.version }}"
git push origin HEAD:main --tags
- name: Dry run summary
if: ${{ inputs.dry_run }}
run: |
echo "=== DRY RUN MODE ==="
echo "Version would be bumped to: ${{ steps.new.outputs.version }}"
echo "Python version would be: ${{ steps.new.outputs.py_version }}"
echo ""
echo "Changes that would be committed:"
git diff Cargo.toml bindings/python/pyproject.toml
echo ""
echo "Builds will proceed, but no commits or publishing will happen."
build-rust-binaries:
name: Build Rust ${{ matrix.target }}
runs-on: ${{ matrix.os }}
needs: [bump-version]
strategy:
matrix:
include:
- os: ubuntu-latest
target: x86_64-unknown-linux-gnu
binary: sbc
- os: macos-latest
target: aarch64-apple-darwin
binary: sbc
- os: windows-latest
target: x86_64-pc-windows-msvc
binary: sbc.exe
steps:
- uses: actions/checkout@v4
with:
ref: ${{ inputs.dry_run && github.ref || 'main' }}
submodules: recursive
- name: Install protoc
uses: arduino/setup-protoc@v2
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}
- name: Build CLI
run: |
cd cli
cargo build --release --target ${{ matrix.target }}
- name: Package binary
shell: bash
run: |
cd target/${{ matrix.target }}/release
if [[ "${{ matrix.os }}" == "windows-latest" ]]; then
7z a ../../../sbc-${{ matrix.target }}.zip ${{ matrix.binary }}
else
tar czf ../../../sbc-${{ matrix.target }}.tar.gz ${{ matrix.binary }}
fi
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: sbc-${{ matrix.target }}
path: sbc-${{ matrix.target }}.*
publish-rust-crates:
name: Publish Rust crates
runs-on: ubuntu-latest
needs: [bump-version, build-rust-binaries]
if: ${{ !inputs.dry_run }}
steps:
- uses: actions/checkout@v4
with:
ref: main
submodules: recursive
- name: Install protoc
uses: arduino/setup-protoc@v2
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Publish protocol to crates.io
run: cd protocol && cargo publish --token ${{ secrets.CARGO_REGISTRY_TOKEN }}
continue-on-error: true
- name: Wait for protocol crate availability
run: sleep 30
- name: Publish CLI to crates.io
run: cd cli && cargo publish --token ${{ secrets.CARGO_REGISTRY_TOKEN }}
continue-on-error: true
build-python-wheels:
name: Build Python wheels ${{ matrix.target }}
needs: [bump-version]
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
target: x86_64-unknown-linux-gnu
manylinux: auto
- os: ubuntu-24.04-arm
target: aarch64-unknown-linux-gnu
manylinux: auto
- os: macos-latest
target: aarch64-apple-darwin
manylinux: off
- os: macos-15-intel
target: x86_64-apple-darwin
manylinux: off
- os: windows-latest
target: x86_64-pc-windows-msvc
manylinux: off
steps:
- uses: actions/checkout@v4
with:
ref: ${{ inputs.dry_run && github.ref || 'main' }}
submodules: recursive
- name: Install protoc
uses: arduino/setup-protoc@v2
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Rust
uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}
- name: Build wheels
uses: PyO3/maturin-action@v1
with:
working-directory: bindings/python
target: ${{ matrix.target }}
args: --release --out dist
sccache: "true"
manylinux: ${{ matrix.manylinux }}
before-script-linux: |
PROTOC_VERSION="28.3"
ARCH="$(uname -m)"
if [ "$ARCH" = "aarch64" ]; then
PROTOC_ARCH="aarch_64"
else
PROTOC_ARCH="$ARCH"
fi
curl -LO "https://github.com/protocolbuffers/protobuf/releases/download/v${PROTOC_VERSION}/protoc-${PROTOC_VERSION}-linux-${PROTOC_ARCH}.zip"
unzip -q protoc-${PROTOC_VERSION}-linux-${PROTOC_ARCH}.zip -d /usr/local
rm protoc-${PROTOC_VERSION}-linux-${PROTOC_ARCH}.zip
protoc --version
- name: Upload wheels
uses: actions/upload-artifact@v4
with:
name: wheels-${{ matrix.target }}
path: bindings/python/dist/*.whl
build-python-sdist:
name: Build Python sdist
needs: [bump-version]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
ref: ${{ inputs.dry_run && github.ref || 'main' }}
submodules: recursive
- name: Install protoc
uses: arduino/setup-protoc@v2
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
- name: Build sdist
uses: PyO3/maturin-action@v1
with:
working-directory: bindings/python
command: sdist
args: --out dist
- name: Upload sdist
uses: actions/upload-artifact@v4
with:
name: sdist
path: bindings/python/dist/*.tar.gz
publish-python:
name: Publish to PyPI
needs: [build-python-wheels, build-python-sdist, publish-rust-crates]
if: ${{ !inputs.dry_run }}
runs-on: ubuntu-latest
permissions:
id-token: write
steps:
- name: Download all wheels
uses: actions/download-artifact@v4
with:
path: dist
pattern: wheels-*
merge-multiple: true
- name: Download sdist
uses: actions/download-artifact@v4
with:
path: dist
name: sdist
- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
packages-dir: dist/
password: ${{ secrets.PYPI_API_TOKEN }}
create-github-release:
name: Create GitHub Release
runs-on: ubuntu-latest
needs: [bump-version, build-rust-binaries, publish-rust-crates, publish-python]
if: ${{ !inputs.dry_run }}
steps:
- uses: actions/checkout@v4
with:
ref: main
submodules: recursive
- name: Download all artifacts
uses: actions/download-artifact@v4
with:
path: artifacts
- name: Create Release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
VERSION="${{ needs.bump-version.outputs.version }}"
PY_VERSION="${{ needs.bump-version.outputs.py_version }}"
NOTES_FILE=$(mktemp)
cat > "$NOTES_FILE" <<EOF
# SyftBox Crypto v${VERSION}
Cryptographic protocol implementation for SyftBox using X3DH key agreement.
## Published Artifacts
### Rust Crates
- \`syftbox-crypto-protocol ${VERSION}\` - [crates.io](https://crates.io/crates/syftbox-crypto-protocol/${VERSION})
- \`syftbox-crypto-cli ${VERSION}\` - [crates.io](https://crates.io/crates/syftbox-crypto-cli/${VERSION})
### Python Package
- \`syftbox-crypto-python ${PY_VERSION}\` - [PyPI](https://pypi.org/project/syftbox-crypto-python/${PY_VERSION})
## Installation
### Rust
\`\`\`bash
cargo install syftbox-crypto-cli
# or
cargo add syftbox-crypto-protocol
\`\`\`
### Python
\`\`\`bash
pip install syftbox-crypto-python
\`\`\`
### From Binary
Download the appropriate binary for your platform from the assets below.
EOF
gh release create "v${VERSION}" \
--title "Release v${VERSION}" \
--notes-file "$NOTES_FILE" \
--repo "${{ github.repository }}" \
--latest
- name: Upload Release Assets
run: |
VERSION="${{ needs.bump-version.outputs.version }}"
for file in artifacts/sbc-*/*.{tar.gz,zip}; do
if [ -f "$file" ]; then
echo "Uploading $(basename "$file")"
gh release upload "v${VERSION}" "$file" --repo ${{ github.repository }}
fi
done
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}