Skip to content

chore: bump version to 0.2.2 #257

chore: bump version to 0.2.2

chore: bump version to 0.2.2 #257

Workflow file for this run

name: Release
on:
push:
tags:
- "v*"
workflow_dispatch:
inputs:
version:
description: "Release version (X.Y.Z)"
required: true
type: string
permissions:
contents: read
id-token: write
env:
CARGO_TERM_COLOR: always
concurrency:
group: release-${{ github.ref_name || inputs.version }}
cancel-in-progress: false
jobs:
resolve-version:
name: Resolve Version
runs-on: ubuntu-latest
outputs:
version: ${{ steps.resolve.outputs.version }}
tag: ${{ steps.resolve.outputs.tag }}
steps:
- id: resolve
shell: bash
run: |
set -euo pipefail
if [[ "${GITHUB_EVENT_NAME}" == "push" ]]; then
TAG="${GITHUB_REF_NAME}"
VERSION="${TAG#v}"
else
VERSION="${{ inputs.version }}"
if ! [[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "Version must be strict semver: X.Y.Z" >&2
exit 1
fi
TAG="v${VERSION}"
fi
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
echo "tag=$TAG" >> "$GITHUB_OUTPUT"
echo "Resolved release version: $VERSION"
echo "Resolved release tag: $TAG"
preflight:
name: Preflight Checks
runs-on: ubuntu-latest
needs: resolve-version
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
- name: Install protoc
run: sudo apt-get update && sudo apt-get install -y protobuf-compiler
- name: Validate version consistency
run: scripts/release-preflight.sh "${{ needs.resolve-version.outputs.version }}"
- name: Run tests
run: cargo test --workspace
- name: Run clippy
run: cargo clippy --all-targets --all-features -- -D warnings
publish-crates:
name: Publish crates.io
runs-on: ubuntu-latest
needs: [resolve-version, preflight]
steps:
- uses: actions/checkout@v6
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
- name: Detect missing crate versions
id: detect
env:
VERSION: ${{ needs.resolve-version.outputs.version }}
shell: bash
run: |
set -euo pipefail
crates=(hush-core hush-proxy hush-spine clawdstrike hush-cli)
missing=()
for crate in "${crates[@]}"; do
code="$(curl -s -o /dev/null -w '%{http_code}' "https://crates.io/api/v1/crates/${crate}/${VERSION}")"
if [[ "$code" == "200" ]]; then
echo "${crate}@${VERSION} already exists"
else
echo "${crate}@${VERSION} missing"
missing+=("$crate")
fi
done
if [[ "${#missing[@]}" -eq 0 ]]; then
echo "none_missing=true" >> "$GITHUB_OUTPUT"
else
echo "none_missing=false" >> "$GITHUB_OUTPUT"
echo "missing_crates=${missing[*]}" >> "$GITHUB_OUTPUT"
fi
- name: Publish missing crates in dependency order
if: steps.detect.outputs.none_missing == 'false'
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
VERSION: ${{ needs.resolve-version.outputs.version }}
shell: bash
run: |
set -euo pipefail
publish_with_retry() {
local crate="$1"
local max_attempts=20
local attempt=1
local output
local status
while (( attempt <= max_attempts )); do
echo "Publishing ${crate}@${VERSION} (attempt ${attempt}/${max_attempts})"
set +e
output="$(cargo publish -p "$crate" --token "$CARGO_REGISTRY_TOKEN" 2>&1)"
status=$?
set -e
printf '%s\n' "$output"
if [[ $status -eq 0 ]]; then
echo "Published ${crate}@${VERSION}"
return 0
fi
if grep -q "already exists on crates.io index" <<<"$output"; then
echo "${crate}@${VERSION} already published; skipping"
return 0
fi
echo "Publish attempt failed for ${crate}; waiting for index propagation"
sleep 30
attempt=$((attempt + 1))
done
echo "Failed to publish ${crate}@${VERSION}"
exit 1
}
publish_with_retry hush-core
publish_with_retry hush-proxy
publish_with_retry hush-spine
publish_with_retry clawdstrike
publish_with_retry hush-cli
- name: Crates already published
if: steps.detect.outputs.none_missing == 'true'
run: echo "All crate versions already exist; skipping crates publish"
publish-npm:
name: Publish npm packages
runs-on: ubuntu-latest
needs: [resolve-version, preflight]
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: "24"
registry-url: "https://registry.npmjs.org"
- name: Detect missing npm package versions
id: detect
shell: bash
run: |
set -euo pipefail
packages=(
packages/adapters/clawdstrike-adapter-core
packages/sdk/hush-ts
packages/sdk/clawdstrike
packages/policy/clawdstrike-policy
packages/adapters/clawdstrike-claude
packages/adapters/clawdstrike-openai
packages/adapters/clawdstrike-vercel-ai
packages/adapters/clawdstrike-langchain
packages/adapters/clawdstrike-openclaw
packages/adapters/clawdstrike-opencode
packages/adapters/clawdstrike-hush-cli-engine
packages/adapters/clawdstrike-hushd-engine
)
missing=()
for pkg in "${packages[@]}"; do
name="$(node -e "console.log(require('./${pkg}/package.json').name)")"
version="$(node -e "console.log(require('./${pkg}/package.json').version)")"
if npm view "${name}@${version}" version >/dev/null 2>&1; then
echo "${name}@${version} already exists"
else
echo "${name}@${version} missing"
missing+=("$pkg")
fi
done
if [[ "${#missing[@]}" -eq 0 ]]; then
echo "none_missing=true" >> "$GITHUB_OUTPUT"
else
echo "none_missing=false" >> "$GITHUB_OUTPUT"
echo "missing_packages=${missing[*]}" >> "$GITHUB_OUTPUT"
fi
- name: Install workspace dependencies
if: steps.detect.outputs.none_missing == 'false'
run: npm ci --ignore-scripts
- name: Publish missing npm packages in dependency order
if: steps.detect.outputs.none_missing == 'false'
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
RELEASE_VERSION: ${{ needs.resolve-version.outputs.version }}
shell: bash
run: |
set -euo pipefail
publish_workspace() {
local pkg="$1"
local name version
name="$(node -e "console.log(require('./${pkg}/package.json').name)")"
version="$(node -e "console.log(require('./${pkg}/package.json').version)")"
if [[ "$version" != "$RELEASE_VERSION" ]]; then
echo "Version mismatch for $name: package.json=$version release=$RELEASE_VERSION" >&2
exit 1
fi
echo "Building ${name}@${version}"
npm run build -w "$pkg"
echo "Testing ${name}@${version}"
npm test -w "$pkg"
if npm view "${name}@${version}" version >/dev/null 2>&1; then
echo "${name}@${version} already published; skipping publish"
return 0
fi
echo "Publishing ${name}@${version}"
local output status
set +e
output="$(npm publish -w "$pkg" --access public --provenance 2>&1)"
status=$?
set -e
printf '%s\n' "$output"
if [[ $status -eq 0 ]]; then
return 0
fi
if grep -q "Cannot publish over previously published version" <<<"$output" \
|| grep -q "You cannot publish over the previously published versions" <<<"$output"; then
echo "${name}@${version} appears already published; continuing"
return 0
fi
return $status
}
publish_workspace packages/adapters/clawdstrike-adapter-core
publish_workspace packages/sdk/hush-ts
publish_workspace packages/sdk/clawdstrike
publish_workspace packages/policy/clawdstrike-policy
publish_workspace packages/adapters/clawdstrike-claude
publish_workspace packages/adapters/clawdstrike-openai
publish_workspace packages/adapters/clawdstrike-vercel-ai
publish_workspace packages/adapters/clawdstrike-langchain
publish_workspace packages/adapters/clawdstrike-openclaw
publish_workspace packages/adapters/clawdstrike-opencode
publish_workspace packages/adapters/clawdstrike-hush-cli-engine
publish_workspace packages/adapters/clawdstrike-hushd-engine
- name: npm packages already published
if: steps.detect.outputs.none_missing == 'true'
run: echo "All npm package versions already exist; skipping npm publish"
publish-wasm-npm:
name: Publish @clawdstrike/wasm
runs-on: ubuntu-latest
needs: [resolve-version, preflight, publish-npm]
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: "24"
registry-url: "https://registry.npmjs.org"
- name: Detect missing wasm npm version
id: detect
env:
RELEASE_VERSION: ${{ needs.resolve-version.outputs.version }}
shell: bash
run: |
set -euo pipefail
name="$(node -e "console.log(require('./crates/libs/hush-wasm/package.json').name)")"
version="$(node -e "console.log(require('./crates/libs/hush-wasm/package.json').version)")"
echo "name=${name}" >> "$GITHUB_OUTPUT"
echo "version=${version}" >> "$GITHUB_OUTPUT"
if [[ "$version" != "$RELEASE_VERSION" ]]; then
echo "Version mismatch for $name: package.json=$version release=$RELEASE_VERSION" >&2
exit 1
fi
if npm view "${name}@${version}" version >/dev/null 2>&1; then
echo "none_missing=true" >> "$GITHUB_OUTPUT"
echo "${name}@${version} already exists"
else
echo "none_missing=false" >> "$GITHUB_OUTPUT"
echo "${name}@${version} missing"
fi
- name: Setup Rust target
if: steps.detect.outputs.none_missing == 'false'
uses: dtolnay/rust-toolchain@stable
with:
targets: wasm32-unknown-unknown
- name: Install wasm-pack
if: steps.detect.outputs.none_missing == 'false'
run: cargo install wasm-pack --locked --version 0.14.0
- name: Build and publish wasm package
if: steps.detect.outputs.none_missing == 'false'
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
shell: bash
run: |
set -euo pipefail
cd crates/libs/hush-wasm
wasm-pack build --target web --release --out-dir pkg
cp package.json pkg/
cp README.npm.md pkg/README.md
cp types/hush_wasm.d.ts pkg/
cd pkg
npm publish --access public --provenance
- name: wasm package already published
if: steps.detect.outputs.none_missing == 'true'
run: echo "wasm package version already exists; skipping wasm publish"
pypi-detect:
name: Detect PyPI package status
runs-on: ubuntu-latest
needs: [resolve-version, preflight]
outputs:
name: ${{ steps.detect.outputs.name }}
version: ${{ steps.detect.outputs.version }}
none_missing: ${{ steps.detect.outputs.none_missing }}
steps:
- uses: actions/checkout@v6
- uses: actions/setup-python@v6
with:
python-version: "3.12"
- name: Detect missing PyPI version
id: detect
env:
RELEASE_VERSION: ${{ needs.resolve-version.outputs.version }}
shell: bash
run: |
set -euo pipefail
python3 - <<'PY'
import tomllib
from pathlib import Path
release_version = "${{ needs.resolve-version.outputs.version }}"
pure = tomllib.loads(Path("packages/sdk/hush-py/pyproject.toml").read_text(encoding="utf-8"))
native = tomllib.loads(Path("packages/sdk/hush-py/hush-native/pyproject.toml").read_text(encoding="utf-8"))
pure_name = pure["project"]["name"]
pure_version = pure["project"]["version"]
native_name = native["project"]["name"]
native_version = native["project"]["version"]
if pure_name != "clawdstrike":
raise SystemExit(f"Unexpected Python package name in pure pyproject: {pure_name}")
if native_name != "clawdstrike":
raise SystemExit(f"Unexpected Python package name in native pyproject: {native_name}")
if pure_version != release_version:
raise SystemExit(
f"Version mismatch for pure pyproject: pyproject={pure_version} release={release_version}"
)
if native_version != release_version:
raise SystemExit(
f"Version mismatch for native pyproject: pyproject={native_version} release={release_version}"
)
PY
pkg_name="$(python3 - <<'PY'
import tomllib
with open("packages/sdk/hush-py/pyproject.toml", "rb") as f:
data = tomllib.load(f)
print(data["project"]["name"])
PY
)"
pkg_version="$(python3 - <<'PY'
import tomllib
with open("packages/sdk/hush-py/pyproject.toml", "rb") as f:
data = tomllib.load(f)
print(data["project"]["version"])
PY
)"
echo "name=${pkg_name}" >> "$GITHUB_OUTPUT"
echo "version=${pkg_version}" >> "$GITHUB_OUTPUT"
code="$(curl -s -o /dev/null -w '%{http_code}' "https://pypi.org/pypi/${pkg_name}/${pkg_version}/json")"
if [[ "$code" == "200" ]]; then
echo "none_missing=true" >> "$GITHUB_OUTPUT"
echo "${pkg_name}==${pkg_version} already exists on PyPI"
else
echo "none_missing=false" >> "$GITHUB_OUTPUT"
echo "${pkg_name}==${pkg_version} missing on PyPI"
fi
build-pypi-native-wheels:
name: Build native PyPI wheels (${{ matrix.os }})
runs-on: ${{ matrix.os }}
needs: [resolve-version, preflight, pypi-detect]
if: needs.pypi-detect.outputs.none_missing == 'false'
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
cibw_archs_linux: "x86_64 aarch64"
cibw_archs_macos: ""
cibw_archs_windows: ""
artifact: pypi-native-wheels-linux
- os: macos-latest
cibw_archs_linux: ""
cibw_archs_macos: "x86_64 arm64"
cibw_archs_windows: ""
artifact: pypi-native-wheels-macos
- os: windows-latest
cibw_archs_linux: ""
cibw_archs_macos: ""
cibw_archs_windows: "AMD64"
artifact: pypi-native-wheels-windows
steps:
- uses: actions/checkout@v6
- uses: actions/setup-python@v6
with:
python-version: "3.12"
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
- name: Setup QEMU (linux aarch64 wheels)
if: runner.os == 'Linux'
uses: docker/setup-qemu-action@v3
- name: Sync Python sources for native wheel build
shell: bash
run: scripts/sync-hush-py-native-sources.sh
- name: Build native wheels
env:
CIBW_BUILD: "cp310-*"
CIBW_SKIP: "*-musllinux_* *-manylinux_i686 *-win32"
CIBW_ARCHS_LINUX: ${{ matrix.cibw_archs_linux }}
CIBW_ARCHS_MACOS: ${{ matrix.cibw_archs_macos }}
CIBW_ARCHS_WINDOWS: ${{ matrix.cibw_archs_windows }}
CIBW_BEFORE_ALL_LINUX: "curl https://sh.rustup.rs -sSf | sh -s -- -y --profile minimal && . $HOME/.cargo/env"
shell: bash
run: |
set -euo pipefail
python -m pip install --upgrade pip
python -m pip install "cibuildwheel==2.23.3"
python -m cibuildwheel packages/sdk/hush-py/hush-native --output-dir wheelhouse
ls -la wheelhouse
- name: Upload native wheel artifacts
uses: actions/upload-artifact@v6
with:
name: ${{ matrix.artifact }}
path: wheelhouse/*.whl
if-no-files-found: error
publish-pypi:
name: Publish PyPI package
runs-on: ubuntu-latest
needs: [resolve-version, preflight, pypi-detect, build-pypi-native-wheels]
if: >
always() &&
needs.resolve-version.result == 'success' &&
needs.preflight.result == 'success' &&
needs.pypi-detect.result == 'success'
steps:
- uses: actions/checkout@v6
- uses: actions/setup-python@v6
with:
python-version: "3.12"
- name: Ensure native wheel matrix succeeded
if: needs.pypi-detect.outputs.none_missing == 'false'
shell: bash
run: |
set -euo pipefail
if [[ "${{ needs.build-pypi-native-wheels.result }}" != "success" ]]; then
echo "Native wheel build matrix did not complete successfully" >&2
exit 1
fi
- name: Build pure Python sdist and universal wheel
if: needs.pypi-detect.outputs.none_missing == 'false'
shell: bash
run: |
set -euo pipefail
python -m pip install --upgrade pip
python -m pip install build
python -m build --sdist --wheel packages/sdk/hush-py
- name: Download native wheel artifacts
if: needs.pypi-detect.outputs.none_missing == 'false'
uses: actions/download-artifact@v6
with:
pattern: pypi-native-wheels-*
path: packages/sdk/hush-py/dist
merge-multiple: true
- name: Verify wheel set completeness
if: needs.pypi-detect.outputs.none_missing == 'false'
shell: bash
run: |
set -euo pipefail
python3 - <<'PY'
from pathlib import Path
dist = Path("packages/sdk/hush-py/dist")
files = [p.name for p in dist.glob("clawdstrike-*.whl")]
if not files:
raise SystemExit("No clawdstrike wheels found in dist/")
checks = {
"pure py3-none-any wheel": any("py3-none-any.whl" in f for f in files),
"linux native wheel": any("manylinux" in f for f in files),
"macOS native wheel": any("macosx" in f for f in files),
"windows x86_64 native wheel": any("win_amd64" in f for f in files),
}
missing = [name for name, ok in checks.items() if not ok]
if missing:
raise SystemExit(f"Missing expected wheel artifacts: {', '.join(missing)}")
if not any(p.suffix == ".gz" and p.name.endswith(".tar.gz") for p in dist.glob("clawdstrike-*.tar.gz")):
raise SystemExit("Missing clawdstrike source distribution (.tar.gz)")
PY
- name: Upload PyPI package
if: needs.pypi-detect.outputs.none_missing == 'false'
env:
TWINE_USERNAME: __token__
TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }}
shell: bash
run: |
set -euo pipefail
python -m pip install --upgrade pip
python -m pip install twine
twine upload packages/sdk/hush-py/dist/*
- name: PyPI package already published
if: needs.pypi-detect.outputs.none_missing == 'true'
run: echo "PyPI version already exists; skipping PyPI publish"
build-binaries:
name: Build Binaries
runs-on: ${{ matrix.os }}
needs: preflight
strategy:
matrix:
include:
- target: x86_64-unknown-linux-gnu
os: ubuntu-latest
artifact: hush-linux-x86_64
archive: clawdstrike-linux-x86_64
- target: x86_64-apple-darwin
os: macos-latest
artifact: hush-darwin-x86_64
archive: clawdstrike-darwin-x86_64
- target: aarch64-apple-darwin
os: macos-latest
artifact: hush-darwin-aarch64
archive: clawdstrike-darwin-aarch64
- target: x86_64-pc-windows-msvc
os: windows-latest
artifact: hush-windows-x86_64.exe
archive: ""
steps:
- uses: actions/checkout@v6
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}
- name: Build release binary
run: cargo build --release --target ${{ matrix.target }} -p hush-cli
- name: Rename binary (Unix)
if: runner.os != 'Windows'
run: cp target/${{ matrix.target }}/release/hush ${{ matrix.artifact }}
- name: Rename binary (Windows)
if: runner.os == 'Windows'
run: cp target/${{ matrix.target }}/release/hush.exe ${{ matrix.artifact }}
- name: Upload artifact
uses: actions/upload-artifact@v6
with:
name: ${{ matrix.artifact }}
path: ${{ matrix.artifact }}
- name: Create release archive (Unix)
if: runner.os != 'Windows'
shell: bash
run: |
mkdir -p _archive
cp target/${{ matrix.target }}/release/hush _archive/
cp target/${{ matrix.target }}/release/clawdstrike _archive/
tar -czf ${{ matrix.archive }}.tar.gz -C _archive .
- name: Upload release archive
if: runner.os != 'Windows'
uses: actions/upload-artifact@v6
with:
name: ${{ matrix.archive }}.tar.gz
path: ${{ matrix.archive }}.tar.gz
build-hushd-binaries:
name: Build hushd Binaries
runs-on: ${{ matrix.os }}
needs: preflight
strategy:
matrix:
include:
- target: x86_64-unknown-linux-gnu
os: ubuntu-latest
artifact: hushd-linux-x86_64
- target: x86_64-apple-darwin
os: macos-latest
artifact: hushd-darwin-x86_64
- target: aarch64-apple-darwin
os: macos-latest
artifact: hushd-darwin-aarch64
- target: x86_64-pc-windows-msvc
os: windows-latest
artifact: hushd-windows-x86_64.exe
steps:
- uses: actions/checkout@v6
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}
- name: Build release hushd binary
run: cargo build --release --target ${{ matrix.target }} -p hushd
- name: Rename hushd binary (Unix)
if: runner.os != 'Windows'
run: cp target/${{ matrix.target }}/release/hushd ${{ matrix.artifact }}
- name: Rename hushd binary (Windows)
if: runner.os == 'Windows'
run: cp target/${{ matrix.target }}/release/hushd.exe ${{ matrix.artifact }}
- name: Upload hushd artifact
uses: actions/upload-artifact@v6
with:
name: ${{ matrix.artifact }}
path: ${{ matrix.artifact }}
create-release:
name: Create GitHub Release
runs-on: ubuntu-latest
needs: [resolve-version, build-binaries, build-hushd-binaries, publish-crates, publish-npm, publish-wasm-npm, publish-pypi]
permissions:
contents: write
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Ensure release tag exists
env:
TAG: ${{ needs.resolve-version.outputs.tag }}
shell: bash
run: |
set -euo pipefail
git fetch --tags --force
if git rev-parse "refs/tags/${TAG}" >/dev/null 2>&1; then
echo "Tag ${TAG} already exists"
exit 0
fi
if [[ "${GITHUB_EVENT_NAME}" != "workflow_dispatch" ]]; then
echo "Expected existing tag ${TAG} for non-dispatch release" >&2
exit 1
fi
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git tag -a "${TAG}" "${GITHUB_SHA}" -m "Release ${TAG}"
git push origin "refs/tags/${TAG}"
echo "Created and pushed missing tag ${TAG}"
- name: Download all artifacts
uses: actions/download-artifact@v6
with:
path: artifacts
- name: Create checksums
shell: bash
run: |
set -euo pipefail
cd artifacts
for dir in */; do
cd "$dir"
for file in *; do
sha256sum "$file" > "$file.sha256"
done
cd ..
done
- name: Flatten artifacts
run: |
mkdir -p release-files
find artifacts -type f -exec mv {} release-files/ \;
- name: Generate and sign hushd OTA manifests
shell: bash
env:
RELEASE_VERSION: ${{ needs.resolve-version.outputs.version }}
RELEASE_TAG: ${{ needs.resolve-version.outputs.tag }}
HUSHD_OTA_SIGNING_PRIVATE_KEY_PEM: ${{ secrets.HUSHD_OTA_SIGNING_PRIVATE_KEY_PEM }}
HUSHD_OTA_SIGNING_PUBLIC_KEY_HEX: ${{ secrets.HUSHD_OTA_SIGNING_PUBLIC_KEY_HEX }}
run: |
set -euo pipefail
if [[ -z "${HUSHD_OTA_SIGNING_PRIVATE_KEY_PEM:-}" ]]; then
echo "Missing required secret: HUSHD_OTA_SIGNING_PRIVATE_KEY_PEM" >&2
exit 1
fi
pub_args=()
if [[ -n "${HUSHD_OTA_SIGNING_PUBLIC_KEY_HEX:-}" ]]; then
pub_args=(--public-key "${HUSHD_OTA_SIGNING_PUBLIC_KEY_HEX}")
fi
scripts/generate-hushd-ota-manifest.sh \
--version "${RELEASE_VERSION}" \
--tag "${RELEASE_TAG}" \
--channel stable \
--assets-dir release-files \
--output release-files/hushd-ota-manifest-stable.unsigned.json \
"${pub_args[@]}"
scripts/sign-hushd-ota-manifest.sh \
--input release-files/hushd-ota-manifest-stable.unsigned.json \
--output release-files/hushd-ota-manifest-stable.json \
"${pub_args[@]}"
rm -f release-files/hushd-ota-manifest-stable.unsigned.json
scripts/generate-hushd-ota-manifest.sh \
--version "${RELEASE_VERSION}" \
--tag "${RELEASE_TAG}" \
--channel beta \
--assets-dir release-files \
--output release-files/hushd-ota-manifest-beta.unsigned.json \
"${pub_args[@]}"
scripts/sign-hushd-ota-manifest.sh \
--input release-files/hushd-ota-manifest-beta.unsigned.json \
--output release-files/hushd-ota-manifest-beta.json \
"${pub_args[@]}"
rm -f release-files/hushd-ota-manifest-beta.unsigned.json
- name: Create or update GitHub Release
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ needs.resolve-version.outputs.tag }}
generate_release_notes: true
files: release-files/*
overwrite_files: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
update-homebrew:
name: Update Homebrew Tap
runs-on: ubuntu-latest
needs: [resolve-version, create-release]
steps:
- name: Download release archives
env:
GH_TOKEN: ${{ secrets.HOMEBREW_TAP_GITHUB_TOKEN }}
TAG: ${{ needs.resolve-version.outputs.tag }}
run: |
mkdir -p dl
gh release download "$TAG" \
--repo backbay-labs/clawdstrike \
--pattern "clawdstrike-*.tar.gz" \
--dir dl
- name: Compute SHA256 checksums
id: sha
run: |
echo "darwin_arm64=$(sha256sum dl/clawdstrike-darwin-aarch64.tar.gz | cut -d' ' -f1)" >> "$GITHUB_OUTPUT"
echo "darwin_x86_64=$(sha256sum dl/clawdstrike-darwin-x86_64.tar.gz | cut -d' ' -f1)" >> "$GITHUB_OUTPUT"
echo "linux_x86_64=$(sha256sum dl/clawdstrike-linux-x86_64.tar.gz | cut -d' ' -f1)" >> "$GITHUB_OUTPUT"
- name: Update homebrew-tap formula
env:
GH_TOKEN: ${{ secrets.HOMEBREW_TAP_GITHUB_TOKEN }}
VERSION: ${{ needs.resolve-version.outputs.version }}
SHA_DARWIN_ARM64: ${{ steps.sha.outputs.darwin_arm64 }}
SHA_DARWIN_X86_64: ${{ steps.sha.outputs.darwin_x86_64 }}
SHA_LINUX_X86_64: ${{ steps.sha.outputs.linux_x86_64 }}
run: |
set -euo pipefail
git clone "https://x-access-token:${GH_TOKEN}@github.com/backbay-labs/homebrew-tap.git" tap
mkdir -p tap/Formula
cat > tap/Formula/clawdstrike.rb << 'RUBY'
class Clawdstrike < Formula
desc "Runtime security enforcement for AI agents"
homepage "https://github.com/backbay-labs/clawdstrike"
version "__VERSION__"
license "Apache-2.0"
on_macos do
if Hardware::CPU.arm?
url "https://github.com/backbay-labs/clawdstrike/releases/download/v#{version}/clawdstrike-darwin-aarch64.tar.gz"
sha256 "__SHA_DARWIN_ARM64__"
else
url "https://github.com/backbay-labs/clawdstrike/releases/download/v#{version}/clawdstrike-darwin-x86_64.tar.gz"
sha256 "__SHA_DARWIN_X86_64__"
end
end
on_linux do
url "https://github.com/backbay-labs/clawdstrike/releases/download/v#{version}/clawdstrike-linux-x86_64.tar.gz"
sha256 "__SHA_LINUX_X86_64__"
end
def install
bin.install "hush"
bin.install "clawdstrike"
end
test do
assert_match version.to_s, shell_output("#{bin}/hush --version")
end
end
RUBY
sed -i "s/__VERSION__/${VERSION}/" tap/Formula/clawdstrike.rb
sed -i "s/__SHA_DARWIN_ARM64__/${SHA_DARWIN_ARM64}/" tap/Formula/clawdstrike.rb
sed -i "s/__SHA_DARWIN_X86_64__/${SHA_DARWIN_X86_64}/" tap/Formula/clawdstrike.rb
sed -i "s/__SHA_LINUX_X86_64__/${SHA_LINUX_X86_64}/" tap/Formula/clawdstrike.rb
cd tap
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add Formula/clawdstrike.rb
git diff --cached --quiet && echo "Formula unchanged; skipping" && exit 0
git commit -m "clawdstrike ${VERSION}"
git push