name: Release
on:
push:
tags:
- 'v*.*.*'
workflow_dispatch:
inputs:
version:
description: 'Version number (e.g., 0.1.1) - required for manual trigger'
required: true
type: string
permissions:
contents: write
packages: write
concurrency:
group: release-${{ github.ref }}
cancel-in-progress: false
env:
CARGO_TERM_COLOR: always
jobs:
build-linux:
name: Build Linux (${{ matrix.target }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-24.04
target: x86_64-unknown-linux-gnu
- os: ubuntu-24.04-arm
target: aarch64-unknown-linux-gnu
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}
- name: Cache Rust build artifacts
uses: Swatinem/rust-cache@v2
with:
shared-key: ${{ runner.os }}-${{ hashFiles('**/Cargo.lock') }}-${{ hashFiles('rust-toolchain*') }}
- name: Build release binary
run: cargo build --release --target ${{ matrix.target }}
- name: Upload binary
uses: actions/upload-artifact@v4
with:
name: binary-${{ matrix.target }}
path: target/${{ matrix.target }}/release/rco
retention-days: 5
build:
name: Build (${{ matrix.target }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- os: macos-14
target: x86_64-apple-darwin
- os: macos-latest
target: aarch64-apple-darwin
- os: windows-latest
target: x86_64-pc-windows-msvc
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install OpenSSL (macOS)
if: matrix.target == 'x86_64-apple-darwin' || matrix.target == 'aarch64-apple-darwin'
run: |
brew install openssl
echo "OPENSSL_LIB_DIR=$(brew --prefix openssl)/lib" >> "$GITHUB_ENV"
echo "OPENSSL_INCLUDE_DIR=$(brew --prefix openssl)/include" >> "$GITHUB_ENV"
- name: Install OpenSSL (Windows)
if: matrix.target == 'x86_64-pc-windows-msvc'
run: |
choco install openssl -y
echo "OPENSSL_DIR=C:\Program Files\OpenSSL" >> "$GITHUB_ENV"
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}
- name: Cache Rust build artifacts
uses: Swatinem/rust-cache@v2
with:
shared-key: ${{ runner.os }}-${{ hashFiles('**/Cargo.lock') }}-${{ hashFiles('rust-toolchain*') }}
- name: Build release binary
run: cargo build --release --target ${{ matrix.target }}
- name: Upload binary
uses: actions/upload-artifact@v4
with:
name: binary-${{ matrix.target }}
path: target/${{ matrix.target }}/release/rco${{ matrix.target == 'x86_64-pc-windows-msvc' && '.exe' || '' }}
retention-days: 5
release:
name: Create Release
needs:
- build
- build-linux
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Download all binaries
uses: actions/download-artifact@v4
with:
path: binaries
pattern: binary-*
merge-multiple: false
- name: Determine version
id: version
run: |
VERSION_RAW="${{ github.event.inputs.version || github.ref_name }}"
VERSION="${VERSION_RAW#v}"
# Validate semver format (X.Y.Z)
if [[ ! "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "Error: Version '$VERSION' does not match semver format (X.Y.Z)"
exit 1
fi
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
- name: Prepare release assets
run: |
VERSION="${{ steps.version.outputs.version }}"
mkdir -p release
for dir in binaries/binary-*; do
target=$(basename "$dir" | sed 's/binary-//')
case "$target" in
x86_64-pc-windows-msvc)
if [ -f "$dir/rco.exe" ]; then
(cd "$dir" && zip -r "$GITHUB_WORKSPACE/release/rusty-commit-v${VERSION}-${target}.zip" rco.exe)
fi
;;
*)
if [ -f "$dir/rco" ]; then
tmpdir=$(mktemp -d)
cp "$dir/rco" "$tmpdir/rco"
tar -C "$tmpdir" -czf "release/rusty-commit-v${VERSION}-${target}.tar.gz" .
rm -rf "$tmpdir"
fi
;;
esac
done
ls -la release
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
draft: false
prerelease: false
generate_release_notes: true
tag_name: v${{ steps.version.outputs.version }}
name: v${{ steps.version.outputs.version }}
files: release/*
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Upload release binaries for finalize job
uses: actions/upload-artifact@v4
with:
name: release
path: release/
retention-days: 5
publish-crates:
name: Publish to crates.io
needs: release
runs-on: ubuntu-latest
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Cache Rust build artifacts
uses: Swatinem/rust-cache@v2
with:
shared-key: ${{ runner.os }}-${{ hashFiles('**/Cargo.lock') }}-${{ hashFiles('rust-toolchain*') }}
- name: Publish to crates.io
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
run: |
if [ -n "$CARGO_REGISTRY_TOKEN" ]; then
cargo publish --registry crates-io --allow-dirty
else
echo "CARGO_REGISTRY_TOKEN not set, skipping crates.io publish"
fi
build-deb:
name: Build Debian Package (${{ matrix.arch }})
needs: build-linux
runs-on: ${{ matrix.runner }}
strategy:
fail-fast: false
matrix:
include:
- arch: amd64
runner: ubuntu-latest
target: x86_64-unknown-linux-gnu
deb_arch: amd64
- arch: arm64
runner: ubuntu-24.04-arm
target: aarch64-unknown-linux-gnu
deb_arch: arm64
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}
- name: Cache Rust build artifacts
uses: Swatinem/rust-cache@v2
with:
shared-key: ${{ runner.os }}-${{ hashFiles('**/Cargo.lock') }}-${{ hashFiles('rust-toolchain*') }}
- name: Install cargo-deb
uses: taiki-e/install-action@v2
with:
tool: cargo-deb
- name: Download release binary
uses: actions/download-artifact@v4
with:
name: binary-${{ matrix.target }}
path: target/${{ matrix.target }}/release
- name: Build .deb package
run: cargo deb --target ${{ matrix.target }} --no-build
- name: Upload .deb package
uses: actions/upload-artifact@v4
with:
name: rusty-commit.deb-${{ matrix.arch }}
path: target/${{ matrix.target }}/debian/*_${{ matrix.deb_arch }}.deb
build-rpm:
name: Build RedHat Package (${{ matrix.arch }})
needs: build-linux
runs-on: ${{ matrix.runner }}
strategy:
fail-fast: false
matrix:
include:
- arch: amd64
runner: ubuntu-latest
target: x86_64-unknown-linux-gnu
rpm_arch: x86_64
- arch: arm64
runner: ubuntu-24.04-arm
target: aarch64-unknown-linux-gnu
rpm_arch: aarch64
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Download release binary
uses: actions/download-artifact@v7
with:
name: binary-${{ matrix.target }}
path: target/${{ matrix.target }}/release
- name: Install rpm build tools
run: |
sudo apt-get update
sudo apt-get install -y rpm
- name: Create RPM structure
run: |
VERSION="${{ github.event.inputs.version || github.ref_name }}"
VERSION="${VERSION#v}"
pkgname="rusty-commit"
mkdir -p rpmbuild/BUILD rpmbuild/BUILDROOT rpmbuild/RPMS rpmbuild/SOURCES rpmbuild/SPECS rpmbuild/SRPMS
# Create tarball with proper directory structure (tarball name without .tar.gz must match extracted dir)
mkdir -p "tmp-pkg-dir/${pkgname}-${VERSION}"
cp target/${{ matrix.target }}/release/rco "tmp-pkg-dir/${pkgname}-${VERSION}/"
tar -C tmp-pkg-dir -czf "rpmbuild/SOURCES/${pkgname}-${VERSION}.tar.gz" "${pkgname}-${VERSION}"
rm -rf tmp-pkg-dir
# Create spec file inline (avoiding YAML heredoc issues with % characters)
printf '%s\n' \
"Name: ${pkgname}" \
"Version: ${VERSION}" \
"Release: 0%{?dist}" \
"Summary: A modern, AI-powered git commit message generator" \
"License: MIT" \
"URL: https://github.com/hongkongkiwi/rusty-commit" \
"Source0: %{name}-%{version}.tar.gz" \
"BuildArch: ${{ matrix.rpm_arch }}" \
"" \
"%description" \
"Rusty-commit is a modern, AI-powered git commit message generator that helps" \
"you create meaningful, standardized commit messages following Conventional Commits." \
"" \
"%prep" \
"%setup -q" \
"" \
"%install" \
"mkdir -p %{buildroot}/usr/bin" \
"install -m 755 %{_builddir}/%{name}-%{version}/rco %{buildroot}/usr/bin/rco" \
"" \
"%files" \
"/usr/bin/rco" \
"" \
"%changelog" \
"* Sat Jan 24 2026 Rusty Commit <rusty-commit@users.noreply.github.com> - ${VERSION}-0" \
"- Initial RPM package" > rpmbuild/SPECS/rusty-commit.spec
- name: Build .rpm package
run: |
rpmbuild --define "_topdir $PWD/rpmbuild" \
-bb rpmbuild/SPECS/rusty-commit.spec \
--target ${{ matrix.rpm_arch }}
- name: Upload .rpm package
uses: actions/upload-artifact@v4
with:
name: rusty-commit.rpm-${{ matrix.arch }}
path: rpmbuild/RPMS/${{ matrix.rpm_arch }}/*.rpm
build-apk:
name: Build Alpine Package (${{ matrix.arch }})
runs-on: ${{ matrix.runner }}
strategy:
fail-fast: false
matrix:
include:
- arch: amd64
runner: ubuntu-latest
target: x86_64-unknown-linux-musl
apk_arch: x86_64
- arch: arm64
runner: ubuntu-24.04-arm
target: aarch64-unknown-linux-musl
apk_arch: aarch64
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up QEMU for Docker
if: matrix.arch == 'amd64'
uses: docker/setup-qemu-action@v3
with:
platforms: amd64
- name: Install Rust and musl-tools
uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}
- name: Cache Rust build artifacts
uses: Swatinem/rust-cache@v2
with:
shared-key: ${{ runner.os }}-${{ hashFiles('**/Cargo.lock') }}-${{ hashFiles('rust-toolchain*') }}
- name: Install musl tools
run: |
sudo apt-get update
sudo apt-get install -y musl-tools
if [ "${{ matrix.arch }}" = "amd64" ]; then
sudo apt-get install -y gcc-multilib
fi
- name: Build static binary
run: cargo build --release --target ${{ matrix.target }}
- name: Create Alpine package structure
run: |
mkdir -p apk-build/bin
cp target/${{ matrix.target }}/release/rco apk-build/bin/
- name: Upload Linux musl binary
uses: actions/upload-artifact@v4
with:
name: binary-musl-${{ matrix.target }}
path: target/${{ matrix.target }}/release/rco
retention-days: 5
- name: Create APKBUILD
run: |
VERSION="${{ github.event.inputs.version || github.ref_name }}"
VERSION="${VERSION#v}"
cat > apk-build/APKBUILD << 'EOF'
# Contributor: Rusty Commit <rusty-commit@users.noreply.github.com>
# Maintainer: Rusty Commit <rusty-commit@users.noreply.github.com>
pkgname=rusty-commit
pkgver=VERSION_PLACEHOLDER
pkgrel=0
pkgdesc="A modern, AI-powered git commit message generator"
url="https://github.com/hongkongkiwi/rusty-commit"
arch="${{ matrix.apk_arch }}"
license="MIT"
depends=""
subpackages=""
source=""
options="!check"
build() {
return 0
}
package() {
mkdir -p "$pkgdir"/usr/bin
install -m 755 "$startdir"/bin/rco "$pkgdir"/usr/bin/rco
}
EOF
sed -i "s/VERSION_PLACEHOLDER/${VERSION}/" apk-build/APKBUILD
- name: Build .apk package
run: |
mkdir -p apk-packages
docker run --rm \
-v "$PWD:/work" \
-w /work/apk-build \
alpine:3.19 \
sh -c 'apk add --no-cache alpine-sdk && \
adduser -D builder && addgroup builder abuild && \
chown -R builder:builder /work/apk-build /work/apk-packages && \
su builder -c "export REPODEST=/work/apk-packages PACKAGER= && abuild-keygen -a -n" && \
cp /home/builder/.abuild/*.rsa.pub /etc/apk/keys/ && \
su builder -c "export REPODEST=/work/apk-packages PACKAGER= && abuild -r"'
- name: Upload Alpine package
uses: actions/upload-artifact@v4
with:
name: rusty-commit.apk-${{ matrix.arch }}
path: apk-packages/**/*.apk
finalize-release:
name: Finalize Release
needs:
- release
- build-deb
- build-rpm
- build-apk
- publish-docker
runs-on: ubuntu-latest
env:
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
steps:
- name: Download release assets
uses: actions/download-artifact@v4
with:
name: release
path: release-assets
- name: List downloaded assets
run: |
echo "=== release-assets contents ==="
find release-assets -type f -exec ls -la {} \;
- name: Download all packages
uses: actions/download-artifact@v4
with:
path: packages
pattern: rusty-commit.*
merge-multiple: false
- name: Prepare release assets
run: |
mkdir -p release-packages
# Copy release assets
cp -r release-assets/* release-packages/ 2>/dev/null || true
# Copy platform packages
for dir in packages/*; do
if [ -d "$dir" ]; then
cp -r "$dir"/* release-packages/ 2>/dev/null || true
fi
done
ls -la release-packages/
- name: Generate SHA256 checksums
run: |
cd release-packages
find . -maxdepth 1 -type f -exec sha256sum {} \; > SHA256SUMS.txt
cat SHA256SUMS.txt
- name: Setup GPG for signing
if: ${{ env.GPG_PRIVATE_KEY != '' }}
uses: actions/cache@v5
with:
path: ~/.gnupg
key: gpg-key-${{ secrets.GPG_PRIVATE_KEY }}
- name: Import GPG key
if: ${{ env.GPG_PRIVATE_KEY != '' }}
run: |
echo '${{ secrets.GPG_PRIVATE_KEY }}' | gpg --batch --import --
gpg --list-secret-keys --keyid-format LONG
- name: Sign checksums
if: ${{ env.GPG_PRIVATE_KEY != '' }}
run: |
cd release-packages
gpg --batch --yes --armor --detach-sign SHA256SUMS.txt
cat SHA256SUMS.txt.asc
- name: Generate per-file GPG signatures
if: ${{ env.GPG_PRIVATE_KEY != '' }}
run: |
cd release-packages
# Import the GPG key
echo '${{ secrets.GPG_PRIVATE_KEY }}' | gpg --batch --import --
# Generate .asc signature for each file (except .asc and .txt files)
for file in *.zip *.tar.gz *.deb *.rpm *.apk *.snap; do
if [ -f "$file" ] && [ ! "$file" = "*.txt" ]; then
echo "Signing $file..."
gpg --batch --yes --armor --detach-sign "$file"
fi
done
# List generated signatures
ls -la -- *.asc 2>/dev/null || echo "No .asc files generated"
- name: Upload per-file GPG signatures
if: ${{ env.GPG_PRIVATE_KEY != '' }}
uses: actions/upload-artifact@v4
with:
name: gpg-signatures
path: release-packages/*.asc
retention-days: 5
- name: Upload SHA256 checksums
uses: actions/upload-artifact@v4
with:
name: sha256sums
path: release-packages/SHA256SUMS.txt
retention-days: 5
- name: Upload GPG signature
if: ${{ env.GPG_PRIVATE_KEY != '' }}
uses: actions/upload-artifact@v4
with:
name: sha256sums-asc
path: release-packages/SHA256SUMS.txt.asc
retention-days: 5
- name: Update GitHub Release
uses: softprops/action-gh-release@v2
with:
draft: false
prerelease: false
files: release-packages/*
publish-docker:
name: Publish Docker Image (Multi-Arch)
needs:
- build-apk
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Download Linux musl binaries (amd64)
uses: actions/download-artifact@v4
with:
name: binary-musl-x86_64-unknown-linux-musl
path: docker-bin/amd64
- name: Download Linux musl binaries (arm64)
uses: actions/download-artifact@v4
with:
name: binary-musl-aarch64-unknown-linux-musl
path: docker-bin/arm64
- name: Prepare Docker binaries
run: |
mkdir -p docker-bin
mv docker-bin/amd64/rco docker-bin/rusty-commit-amd64
mv docker-bin/arm64/rco docker-bin/rusty-commit-arm64
chmod +x docker-bin/rusty-commit-amd64 docker-bin/rusty-commit-arm64
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Set up QEMU for multi-arch builds
uses: docker/setup-qemu-action@v3
with:
platforms: amd64,arm64
- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata for Docker
id: meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/${{ github.repository }}
tags: |
type=ref,event=tag
type=raw,value=latest
- name: Build and push multi-architecture Docker image
uses: docker/build-push-action@v6
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
platforms: linux/amd64,linux/arm64
cache-from: type=gha
cache-to: type=gha,mode=max
update-homebrew:
name: Update Homebrew Formula
needs: finalize-release
runs-on: ubuntu-latest
env:
HOMEBREW_REPO_UPDATE_TOKEN: ${{ secrets.HOMEBREW_REPO_UPDATE_TOKEN }}
steps:
- name: Checkout homebrew-rusty-commit
uses: actions/checkout@v4
with:
repository: hongkongkiwi/homebrew-rusty-commit
token: ${{ secrets.HOMEBREW_REPO_UPDATE_TOKEN }}
path: homebrew-tap
- name: Download SHA256 checksums
uses: actions/download-artifact@v4
with:
name: sha256sums
path: homebrew-tmp
- name: Extract SHA256 checksums for all platforms
id: sha256
run: |
# Extract SHA256 for each platform (files include version prefix like v1.0.18)
# Note: releases use musl for Linux, not gnu
SHA256_X86_64_DARWIN=$(grep -E 'rusty-commit-.*-x86_64-apple-darwin\.tar\.gz' homebrew-tmp/SHA256SUMS.txt | awk '{print $1}')
SHA256_AARCH64_DARWIN=$(grep -E 'rusty-commit-.*-aarch64-apple-darwin\.tar\.gz' homebrew-tmp/SHA256SUMS.txt | awk '{print $1}')
SHA256_X86_64_LINUX=$(grep -E 'rusty-commit-.*-x86_64-unknown-linux-musl\.tar\.gz' homebrew-tmp/SHA256SUMS.txt | awk '{print $1}')
SHA256_AARCH64_LINUX=$(grep -E 'rusty-commit-.*-aarch64-unknown-linux-musl\.tar\.gz' homebrew-tmp/SHA256SUMS.txt | awk '{print $1}')
for value in "$SHA256_X86_64_DARWIN" "$SHA256_AARCH64_DARWIN" "$SHA256_X86_64_LINUX" "$SHA256_AARCH64_LINUX"; do
if [ -z "$value" ]; then
echo "Missing SHA256 checksum for one or more platforms" >&2
exit 1
fi
done
{
echo "sha256_x86_64_darwin=${SHA256_X86_64_DARWIN}"
echo "sha256_aarch64_darwin=${SHA256_AARCH64_DARWIN}"
echo "sha256_x86_64_linux=${SHA256_X86_64_LINUX}"
echo "sha256_aarch64_linux=${SHA256_AARCH64_LINUX}"
} >> "$GITHUB_OUTPUT"
echo "SHA256 x86_64-darwin: $SHA256_X86_64_DARWIN"
echo "SHA256 aarch64-darwin: $SHA256_AARCH64_DARWIN"
echo "SHA256 x86_64-linux-musl: $SHA256_X86_64_LINUX"
echo "SHA256 aarch64-linux-musl: $SHA256_AARCH64_LINUX"
- name: Update Homebrew formula
run: |
VERSION_RAW='${{ github.event.inputs.version || github.ref_name }}'
VERSION="${VERSION_RAW#v}"
SHA256_X86_64_DARWIN="${{ steps.sha256.outputs.sha256_x86_64_darwin }}"
SHA256_AARCH64_DARWIN="${{ steps.sha256.outputs.sha256_aarch64_darwin }}"
SHA256_X86_64_LINUX="${{ steps.sha256.outputs.sha256_x86_64_linux }}"
SHA256_AARCH64_LINUX="${{ steps.sha256.outputs.sha256_aarch64_linux }}"
cat > homebrew-tap/Formula/rusty-commit.rb << EOF
class RustyCommit < Formula
desc "Rust-powered AI commit message generator with 18+ AI providers"
homepage "https://github.com/hongkongkiwi/rusty-commit"
license "MIT"
version "${VERSION}"
on_macos do
on_arm do
url "https://github.com/hongkongkiwi/rusty-commit/releases/download/v${VERSION}/rusty-commit-v${VERSION}-aarch64-apple-darwin.tar.gz"
sha256 "${SHA256_AARCH64_DARWIN}"
end
on_intel do
url "https://github.com/hongkongkiwi/rusty-commit/releases/download/v${VERSION}/rusty-commit-v${VERSION}-x86_64-apple-darwin.tar.gz"
sha256 "${SHA256_X86_64_DARWIN}"
end
end
on_linux do
on_arm do
url "https://github.com/hongkongkiwi/rusty-commit/releases/download/v${VERSION}/rusty-commit-v${VERSION}-aarch64-unknown-linux-musl.tar.gz"
sha256 "${SHA256_AARCH64_LINUX}"
end
on_x86_64 do
url "https://github.com/hongkongkiwi/rusty-commit/releases/download/v${VERSION}/rusty-commit-v${VERSION}-x86_64-unknown-linux-musl.tar.gz"
sha256 "${SHA256_X86_64_LINUX}"
end
end
def install
bin.install "rco"
end
test do
assert_match version.to_s, shell_output("#{bin}/rco --version").strip
end
end
EOF
cat homebrew-tap/Formula/rusty-commit.rb
- name: Commit and push changes
working-directory: ./homebrew-tap
run: |
VERSION_RAW='${{ github.event.inputs.version || github.ref_name }}'
VERSION="${VERSION_RAW#v}"
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
# Check if there are changes to commit
git add Formula/rusty-commit.rb README.md
if git diff --cached --quiet; then
echo "No changes to commit for homebrew formula"
exit 0
fi
git commit -m "Update rusty-commit to v${VERSION}"
git push
update-precommit:
name: Update Pre-commit Hook
needs: finalize-release
runs-on: ubuntu-latest
env:
PRECOMMIT_REPO_UPDATE_TOKEN: ${{ secrets.PRECOMMIT_REPO_UPDATE_TOKEN }}
steps:
- name: Checkout precommit-rusty-commit
uses: actions/checkout@v4
with:
repository: hongkongkiwi/precommit-rusty-commit
token: ${{ secrets.PRECOMMIT_REPO_UPDATE_TOKEN }}
path: precommit-tap
- name: Update pre-commit hooks version
run: |
VERSION_RAW='${{ github.event.inputs.version || github.ref_name }}'
VERSION="${VERSION_RAW#v}"
# Update the rev in .pre-commit-hooks.yaml
sed -i "s/rev: v[0-9]\+\.[0-9]\+\.[0-9]\+/rev: v${VERSION}/" precommit-tap/.pre-commit-hooks.yaml
# Update version references in README.md
sed -i "s/rusty-commit v[0-9]\+\.[0-9]\+\.[0-9]\+/rusty-commit v${VERSION}/g" precommit-tap/README.md
sed -i "s/v[0-9]\+\.[0-9]\+\.[0-9]\]*/v${VERSION}/g" precommit-tap/README.md
cat precommit-tap/.pre-commit-hooks.yaml
- name: Commit and push changes
working-directory: ./precommit-tap
run: |
VERSION_RAW='${{ github.event.inputs.version || github.ref_name }}'
VERSION="${VERSION_RAW#v}"
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
# Check if there are changes to commit
git add .pre-commit-hooks.yaml README.md
if git diff --cached --quiet; then
echo "No changes to commit for pre-commit hooks"
exit 0
fi
git commit -m "Update rusty-commit to v${VERSION}"
git push
update-actions:
name: Update GitHub Action
needs: finalize-release
runs-on: ubuntu-latest
env:
ACTIONS_REPO_UPDATE_TOKEN: ${{ secrets.ACTIONS_REPO_UPDATE_TOKEN }}
steps:
- name: Checkout action-rusty-commit
uses: actions/checkout@v4
with:
repository: hongkongkiwi/action-rusty-commit
token: ${{ secrets.ACTIONS_REPO_UPDATE_TOKEN }}
path: actions-tap
- name: Update action version
run: |
VERSION_RAW='${{ github.event.inputs.version || github.ref_name }}'
VERSION="${VERSION_RAW#v}"
# Update the version in action.yml (if it has a tag reference)
sed -i "s/tag: v[0-9]\+\.[0-9]\+\.[0-9]\+/tag: v${VERSION}/" actions-tap/action.yml 2>/dev/null || true
cat actions-tap/action.yml
- name: Commit and push changes
working-directory: ./actions-tap
run: |
VERSION_RAW='${{ github.event.inputs.version || github.ref_name }}'
VERSION="${VERSION_RAW#v}"
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add action.yml README.md 2>/dev/null || git add action.yml
git commit -m "Update rusty-commit to v${VERSION}"
git push
update-scoop:
name: Update Scoop Bucket
needs: finalize-release
runs-on: ubuntu-latest
env:
SCOOP_REPO_UPDATE_TOKEN: ${{ secrets.SCOOP_REPO_UPDATE_TOKEN }}
steps:
- name: Determine version
id: version
run: |
VERSION_RAW="${{ github.event.inputs.version || github.ref_name }}"
VERSION="${VERSION_RAW#v}"
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
- name: Checkout scoop-bucket
uses: actions/checkout@v4
with:
repository: hongkongkiwi/scoop-rusty-commit
token: ${{ secrets.SCOOP_REPO_UPDATE_TOKEN }}
path: scoop-tap
- name: Download SHA256 checksums
uses: actions/download-artifact@v7
with:
name: sha256sums
path: scoop-tmp
- name: Extract SHA256 for Windows x86_64
id: sha256
run: |
SHA256_X86_64_WINDOWS=$(grep -E 'rusty-commit-.*-x86_64-pc-windows-msvc\.zip' scoop-tmp/SHA256SUMS.txt | awk '{print $1}')
if [ -z "$SHA256_X86_64_WINDOWS" ]; then
echo "Missing SHA256 checksum for Windows x86_64" >&2
exit 1
fi
echo "sha256_x86_64_windows=${SHA256_X86_64_WINDOWS}" >> "$GITHUB_OUTPUT"
echo "SHA256 x86_64-windows: $SHA256_X86_64_WINDOWS"
- name: Update Scoop manifest
run: |
VERSION="${{ steps.version.outputs.version }}"
SHA256_X86_64_WINDOWS="${{ steps.sha256.outputs.sha256_x86_64_windows }}"
# Update the manifest version
sed -i "s/\"version\": \"[0-9]\+\.[0-9]\+\.[0-9]\+\"/\"version\": \"${VERSION}\"/" scoop-tap/rusty-commit.json
# Update the URL with the correct version
sed -i "s|https://github.com/hongkongkiwi/rusty-commit/releases/download/v[0-9.]*/rusty-commit-v[0-9.]*-x86_64-pc-windows-msvc.zip|https://github.com/hongkongkiwi/rusty-commit/releases/download/v${VERSION}/rusty-commit-v${VERSION}-x86_64-pc-windows-msvc.zip|g" scoop-tap/rusty-commit.json
# Update the hash
sed -i "s/\"hash\": \"[a-f0-9]+\"/\"hash\": \"${SHA256_X86_64_WINDOWS}\"/" scoop-tap/rusty-commit.json
cat scoop-tap/rusty-commit.json
- name: Commit and push changes
working-directory: ./scoop-tap
run: |
VERSION="${{ steps.version.outputs.version }}"
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add rusty-commit.json
if git diff --cached --quiet; then
echo "No changes to commit for scoop manifest"
exit 0
fi
git commit -m "Update rusty-commit to v${VERSION}"
git push
update-winget:
name: Update Winget Package
needs: finalize-release
runs-on: ubuntu-latest
env:
WINGET_PKGS_TOKEN: ${{ secrets.WINGET_PKGS_TOKEN }}
steps:
- name: Determine version
id: version
run: |
VERSION_RAW="${{ github.event.inputs.version || github.ref_name }}"
VERSION="${VERSION_RAW#v}"
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
- name: Checkout winget-pkgs
uses: actions/checkout@v4
with:
repository: microsoft/winget-pkgs
token: ${{ secrets.WINGET_PKGS_TOKEN }}
path: winget-pkgs
- name: Download SHA256 checksums
uses: actions/download-artifact@v4
with:
name: sha256sums
path: winget-tmp
- name: Extract SHA256 for Windows x86_64
id: sha256
run: |
SHA256_X86_64_WINDOWS=$(grep -E 'rusty-commit-.*-x86_64-pc-windows-msvc\.zip' winget-tmp/SHA256SUMS.txt | awk '{print $1}')
if [ -z "$SHA256_X86_64_WINDOWS" ]; then
echo "Missing SHA256 checksum for Windows x86_64" >&2
exit 1
fi
echo "sha256_x86_64_windows=${SHA256_X86_64_WINDOWS}" >> "$GITHUB_OUTPUT"
echo "SHA256 x86_64-windows: $SHA256_X86_64_WINDOWS"
- name: Checkout winget-pkgs fork
uses: actions/checkout@v4
with:
repository: hongkongkiwi/winget-pkgs
token: ${{ env.WINGET_PKGS_TOKEN }}
path: winget-pkgs
- name: Configure git for winget-pkgs
run: |
cd winget-pkgs
git remote add upstream https://github.com/microsoft/winget-pkgs.git
git fetch upstream
git checkout -b update-rusty-commit-${{ steps.version.outputs.version }} upstream/master
- name: Prepare Winget manifest directory
run: |
VERSION="${{ steps.version.outputs.version }}"
mkdir -p winget-pkgs/manifests/h/hongkongkiwi/rusty-commit/${VERSION}
- name: Checkout packaging templates
uses: actions/checkout@v4
with:
path: packaging-templates
sparse-checkout: |
packaging/winget
- name: Update Winget manifests
run: |
VERSION="${{ steps.version.outputs.version }}"
SHA256_X86_64_WINDOWS="${{ steps.sha256.outputs.sha256_x86_64_windows }}"
# Copy and update version manifest
sed "s/__VERSION__/${VERSION}/g" packaging-templates/packaging/winget/hongkongkiwi.rusty-commit.yaml > \
winget-pkgs/manifests/h/hongkongkiwi/rusty-commit/${VERSION}/hongkongkiwi.rusty-commit.yaml
# Copy and update installer manifest with SHA256
sed -e "s/__VERSION__/${VERSION}/g" \
-e "s/__SHA256__/${SHA256_X86_64_WINDOWS}/g" \
packaging-templates/packaging/winget/hongkongkiwi.rusty-commit.installer.yaml > \
winget-pkgs/manifests/h/hongkongkiwi/rusty-commit/${VERSION}/hongkongkiwi.rusty-commit.installer.yaml
# Copy locale manifest
sed "s/__VERSION__/${VERSION}/g" packaging-templates/packaging/winget/hongkongkiwi.rusty-commit.locale.en-US.yaml > \
winget-pkgs/manifests/h/hongkongkiwi/rusty-commit/${VERSION}/hongkongkiwi.rusty-commit.locale.en-US.yaml
# Verify the manifests
ls -la winget-pkgs/manifests/h/hongkongkiwi/rusty-commit/${VERSION}/
- name: Create Pull Request
uses: peter-evans/create-pull-request@v7
with:
token: ${{ env.WINGET_PKGS_TOKEN }}
path: winget-pkgs
commit-message: "Update rusty-commit to v${{ steps.version.outputs.version }}"
title: "Update rusty-commit to v${{ steps.version.outputs.version }}"
body: |
Automated changes by GitHub Actions.
SHA256: ${{ steps.sha256.outputs.sha256_x86_64_windows }}
labels: |
validation-passed
update-manifest
push-to-fork: hongkongkiwi/winget-pkgs-fork
update-aur:
name: Update AUR Package
needs: finalize-release
runs-on: ubuntu-latest
env:
AUR_SSH_KEY: ${{ secrets.AUR_SSH_KEY }}
steps:
- name: Determine version
id: version
run: |
VERSION_RAW="${{ github.event.inputs.version || github.ref_name }}"
VERSION="${VERSION_RAW#v}"
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
- name: Checkout packaging templates
uses: actions/checkout@v4
with:
path: packaging-templates
sparse-checkout: |
packaging/aur
- name: Download SHA256 checksums
uses: actions/download-artifact@v4
with:
name: sha256sums
path: aur-tmp
- name: Extract SHA256 for Linux x86_64 musl
id: sha256
run: |
SHA256_X86_64_LINUX=$(grep -E 'rusty-commit-.*-x86_64-unknown-linux-musl\.tar\.gz' aur-tmp/SHA256SUMS.txt | awk '{print $1}')
if [ -z "$SHA256_X86_64_LINUX" ]; then
echo "Missing SHA256 checksum for Linux x86_64" >&2
exit 1
fi
echo "sha256_x86_64_linux=${SHA256_X86_64_LINUX}" >> "$GITHUB_OUTPUT"
echo "SHA256 x86_64-linux-musl: $SHA256_X86_64_LINUX"
- name: Setup SSH for AUR
run: |
mkdir -p ~/.ssh
echo "${{ secrets.AUR_SSH_KEY }}" > ~/.ssh/id_ed25519
chmod 600 ~/.ssh/id_ed25519
ssh-keyscan -H aur.archlinux.org >> ~/.ssh/known_hosts 2>/dev/null
- name: Clone AUR repository
run: |
git clone aur@aur.archlinux.org:rusty-commit.git aur-repo
cd aur-repo
- name: Update PKGBUILD
run: |
VERSION="${{ steps.version.outputs.version }}"
SHA256_X86_64_LINUX="${{ steps.sha256.outputs.sha256_x86_64_linux }}"
# Update PKGBUILD with version and SHA256
sed "s/__VERSION__/${VERSION}/g" packaging-templates/packaging/aur/PKGBUILD > aur-repo/PKGBUILD
sed -i "s/__SHA256_X86_64__/${SHA256_X86_64_LINUX}/g" aur-repo/PKGBUILD
# Update .SRCINFO
sed "s/__VERSION__/${VERSION}/g" packaging-templates/packaging/aur/.SRCINFO > aur-repo/.SRCINFO
sed -i "s/__SHA256_X86_64__/${SHA256_X86_64_LINUX}/g" aur-repo/.SRCINFO
cat aur-repo/PKGBUILD
- name: Copy LICENSE and README
run: |
cp packaging-templates/LICENSE aur-repo/ 2>/dev/null || true
cp packaging-templates/README.md aur-repo/ 2>/dev/null || true
- name: Commit and push to AUR
working-directory: ./aur-repo
run: |
VERSION="${{ steps.version.outputs.version }}"
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add PKGBUILD .SRCINFO LICENSE README.md 2>/dev/null || git add PKGBUILD .SRCINFO
if git diff --cached --quiet; then
echo "No changes to commit for AUR package"
exit 0
fi
git commit -m "Update to ${VERSION}"
git push
update-nix-overlay:
name: Update Nix Overlay
needs: finalize-release
runs-on: ubuntu-latest
env:
GH_TOKEN: ${{ secrets.GH_TOKEN }}
steps:
- name: Determine version
id: version
run: |
VERSION_RAW="${{ github.event.inputs.version || github.ref_name }}"
VERSION="${VERSION_RAW#v}"
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
- name: Checkout nixpkgs-overlays
uses: actions/checkout@v4
with:
repository: hongkongkiwi/nixpkgs-overlays
token: ${{ secrets.GH_TOKEN }}
path: nix-overlay
- name: Download SHA256 checksums
uses: actions/download-artifact@v4
with:
name: sha256sums
path: nix-tmp
- name: Extract SHA256 for Linux x86_64 musl
id: sha256
run: |
SHA256_X86_64_LINUX=$(grep -E 'rusty-commit-.*-x86_64-unknown-linux-musl\.tar\.gz' nix-tmp/SHA256SUMS.txt | awk '{print $1}')
SHA256_AARCH64_LINUX=$(grep -E 'rusty-commit-.*-aarch64-unknown-linux-musl\.tar\.gz' nix-tmp/SHA256SUMS.txt | awk '{print $1}')
if [ -z "$SHA256_X86_64_LINUX" ]; then
echo "Missing SHA256 checksum for Linux x86_64" >&2
exit 1
fi
echo "sha256_x86_64_linux=${SHA256_X86_64_LINUX}" >> "$GITHUB_OUTPUT"
echo "sha256_aarch64_linux=${SHA256_AARCH64_LINUX}" >> "$GITHUB_OUTPUT"
echo "SHA256 x86_64-linux-musl: $SHA256_X86_64_LINUX"
echo "SHA256 aarch64-linux-musl: $SHA256_AARCH64_LINUX"
- name: Checkout packaging templates
uses: actions/checkout@v4
with:
path: packaging-templates
sparse-checkout: |
packaging/nix
- name: Update Nix overlay
run: |
VERSION="${{ steps.version.outputs.version }}"
SHA256_X86_64_LINUX="${{ steps.sha256.outputs.sha256_x86_64_linux }}"
SHA256_AARCH64_LINUX="${{ steps.sha256.outputs.sha256_aarch64_linux }}"
# Copy and update the nix package expression
mkdir -p nix-overlay/rusty-commit
sed -e "s/__VERSION__/${VERSION}/g" \
-e "s/__SHA256_X86_64__/${SHA256_X86_64_LINUX}/g" \
-e "s/__SHA256_AARCH64__/${SHA256_AARCH64_LINUX}/g" \
packaging-templates/packaging/nix/default.nix > nix-overlay/rusty-commit/default.nix
cat nix-overlay/rusty-commit/default.nix
- name: Commit and push to overlay repo
working-directory: ./nix-overlay
run: |
VERSION="${{ steps.version.outputs.version }}"
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add rusty-commit/default.nix
if git diff --cached --quiet; then
echo "No changes to commit for nix overlay"
exit 0
fi
git commit -m "Update rusty-commit to v${VERSION}"
git push
update-chocolatey:
name: Update Chocolatey Package
needs: finalize-release
runs-on: windows-latest
env:
CHOCOLATEY_API_KEY: ${{ secrets.CHOCOLATEY_API_KEY }}
steps:
- name: Determine version
id: version
run: |
$VERSION_RAW = "${{ github.event.inputs.version || github.ref_name }}"
$VERSION = $VERSION_RAW.TrimStart('v')
echo "version=$VERSION" >> $env:GITHUB_OUTPUT
- name: Checkout packaging templates
uses: actions/checkout@v4
with:
path: packaging-templates
sparse-checkout: |
packaging/chocolatey
- name: Download SHA256 checksums
uses: actions/download-artifact@v4
with:
name: sha256sums
path: chocolatey-tmp
- name: Extract SHA256 for Windows x86_64
id: sha256
run: |
$SHA256_CONTENT = Get-Content -Path "chocolatey-tmp/SHA256SUMS.txt" -Raw
$SHA256_X86_64_WINDOWS = ($SHA256_CONTENT | Select-String -Pattern 'rusty-commit-.*-x86_64-pc-windows-msvc\.zip' | ForEach-Object { $_ -replace '^\s*([a-f0-9]+)\s+.*$', '$1' }).Trim()
if ([string]::IsNullOrEmpty($SHA256_X86_64_WINDOWS)) {
Write-Error "Missing SHA256 checksum for Windows x86_64"
exit 1
}
echo "sha256_x86_64_windows=$SHA256_X86_64_WINDOWS" >> $env:GITHUB_OUTPUT
echo "SHA256 x86_64-windows: $SHA256_X86_64_WINDOWS"
- name: Download release asset
uses: actions/download-artifact@v4
with:
name: release
path: release
- name: Update Chocolatey package
run: |
$VERSION = "${{ steps.version.outputs.version }}"
$SHA256_X86_64_WINDOWS = "${{ steps.sha256.outputs.sha256_x86_64_windows }}"
# Create Chocolatey package directory
New-Item -ItemType Directory -Force -Path "chocolatey-package" | Out-Null
New-Item -ItemType Directory -Force -Path "chocolatey-package/tools" | Out-Null
# Update nuspec
(Get-Content -Path "packaging-templates/packaging/chocolatey/rusty-commit.nuspec" -Raw) `
-replace '__VERSION__', $VERSION |
Out-File -FilePath "chocolatey-package/rusty-commit.nuspec" -Encoding UTF8
# Update install script
(Get-Content -Path "packaging-templates/packaging/chocolatey/tools/chocolateyInstall.ps1" -Raw) `
-replace '__VERSION__', $VERSION |
Out-File -FilePath "chocolatey-package/tools/chocolateyInstall.ps1" -Encoding UTF8
# Update uninstall script
(Get-Content -Path "packaging-templates/packaging/chocolatey/tools/chocolateyUninstall.ps1" -Raw) `
-replace '__VERSION__', $VERSION |
Out-File -FilePath "chocolatey-package/tools/chocolateyUninstall.ps1" -Encoding UTF8
# Copy release binary
Expand-Archive -Path "release/rusty-commit-v${VERSION}-x86_64-pc-windows-msvc.zip" -DestinationPath "chocolatey-package/extracted" -Force
Get-ChildItem -Path "chocolatey-package"
- name: Install Chocolatey
run: |
Set-ExecutionPolicy Bypass -Scope Process -Force
iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))
- name: Pack Chocolatey package
run: |
$VERSION = "${{ steps.version.outputs.version }}"
choco pack "chocolatey-package/rusty-commit.nuspec" --version $VERSION
$nupkg = Get-ChildItem -Path "." -Filter "rusty-commit.$VERSION.nupkg" -Recurse
Write-Host "Created package: $($nupkg.FullName)"
- name: Push to Chocolatey
if: ${{ env.CHOCOLATEY_API_KEY != '' }}
run: |
$VERSION = "${{ steps.version.outputs.version }}"
$nupkg = Get-ChildItem -Path "." -Filter "rusty-commit.$VERSION.nupkg" -Recurse | Select-Object -First 1
choco apikey -k "$env:CHOCOLATEY_API_KEY" -source https://push.chocolatey.org/
choco push "$($nupkg.FullName)" --source https://push.chocolatey.org/
build-snap:
name: Build and Publish Snap
needs: build-apk
runs-on: ubuntu-latest
env:
SNAP_STORE_LOGIN: ${{ secrets.SNAP_STORE_LOGIN }}
steps:
- name: Determine version
id: version
run: |
VERSION_RAW="${{ github.event.inputs.version || github.ref_name }}"
VERSION="${VERSION_RAW#v}"
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
- name: Checkout
uses: actions/checkout@v4
- name: Download musl binary
uses: actions/download-artifact@v4
with:
name: binary-musl-x86_64-unknown-linux-musl
path: snap-src
- name: Prepare Snapcraft directory
run: |
mkdir -p snap-src/snap
- name: Create snap directory structure and snapcraft.yaml
run: |
VERSION="${{ steps.version.outputs.version }}"
mkdir -p snap/snap
# Move the binary to the snap directory
mv snap-src/rco snap/
# Create snapcraft.yaml using printf to avoid YAML parsing issues
printf '%s\n' \
'name: rusty-commit' \
'base: core22' \
"version: '${VERSION}'" \
'summary: AI-powered commit message generator' \
'description: |' \
' Rusty-commit is a modern, AI-powered git commit message generator that helps' \
' you create meaningful, standardized commit messages following Conventional Commits.' \
' Supports 100+ AI providers including OpenAI, Claude, Groq, and more.' \
'grade: stable' \
'confinement: classic' \
'license: MIT' \
'' \
'parts:' \
' rco:' \
' plugin: dump' \
' source: .' \
' organize:' \
' rco: bin/rco' \
'' \
'apps:' \
' rco:' \
' command: bin/rco' > snap/snap/snapcraft.yaml
cat snap/snap/snapcraft.yaml
- name: Build Snap package
uses: snapcore/action-build@v1
with:
path: snap
snapcraft-channel: stable
- name: Upload Snap package to release
uses: actions/upload-artifact@v4
with:
name: rusty-commit.snap
path: snap/*.snap
retention-days: 5
- name: Upload Snap to GitHub Release
uses: softprops/action-gh-release@v2
with:
draft: false
prerelease: false
files: snap/*.snap
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Publish to Snap Store
if: ${{ env.SNAP_STORE_LOGIN != '' }}
uses: snapcore/action-publish@v1
with:
snapcraft-channel: stable
login: ${{ env.SNAP_STORE_LOGIN }}