name: Release
on:
push:
tags:
- 'v*'
branches:
- 'master' workflow_dispatch:
env:
CARGO_TERM_COLOR: always
permissions:
contents: write
jobs:
ci:
name: Run CI Pipeline
if: startsWith(github.ref, 'refs/tags/v')
uses: ./.github/workflows/ci.yml
secrets: inherit
ci-master:
name: Run CI Pipeline (Master)
if: github.ref == 'refs/heads/master'
uses: ./.github/workflows/ci.yml
secrets: inherit
ci-manual:
name: Run CI Pipeline (Manual)
if: github.event_name == 'workflow_dispatch'
uses: ./.github/workflows/ci.yml
secrets: inherit
create-release:
name: Create Release
runs-on: ubuntu-latest
needs: ci
if: startsWith(github.ref, 'refs/tags/v')
outputs:
upload_url: ${{ steps.create_release.outputs.upload_url }}
release_id: ${{ steps.create_release.outputs.id }}
steps:
- uses: actions/checkout@v4
create-release-master:
name: Create Release (Master)
runs-on: ubuntu-latest
needs: ci-master
if: github.ref == 'refs/heads/master'
outputs:
upload_url: ${{ steps.create_release.outputs.upload_url }}
release_id: ${{ steps.create_release.outputs.id }}
tag_name: ${{ steps.tag_version.outputs.tag }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Create and push tag
id: tag_version
run: |
if [ -f RELEASE_NOTE.md ]; then
TAG=$(head -1 RELEASE_NOTE.md | grep -oP 'v[\d.]+' | head -1)
echo "🏷️ Found version in RELEASE_NOTE.md: $TAG"
else
TAG="v0.3.$(date +%Y%m%d%H%M)"
echo "⚠️ No RELEASE_NOTE.md, using fallback: $TAG"
fi
echo "🔧 Configuring git..."
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
echo "🏷️ Creating local tag: $TAG"
git tag -f "$TAG"
REMOTE_URL=$(git remote get-url origin)
echo "📡 Remote URL: $REMOTE_URL"
echo "🚀 Pushing tag to remote..."
git push origin "$TAG" --force
VERIFY_TAG=$(git ls-remote --tags origin "$TAG" | head -1)
echo "✅ Tag verified on remote: $VERIFY_TAG"
echo "tag=$TAG" >> $GITHUB_OUTPUT
- name: Read RELEASE_NOTE.md
id: release_notes
run: |
echo "📋 Looking for RELEASE_NOTE.md..."
if [ -f RELEASE_NOTE.md ]; then
echo "✅ Found RELEASE_NOTE.md"
NOTES=$(cat RELEASE_NOTE.md)
NOTES="${NOTES//'%'/'%25'}"
NOTES="${NOTES//$'\n'/'%0A'}"
NOTES="${NOTES//$'\r'/'%0D'}"
echo "notes=$NOTES" >> $GITHUB_OUTPUT
else
echo "⚠️ No RELEASE_NOTE.md found, using default message"
echo "notes=Release ${{ steps.tag_version.outputs.tag }}" >> $GITHUB_OUTPUT
fi
- name: Create Release
id: create_release
uses: softprops/action-gh-release@v1
with:
tag_name: ${{ steps.tag_version.outputs.tag }}
name: "Release ${{ steps.tag_version.outputs.tag }}"
body: ${{ steps.release_notes.outputs.notes }}
draft: false
prerelease: false
generate_release_notes: false
build-release:
name: Build Release Binary (${{ matrix.os }})
runs-on: ${{ matrix.os }}
needs: create-release
if: startsWith(github.ref, 'refs/tags/v')
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
asset_name: garbage-code-hunter-linux-x86_64.tar.gz
- os: macos-latest
asset_name: garbage-code-hunter-macos-x86_64.tar.gz
steps:
- uses: actions/checkout@v4
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
- name: Cache cargo registry
uses: actions/cache@v4
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-cargo-release-${{ hashFiles('**/Cargo.lock') }}
restore-keys: ${{ runner.os }}-cargo-release-
- name: Build release binary
run: cargo build --release
- name: Show binary info
run: |
BINARY="target/release/garbage-code-hunter"
if [ -f "$BINARY" ] || [ -f "${BINARY}.exe" ]; then
echo "📦 Binary built successfully:"
ls -lh target/release/garbage-code-hunter* 2>/dev/null || true
file target/release/garbage-code-hunter* 2>/dev/null || true
else
echo "❌ Binary not found at $BINARY"
exit 1
fi
- name: Package binary
run: |
cd target/release
ASSET="${{ matrix.asset_name }}"
tar -czf "$ASSET" garbage-code-hunter
echo "📦 Package created:"
ls -lh "$ASSET"
echo "ASSET_PATH=target/release/$ASSET" >> $GITHUB_ENV
echo "ASSET_NAME=$ASSET" >> $GITHUB_ENV
- name: Upload Release Asset
uses: softprops/action-gh-release@v1
if: success()
with:
files: ${{ env.ASSET_PATH }}
tag_name: ${{ needs.create-release-master.outputs.tag_name }}
build-release-master:
name: Build Release Binary (${{ matrix.os }})
runs-on: ${{ matrix.os }}
needs: create-release-master
if: github.ref == 'refs/heads/master'
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
asset_name: garbage-code-hunter-linux-x86_64.tar.gz
- os: macos-latest
path: |
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-cargo-release-${{ hashFiles('**/Cargo.lock') }}
restore-keys: ${{ runner.os }}-cargo-release-
- name: Build release binary
run: cargo build --release
- name: Show binary info
run: |
BINARY="target/release/garbage-code-hunter"
if [ -f "$BINARY" ] || [ -f "${BINARY}.exe" ]; then
echo "📦 Binary built successfully:"
ls -lh target/release/garbage-code-hunter* 2>/dev/null || true
file target/release/garbage-code-hunter* 2>/dev/null || true
else
echo "❌ Binary not found at $BINARY"
exit 1
fi
- name: Package binary
run: |
cd target/release
ASSET="${{ matrix.asset_name }}"
tar -czf "$ASSET" garbage-code-hunter
echo "📦 Package created:"
ls -lh "$ASSET"
echo "ASSET_PATH=target/release/$ASSET" >> $GITHUB_ENV
echo "ASSET_NAME=$ASSET" >> $GITHUB_ENV
- name: Upload Release Asset
uses: softprops/action-gh-release@v1
if: success()
with:
files: ${{ env.ASSET_PATH }}
tag_name: ${{ needs.create-release-master.outputs.tag_name }}
publish-crate:
name: Publish to crates.io
runs-on: ubuntu-latest
needs: create-release
if: startsWith(github.ref, 'refs/tags/v') && contains(fromJSON('["publish", "all"]'), 'publish')
steps:
- uses: actions/checkout@v4
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
- name: Cache cargo registry
uses: actions/cache@v4
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-cargo-publish-${{ hashFiles('**/Cargo.lock') }}
- name: Verify package
run: cargo publish --dry-run --allow-dirty
- name: Publish to crates.io
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
run: cargo publish --token ${{ secrets.CARGO_REGISTRY_TOKEN }}
release-success:
name: ✅ Release Status Summary
runs-on: ubuntu-latest
needs: [create-release, build-release]
if: always() && startsWith(github.ref, 'refs/tags/v')
steps:
- name: Generate summary
run: |
echo "========================================="
echo "🎉 Release Summary: ${{ github.ref_name }}"
echo "========================================="
echo ""
echo "📅 Time: $(date '+%Y-%m-%d %H:%M:%S UTC')"
echo "🏷️ Tag: ${{ github.ref }}"
echo "🔗 Commit: ${{ github.sha }}"
echo ""
echo "Jobs Status:"
echo " 📝 Create Release: ${{ needs.create-release.result }}"
echo " 🔨 Build Binaries: ${{ needs.build-release.result }}"
echo ""
if [ "${{ needs.create-release.result }}" == "success" ] && \
[ "${{ needs.build-release.result }}" == "success" ]; then
echo "========================================="
echo "✅ Release completed successfully!"
echo "========================================="
# List all uploaded assets
echo ""
echo "📦 Uploaded Assets:"
echo " • garbage-code-hunter-linux-x86_64.tar.gz"
echo " • garbage-code-hunter-macos-x86_64.tar.gz"
exit 0
else
echo "========================================="
echo "❌ Release failed!"
echo "========================================="
exit 1
fi