name: Release
on:
push:
tags:
- "v*"
workflow_dispatch:
inputs:
version:
description: "Version to rehearse (defaults to Cargo.toml)"
required: false
type: string
dry_run:
description: "Build and validate without publishing"
required: true
default: true
type: boolean
permissions:
contents: read
jobs:
metadata:
name: Validate release metadata
runs-on: ubuntu-latest
outputs:
version: ${{ steps.release.outputs.version }}
tag: ${{ steps.release.outputs.tag }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
- name: Resolve and validate release metadata
id: release
shell: bash
env:
REQUESTED_VERSION: ${{ inputs.version }}
DRY_RUN: ${{ inputs.dry_run }}
run: |
manifest_version=$(sed -n 's/^version = "\([^"]*\)"/\1/p' Cargo.toml | head -1)
version="${REQUESTED_VERSION#v}"
version="${version:-$manifest_version}"
[[ "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+([+-][0-9A-Za-z.-]+)?$ ]]
test "$manifest_version" = "$version"
tag="v${version}"
if [[ "$GITHUB_EVENT_NAME" == "push" ]]; then
test "$GITHUB_REF_NAME" = "$tag"
elif [[ "$DRY_RUN" != "true" ]]; then
test "$GITHUB_REF_TYPE" = "tag"
test "$GITHUB_REF_NAME" = "$tag"
fi
echo "version=$version" >> "$GITHUB_OUTPUT"
echo "tag=$tag" >> "$GITHUB_OUTPUT"
check:
needs: metadata
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
- uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c
with:
toolchain: 1.96.0
components: rustfmt, clippy
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6
- uses: taiki-e/install-action@82cd3e7658a6f96c86c0234aeeda1748937cb0a1
with:
tool: nextest
- run: make fmt-check
- run: make lint
- run: make test
build:
needs: [metadata, check]
if: github.event_name != 'pull_request'
strategy:
matrix:
include:
- target: x86_64-unknown-linux-gnu
os: ubuntu-latest
maturin_args: --compatibility manylinux_2_28 --zig
- target: aarch64-unknown-linux-gnu
os: ubuntu-latest
maturin_args: --compatibility manylinux_2_28 --zig
- target: x86_64-apple-darwin
os: macos-latest
maturin_args: ""
- target: aarch64-apple-darwin
os: macos-latest
maturin_args: ""
- target: x86_64-pc-windows-msvc
os: windows-latest
maturin_args: ""
runs-on: ${{ matrix.os }}
permissions:
contents: read
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
- uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c
with:
toolchain: 1.96.0
components: rustfmt, clippy
targets: ${{ matrix.target }}
- uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d
- name: Install maturin and zig
shell: bash
run: |
uv venv "${RUNNER_TEMP}/build-venv"
if [[ "$RUNNER_OS" == "Windows" ]]; then
VENV_BIN="${RUNNER_TEMP}/build-venv/Scripts"
PYTHON="${VENV_BIN}/python.exe"
else
VENV_BIN="${RUNNER_TEMP}/build-venv/bin"
PYTHON="${VENV_BIN}/python"
fi
uv pip install --python "$PYTHON" maturin ziglang
echo "$VENV_BIN" >> "$GITHUB_PATH"
- name: Build wheel and binary
run: maturin build --release --target ${{ matrix.target }} ${{ matrix.maturin_args }}
shell: bash
- name: Package binary (unix)
if: matrix.os != 'windows-latest'
run: |
cd target/${{ matrix.target }}/release
tar czf ../../../sharepoint-${{ github.ref_name }}-${{ matrix.target }}.tar.gz sharepoint
shell: bash
- name: Package binary (windows)
if: matrix.os == 'windows-latest'
run: |
cd target/${{ matrix.target }}/release
7z a ../../../sharepoint-${{ github.ref_name }}-${{ matrix.target }}.zip sharepoint.exe
shell: bash
- name: Compute SHA256 (unix)
if: matrix.os != 'windows-latest'
run: |
if [[ "${{ runner.os }}" == "macOS" ]]; then
shasum -a 256 sharepoint-${{ github.ref_name }}-${{ matrix.target }}.tar.gz > sharepoint-${{ github.ref_name }}-${{ matrix.target }}.tar.gz.sha256
else
sha256sum sharepoint-${{ github.ref_name }}-${{ matrix.target }}.tar.gz > sharepoint-${{ github.ref_name }}-${{ matrix.target }}.tar.gz.sha256
fi
shell: bash
- name: Upload binary artifact
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a
with:
name: binary-${{ matrix.target }}
path: sharepoint-${{ github.ref_name }}-${{ matrix.target }}.*
- name: Upload wheel artifact
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a
with:
name: wheel-${{ matrix.target }}
path: target/wheels/*.whl
sdist:
needs: [metadata, check]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
- uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d
- name: Install maturin
shell: bash
run: |
uv venv "${RUNNER_TEMP}/build-venv"
uv pip install --python "${RUNNER_TEMP}/build-venv/bin/python" maturin
echo "${RUNNER_TEMP}/build-venv/bin" >> "$GITHUB_PATH"
- run: maturin sdist
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a
with:
name: sdist
path: target/wheels/*.tar.gz
release:
needs: [metadata, build]
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c
with:
pattern: binary-*
merge-multiple: true
- name: Create GitHub Release
if: ${{ inputs.dry_run != true }}
uses: softprops/action-gh-release@3d0d9888cb7fd7b750713d6e236d1fcb99157228
with:
generate_release_notes: true
files: |
sharepoint-*.tar.gz
sharepoint-*.tar.gz.sha256
sharepoint-*.zip
publish-crates:
needs: [metadata, release]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
- uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c
with:
toolchain: 1.96.0
components: rustfmt, clippy
- name: Publish to crates.io
if: ${{ inputs.dry_run != true }}
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
run: |
if ! output=$(cargo publish --locked 2>&1); then
if echo "$output" | grep -q "already uploaded"; then
echo "Crate already published to crates.io, skipping"
else
echo "$output"
exit 1
fi
fi
- name: Validate crates.io package
if: ${{ inputs.dry_run == true }}
run: cargo publish --locked --dry-run
publish-pypi:
needs: [metadata, release, build, sdist]
runs-on: ubuntu-latest
steps:
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c
with:
pattern: wheel-*
path: artifacts/wheels
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c
with:
name: sdist
path: artifacts/sdist
- uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d
- name: Publish to PyPI
if: ${{ inputs.dry_run != true }}
env:
UV_PUBLISH_TOKEN: ${{ secrets.PYPI_API_TOKEN }}
run: uv publish artifacts/wheels/wheel-*/*.whl artifacts/sdist/*.tar.gz
- name: Validate PyPI artifacts
if: ${{ inputs.dry_run == true }}
run: uvx --from twine==6.2.0 twine check --strict artifacts/wheels/wheel-*/*.whl artifacts/sdist/*.tar.gz
attest-release:
name: Attest release artifacts
needs: release
if: ${{ inputs.dry_run != true }}
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
attestations: write
steps:
- name: Download release artifacts
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c
with:
path: artifacts
- name: Attest release artifacts
uses: actions/attest@1e69f48acb82d1966a394da916b4c1698aa569d6
with:
subject-path: artifacts/**/*