fix: use bazel cquery for reliable artifact collection #3
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
| name: Release | |
| on: | |
| push: | |
| tags: | |
| - "v*" | |
| workflow_dispatch: | |
| inputs: | |
| tag: | |
| description: "Tag to release (e.g., v1.0.0)" | |
| required: true | |
| type: string | |
| permissions: | |
| contents: write | |
| id-token: write | |
| attestations: write | |
| jobs: | |
| build-release: | |
| name: Build Release Components | |
| runs-on: ubuntu-latest | |
| outputs: | |
| tag_name: ${{ steps.get_tag.outputs.tag_name }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Get tag name | |
| id: get_tag | |
| run: | | |
| if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then | |
| echo "tag_name=${{ inputs.tag }}" >> $GITHUB_OUTPUT | |
| else | |
| echo "tag_name=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Install Bazelisk | |
| run: | | |
| curl -LO https://github.com/bazelbuild/bazelisk/releases/latest/download/bazelisk-linux-amd64 | |
| chmod +x bazelisk-linux-amd64 | |
| sudo mv bazelisk-linux-amd64 /usr/local/bin/bazel | |
| - name: Build all components | |
| run: bazel build //... | |
| - name: Collect release artifacts | |
| id: collect | |
| run: | | |
| TAG_NAME="${{ steps.get_tag.outputs.tag_name }}" | |
| VERSION=${TAG_NAME#v} | |
| RELEASE_DIR="release-${VERSION}" | |
| mkdir -p "${RELEASE_DIR}/c" "${RELEASE_DIR}/cpp" "${RELEASE_DIR}/rust" | |
| # Use bazel cquery to reliably find built WASM files | |
| echo "=== Collecting C components ===" | |
| for f in $(bazel cquery --output=files '//c:all' 2>/dev/null); do | |
| if [[ "$f" == *.wasm ]]; then | |
| echo "Found: $f" | |
| cp "$f" "${RELEASE_DIR}/c/" | |
| fi | |
| done | |
| echo "=== Collecting C++ components ===" | |
| for f in $(bazel cquery --output=files '//cpp:all' 2>/dev/null); do | |
| if [[ "$f" == *.wasm ]]; then | |
| echo "Found: $f" | |
| cp "$f" "${RELEASE_DIR}/cpp/" | |
| fi | |
| done | |
| echo "=== Collecting Rust CLI components ===" | |
| for target in hello_rust calculator datetime; do | |
| f=$(bazel cquery --output=files "//rust:${target}" 2>/dev/null | head -1) | |
| if [ -n "$f" ] && [[ "$f" == *.wasm ]]; then | |
| echo "Found: $f" | |
| cp "$f" "${RELEASE_DIR}/rust/" | |
| fi | |
| done | |
| echo "=== Collecting Rust YOLO components ===" | |
| for profile in debug release; do | |
| # Query for specific profile targets | |
| f=$(bazel cquery --output=files "//rust:yolo_inference_${profile}" 2>/dev/null | grep '\.wasm$' | head -1) | |
| if [ -n "$f" ]; then | |
| echo "Found: $f" | |
| cp "$f" "${RELEASE_DIR}/rust/yolo_inference_${profile}.wasm" | |
| fi | |
| done | |
| # List what we collected | |
| echo "=== Collected artifacts ===" | |
| find "${RELEASE_DIR}" -name "*.wasm" -type f | |
| # Verify we have files | |
| WASM_COUNT=$(find "${RELEASE_DIR}" -name "*.wasm" -type f | wc -l) | |
| echo "Total WASM files collected: $WASM_COUNT" | |
| if [ "$WASM_COUNT" -eq 0 ]; then | |
| echo "ERROR: No WASM files were collected!" | |
| echo "Listing bazel-out structure:" | |
| find bazel-out -name "*.wasm" -type f 2>/dev/null | head -20 || echo "No files in bazel-out" | |
| echo "Listing bazel-bin structure:" | |
| find bazel-bin -name "*.wasm" -type f 2>/dev/null | head -20 || echo "No files in bazel-bin" | |
| exit 1 | |
| fi | |
| # Create archive | |
| ARCHIVE_NAME="wasm-components-${VERSION}.tar.gz" | |
| tar -czvf "$ARCHIVE_NAME" "${RELEASE_DIR}" | |
| echo "archive_name=$ARCHIVE_NAME" >> $GITHUB_OUTPUT | |
| echo "release_dir=$RELEASE_DIR" >> $GITHUB_OUTPUT | |
| echo "" | |
| echo "Created release archive:" | |
| ls -la "$ARCHIVE_NAME" | |
| echo "" | |
| echo "Contents:" | |
| tar -tzvf "$ARCHIVE_NAME" | |
| - name: Build wsc for signing | |
| run: | | |
| echo "Building wsc (WebAssembly Signature Component)..." | |
| git clone --depth 1 https://github.com/pulseengine/wsc.git /tmp/wsc | |
| cd /tmp/wsc | |
| cargo build --release --bin wsc | |
| sudo cp target/release/wsc /usr/local/bin/wsc | |
| wsc --version || echo "wsc built successfully" | |
| - name: Sign release components (keyless) | |
| run: | | |
| RELEASE_DIR="${{ steps.collect.outputs.release_dir }}" | |
| echo "🔐 Signing WASM artifacts with wsc keyless signing..." | |
| echo " - Identity: GitHub Actions OIDC" | |
| echo " - Certificate: Short-lived from Fulcio (Sigstore)" | |
| echo " - Transparency: Logged in Rekor" | |
| echo "" | |
| # Sign all WASM files with keyless signing | |
| find "${RELEASE_DIR}" -name "*.wasm" | while read wasm_file; do | |
| echo "Signing: $wasm_file" | |
| signed_file="${wasm_file%.wasm}.signed.wasm" | |
| wsc sign --keyless \ | |
| --input-file "$wasm_file" \ | |
| --output-file "$signed_file" | |
| # Replace original with signed version | |
| mv "$signed_file" "$wasm_file" | |
| done | |
| echo "✅ All WASM files signed with wsc (keyless)" | |
| # Create signed archive | |
| SIGNED_ARCHIVE="wasm-components-${{ steps.get_tag.outputs.tag_name }}-signed.tar.gz" | |
| tar -czvf "$SIGNED_ARCHIVE" "${RELEASE_DIR}" | |
| echo "signed_archive=$SIGNED_ARCHIVE" >> $GITHUB_OUTPUT | |
| - name: Generate Release Notes | |
| run: | | |
| TAG_NAME="${{ steps.get_tag.outputs.tag_name }}" | |
| cat > RELEASE_NOTES.md << 'EOF' | |
| ## WebAssembly Component Examples ${{ steps.get_tag.outputs.tag_name }} | |
| This release contains WebAssembly components built with [rules_wasm_component](https://github.com/pulseengine/rules_wasm_component). | |
| ### Components Included | |
| **C Components:** | |
| - `hello_c_debug.wasm` - Debug build | |
| - `hello_c_release.wasm` - Release build (optimized) | |
| **C++ Components:** | |
| - `hello_cpp_debug.wasm` - Debug build | |
| - `hello_cpp_release.wasm` - Release build (optimized) | |
| **Rust Components:** | |
| - `hello_rust.wasm` - Hello World CLI | |
| - `calculator.wasm` - Arithmetic calculator | |
| - `datetime.wasm` - Date/time display | |
| - `yolo_inference_debug.wasm` - YOLO detection (debug) | |
| - `yolo_inference_release.wasm` - YOLO detection (release) | |
| ### Security | |
| All components are signed using [wsc](https://github.com/pulseengine/wsc) with keyless Sigstore signing: | |
| - **Identity**: GitHub Actions OIDC | |
| - **Certificate**: Short-lived from Fulcio | |
| - **Transparency**: Logged in Rekor | |
| ### Running Components | |
| ```bash | |
| # Install wasmtime | |
| curl https://wasmtime.dev/install.sh -sSf | bash | |
| # Run Rust CLI examples | |
| wasmtime run hello_rust.wasm | |
| wasmtime run calculator.wasm 8 + 8 | |
| wasmtime run datetime.wasm | |
| # Run YOLO detection (requires ONNX model) | |
| wasmtime run --dir . -S cli -S nn -S nn-graph=onnx::./models/yolov8n \ | |
| yolo_inference_release.wasm ./image.jpg | |
| ``` | |
| ### Verification | |
| Signatures can be verified using wsc: | |
| ```bash | |
| wsc verify --keyless \ | |
| --identity "https://github.com/${{ github.repository }}" \ | |
| --issuer "https://token.actions.githubusercontent.com" \ | |
| --input-file component.wasm | |
| ``` | |
| EOF | |
| - name: Create GitHub Release | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| tag_name: ${{ steps.get_tag.outputs.tag_name }} | |
| name: Release ${{ steps.get_tag.outputs.tag_name }} | |
| body_path: RELEASE_NOTES.md | |
| files: | | |
| ${{ steps.collect.outputs.archive_name }} | |
| wasm-components-${{ steps.get_tag.outputs.tag_name }}-signed.tar.gz | |
| draft: false | |
| prerelease: false | |
| generate_release_notes: true | |
| make_latest: true | |
| - name: Generate Provenance Attestation | |
| uses: actions/attest-build-provenance@v2 | |
| with: | |
| subject-path: ${{ steps.collect.outputs.archive_name }} |