Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
337 changes: 337 additions & 0 deletions .github/workflows/post-release-smoke.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,337 @@
# πŸ§ͺ Post-release smoke matrix β€” install from every channel + verify it works.
#
# BEE-1786 β€” fires after each published release and exercises every
# distribution channel against its native OS:
#
# - Homebrew tap (macOS arm64 + intel, Linux)
# - Scoop bucket (Windows)
# - `cargo install` (Linux + macOS + Windows)
# - Direct GitHub Releases tarball/zip (all 5 native targets)
#
# Channels still queued (BEE-1784 AppImage / BEE-1787 winget /
# BEE-1788 Chocolatey / BEE-1789 Homebrew core / BEE-1790 .deb+.rpm)
# add their own matrix rows when they land. No "skipped because not
# yet implemented" rows here β€” only test what exists.
#
# Pre-release tags (`-rc`/`-test`/`-alpha`) skip Homebrew + Scoop +
# crates.io smokes because BEE-1780/1782/1783 also skip those
# pipelines for pre-releases. The direct-tarball + manual install
# smoke runs against every tag β€” that's the universal floor.
#
# The summary job aggregates `needs.*.result` into a GitHub Actions
# Job Summary table (channel Γ— platform β†’ βœ… / ❌) and fails the
# workflow if any cell failed. GitHub's commit-status banner +
# workflow-level email (per user GH settings) are the notification
# surface; a richer Slack hook is a follow-up.

name: πŸ§ͺ Post-release smoke

on:
release:
types: [published]
workflow_dispatch:
inputs:
tag:
description: "Release tag to smoke-test (e.g. v0.1.0). Required for workflow_dispatch."
type: string
required: true

permissions:
contents: read

concurrency:
group: post-release-smoke-${{ github.workflow }}-${{ github.event.release.tag_name || inputs.tag }}
cancel-in-progress: false

env:
TAG: ${{ github.event.release.tag_name || inputs.tag }}
SMOKE_PAYLOAD: "beep-smoke-payload"

jobs:
resolve:
name: πŸ“‹ Resolve target + flags
runs-on: ubuntu-latest
outputs:
tag: ${{ steps.r.outputs.tag }}
version: ${{ steps.r.outputs.version }}
prerelease: ${{ steps.r.outputs.prerelease }}
steps:
- id: r
run: |
set -euo pipefail
if [ -z "${TAG:-}" ]; then
echo "::error::no tag β€” release event must carry a tag or workflow_dispatch must pass one"
exit 1
fi
VERSION="${TAG#v}"
# Same heuristic as release.yml's `publish-crates`/`notify-*`
# predicates: tags containing `-` are pre-release.
if [[ "$TAG" == *-* ]]; then
PRE=true
else
PRE=false
fi
echo "tag=$TAG" >> "$GITHUB_OUTPUT"
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
echo "prerelease=$PRE" >> "$GITHUB_OUTPUT"
echo "::notice::Smoke-testing $TAG (version=$VERSION, prerelease=$PRE)"

# ──────────────────────────────────────────────────────────────────
# Direct tarball / zip (BEE-150 release artifacts)
# Runs unconditionally for every tag β€” the universal floor.
# ──────────────────────────────────────────────────────────────────
direct-tarball:
name: πŸ“¦ Direct ${{ matrix.target }}
needs: resolve
runs-on: ${{ matrix.runner }}
strategy:
fail-fast: false
matrix:
include:
- target: aarch64-apple-darwin
runner: macos-latest
archive: tar.xz
- target: x86_64-apple-darwin
runner: macos-15-intel
archive: tar.xz
- target: x86_64-unknown-linux-gnu
runner: ubuntu-latest
archive: tar.xz
- target: aarch64-unknown-linux-gnu
runner: ubuntu-24.04-arm
archive: tar.xz
- target: x86_64-pc-windows-msvc
runner: windows-latest
archive: zip
steps:
- name: 🐧 Install ALSA runtime (Linux only)
if: runner.os == 'Linux'
run: sudo apt-get update -qq && sudo apt-get install -y libasound2

