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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
# will have compiled files and executables
debug
target
dist

# These are backup files generated by rustfmt
**/*.rs.bk
Expand Down
62 changes: 58 additions & 4 deletions Taskfile.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,22 @@ vars:
BINARY_NAME: commitbot

tasks:
setup:
desc: "Setup development environment"
silent: true
status:
- rustup target list --installed | grep -q x86_64-apple-darwin
- rustup target list --installed | grep -q aarch64-apple-darwin
cmds:
- rustup target add x86_64-apple-darwin
- rustup target add aarch64-apple-darwin

test:
desc: "Run all tests"
cmds:
- cargo clippy --all-targets --all-features -- -D warnings
- cargo test --all-features

run:simple:
desc: "Run commitbot in simple mode (no model, debug) using cargo run"
cmds:
Expand Down Expand Up @@ -32,14 +48,52 @@ tasks:
- cargo install --path . --force

build:release:
desc: "Build optimized release binary"
desc: "Build optimized release binary for the host"
silent: true
cmds:
- cargo build --release
- du -h target/release/{{.BINARY_NAME}}

build:release:*:
desc: "Build release binaries for all targets and package into dist/"
vars:
VERSION: '{{index .MATCH 0}}'
silent: true
cmds:
- mkdir -p dist
- rm -f dist/commitbot-*.tar.gz
- |
set -euo pipefail

echo "Building release binaries..."
cargo build --release --target aarch64-apple-darwin
cargo build --release --target x86_64-apple-darwin

du -h target/aarch64-apple-darwin/release/commitbot
du -h target/x86_64-apple-darwin/release/commitbot

echo "Packaging into dist with version {{.VERSION}}..."

tar -C target/aarch64-apple-darwin/release \
-czf dist/commitbot-{{.VERSION}}-aarch64-apple-darwin.tar.gz \
commitbot

tar -C target/x86_64-apple-darwin/release \
-czf dist/commitbot-{{.VERSION}}-x86_64-apple-darwin.tar.gz \
commitbot

echo "Artifacts created:"
ls -lh dist

path:binary:
desc: "Show the path to the release binary"
release:*:
desc: "Create a release by building and publishing to crates.io (requires VERSION arg)"
vars:
VERSION: '{{index .MATCH 0}}'
interactive: true
cmds:
- echo "target/release/{{.BINARY_NAME}}"
- sh devops/release.sh {{.VERSION}}
- publish:dry
- publish

publish:dry:
desc: "Dry-run crates.io publish to verify packaging"
Expand Down
190 changes: 190 additions & 0 deletions devops/release.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
#!/bin/zsh
set -euo pipefail

# ---------------------------------------------------------
# Config
# ---------------------------------------------------------
DIST_DIR="dist"

STEP=${1:?usage: release.sh [major|minor|patch|X.Y.Z]}

# ---------------------------------------------------------
# Pre-flight checks
# ---------------------------------------------------------
if ! command -v task >/dev/null 2>&1; then
echo "Error: 'task' command not found. Install Taskfile runner first."
exit 1
fi

if ! command -v gh >/dev/null 2>&1; then
echo "Error: 'gh' CLI not found. Install GitHub CLI (gh) first."
exit 1
fi

BRANCH=$(git rev-parse --abbrev-ref HEAD)
if [ "$BRANCH" != "main" ]; then
echo "Error: releases may only be created from 'main' (current: $BRANCH)."
exit 1
fi

# Ensure working tree is clean
if ! git diff --quiet || ! git diff --cached --quiet; then
echo "Error: working tree is not clean. Commit or stash changes before releasing."
exit 1
fi

# ---------------------------------------------------------
# Read current version from Cargo.toml
# ---------------------------------------------------------
CURRENT_VERSION=$(
grep -E '^version = "[0-9]+\.[0-9]+\.[0-9]+"' Cargo.toml \
| head -n1 \
| sed -E 's/version = "([^"]+)"/\1/'
)

if [ -z "$CURRENT_VERSION" ]; then
echo "Error: could not determine current version from Cargo.toml"
exit 1
fi

MAJOR=$(echo "$CURRENT_VERSION" | cut -d. -f1)
MINOR=$(echo "$CURRENT_VERSION" | cut -d. -f2)
PATCH=$(echo "$CURRENT_VERSION" | cut -d. -f3)
LATEST=true

# ---------------------------------------------------------
# Compute new version
# ---------------------------------------------------------
case "$STEP" in
major)
MAJOR=$((MAJOR+1))
MINOR=0
PATCH=0
;;
minor)
MINOR=$((MINOR+1))
PATCH=0
;;
patch)
PATCH=$((PATCH+1))
;;
*)
if [[ "$STEP" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
MAJOR=$(echo "$STEP" | cut -d. -f1)
MINOR=$(echo "$STEP" | cut -d. -f2)
PATCH=$(echo "$STEP" | cut -d. -f3)
LATEST=false
else
echo "Invalid step: $STEP"
echo "Usage: release.sh [major|minor|patch|X.Y.Z]"
exit 1
fi
;;
esac

NEW_VERSION="$MAJOR.$MINOR.$PATCH"

# Check that tag does not already exist
if git rev-parse -q --verify "refs/tags/$NEW_VERSION" >/dev/null 2>&1; then
echo "Error: git tag $NEW_VERSION already exists."
exit 1
fi

echo "Current version: $CURRENT_VERSION"
echo "New version: $NEW_VERSION"
echo "Branch: $BRANCH"
echo

read -r -p "Proceed with release? (y/N): " CONFIRM
if [[ ! "$CONFIRM" =~ ^[Yy]$ ]]; then
echo "Aborted."
exit 1
fi

# ---------------------------------------------------------
# Bump Cargo.toml with rollback on failure
# ---------------------------------------------------------
CARGO_BAK="Cargo.toml.bak.$$"
cp Cargo.toml "$CARGO_BAK"

cleanup_on_error() {
rc=$?
echo "Error during release (exit code $rc). Restoring Cargo.toml..."
if [ -f "$CARGO_BAK" ]; then
mv "$CARGO_BAK" Cargo.toml
fi
exit $rc
}
trap cleanup_on_error INT TERM ERR

echo "Updating Cargo.toml to version $NEW_VERSION..."
perl -pi -e 's/^version = "[0-9]+\.[0-9]+\.[0-9]+"/version = "'"$NEW_VERSION"'"/' Cargo.toml

# ---------------------------------------------------------
# Run tasks: setup, test, build
# ---------------------------------------------------------
echo "Running task setup..."
task setup

echo "Running task test..."
task test

echo "Running task build:release:all..."
task "build:release:${NEW_VERSION}"

# If we got here, tasks succeeded – stop rollback trap
trap - INT TERM ERR
rm -f "$CARGO_BAK"

# ---------------------------------------------------------
# Commit, tag, push
# ---------------------------------------------------------
if git diff --quiet -- Cargo.toml; then
echo "Warning: Cargo.toml did not change; nothing to commit."
else
git commit Cargo.toml -m "$NEW_VERSION"
fi

HASH=$(git rev-parse HEAD)

echo "Tagging $NEW_VERSION..."
git tag "$NEW_VERSION" "$HASH"

echo "Pushing main and tag..."
git push origin main
git push origin "refs/tags/$NEW_VERSION"

# ---------------------------------------------------------
# Create GitHub release from artifacts in DIST_DIR
# ---------------------------------------------------------
if [ ! -d "$DIST_DIR" ]; then
echo "Error: build artifacts directory '$DIST_DIR' not found."
echo "Check task build:release:all"
exit 1
fi

ASSETS=("$DIST_DIR"/*)
if [ ${#ASSETS[@]} -eq 0 ]; then
echo "Error: no build artifacts found in '$DIST_DIR'."
exit 1
fi

LATEST_FLAG=""
if [ "$LATEST" = true ]; then
LATEST_FLAG="--latest"
fi

echo "Creating GitHub release $NEW_VERSION with assets:"
for a in "${ASSETS[@]}"; do
echo " - $a"
done

gh release create "$NEW_VERSION" \
--fail-on-no-commits \
--generate-notes \
$LATEST_FLAG \
--target "$HASH" \
"${ASSETS[@]}"

echo "Release $NEW_VERSION created successfully."
exit 0