name: Release
on:
push:
branches:
- main
tags:
- "v*.*.*"
workflow_dispatch:
permissions:
contents: write
env:
CARGO_TERM_COLOR: always
jobs:
create-release:
name: Create Release
runs-on: ubuntu-latest
outputs:
version: ${{ steps.determine_version.outputs.version }}
should_release: ${{ steps.determine_version.outputs.should_release }}
steps:
- name: Checkout code
uses: actions/checkout@v5
with:
fetch-depth: 0
- name: Determine version and create tag
id: determine_version
run: |
# Check if this is a tag push
if [[ "$GITHUB_REF" == refs/tags/* ]]; then
VERSION="${GITHUB_REF#refs/tags/}"
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "should_release=true" >> $GITHUB_OUTPUT
exit 0
fi
# Main branch push - analyze commits for version bump
LAST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "v0.0.0")
if [ "$LAST_TAG" = "v0.0.0" ]; then
COMMITS=$(git log --pretty=format:"%s" HEAD)
else
COMMITS=$(git log --pretty=format:"%s" ${LAST_TAG}..HEAD)
fi
# Determine bump type
BUMP_TYPE="none"
if echo "$COMMITS" | grep -qE "^[a-zA-Z]+(\(.+\))?!:|BREAKING CHANGE:"; then
BUMP_TYPE="major"
elif echo "$COMMITS" | grep -qE "^feat(\(.+\))?:"; then
BUMP_TYPE="minor"
elif echo "$COMMITS" | grep -qE "^fix(\(.+\))?:"; then
BUMP_TYPE="patch"
fi
if [ "$BUMP_TYPE" = "none" ]; then
echo "should_release=false" >> $GITHUB_OUTPUT
exit 0
fi
# Calculate new version
CURRENT_VERSION="${LAST_TAG#v}"
IFS='.' read -r MAJOR MINOR PATCH <<< "$CURRENT_VERSION"
case $BUMP_TYPE in
major) MAJOR=$((MAJOR + 1)); MINOR=0; PATCH=0 ;;
minor) MINOR=$((MINOR + 1)); PATCH=0 ;;
patch) PATCH=$((PATCH + 1)) ;;
esac
NEW_VERSION="$MAJOR.$MINOR.$PATCH"
NEW_TAG="v$NEW_VERSION"
# Update Cargo.toml
sed -i.bak "s/^version = .*/version = \"$NEW_VERSION\"/" Cargo.toml
rm Cargo.toml.bak
# Commit and push
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add Cargo.toml
git commit -m "chore: bump version to $NEW_VERSION [skip ci]"
git tag -a "$NEW_TAG" -m "Release $NEW_TAG"
git push origin HEAD:main
git push origin "$NEW_TAG"
echo "version=$NEW_TAG" >> $GITHUB_OUTPUT
echo "should_release=true" >> $GITHUB_OUTPUT
- name: Create Release
if: steps.determine_version.outputs.should_release == 'true'
env:
GH_TOKEN: ${{ github.token }}
run: |
VERSION="${{ steps.determine_version.outputs.version }}"
# Check if release already exists
if gh release view "$VERSION" >/dev/null 2>&1; then
echo "Release $VERSION already exists, skipping creation"
else
gh release create "$VERSION" \
--title "Release $VERSION" \
--notes "Create Kubernetes SealedSecrets with a single secure command. Download the binary for your platform below and add it to your PATH."
fi
build-release:
name: Build Release Binary
needs: create-release
if: needs.create-release.outputs.should_release == 'true'
strategy:
matrix:
include:
- os: ubuntu-latest
target: x86_64-unknown-linux-gnu
artifact_name: seal
asset_name: seal-linux-amd64
- os: ubuntu-latest
target: aarch64-unknown-linux-gnu
artifact_name: seal
asset_name: seal-linux-arm64
- os: macos-latest
target: x86_64-apple-darwin
artifact_name: seal
asset_name: seal-macos-amd64
- os: macos-latest
target: aarch64-apple-darwin
artifact_name: seal
asset_name: seal-macos-arm64
- os: windows-latest
target: x86_64-pc-windows-msvc
artifact_name: seal.exe
asset_name: seal-windows-amd64.exe
runs-on: ${{ matrix.os }}
steps:
- name: Checkout code
uses: actions/checkout@v5
- name: Setup Rust
uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}
- name: Install cross-compilation tools (Linux ARM64)
if: matrix.target == 'aarch64-unknown-linux-gnu'
run: |
sudo apt-get update
sudo apt-get install -y gcc-aarch64-linux-gnu
- name: Build release binary
run: cargo build --release --target ${{ matrix.target }} --verbose
- name: Strip binary (Linux and macOS)
if: matrix.os != 'windows-latest'
run: strip target/${{ matrix.target }}/release/${{ matrix.artifact_name }}
- name: Upload Release Asset
env:
GH_TOKEN: ${{ github.token }}
run: |
cp "target/${{ matrix.target }}/release/${{ matrix.artifact_name }}" "${{ matrix.asset_name }}"
gh release upload "${{ needs.create-release.outputs.version }}" "${{ matrix.asset_name }}"
publish-crate:
name: Publish to crates.io
needs: create-release
if: needs.create-release.outputs.should_release == 'true' && startsWith(github.ref, 'refs/tags/')
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v5
- name: Setup Rust
uses: dtolnay/rust-toolchain@stable
- name: Security Audit
run: |
cargo install cargo-audit
cargo audit --deny warnings
- name: Validate Version Consistency
run: |
# Extract version from Git tag (remove 'v' prefix)
GIT_TAG="${GITHUB_REF#refs/tags/v}"
# Extract version from Cargo.toml
CARGO_VERSION=$(cargo metadata --no-deps --format-version 1 | jq -r '.packages[0].version')
# Compare versions
if [ "$GIT_TAG" != "$CARGO_VERSION" ]; then
echo "â ERROR: Version mismatch!"
echo "Git tag: v$GIT_TAG"
echo "Cargo.toml: $CARGO_VERSION"
echo ""
echo "Please update Cargo.toml version to match the release tag."
exit 1
fi
echo "â
Version validation passed: $CARGO_VERSION"
- name: Verify Build
run: cargo build --release --verbose
- name: Run Tests
run: cargo test --all --verbose
- name: Publish to crates.io
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
run: |
echo "đĻ Publishing quickseal to crates.io..."
# Attempt publication
if cargo publish --token "$CARGO_REGISTRY_TOKEN"; then
echo "â
Successfully published to crates.io"
else
EXIT_CODE=$?
# Check if already published (common scenario, not an error)
if cargo search quickseal | grep -q "^quickseal ="; then
echo "âšī¸ Package version already published to crates.io"
exit 0
else
echo "â Publication failed with exit code $EXIT_CODE"
exit $EXIT_CODE
fi
fi
- name: Update Release Notes
if: success()
env:
GH_TOKEN: ${{ github.token }}
run: |
gh release edit ${{ github.ref_name }} --notes-file - <<'NOTES'
## đĻ crates.io Publication
â
Successfully published to crates.io as **quickseal**
**Installation:** `cargo install quickseal`
**Verification:** `seal --version`
**Package:** https://crates.io/crates/quickseal
NOTES
echo "â
Release notes updated"
docker-image:
name: Build Docker Image
needs: create-release
runs-on: ubuntu-latest
if: false steps:
- name: Checkout code
uses: actions/checkout@v5
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/${{ github.repository }}
tags: |
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
type=sha
- name: Build and push
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