Skip to content

chore: release 0.4.9 #11

chore: release 0.4.9

chore: release 0.4.9 #11

Workflow file for this run

name: Release
on:
push:
tags:
- 'v*'
workflow_dispatch:
inputs:
tag:
description: 'Tag to release (e.g. v0.3.0). For testing — does not move the git tag.'
required: true
permissions:
contents: write
jobs:
build:
name: Build (${{ matrix.target }})
runs-on: macos-14
strategy:
fail-fast: false
matrix:
include:
- target: aarch64-apple-darwin
arch: arm64
- target: x86_64-apple-darwin
arch: x86_64
steps:
- uses: actions/checkout@v4
- name: Resolve tag
id: tag
shell: bash
run: |
if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
echo "tag=${{ inputs.tag }}" >> "$GITHUB_OUTPUT"
else
echo "tag=${GITHUB_REF_NAME}" >> "$GITHUB_OUTPUT"
fi
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}
- name: Cache cargo registry + target
uses: actions/cache@v4
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-cargo-${{ matrix.target }}-${{ hashFiles('Cargo.lock') }}
- name: Build release binary
run: cargo build --release --locked --target ${{ matrix.target }}
# Codesign + notarize only when the Apple secrets are present.
# Without them the artifact is still produced — users see a Gatekeeper
# warning on first launch and must right-click → Open once.
- name: Codesign + notarize
if: env.APPLE_ID != ''
env:
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
APPLE_DEVELOPER_ID: ${{ secrets.APPLE_DEVELOPER_ID }}
APPLE_CERTIFICATE_P12_BASE64: ${{ secrets.APPLE_CERTIFICATE_P12_BASE64 }}
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
shell: bash
run: |
set -euo pipefail
BIN=target/${{ matrix.target }}/release/small-harness
# Import the certificate into a temporary keychain.
KEYCHAIN=build.keychain
KEYCHAIN_PASSWORD=$(openssl rand -hex 16)
security create-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN"
security default-keychain -s "$KEYCHAIN"
security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN"
security set-keychain-settings -lut 21600 "$KEYCHAIN"
CERT_PATH=$RUNNER_TEMP/dev-id.p12
echo "$APPLE_CERTIFICATE_P12_BASE64" | base64 --decode > "$CERT_PATH"
security import "$CERT_PATH" -k "$KEYCHAIN" \
-P "$APPLE_CERTIFICATE_PASSWORD" -T /usr/bin/codesign
security set-key-partition-list -S apple-tool:,apple: -s -k "$KEYCHAIN_PASSWORD" "$KEYCHAIN"
codesign --force --options runtime --timestamp \
--sign "$APPLE_DEVELOPER_ID" "$BIN"
# Notarize via ditto-zipped binary, then staple is N/A for a bare
# binary (no app bundle). The signature + notarization ticket lives
# on Apple's servers; Gatekeeper queries online on first launch.
ZIP=$RUNNER_TEMP/small-harness-${{ matrix.target }}.zip
ditto -c -k --keepParent "$BIN" "$ZIP"
xcrun notarytool submit "$ZIP" \
--apple-id "$APPLE_ID" \
--team-id "$APPLE_TEAM_ID" \
--password "$APPLE_APP_SPECIFIC_PASSWORD" \
--wait
- name: Package tarball + checksum
shell: bash
run: |
set -euo pipefail
OUT_DIR="$RUNNER_TEMP/dist"
mkdir -p "$OUT_DIR"
NAME="small-harness-${{ steps.tag.outputs.tag }}-${{ matrix.target }}"
ARTIFACT="$OUT_DIR/$NAME.tar.gz"
tar -czf "$ARTIFACT" \
-C target/${{ matrix.target }}/release small-harness \
-C "$GITHUB_WORKSPACE" LICENSE README.md Quickstart.md
(cd "$OUT_DIR" && shasum -a 256 "$NAME.tar.gz" > "$NAME.sha256")
echo "ARTIFACT_DIR=$OUT_DIR" >> "$GITHUB_ENV"
echo "ARTIFACT_NAME=$NAME" >> "$GITHUB_ENV"
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: ${{ env.ARTIFACT_NAME }}
path: ${{ env.ARTIFACT_DIR }}
publish:
name: Publish release
needs: build
runs-on: ubuntu-latest
if: github.event_name == 'push'
outputs:
version: ${{ steps.meta.outputs.version }}
arm_sha: ${{ steps.meta.outputs.arm_sha }}
x86_sha: ${{ steps.meta.outputs.x86_sha }}
steps:
- uses: actions/checkout@v4
- name: Download all artifacts
uses: actions/download-artifact@v4
with:
path: dist
- name: Flatten artifacts
shell: bash
run: |
set -euo pipefail
mkdir -p release
find dist -type f \( -name '*.tar.gz' -o -name '*.sha256' \) -exec cp {} release/ \;
(cd release && sha256sum *.tar.gz > SHA256SUMS)
ls -la release/
- name: Extract release metadata
id: meta
shell: bash
run: |
set -euo pipefail
version="${GITHUB_REF_NAME#v}"
arm_sha=$(awk '/aarch64-apple-darwin\.tar\.gz/{print $1}' release/SHA256SUMS)
x86_sha=$(awk '/x86_64-apple-darwin\.tar\.gz/{print $1}' release/SHA256SUMS)
{
echo "version=$version"
echo "arm_sha=$arm_sha"
echo "x86_sha=$x86_sha"
} >> "$GITHUB_OUTPUT"
- name: Create GitHub release
uses: softprops/action-gh-release@v2
with:
files: release/*
fail_on_unmatched_files: true
generate_release_notes: true
update-tap:
name: Update Homebrew tap
needs: publish
runs-on: ubuntu-latest
if: github.event_name == 'push'
env:
TAP_TOKEN: ${{ secrets.TAP_GITHUB_TOKEN }}
steps:
- name: Check for tap token
id: gate
shell: bash
run: |
if [[ -z "${TAP_TOKEN}" ]]; then
echo "::notice::TAP_GITHUB_TOKEN not set — skipping Homebrew tap update."
echo "enabled=false" >> "$GITHUB_OUTPUT"
else
echo "enabled=true" >> "$GITHUB_OUTPUT"
fi
- name: Checkout tap repo
if: steps.gate.outputs.enabled == 'true'
uses: actions/checkout@v4
with:
repository: GetSmallAI/homebrew-tap
token: ${{ secrets.TAP_GITHUB_TOKEN }}
path: tap
- name: Regenerate formula
if: steps.gate.outputs.enabled == 'true'
shell: bash
env:
VERSION: ${{ needs.publish.outputs.version }}
ARM_SHA: ${{ needs.publish.outputs.arm_sha }}
X86_SHA: ${{ needs.publish.outputs.x86_sha }}
run: |
set -euo pipefail
[[ -n "$VERSION" && -n "$ARM_SHA" && -n "$X86_SHA" ]] || {
echo "::error::missing version/sha outputs from publish job"; exit 1; }
mkdir -p tap/Formula
cat > tap/Formula/small-harness.rb <<EOF
# Generated by SmallHarness release workflow — do not edit by hand.
class SmallHarness < Formula
desc "Terminal-based agent harness for running small LLMs on your Mac"
homepage "https://github.com/GetSmallAI/SmallHarness"
version "${VERSION}"
license "MIT"
on_macos do
on_arm do
url "https://github.com/GetSmallAI/SmallHarness/releases/download/v${VERSION}/small-harness-v${VERSION}-aarch64-apple-darwin.tar.gz"
sha256 "${ARM_SHA}"
end
on_intel do
url "https://github.com/GetSmallAI/SmallHarness/releases/download/v${VERSION}/small-harness-v${VERSION}-x86_64-apple-darwin.tar.gz"
sha256 "${X86_SHA}"
end
end
def install
bin.install "small-harness"
doc.install Dir["README.md", "Quickstart.md", "LICENSE"]
generate_completions_from_executable(bin/"small-harness", "completions")
end
test do
assert_match "small-harness", shell_output("#{bin}/small-harness completions bash")
end
end
EOF
- name: Commit + push formula
if: steps.gate.outputs.enabled == 'true'
shell: bash
working-directory: tap
run: |
set -euo pipefail
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
if git diff --quiet -- Formula/small-harness.rb; then
echo "Formula already at ${{ needs.publish.outputs.version }} — nothing to do."
exit 0
fi
git add Formula/small-harness.rb
git commit -m "small-harness ${{ needs.publish.outputs.version }}"
git push