- name: ⬇️ Download artifact
env:
TAG: ${{ needs.resolve.outputs.tag }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
shell: bash
run: |
set -euo pipefail
ART="beeping-cli-${{ matrix.target }}.${{ matrix.archive }}"
gh release download "$TAG" --repo "${GITHUB_REPOSITORY}" --pattern "$ART" --clobber
ls -lah "$ART"

- name: πŸ“‚ Extract + install
shell: bash
env:
TARGET: ${{ matrix.target }}
run: |
set -euo pipefail
ART="beeping-cli-${TARGET}.${{ matrix.archive }}"
if [[ "$ART" == *.zip ]]; then
unzip -q "$ART"
else
tar -xJf "$ART"
fi
ls -lah "beeping-cli-${TARGET}/"
# macOS Gatekeeper quarantine workaround for tarballs
# downloaded via curl/gh (until BEE-1791 lands):
if [ "${RUNNER_OS}" = "macOS" ]; then
xattr -d com.apple.quarantine "beeping-cli-${TARGET}/beeping" 2>/dev/null || true
fi
echo "BIN=$PWD/beeping-cli-${TARGET}/beeping${{ runner.os == 'Windows' && '.exe' || '' }}" >> "$GITHUB_ENV"

- name: πŸ”’ --version matches release tag
shell: bash
env:
EXPECTED_VERSION: ${{ needs.resolve.outputs.version }}
run: |
set -euo pipefail
"$BIN" --version
# `beeping --version` emits `beeping <version>`; assert match.
got=$("$BIN" --version | awk '{print $2}')
if [ "$got" != "$EXPECTED_VERSION" ]; then
echo "::error::version mismatch: got '$got' expected '$EXPECTED_VERSION'"
exit 1
fi

- name: 🩺 Doctor offline
shell: bash
run: |
"$BIN" doctor --mode offline

- name: πŸ” Offline round-trip (skip Windows until BEE-2222)
if: runner.os != 'Windows'
shell: bash
run: |
set -euo pipefail
# Offline-only encode (no live audio output, just write WAV)
# then decode it back. base32 alphabet only β€” see BEE-1886.
"$BIN" encode "$SMOKE_PAYLOAD" --out /tmp/smoke.wav --mode offline
got=$("$BIN" decode --file /tmp/smoke.wav --mode offline)
echo "decoded: $got"
if ! echo "$got" | grep -qF "$SMOKE_PAYLOAD"; then
echo "::error::round-trip mismatch: decoded payload missing '$SMOKE_PAYLOAD'"
exit 1
fi

# ──────────────────────────────────────────────────────────────────
# `cargo install beeping-cli` (BEE-151)
# Skipped on pre-release (crates.io publish also skipped per
# release.yml predicate).
# ──────────────────────────────────────────────────────────────────
cargo-install:
name: πŸ“¦ cargo install on ${{ matrix.os }}
needs: resolve
if: needs.resolve.outputs.prerelease == 'false'
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
steps:
- name: 🐧 Install ALSA dev headers (Linux build dep)
if: runner.os == 'Linux'
run: sudo apt-get update -qq && sudo apt-get install -y libasound2-dev pkg-config

- name: πŸ¦€ Setup Rust stable
uses: dtolnay/rust-toolchain@stable

- name: πŸ“¦ cargo install beeping-cli
shell: bash
env:
VERSION: ${{ needs.resolve.outputs.version }}
run: cargo install beeping-cli --version "$VERSION" --locked

- name: πŸ”’ --version
shell: bash
run: beeping --version

- name: 🩺 Doctor offline
shell: bash
run: beeping doctor --mode offline

# ──────────────────────────────────────────────────────────────────
# Homebrew tap (BEE-151 + BEE-1782 auto-update)
# Skipped on pre-release (tap auto-update also skipped).
# ──────────────────────────────────────────────────────────────────
homebrew-tap:
name: 🍺 brew on ${{ matrix.os }}
needs: resolve
if: needs.resolve.outputs.prerelease == 'false'
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
# macOS arm64 (latest) + intel (macos-15-intel) + Linuxbrew
os: [macos-latest, macos-15-intel, ubuntu-latest]
steps:
- name: 🍺 brew install beeping-io/tap/beeping-cli
shell: bash
env:
# Homebrew on GH Actions sometimes pre-installs incompatible
# bottles; pass --verbose so failures surface.
HOMEBREW_NO_INSTALL_CLEANUP: "1"
HOMEBREW_NO_AUTO_UPDATE: "1"
run: |
set -euo pipefail
brew tap beeping-io/tap https://github.com/beeping-io/tap
brew install --verbose beeping-io/tap/beeping-cli

- name: πŸ”’ --version
run: beeping --version

- name: 🩺 Doctor offline
run: beeping doctor --mode offline

# ──────────────────────────────────────────────────────────────────
# Scoop bucket (BEE-151 + BEE-1783 auto-update)
# Skipped on pre-release (bucket auto-update also skipped).
# ──────────────────────────────────────────────────────────────────
scoop-bucket:
name: πŸ₯„ scoop on windows-latest
needs: resolve
if: needs.resolve.outputs.prerelease == 'false'
runs-on: windows-latest
steps:
- name: πŸ₯„ Install Scoop
shell: powershell
run: |
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
irm https://get.scoop.sh -outfile install-scoop.ps1
.\install-scoop.ps1 -RunAsAdmin

- name: πŸ₯„ scoop install beeping-cli
shell: powershell
run: |
scoop bucket add beeping https://github.com/beeping-io/scoop-bucket
scoop install beeping-cli

- name: πŸ”’ --version
shell: powershell
run: beeping --version

# No `doctor --mode offline` on Windows: BEE-2222 (FFI runtime
# crash) makes any FFI-touching subcommand crash with
# STATUS_ACCESS_VIOLATION on x86_64-pc-windows-msvc. --version
# is enough to prove the install path works.

# ──────────────────────────────────────────────────────────────────
# Aggregate summary β€” always runs, fails if any cell failed.
# ──────────────────────────────────────────────────────────────────
summary:
name: πŸ“Š Smoke summary
needs: [resolve, direct-tarball, cargo-install, homebrew-tap, scoop-bucket]
if: ${{ !cancelled() }}
runs-on: ubuntu-latest
steps:
- name: πŸ“Š Compose markdown summary
env:
TAG: ${{ needs.resolve.outputs.tag }}
PRERELEASE: ${{ needs.resolve.outputs.prerelease }}
R_DIRECT: ${{ needs.direct-tarball.result }}
R_CARGO: ${{ needs.cargo-install.result }}
R_BREW: ${{ needs.homebrew-tap.result }}
R_SCOOP: ${{ needs.scoop-bucket.result }}
run: |
set -euo pipefail
mark() {
case "$1" in
success) echo "βœ…" ;;
failure) echo "❌" ;;
cancelled) echo "🚫" ;;
skipped) echo "⏭️" ;;
*) echo "?" ;;
esac
}
{
echo "## πŸ§ͺ Post-release smoke β€” $TAG"
echo ""
echo "Prerelease: \`$PRERELEASE\` (pre-release tags skip Homebrew + Scoop + crates.io because those channels also skip dispatching for pre-releases β€” see BEE-1780/1782/1783)."
echo ""
echo "| Channel | Result |"
echo "|---|---|"
echo "| πŸ“¦ Direct tarball/zip (5 targets) | $(mark "$R_DIRECT") |"
echo "| πŸ“¦ \`cargo install beeping-cli\` (3 OSes) | $(mark "$R_CARGO") |"
echo "| 🍺 Homebrew tap (3 OSes) | $(mark "$R_BREW") |"
echo "| πŸ₯„ Scoop bucket (Windows) | $(mark "$R_SCOOP") |"
} >> "$GITHUB_STEP_SUMMARY"

- name: 🚦 Gate β€” fail if any active channel failed
env:
R_DIRECT: ${{ needs.direct-tarball.result }}
R_CARGO: ${{ needs.cargo-install.result }}
R_BREW: ${{ needs.homebrew-tap.result }}
R_SCOOP: ${{ needs.scoop-bucket.result }}
run: |
set -euo pipefail
fail=0
for r in "$R_DIRECT" "$R_CARGO" "$R_BREW" "$R_SCOOP"; do
case "$r" in
success|skipped) ;;
*) fail=1 ;;
esac
done
if [ "$fail" = 1 ]; then
echo "::error::At least one channel smoke failed β€” see Job Summary"
exit 1
fi
echo "::notice::All active channels smoked green for ${{ needs.resolve.outputs.tag }}"
Loading
Loading