Skip to content

fix: correct repo name and decouple release from crates publish (#70) #82

fix: correct repo name and decouple release from crates publish (#70)

fix: correct repo name and decouple release from crates publish (#70) #82

Workflow file for this run

name: Release
on:
push:
branches:
- main
tags:
- 'v*'
release:
types: [published]
workflow_dispatch:
inputs:
tag:
description: 'Release tag (e.g., v0.2.7)'
required: true
type: string
env:
REGISTRY: ghcr.io
IMAGE_NAME: pulseengine/wsc
RUST_VERSION: "1.90.0"
jobs:
# Generate compliance report from rivet artifacts
build-compliance:
name: Build compliance report
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Generate compliance report
id: report
uses: pulseengine/rivet/.github/actions/compliance@main
with:
theme: dark
rivet-version: v0.3.0
- uses: actions/upload-artifact@v4
with:
name: compliance-report
path: ${{ steps.report.outputs.archive-path }}
# Build native CLI binaries for all platforms
build-native-cli:
name: Build CLI (${{ matrix.target }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
# Linux x86_64
- target: x86_64-unknown-linux-gnu
os: ubuntu-latest
artifact_name: wsc
asset_name: wsc-linux-x86_64
# Linux x86_64 with TPM2
- target: x86_64-unknown-linux-gnu
os: ubuntu-latest
artifact_name: wsc
asset_name: wsc-linux-x86_64-tpm2
features: tpm2
# Linux aarch64
- target: aarch64-unknown-linux-gnu
os: ubuntu-latest
artifact_name: wsc
asset_name: wsc-linux-aarch64
cross: true
# macOS x86_64 (Intel) - using macos-15-intel (macos-13 deprecated Dec 2025)
- target: x86_64-apple-darwin
os: macos-15-intel
artifact_name: wsc
asset_name: wsc-macos-x86_64
# macOS aarch64 (Apple Silicon)
- target: aarch64-apple-darwin
os: macos-latest
artifact_name: wsc
asset_name: wsc-macos-aarch64
# Windows x86_64
- target: x86_64-pc-windows-msvc
os: windows-latest
artifact_name: wsc.exe
asset_name: wsc-windows-x86_64.exe
steps:
- uses: actions/checkout@v4
- name: Install Rust
run: |
rustup update stable
rustup default stable
rustup target add ${{ matrix.target }}
- name: Install cross-compilation tools (Linux aarch64)
if: matrix.cross
run: |
sudo apt-get update
sudo apt-get install -y gcc-aarch64-linux-gnu g++-aarch64-linux-gnu
echo "CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc" >> $GITHUB_ENV
- name: Install TPM2 dependencies
if: matrix.features == 'tpm2'
run: |
sudo apt-get update
sudo apt-get install -y libtss2-dev
- name: Build CLI
run: |
cargo build --release --bin wsc --target ${{ matrix.target }} ${{ matrix.features && format('--features {0}', matrix.features) || '' }}
- name: Create artifact directory
shell: bash
run: |
mkdir -p artifacts
cp target/${{ matrix.target }}/release/${{ matrix.artifact_name }} artifacts/${{ matrix.asset_name }}
- name: Create checksum
shell: bash
run: |
cd artifacts
if [[ "$RUNNER_OS" == "Windows" ]]; then
certutil -hashfile ${{ matrix.asset_name }} SHA256 > ${{ matrix.asset_name }}.sha256
else
shasum -a 256 ${{ matrix.asset_name }} > ${{ matrix.asset_name }}.sha256
fi
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.asset_name }}
path: artifacts/*
retention-days: 1
publish-crates:
# Only publish on tag pushes to prevent race condition with main branch
if: github.repository == 'pulseengine/sigil' && github.ref_type == 'tag'
name: Publish Rust Crates
runs-on: ubuntu-latest
permissions:
id-token: write # Required for trusted publishing
contents: read
steps:
- uses: actions/checkout@v4
- name: Install Rust
run: rustup update stable && rustup default stable
- name: Build publish script
run: rustc scripts/publish.rs
- name: Publish crates to crates.io
run: ./publish publish
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CRATES_IO_TOKEN }}
build-and-release:
name: Build & Release WASM Component
runs-on: ubuntu-latest
permissions:
contents: write
packages: write
id-token: write # For Cosign keyless signing
needs: [publish-crates, build-native-cli]
if: always() && needs.build-native-cli.result == 'success'
steps:
- name: Checkout code
uses: actions/checkout@v5
- name: Setup C++ Build Tools
run: |
sudo apt-get update
sudo apt-get install -y build-essential gcc g++
- name: Setup Bazel
uses: bazel-contrib/setup-bazel@0.15.0
with:
bazelisk-cache: true
# Use a shared cache key across all builds for better reuse
disk-cache: ${{ runner.os }}-bazel
repository-cache: true
- name: Install Cosign
uses: sigstore/cosign-installer@v3
with:
cosign-release: 'v2.4.1'
- name: Verify cosign version
run: cosign version
- name: Build WASM Components
run: |
echo "Building wsc WebAssembly components..."
# Build component library (WIT interface)
echo "Building signing library component..."
bazel build //src/component:signing_lib_release
# Build CLI WASM binary with wasm32-wasip2 platform
echo "Building CLI WASM binary..."
bazel build //src/cli:wasmsign_cli_wasm --platforms=@rules_wasm_component//platforms:wasm32-wasip2 || echo "CLI WASM build failed, continuing..."
# Find and copy the component WASM
# Pattern: bazel-out/<platform>-fastbuild-ST-<hash>/bin/src/component/*.wasm
COMPONENT_WASM=$(ls bazel-out/k8-fastbuild-ST-*/bin/src/component/*.wasm 2>/dev/null | head -n 1)
if [ -z "$COMPONENT_WASM" ]; then
echo "Error: Could not find component WASM file"
echo "Searching for any WASM files in bazel-out:"
find bazel-out -name "*.wasm" -type f
exit 1
fi
echo "Found component WASM: $COMPONENT_WASM"
cp "$COMPONENT_WASM" ./wsc-component.wasm
# Find and copy the CLI WASM if it exists
# After building with --platforms flag, it's at bazel-bin/src/cli/wasmsign_cli.wasm
CLI_WASM="bazel-bin/src/cli/wasmsign_cli.wasm"
if [ -f "$CLI_WASM" ]; then
echo "Found CLI WASM: $CLI_WASM"
cp "$CLI_WASM" ./wsc-cli.wasm
else
echo "Warning: CLI WASM not found, will only release component"
fi
# Verify files
echo "Built WASM files:"
ls -lh wsc-*.wasm
file wsc-*.wasm
- name: Build wsc Native CLI
run: |
echo "Building wsc native CLI for signing..."
cargo build --release --bin wsc
# Verify the CLI works
./target/release/wsc --version || echo "CLI version check skipped"
echo "✅ Native wsc CLI built"
- name: Sign WASM Files with wsc (Keyless)
run: |
echo "🔐 Signing WASM artifacts with wsc keyless signing..."
# Sign the component WASM with keyless signing
echo "Signing wsc-component.wasm..."
./target/release/wsc sign --keyless \
--input-file wsc-component.wasm \
--output-file wsc-component.signed.wasm
# Create a detached signature for verification
mv wsc-component.signed.wasm wsc-component.wasm
# Sign the CLI WASM if it exists
if [ -f wsc-cli.wasm ]; then
echo "Signing wsc-cli.wasm..."
./target/release/wsc sign --keyless \
--input-file wsc-cli.wasm \
--output-file wsc-cli.signed.wasm
mv wsc-cli.signed.wasm wsc-cli.wasm
fi
echo "✅ WASM files signed with wsc (keyless)"
echo ""
echo "📋 Signature Details:"
echo " - Identity: GitHub Actions OIDC"
echo " - Certificate: Short-lived from Fulcio"
echo " - Transparency: Logged in Rekor"
- name: Create SHA256 checksums
run: |
# Create checksums AFTER signing so they match the signed files
sha256sum wsc-component.wasm > wsc-component.wasm.sha256
cat wsc-component.wasm.sha256
if [ -f wsc-cli.wasm ]; then
sha256sum wsc-cli.wasm > wsc-cli.wasm.sha256
cat wsc-cli.wasm.sha256
fi
- name: Log in to Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Create OCI Image with WASM Component
run: |
# Install oras for OCI artifact management
VERSION="1.2.2"
curl -LO "https://github.com/oras-project/oras/releases/download/v${VERSION}/oras_${VERSION}_linux_amd64.tar.gz"
mkdir -p oras-install/
tar -zxf "oras_${VERSION}_linux_amd64.tar.gz" -C oras-install/
sudo mv oras-install/oras /usr/local/bin/
rm -rf "oras_${VERSION}_linux_amd64.tar.gz" oras-install/
# Determine tag
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
TAG="${{ inputs.tag }}"
elif [ "${{ github.event_name }}" = "release" ]; then
TAG="${{ github.event.release.tag_name }}"
elif [ "${{ github.ref_type }}" = "tag" ]; then
TAG="${{ github.ref_name }}"
else
# For push to branches, use branch name + short SHA
TAG="${{ github.ref_name }}-${GITHUB_SHA:0:7}"
fi
# Sanitize tag for OCI (replace / with -)
TAG="${TAG//\//-}"
# Create OCI artifact references
IMAGE_TAG="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${TAG}"
IMAGE_LATEST="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest"
# Push component WASM as OCI artifact
echo "Publishing wsc component..."
oras push "${IMAGE_TAG}" \
--artifact-type application/vnd.wasm.component.layer.v1+wasm \
wsc-component.wasm:application/vnd.wasm.component.layer.v1+wasm
# Tag as latest
oras tag "${IMAGE_TAG}" latest
# Push CLI WASM as OCI artifact if it exists
if [ -f wsc-cli.wasm ]; then
CLI_TAG="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${TAG}-cli"
CLI_LATEST="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest-cli"
echo "Publishing wsc CLI..."
oras push "${CLI_TAG}" \
--artifact-type application/vnd.wasm.component.layer.v1+wasm \
wsc-cli.wasm:application/vnd.wasm.component.layer.v1+wasm
# Tag as latest-cli
oras tag "${CLI_TAG}" latest-cli
echo "CLI_TAG=${CLI_TAG}" >> $GITHUB_ENV
echo "CLI_LATEST=${CLI_LATEST}" >> $GITHUB_ENV
fi
echo "Published OCI artifacts:"
echo " Component: ${IMAGE_TAG}"
echo " Component (latest): ${IMAGE_LATEST}"
if [ -f wsc-cli.wasm ]; then
echo " CLI: ${CLI_TAG}"
echo " CLI (latest): ${CLI_LATEST}"
fi
echo "IMAGE_TAG=${IMAGE_TAG}" >> $GITHUB_ENV
echo "IMAGE_LATEST=${IMAGE_LATEST}" >> $GITHUB_ENV
- name: Install crane
uses: imjasonh/setup-crane@v0.4
- name: Resolve image digests
id: digest
run: |
COMPONENT_DIGEST=$(crane digest "${IMAGE_TAG}")
echo "component=${COMPONENT_DIGEST}" >> "$GITHUB_OUTPUT"
echo "Resolved ${IMAGE_TAG} to ${COMPONENT_DIGEST}"
LATEST_DIGEST=$(crane digest "${IMAGE_LATEST}")
echo "latest=${LATEST_DIGEST}" >> "$GITHUB_OUTPUT"
echo "Resolved ${IMAGE_LATEST} to ${LATEST_DIGEST}"
if [ -n "${CLI_TAG:-}" ]; then
CLI_DIGEST=$(crane digest "${CLI_TAG}")
echo "cli=${CLI_DIGEST}" >> "$GITHUB_OUTPUT"
echo "Resolved ${CLI_TAG} to ${CLI_DIGEST}"
CLI_LATEST_DIGEST=$(crane digest "${CLI_LATEST}")
echo "cli_latest=${CLI_LATEST_DIGEST}" >> "$GITHUB_OUTPUT"
echo "Resolved ${CLI_LATEST} to ${CLI_LATEST_DIGEST}"
fi
- name: Sign OCI Images with Cosign (digest-bound)
run: |
# Sign using keyless signing with GitHub OIDC, binding to digest
echo "Signing wsc component..."
cosign sign --yes "${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@${{ steps.digest.outputs.component }}"
cosign sign --yes "${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@${{ steps.digest.outputs.latest }}"
if [ -n "${CLI_TAG:-}" ]; then
echo "Signing wsc CLI..."
cosign sign --yes "${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@${{ steps.digest.outputs.cli }}"
cosign sign --yes "${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@${{ steps.digest.outputs.cli_latest }}"
fi
echo "OCI images signed with Cosign (keyless, digest-bound)"
- name: Generate SLSA Provenance
run: |
# Create provenance attestation
cat > provenance.json <<EOF
{
"buildType": "https://github.com/pulseengine/wsc/release@v1",
"builder": {
"id": "https://github.com/actions/runner"
},
"invocation": {
"configSource": {
"uri": "${{ github.server_url }}/${{ github.repository }}",
"digest": {
"sha1": "${{ github.sha }}"
}
}
},
"metadata": {
"buildStartedOn": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
"completeness": {
"parameters": true,
"environment": false,
"materials": false
}
},
"materials": [
{
"uri": "${{ github.server_url }}/${{ github.repository }}",
"digest": {
"sha1": "${{ github.sha }}"
}
}
]
}
EOF
# Attest the provenance for component (digest-bound)
cosign attest --yes \
--predicate provenance.json \
--type slsaprovenance \
"${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@${{ steps.digest.outputs.component }}"
# Attest the provenance for CLI if it exists (digest-bound)
if [ -n "${CLI_TAG:-}" ]; then
cosign attest --yes \
--predicate provenance.json \
--type slsaprovenance \
"${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@${{ steps.digest.outputs.cli }}"
fi
echo "✅ SLSA provenance attestation created"
- name: Generate Sigstore bundle
run: |
# Generate Sigstore bundle from cosign signature for interoperability
# This produces a portable bundle that can be verified with cosign verify-blob
COMPONENT_REF="${REGISTRY}/${IMAGE_NAME}@${{ steps.digest.outputs.component }}"
# Save cosign signature as Sigstore bundle
cosign save "${COMPONENT_REF}" --dir sigstore-bundle/ || true
if [ -d sigstore-bundle ]; then
echo "Sigstore bundle generated:"
ls -la sigstore-bundle/
fi
- name: Generate build environment attestation
run: |
# Use wsc's build-env command for structured environment capture
if command -v wsc >/dev/null 2>&1; then
wsc build-env --json > build-env.json
echo "Build environment attestation:"
cat build-env.json
else
echo "wsc CLI not available, using basic capture"
echo "{}" > build-env.json
fi
- name: Verify Signatures
run: |
# Verify the signature (digest-bound)
cosign verify \
--certificate-identity-regexp="https://github.com/${{ github.repository }}" \
--certificate-oidc-issuer="https://token.actions.githubusercontent.com" \
"${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@${{ steps.digest.outputs.component }}" || echo "Note: Signature verification may require specific conditions"
echo "Signature verification completed"
- name: Capture build environment
run: |
echo "rustc: $(rustc --version)" >> build-env.txt
echo "cargo: $(cargo --version)" >> build-env.txt
echo "bazel: $(cat .bazelversion)" >> build-env.txt
echo "cosign: $(cosign version 2>&1 | head -1)" >> build-env.txt
echo "platform: $(uname -sm)" >> build-env.txt
cat build-env.txt
- name: Download Native CLI Artifacts
uses: actions/download-artifact@v4
with:
path: native-cli
pattern: wsc-*
merge-multiple: true
- name: Download Compliance Report
uses: actions/download-artifact@v4
with:
name: compliance-report
path: compliance/
continue-on-error: true
- name: List downloaded artifacts
run: |
echo "Downloaded native CLI artifacts:"
ls -la native-cli/ || echo "No native-cli directory"
echo "Compliance report:"
ls -la compliance/ || echo "No compliance directory"
find . -name "wsc-*" -type f | head -20
- name: Upload Release Assets
if: github.event_name == 'release' || github.event_name == 'workflow_dispatch' || github.ref_type == 'tag'
uses: softprops/action-gh-release@v2
with:
files: |
wsc-component.wasm
wsc-component.wasm.sha256
wsc-cli.wasm
wsc-cli.wasm.sha256
native-cli/wsc-linux-x86_64
native-cli/wsc-linux-x86_64.sha256
native-cli/wsc-linux-x86_64-tpm2
native-cli/wsc-linux-x86_64-tpm2.sha256
native-cli/wsc-linux-aarch64
native-cli/wsc-linux-aarch64.sha256
native-cli/wsc-macos-x86_64
native-cli/wsc-macos-x86_64.sha256
native-cli/wsc-macos-aarch64
native-cli/wsc-macos-aarch64.sha256
native-cli/wsc-windows-x86_64.exe
native-cli/wsc-windows-x86_64.exe.sha256
compliance/*
body: |
## 🎉 wsc v${{ github.event_name == 'workflow_dispatch' && inputs.tag || github.ref_name }} Release
### 📦 Native CLI Binaries
| Platform | Binary | TPM2 Support |
|----------|--------|--------------|
| Linux x86_64 | `wsc-linux-x86_64` | ❌ |
| Linux x86_64 | `wsc-linux-x86_64-tpm2` | ✅ |
| Linux aarch64 | `wsc-linux-aarch64` | ❌ |
| macOS x86_64 (Intel) | `wsc-macos-x86_64` | ❌ |
| macOS aarch64 (Apple Silicon) | `wsc-macos-aarch64` | ❌ |
| Windows x86_64 | `wsc-windows-x86_64.exe` | ❌ |
### 📦 WebAssembly Components
**Component Library (WIT Interface):**
- `wsc-component.wasm` - WebAssembly component with WIT bindings
- Signed OCI artifact: `${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.event_name == 'workflow_dispatch' && inputs.tag || github.ref_name }}`
**CLI Tool (WASI Binary):**
- `wsc-cli.wasm` - WASI command-line tool for Wasmtime
- Signed OCI artifact: `${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.event_name == 'workflow_dispatch' && inputs.tag || github.ref_name }}-cli`
### 🔐 Security Features
- ✅ **WASM Module Signing** - Signed with wsc keyless signing (dogfooding!)
- ✅ **OCI Artifact Signing** - Signed with Cosign using GitHub OIDC (keyless)
- ✅ **SLSA Provenance** - Build attestation included
- ✅ **SHA256 Checksums** - For download verification
**Keyless Signing:**
- Identity: GitHub Actions OIDC
- Certificate: Short-lived from Fulcio (Sigstore)
- Transparency: Logged in Rekor
### 🚀 Quick Start
```bash
# Download native CLI for your platform
TAG=${{ github.event_name == 'workflow_dispatch' && inputs.tag || github.ref_name }}
# Linux x86_64
curl -LO https://github.com/${{ github.repository }}/releases/download/${TAG}/wsc-linux-x86_64
chmod +x wsc-linux-x86_64
./wsc-linux-x86_64 --version
# macOS Apple Silicon
curl -LO https://github.com/${{ github.repository }}/releases/download/${TAG}/wsc-macos-aarch64
chmod +x wsc-macos-aarch64
./wsc-macos-aarch64 --version
```
### 🔍 Verify Signatures
```bash
# Verify WASM module signature
wsc verify --keyless -i wsc-component.wasm
# Verify OCI artifact signature
cosign verify \
--certificate-identity-regexp="https://github.com/${{ github.repository }}" \
--certificate-oidc-issuer="https://token.actions.githubusercontent.com" \
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${TAG}
```
### 📚 Documentation
See [README.md](https://github.com/${{ github.repository }}/blob/main/README.md) for full documentation.
tag_name: ${{ github.event_name == 'workflow_dispatch' && inputs.tag || github.ref_name }}
- name: Create Release Summary
run: |
# Determine tag
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
TAG="${{ inputs.tag }}"
elif [ "${{ github.event_name }}" = "release" ]; then
TAG="${{ github.event.release.tag_name }}"
elif [ "${{ github.ref_type }}" = "tag" ]; then
TAG="${{ github.ref_name }}"
else
# For push to branches, use branch name + short SHA
TAG="${{ github.ref_name }}-${GITHUB_SHA:0:7}"
fi
# Sanitize tag for OCI (replace / with -)
TAG="${TAG//\//-}"
echo "## 🚀 Release Summary" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### 📦 Published Artifacts" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**WASM Component:**" >> $GITHUB_STEP_SUMMARY
echo "- **File**: \`wsc.wasm\` ($(ls -lh wsc.wasm | awk '{print $5}'))" >> $GITHUB_STEP_SUMMARY
echo "- **OCI Artifact**: \`${IMAGE_TAG}\`" >> $GITHUB_STEP_SUMMARY
echo "- **OCI Artifact (latest)**: \`${IMAGE_LATEST}\`" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### 🔐 Security" >> $GITHUB_STEP_SUMMARY
echo "- ✅ WASM modules signed with wsc (keyless)" >> $GITHUB_STEP_SUMMARY
echo "- ✅ OCI artifact signed with Cosign (keyless OIDC)" >> $GITHUB_STEP_SUMMARY
echo "- ✅ SLSA provenance attestation" >> $GITHUB_STEP_SUMMARY
echo "- ✅ SHA256 checksum provided" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### 🔗 Links" >> $GITHUB_STEP_SUMMARY
echo "- [Download WASM](https://github.com/${{ github.repository }}/releases/tag/${TAG})" >> $GITHUB_STEP_SUMMARY
echo "- [Pull OCI Artifact](${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${TAG})" >> $GITHUB_STEP_SUMMARY