name: Release
on:
push:
tags:
- "v*"
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt, clippy
- uses: Swatinem/rust-cache@v2
- run: make fmt-check
- run: make lint
- run: make test
build:
needs: check
if: github.event_name != 'pull_request'
strategy:
fail-fast: false
matrix:
include:
- target: x86_64-unknown-linux-gnu
os: ubuntu-latest
maturin_args: --compatibility manylinux_2_28 --zig
- target: aarch64-unknown-linux-gnu
os: ubuntu-latest
maturin_args: --compatibility manylinux_2_28 --zig
- target: x86_64-apple-darwin
os: [self-hosted, macOS, ARM64]
maturin_args: ""
- target: aarch64-apple-darwin
os: [self-hosted, macOS, ARM64]
maturin_args: ""
- target: x86_64-pc-windows-msvc
os: windows-latest
maturin_args: ""
runs-on: ${{ matrix.os }}
permissions:
contents: write
steps:
- uses: actions/checkout@v6
- uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}
- uses: astral-sh/setup-uv@v6
- name: Install maturin and zig
shell: bash
run: |
uv venv "${RUNNER_TEMP}/build-venv"
VENV_BIN="${RUNNER_TEMP}/build-venv/bin"
[ -d "$VENV_BIN" ] || VENV_BIN="${RUNNER_TEMP}/build-venv/Scripts"
VIRTUAL_ENV="${RUNNER_TEMP}/build-venv" uv pip install maturin ziglang
echo "$VENV_BIN" >> "$GITHUB_PATH"
- name: Build wheel and binary
run: maturin build --release --target ${{ matrix.target }} ${{ matrix.maturin_args }}
shell: bash
- name: Package binary (unix)
if: matrix.os != 'windows-latest'
run: |
cd target/${{ matrix.target }}/release
tar czf ../../../shelly-${{ github.ref_name }}-${{ matrix.target }}.tar.gz shelly
shell: bash
- name: Package binary (windows)
if: matrix.os == 'windows-latest'
run: |
cd target/${{ matrix.target }}/release
7z a ../../../shelly-${{ github.ref_name }}-${{ matrix.target }}.zip shelly.exe
shell: bash
- name: Upload binary artifact
uses: actions/upload-artifact@v6
with:
name: binary-${{ matrix.target }}
path: shelly-${{ github.ref_name }}-${{ matrix.target }}.*
- name: Upload wheel artifact
uses: actions/upload-artifact@v6
with:
name: wheel-${{ matrix.target }}
path: target/wheels/*.whl
sdist:
needs: check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: astral-sh/setup-uv@v6
- name: Install maturin
shell: bash
run: |
uv venv "${RUNNER_TEMP}/build-venv"
VENV_BIN="${RUNNER_TEMP}/build-venv/bin"
[ -d "$VENV_BIN" ] || VENV_BIN="${RUNNER_TEMP}/build-venv/Scripts"
VIRTUAL_ENV="${RUNNER_TEMP}/build-venv" uv pip install maturin
echo "$VENV_BIN" >> "$GITHUB_PATH"
- run: maturin sdist
- uses: actions/upload-artifact@v6
with:
name: sdist
path: target/wheels/*.tar.gz
release:
needs: build
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/download-artifact@v6
with:
pattern: binary-*
merge-multiple: true
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
generate_release_notes: true
files: |
shelly-*.tar.gz
shelly-*.zip
publish-crates:
needs: release
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: dtolnay/rust-toolchain@stable
- run: cargo publish --locked
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
publish-pypi:
needs: [build, sdist]
runs-on: ubuntu-latest
steps:
- uses: actions/download-artifact@v6
with:
pattern: wheel-*
path: artifacts/wheels
- uses: actions/download-artifact@v6
with:
name: sdist
path: artifacts/sdist
- uses: astral-sh/setup-uv@v6
- name: Publish to PyPI
env:
TWINE_USERNAME: __token__
TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
run: uv tool run twine upload artifacts/wheels/**/*.whl artifacts/sdist/*.tar.gz
update-homebrew:
needs: release
runs-on: ubuntu-latest
env:
HOMEBREW_TAP_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }}
steps:
- name: Check for tap token
id: check-token
run: |
if [ -z "$HOMEBREW_TAP_TOKEN" ]; then
echo "skip=true" >> "$GITHUB_OUTPUT"
echo "HOMEBREW_TAP_TOKEN not set, skipping Homebrew formula update"
else
echo "skip=false" >> "$GITHUB_OUTPUT"
fi
- name: Download release assets and compute SHA256 hashes
if: steps.check-token.outputs.skip == 'false'
run: |
TAG="${{ github.ref_name }}"
BASE_URL="https://github.com/rvben/shelly-cli/releases/download/${TAG}"
for target in aarch64-apple-darwin x86_64-apple-darwin aarch64-unknown-linux-gnu x86_64-unknown-linux-gnu; do
curl -fsSL -o "shelly-${TAG}-${target}.tar.gz" "${BASE_URL}/shelly-${TAG}-${target}.tar.gz"
done
echo "SHA_MACOS_ARM64=$(sha256sum shelly-${TAG}-aarch64-apple-darwin.tar.gz | cut -d' ' -f1)" >> "$GITHUB_ENV"
echo "SHA_MACOS_X86=$(sha256sum shelly-${TAG}-x86_64-apple-darwin.tar.gz | cut -d' ' -f1)" >> "$GITHUB_ENV"
echo "SHA_LINUX_ARM64=$(sha256sum shelly-${TAG}-aarch64-unknown-linux-gnu.tar.gz | cut -d' ' -f1)" >> "$GITHUB_ENV"
echo "SHA_LINUX_X86=$(sha256sum shelly-${TAG}-x86_64-unknown-linux-gnu.tar.gz | cut -d' ' -f1)" >> "$GITHUB_ENV"
- name: Update Homebrew formula
if: steps.check-token.outputs.skip == 'false'
run: |
TAG="${{ github.ref_name }}"
VERSION="${TAG#v}"
git clone "https://x-access-token:${HOMEBREW_TAP_TOKEN}@github.com/rvben/homebrew-tap.git" homebrew-tap
cd homebrew-tap
cat > Formula/shelly-cli.rb << 'FORMULA'
class ShellyCli < Formula
desc "Fast CLI for discovering, monitoring, and controlling Shelly devices"
homepage "https://github.com/rvben/shelly-cli"
version "VERSION_PLACEHOLDER"
license "MIT"
on_macos do
if Hardware::CPU.arm?
url "https://github.com/rvben/shelly-cli/releases/download/v#{version}/shelly-v#{version}-aarch64-apple-darwin.tar.gz"
sha256 "SHA_MACOS_ARM64_PLACEHOLDER"
else
url "https://github.com/rvben/shelly-cli/releases/download/v#{version}/shelly-v#{version}-x86_64-apple-darwin.tar.gz"
sha256 "SHA_MACOS_X86_PLACEHOLDER"
end
end
on_linux do
if Hardware::CPU.arm?
url "https://github.com/rvben/shelly-cli/releases/download/v#{version}/shelly-v#{version}-aarch64-unknown-linux-gnu.tar.gz"
sha256 "SHA_LINUX_ARM64_PLACEHOLDER"
else
url "https://github.com/rvben/shelly-cli/releases/download/v#{version}/shelly-v#{version}-x86_64-unknown-linux-gnu.tar.gz"
sha256 "SHA_LINUX_X86_PLACEHOLDER"
end
end
def install
bin.install "shelly"
bin.install_symlink "shelly" => "shelly-cli"
end
test do
assert_match "shelly #{version}", shell_output("#{bin}/shelly --version")
end
end
FORMULA
# Remove leading whitespace from heredoc and replace placeholders
sed -i 's/^ //' Formula/shelly-cli.rb
sed -i "s/VERSION_PLACEHOLDER/${VERSION}/" Formula/shelly-cli.rb
sed -i "s/SHA_MACOS_ARM64_PLACEHOLDER/${SHA_MACOS_ARM64}/" Formula/shelly-cli.rb
sed -i "s/SHA_MACOS_X86_PLACEHOLDER/${SHA_MACOS_X86}/" Formula/shelly-cli.rb
sed -i "s/SHA_LINUX_ARM64_PLACEHOLDER/${SHA_LINUX_ARM64}/" Formula/shelly-cli.rb
sed -i "s/SHA_LINUX_X86_PLACEHOLDER/${SHA_LINUX_X86}/" Formula/shelly-cli.rb
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add Formula/shelly-cli.rb
git commit -m "Update shelly-cli to ${VERSION}"
git push