release: v0.1.4 #8
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # ============================================================================= | |
| # Release Pipeline -- Docker + crates.io + PyPI + GitHub Release on v* tags | |
| # ============================================================================= | |
| name: Release | |
| on: | |
| push: | |
| tags: | |
| - "v*" | |
| env: | |
| REGISTRY: ghcr.io | |
| IMAGE_NAME: epappas/llmtrace-proxy | |
| permissions: | |
| contents: write | |
| packages: write | |
| security-events: write | |
| jobs: | |
| # --------------------------------------------------------------------------- | |
| # Job 1: Validate tag and extract metadata | |
| # --------------------------------------------------------------------------- | |
| validate: | |
| name: Validate Tag | |
| runs-on: ubuntu-latest | |
| outputs: | |
| tag: ${{ steps.meta.outputs.tag }} | |
| version: ${{ steps.meta.outputs.version }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Extract and validate version | |
| id: meta | |
| run: | | |
| TAG="${GITHUB_REF#refs/tags/}" | |
| VERSION="${TAG#v}" | |
| echo "tag=$TAG" >> "$GITHUB_OUTPUT" | |
| echo "version=$VERSION" >> "$GITHUB_OUTPUT" | |
| # Verify tag version matches workspace Cargo.toml | |
| CARGO_VERSION=$(grep -m1 '^version' Cargo.toml | sed 's/.*"\(.*\)".*/\1/') | |
| if [ "$VERSION" != "$CARGO_VERSION" ]; then | |
| echo "ERROR: Tag version ($VERSION) does not match Cargo.toml version ($CARGO_VERSION)" | |
| exit 1 | |
| fi | |
| - name: Generate changelog | |
| run: | | |
| TAG="${GITHUB_REF#refs/tags/}" | |
| VERSION="${TAG#v}" | |
| DATE=$(date +%Y-%m-%d) | |
| PREV_TAG=$(git describe --tags --abbrev=0 HEAD^ 2>/dev/null || echo "") | |
| # Generate release body for GitHub Release | |
| { | |
| echo "### Install" | |
| echo "" | |
| echo '```bash' | |
| echo "curl -sS https://raw.githubusercontent.com/epappas/llmtrace/main/scripts/install.sh | bash" | |
| echo "" | |
| echo "# Or use:" | |
| echo "cargo install llmtrace # Rust proxy" | |
| echo "pip install llmtracing # Python SDK" | |
| echo "docker pull ghcr.io/epappas/llmtrace-proxy:$VERSION" | |
| echo '```' | |
| echo "" | |
| echo "- [crates.io/crates/llmtrace](https://crates.io/crates/llmtrace)" | |
| echo "- [pypi.org/project/llmtracing](https://pypi.org/project/llmtracing)" | |
| echo "" | |
| } > changelog.md | |
| if [ -n "$PREV_TAG" ]; then | |
| echo "### Changes since $PREV_TAG" >> changelog.md | |
| git log --pretty=format:"- %s (%h)" "$PREV_TAG"..HEAD >> changelog.md | |
| else | |
| echo "### Changes" >> changelog.md | |
| git log --pretty=format:"- %s (%h)" >> changelog.md | |
| fi | |
| # Generate entry for CHANGELOG.md | |
| ENTRY="## [$VERSION] - $DATE" | |
| if [ -n "$PREV_TAG" ]; then | |
| ENTRY="$ENTRY"$'\n\n'"$(git log --pretty=format:"- %s (%h)" "$PREV_TAG"..HEAD)" | |
| else | |
| ENTRY="$ENTRY"$'\n\n'"$(git log --pretty=format:"- %s (%h)")" | |
| fi | |
| # Prepend new entry after the header in CHANGELOG.md | |
| HEADER_END=$(grep -n '^and this project adheres' CHANGELOG.md | head -1 | cut -d: -f1) | |
| { | |
| head -n "$HEADER_END" CHANGELOG.md | |
| echo "" | |
| echo "$ENTRY" | |
| tail -n +"$((HEADER_END + 1))" CHANGELOG.md | |
| } > CHANGELOG.md.tmp | |
| mv CHANGELOG.md.tmp CHANGELOG.md | |
| - name: Upload changelog artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: changelog | |
| path: | | |
| changelog.md | |
| CHANGELOG.md | |
| # --------------------------------------------------------------------------- | |
| # Job 2: Run tests (gate on passing) | |
| # --------------------------------------------------------------------------- | |
| test: | |
| name: Test Suite | |
| needs: validate | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Install protoc | |
| run: sudo apt-get update && sudo apt-get install -y protobuf-compiler | |
| - name: Install Rust toolchain | |
| uses: dtolnay/rust-toolchain@stable | |
| with: | |
| components: rustfmt, clippy | |
| - name: Cache cargo | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| ~/.cargo/registry | |
| ~/.cargo/git | |
| target | |
| key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} | |
| restore-keys: ${{ runner.os }}-cargo- | |
| - name: Check formatting | |
| run: cargo fmt --check | |
| - name: Clippy | |
| run: cargo clippy --workspace -- -D warnings | |
| - name: Run tests | |
| run: cargo test --workspace | |
| # --------------------------------------------------------------------------- | |
| # Job 3: Multi-arch Docker build + push to GHCR | |
| # --------------------------------------------------------------------------- | |
| docker: | |
| name: Docker Build & Push | |
| needs: [validate, test] | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Set up QEMU | |
| uses: docker/setup-qemu-action@v3 | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| - name: Log in to GHCR | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ${{ env.REGISTRY }} | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Build image for scanning (amd64 only) | |
| uses: docker/build-push-action@v6 | |
| with: | |
| context: . | |
| push: false | |
| load: true | |
| platforms: linux/amd64 | |
| tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:scan | |
| cache-from: type=gha | |
| cache-to: type=gha,mode=max | |
| - name: Trivy vulnerability scan | |
| uses: aquasecurity/trivy-action@0.33.1 | |
| with: | |
| image-ref: "${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:scan" | |
| format: sarif | |
| output: trivy-results.sarif | |
| severity: CRITICAL,HIGH | |
| exit-code: "1" | |
| - name: Upload Trivy SARIF to GitHub Security | |
| uses: github/codeql-action/upload-sarif@v4 | |
| if: always() | |
| with: | |
| sarif_file: trivy-results.sarif | |
| - name: Build and push multi-arch image | |
| uses: docker/build-push-action@v6 | |
| with: | |
| context: . | |
| push: true | |
| platforms: linux/amd64,linux/arm64 | |
| tags: | | |
| ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ needs.validate.outputs.version }} | |
| ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest | |
| labels: | | |
| org.opencontainers.image.source=https://github.com/${{ github.repository }} | |
| org.opencontainers.image.revision=${{ github.sha }} | |
| org.opencontainers.image.version=${{ needs.validate.outputs.version }} | |
| org.opencontainers.image.created=${{ github.event.head_commit.timestamp }} | |
| cache-from: type=gha | |
| cache-to: type=gha,mode=max | |
| # --------------------------------------------------------------------------- | |
| # Job 4: Publish crates to crates.io (in dependency order) | |
| # --------------------------------------------------------------------------- | |
| publish-crates: | |
| name: Publish to crates.io | |
| needs: test | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Install Rust toolchain | |
| uses: dtolnay/rust-toolchain@stable | |
| - name: Publish crates in dependency order | |
| env: | |
| CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} | |
| run: | | |
| publish() { | |
| echo "Publishing $1..." | |
| cargo publish -p "$1" --no-verify 2>&1 || { | |
| if cargo publish -p "$1" --no-verify 2>&1 | grep -q "already exists"; then | |
| echo "$1 already published, skipping" | |
| else | |
| exit 1 | |
| fi | |
| } | |
| } | |
| publish llmtrace-core | |
| sleep 30 | |
| publish llmtrace-storage | |
| publish llmtrace-security | |
| sleep 30 | |
| publish llmtrace-sdk | |
| publish llmtrace | |
| # --------------------------------------------------------------------------- | |
| # Job 5: Build Python wheels and publish to PyPI | |
| # --------------------------------------------------------------------------- | |
| publish-pypi: | |
| name: Python Wheels | |
| needs: test | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - os: ubuntu-latest | |
| target: x86_64 | |
| - os: ubuntu-latest | |
| target: aarch64 | |
| - os: macos-latest | |
| target: universal2-apple-darwin | |
| runs-on: ${{ matrix.os }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Set up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.12" | |
| - name: Build wheel | |
| uses: PyO3/maturin-action@v1 | |
| with: | |
| target: ${{ matrix.target }} | |
| args: --release --out dist -i python3.12 | |
| manylinux: auto | |
| working-directory: crates/llmtrace-python | |
| - name: Upload wheel artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: wheel-${{ matrix.target }} | |
| path: crates/llmtrace-python/dist/*.whl | |
| publish-pypi-upload: | |
| name: Publish to PyPI | |
| needs: publish-pypi | |
| if: always() && !cancelled() | |
| runs-on: ubuntu-latest | |
| permissions: | |
| id-token: write | |
| steps: | |
| - name: Download all wheels | |
| uses: actions/download-artifact@v4 | |
| with: | |
| pattern: wheel-* | |
| merge-multiple: true | |
| path: dist/ | |
| - name: Publish to PyPI | |
| uses: pypa/gh-action-pypi-publish@release/v1 | |
| with: | |
| password: ${{ secrets.PYPI_TOKEN }} | |
| packages-dir: dist/ | |
| # --------------------------------------------------------------------------- | |
| # Job 6: Build proxy binaries for release assets | |
| # --------------------------------------------------------------------------- | |
| build-binaries: | |
| name: Build Binaries | |
| needs: test | |
| strategy: | |
| matrix: | |
| include: | |
| - os: ubuntu-latest | |
| target: x86_64-unknown-linux-musl | |
| artifact: llmtrace-proxy-linux-amd64 | |
| - os: macos-latest | |
| target: aarch64-apple-darwin | |
| artifact: llmtrace-proxy-macos-arm64 | |
| runs-on: ${{ matrix.os }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Install musl toolchain and protoc (Linux) | |
| if: runner.os == 'Linux' | |
| run: sudo apt-get update && sudo apt-get install -y musl-tools protobuf-compiler | |
| - name: Install protoc (macOS) | |
| if: runner.os == 'macOS' | |
| run: brew install protobuf | |
| - name: Install Rust toolchain | |
| uses: dtolnay/rust-toolchain@stable | |
| with: | |
| targets: ${{ matrix.target }} | |
| - name: Cache cargo | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| ~/.cargo/registry | |
| ~/.cargo/git | |
| target | |
| key: ${{ runner.os }}-${{ matrix.target }}-cargo-${{ hashFiles('**/Cargo.lock') }} | |
| restore-keys: ${{ runner.os }}-${{ matrix.target }}-cargo- | |
| - name: Build | |
| run: cargo build --release --bin llmtrace-proxy --target ${{ matrix.target }} | |
| - name: Package binary | |
| run: | | |
| mkdir -p dist | |
| cp target/${{ matrix.target }}/release/llmtrace-proxy dist/${{ matrix.artifact }} | |
| chmod +x dist/${{ matrix.artifact }} | |
| - name: Upload binary artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: ${{ matrix.artifact }} | |
| path: dist/${{ matrix.artifact }} | |
| # --------------------------------------------------------------------------- | |
| # Job 7: Create GitHub Release with all assets | |
| # --------------------------------------------------------------------------- | |
| github-release: | |
| name: GitHub Release | |
| needs: [validate, docker, publish-crates, publish-pypi-upload, build-binaries] | |
| if: always() && needs.validate.result == 'success' && needs.docker.result == 'success' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| ref: main | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Download changelog | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: changelog | |
| - name: Download all release assets | |
| uses: actions/download-artifact@v4 | |
| with: | |
| pattern: "{wheel-*,llmtrace-proxy-*}" | |
| merge-multiple: true | |
| path: assets/ | |
| - name: Commit CHANGELOG.md | |
| run: | | |
| git config user.name "github-actions[bot]" | |
| git config user.email "github-actions[bot]@users.noreply.github.com" | |
| git add CHANGELOG.md | |
| git diff --cached --quiet && exit 0 | |
| git commit -m "changelog: update for ${{ needs.validate.outputs.tag }}" | |
| git push origin main | |
| - name: Create GitHub Release | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| tag_name: ${{ needs.validate.outputs.tag }} | |
| name: "LLMTrace ${{ needs.validate.outputs.tag }}" | |
| body_path: changelog.md | |
| draft: false | |
| prerelease: ${{ contains(needs.validate.outputs.tag, '-') }} | |
| files: assets/* | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} |