682bf063ca
Prepare the first patch release after v1.0.0 by finalizing the outstanding release, packaging, and workflow fixes. This release keeps PyPI and crates.io publishing anchored to CI.yml, splits the large CI workflow into focused rust/python/wasm/docs suites, fixes the ci-complete gate, and updates the release SBOM action pin. It also switches the npm publish workflow to GitHub OIDC, installs the wasm32 target required for packaging, ensures the WASM npm tarball includes pkg/ artifacts during prepack, and adds a dedicated README plus docs.rs metadata for ferro_ta_core. Finally, bump all published package versions to 1.0.1 and record the patch release notes in CHANGELOG.md.
258 lines
8.3 KiB
YAML
258 lines
8.3 KiB
YAML
name: CI
|
|
|
|
on:
|
|
push:
|
|
branches: ["main"]
|
|
pull_request:
|
|
branches: ["main"]
|
|
release:
|
|
types: [published]
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
jobs:
|
|
# -------------------------------------------------------------------------
|
|
# Version consistency — Cargo.toml and pyproject.toml must have same version
|
|
# -------------------------------------------------------------------------
|
|
changelog-check:
|
|
name: CHANGELOG has [Unreleased] entry
|
|
runs-on: ubuntu-latest
|
|
if: github.event_name == 'pull_request'
|
|
steps:
|
|
- uses: actions/checkout@v6
|
|
- name: Check CHANGELOG.md
|
|
run: python3 scripts/check_changelog.py
|
|
|
|
version-check:
|
|
name: Version consistency (Cargo.toml == pyproject.toml)
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v6
|
|
- name: Check version parity
|
|
run: |
|
|
CARGO_VERSION=$(grep '^version' Cargo.toml | head -1 | sed 's/version = "\(.*\)"/\1/')
|
|
PYPROJECT_VERSION=$(grep '^version' pyproject.toml | head -1 | sed 's/version = "\(.*\)"/\1/')
|
|
echo "Cargo.toml version: $CARGO_VERSION"
|
|
echo "pyproject.toml version: $PYPROJECT_VERSION"
|
|
if [ "$CARGO_VERSION" != "$PYPROJECT_VERSION" ]; then
|
|
echo "ERROR: Version mismatch! Update both files before releasing."
|
|
exit 1
|
|
fi
|
|
echo "OK: versions match ($CARGO_VERSION)"
|
|
|
|
# -------------------------------------------------------------------------
|
|
# Modularized CI suites.
|
|
# Keep release publishing jobs in this file so trusted-publisher bindings
|
|
# remain anchored to CI.yml for PyPI and crates.io.
|
|
# -------------------------------------------------------------------------
|
|
rust-suite:
|
|
name: Rust and audit
|
|
uses: ./.github/workflows/ci-rust.yml
|
|
|
|
python-suite:
|
|
name: Python quality and tests
|
|
uses: ./.github/workflows/ci-python.yml
|
|
|
|
wasm:
|
|
name: WASM binding
|
|
uses: ./.github/workflows/ci-wasm.yml
|
|
|
|
docs:
|
|
name: Documentation
|
|
uses: ./.github/workflows/ci-docs.yml
|
|
with:
|
|
upload-pages-artifact: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
|
|
|
|
# -------------------------------------------------------------------------
|
|
# Deploy docs to GitHub Pages (on push to main only)
|
|
# -------------------------------------------------------------------------
|
|
deploy-docs:
|
|
name: Deploy docs to GitHub Pages
|
|
runs-on: ubuntu-latest
|
|
needs: docs
|
|
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
|
environment:
|
|
name: github-pages
|
|
url: ${{ steps.deployment.outputs.page_url }}
|
|
permissions:
|
|
pages: write
|
|
id-token: write
|
|
steps:
|
|
- name: Deploy to GitHub Pages
|
|
id: deployment
|
|
uses: actions/deploy-pages@v4
|
|
|
|
# -------------------------------------------------------------------------
|
|
# CI gate — all required jobs must pass before this job succeeds.
|
|
# Set "ci-complete" as a required status check in branch protection rules
|
|
# to block merging of PRs that fail any required job.
|
|
# -------------------------------------------------------------------------
|
|
ci-complete:
|
|
name: CI complete (required gate)
|
|
runs-on: ubuntu-latest
|
|
needs:
|
|
- changelog-check
|
|
- version-check
|
|
- rust-suite
|
|
- python-suite
|
|
- wasm
|
|
- docs
|
|
if: always()
|
|
steps:
|
|
- name: Check all required jobs passed
|
|
run: |
|
|
results='${{ toJSON(needs) }}'
|
|
echo "Job results: $results"
|
|
failed="$(
|
|
RESULTS_JSON="$results" python3 - <<'PY'
|
|
import json
|
|
import os
|
|
|
|
needs = json.loads(os.environ["RESULTS_JSON"])
|
|
failed = [
|
|
name
|
|
for name, data in needs.items()
|
|
if data["result"] not in ("success", "skipped")
|
|
]
|
|
if failed:
|
|
print(" ".join(failed))
|
|
PY
|
|
)"
|
|
if [ -n "$failed" ]; then
|
|
echo "FAILED jobs: $failed"
|
|
exit 1
|
|
fi
|
|
echo "All required CI jobs passed."
|
|
|
|
# -------------------------------------------------------------------------
|
|
# Build wheels for all platforms and publish to PyPI on release
|
|
# Keep release jobs here because trusted-publisher configuration points to
|
|
# CI.yml specifically.
|
|
# -------------------------------------------------------------------------
|
|
build-wheels:
|
|
name: Build wheels (${{ matrix.os }})
|
|
runs-on: ${{ matrix.os }}
|
|
if: github.event_name == 'release' && github.event.action == 'published'
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
os: [ubuntu-latest, windows-latest, macos-latest]
|
|
|
|
steps:
|
|
- uses: actions/checkout@v6
|
|
|
|
- name: Build wheels
|
|
uses: PyO3/maturin-action@v1
|
|
with:
|
|
command: build
|
|
args: --release --out dist
|
|
manylinux: auto
|
|
# Build for both Intel and Apple Silicon on macOS
|
|
target: ${{ matrix.os == 'macos-latest' && 'universal2-apple-darwin' || '' }}
|
|
|
|
- name: Upload wheels as artifact
|
|
uses: actions/upload-artifact@v7
|
|
with:
|
|
name: wheels-${{ matrix.os }}
|
|
path: dist
|
|
|
|
# -------------------------------------------------------------------------
|
|
# Publish to PyPI
|
|
# -------------------------------------------------------------------------
|
|
publish:
|
|
name: Publish to PyPI
|
|
runs-on: ubuntu-latest
|
|
needs: build-wheels
|
|
if: github.event_name == 'release' && github.event.action == 'published'
|
|
environment:
|
|
name: pypi
|
|
url: https://pypi.org/p/ferro-ta
|
|
permissions:
|
|
id-token: write
|
|
steps:
|
|
- name: Download all wheels
|
|
uses: actions/download-artifact@v8
|
|
with:
|
|
pattern: wheels-*
|
|
merge-multiple: true
|
|
path: dist
|
|
|
|
- name: Publish to PyPI
|
|
uses: pypa/gh-action-pypi-publish@release/v1
|
|
|
|
# -------------------------------------------------------------------------
|
|
# Publish ferro_ta_core to crates.io (requires CARGO_REGISTRY_TOKEN secret)
|
|
# -------------------------------------------------------------------------
|
|
publish-cratesio:
|
|
name: Publish to crates.io
|
|
runs-on: ubuntu-latest
|
|
if: github.event_name == 'release' && github.event.action == 'published'
|
|
steps:
|
|
- uses: actions/checkout@v6
|
|
|
|
- name: Install Rust
|
|
uses: dtolnay/rust-toolchain@v1
|
|
with:
|
|
toolchain: stable
|
|
|
|
- name: Publish ferro_ta_core to crates.io
|
|
run: cargo publish -p ferro_ta_core --token ${{ secrets.CARGO_REGISTRY_TOKEN }}
|
|
|
|
# -------------------------------------------------------------------------
|
|
# SBOM generation — Software Bill of Materials for supply-chain transparency
|
|
# Generates SBOMs for both Python (syft/CycloneDX) and Rust (cargo-cyclonedx)
|
|
# and uploads them as GitHub Release assets.
|
|
# -------------------------------------------------------------------------
|
|
sbom:
|
|
name: Generate SBOM (Python + Rust)
|
|
runs-on: ubuntu-latest
|
|
needs: build-wheels
|
|
if: github.event_name == 'release' && github.event.action == 'published'
|
|
permissions:
|
|
contents: write
|
|
id-token: write
|
|
steps:
|
|
- uses: actions/checkout@v6
|
|
|
|
- name: Set up Python 3.12
|
|
uses: actions/setup-python@v6
|
|
with:
|
|
python-version: "3.12"
|
|
|
|
- name: Install maturin
|
|
run: pip install maturin numpy
|
|
|
|
- name: Build and install ferro_ta wheel
|
|
run: |
|
|
maturin build --release --out dist
|
|
pip install dist/*.whl
|
|
|
|
- name: Generate Python SBOM (CycloneDX via anchore/sbom-action)
|
|
uses: anchore/sbom-action@v0.24.0
|
|
with:
|
|
artifact-name: ferro-ta-python-sbom.spdx.json
|
|
output-file: ferro-ta-python-sbom.spdx.json
|
|
format: spdx-json
|
|
|
|
- name: Install Rust stable
|
|
uses: dtolnay/rust-toolchain@v1
|
|
with:
|
|
toolchain: stable
|
|
|
|
- name: Install cargo-cyclonedx
|
|
run: cargo install cargo-cyclonedx --locked
|
|
|
|
- name: Generate Rust SBOM (CycloneDX)
|
|
run: cargo cyclonedx --format json --output-cdx ferro-ta-rust-sbom.cdx.json
|
|
|
|
- name: Upload Python SBOM to release
|
|
uses: softprops/action-gh-release@v2
|
|
with:
|
|
files: ferro-ta-python-sbom.spdx.json
|
|
|
|
- name: Upload Rust SBOM to release
|
|
uses: softprops/action-gh-release@v2
|
|
with:
|
|
files: ferro-ta-rust-sbom.cdx.json
|