Skip to content

fix(release): use ubuntu-22.04 for portable Linux binary #10

fix(release): use ubuntu-22.04 for portable Linux binary

fix(release): use ubuntu-22.04 for portable Linux binary #10

Workflow file for this run

# =============================================================================
# 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:
fail-fast: false
matrix:
include:
- os: ubuntu-22.04
target: x86_64-unknown-linux-gnu
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 protoc (Linux)
if: runner.os == 'Linux'
run: sudo apt-get update && sudo apt-get install -y 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 }}