chore(release): 0.12.1 #64
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Release | |
| on: | |
| push: | |
| tags: | |
| - '[0-9]+.[0-9]+.[0-9]+*' # Matches semver tags like 0.1.0, 1.0.0, etc. | |
| workflow_dispatch: | |
| inputs: | |
| tag: | |
| description: 'Tag to release (semver format, e.g., 0.1.0)' | |
| required: true | |
| type: string | |
| env: | |
| CARGO_TERM_COLOR: always | |
| RUST_BACKTRACE: 1 | |
| jobs: | |
| create-release: | |
| name: Create Release | |
| runs-on: ubuntu-latest | |
| outputs: | |
| upload_url: ${{ steps.create_release.outputs.upload_url }} | |
| release_id: ${{ steps.create_release.outputs.id }} | |
| version: ${{ steps.get_version.outputs.VERSION }} | |
| changelog: ${{ steps.changelog.outputs.CHANGELOG }} | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 # Fetch full history for changelog generation | |
| - name: Get version from tag | |
| id: get_version | |
| run: | | |
| if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then | |
| VERSION="${{ github.event.inputs.tag }}" | |
| else | |
| VERSION="${GITHUB_REF#refs/tags/}" | |
| fi | |
| echo "VERSION=${VERSION}" >> $GITHUB_OUTPUT | |
| echo "VERSION_NO_V=${VERSION#v}" >> $GITHUB_OUTPUT | |
| - name: Validate semver format | |
| run: | | |
| VERSION="${{ steps.get_version.outputs.VERSION }}" | |
| # POSIX-compatible regex check for semver | |
| case "$VERSION" in | |
| [0-9]*.[0-9]*.[0-9]*) | |
| # Basic semver format matched | |
| ;; | |
| *) | |
| echo "Error: Tag '$VERSION' is not a valid semver format" | |
| echo "Expected format: X.Y.Z, X.Y.Z-prerelease, or X.Y.Z+build" | |
| exit 1 | |
| ;; | |
| esac | |
| - name: Generate changelog | |
| id: changelog | |
| run: | | |
| # Get the previous tag | |
| PREV_TAG=$(git describe --tags --abbrev=0 HEAD^ 2>/dev/null || echo "") | |
| if [ -z "$PREV_TAG" ]; then | |
| CHANGELOG="Initial release" | |
| else | |
| # Generate changelog from commits since last tag | |
| CHANGELOG=$(git log ${PREV_TAG}..HEAD --pretty=format:"- %s" --no-merges | head -20) | |
| if [ -z "$CHANGELOG" ]; then | |
| CHANGELOG="- No changes since last release" | |
| fi | |
| fi | |
| # Escape for GitHub output (POSIX compatible) | |
| CHANGELOG=$(printf '%s\n' "$CHANGELOG" | sed 's/%/%25/g; s/\r/%0D/g' | awk '{printf "%s%0A", $0}' | sed 's/%0A$//') | |
| echo "CHANGELOG=$CHANGELOG" >> $GITHUB_OUTPUT | |
| - name: Check if release exists | |
| id: check_release | |
| run: | | |
| VERSION="${{ steps.get_version.outputs.VERSION }}" | |
| if gh release view "$VERSION" >/dev/null 2>&1; then | |
| echo "exists=true" >> $GITHUB_OUTPUT | |
| else | |
| echo "exists=false" >> $GITHUB_OUTPUT | |
| fi | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Create Release | |
| id: create_release | |
| if: steps.check_release.outputs.exists == 'false' | |
| run: | | |
| VERSION="${{ steps.get_version.outputs.version }}" | |
| IS_PRERELEASE="false" | |
| # Check if this is a prerelease (contains - or is 0.x.x) | |
| case "$VERSION" in | |
| *-*|0.*) | |
| IS_PRERELEASE="true" | |
| ;; | |
| esac | |
| # Create the release as draft | |
| if [ "$IS_PRERELEASE" = "true" ]; then | |
| gh release create "$VERSION" \ | |
| --title "Release $VERSION" \ | |
| --notes "${{ steps.changelog.outputs.CHANGELOG }}" \ | |
| --draft \ | |
| --prerelease | |
| else | |
| gh release create "$VERSION" \ | |
| --title "Release $VERSION" \ | |
| --notes "${{ steps.changelog.outputs.CHANGELOG }}" \ | |
| --draft | |
| fi | |
| # Get release info | |
| RELEASE_INFO=$(gh release view "$VERSION" --json id,uploadUrl) | |
| RELEASE_ID=$(echo "$RELEASE_INFO" | jq -r '.id') | |
| UPLOAD_URL=$(echo "$RELEASE_INFO" | jq -r '.uploadUrl') | |
| echo "id=${RELEASE_ID}" >> $GITHUB_OUTPUT | |
| echo "upload_url=${UPLOAD_URL}" >> $GITHUB_OUTPUT | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| build: | |
| name: Build ${{ matrix.target }} | |
| needs: create-release | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - target: x86_64-unknown-linux-musl | |
| os: ubuntu-latest | |
| archive: tar.gz | |
| - target: aarch64-unknown-linux-musl | |
| os: ubuntu-22.04-arm | |
| archive: tar.gz | |
| - target: x86_64-pc-windows-msvc | |
| os: windows-latest | |
| archive: zip | |
| - target: aarch64-pc-windows-msvc | |
| os: windows-11-arm | |
| archive: zip | |
| - target: x86_64-apple-darwin | |
| os: macos-15-intel | |
| archive: tar.gz | |
| - target: aarch64-apple-darwin | |
| os: macos-15 | |
| archive: tar.gz | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Install Rust | |
| uses: dtolnay/rust-toolchain@1.92.0 | |
| with: | |
| targets: ${{ matrix.target }} | |
| - name: Install protobuf compiler | |
| uses: arduino/setup-protoc@v3 | |
| with: | |
| repo-token: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Cache dependencies | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| ~/.cargo/registry | |
| ~/.cargo/git | |
| target | |
| key: ${{ endsWith(matrix.target, '-unknown-linux-musl') && 'docker-alpine' || runner.os }}-${{ matrix.target }}-cargo-${{ hashFiles('**/Cargo.lock') }} | |
| restore-keys: | | |
| ${{ endsWith(matrix.target, '-unknown-linux-musl') && 'docker-alpine' || runner.os }}-${{ matrix.target }}-cargo- | |
| ${{ endsWith(matrix.target, '-unknown-linux-musl') && 'docker-alpine' || runner.os }}-cargo- | |
| - name: Build binary | |
| shell: sh | |
| run: | | |
| if [ "${{ runner.os }}" = "Linux" ]; then | |
| # Use official Rust Docker image for Linux builds | |
| # Disable default features to avoid ONNX Runtime issues (same as Windows) | |
| docker run --rm \ | |
| -v "$(pwd):/workspace" \ | |
| -w /workspace \ | |
| rust:1.92.0-alpine3.21 \ | |
| sh -c " | |
| apk add --no-cache musl-dev openssl-dev openssl-libs-static pkgconfig gcc g++ protobuf-dev && \ | |
| rustup target add ${{ matrix.target }} && \ | |
| cargo build --release --target ${{ matrix.target }} --no-default-features | |
| " | |
| elif [ "${{ runner.os }}" = "Windows" ]; then | |
| # Windows: disable default features to avoid ONNX Runtime issues | |
| cargo build --release --target ${{ matrix.target }} --no-default-features | |
| elif [ "${{ runner.os }}" = "macOS" ]; then | |
| # macOS: only use --no-default-features for x86_64 (Intel) due to ONNX Runtime | |
| # not providing prebuilt binaries for this target. aarch64 (Apple Silicon) works. | |
| if [ "${{ matrix.target }}" = "x86_64-apple-darwin" ]; then | |
| cargo build --release --target ${{ matrix.target }} --no-default-features | |
| else | |
| cargo build --release --target ${{ matrix.target }} | |
| fi | |
| fi | |
| - name: Create archive directory | |
| shell: sh | |
| run: mkdir -p dist | |
| - name: Create archive (Unix) | |
| if: matrix.archive == 'tar.gz' | |
| shell: sh | |
| run: | | |
| cd target/${{ matrix.target }}/release | |
| case "${{ matrix.target }}" in | |
| *windows*) | |
| tar czf ../../../dist/octocode-${{ needs.create-release.outputs.version }}-${{ matrix.target }}.tar.gz octocode.exe | |
| ;; | |
| *) | |
| tar czf ../../../dist/octocode-${{ needs.create-release.outputs.version }}-${{ matrix.target }}.tar.gz octocode | |
| ;; | |
| esac | |
| - name: Create archive (Windows) | |
| if: matrix.archive == 'zip' | |
| shell: sh | |
| run: | | |
| cd target/${{ matrix.target }}/release | |
| 7z a ../../../dist/octocode-${{ needs.create-release.outputs.version }}-${{ matrix.target }}.zip octocode.exe | |
| - name: Upload release asset | |
| shell: sh | |
| run: | | |
| gh release upload "${{ needs.create-release.outputs.version }}" \ | |
| "./dist/octocode-${{ needs.create-release.outputs.version }}-${{ matrix.target }}.${{ matrix.archive }}" \ | |
| --clobber | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| publish-crate: | |
| name: Publish to Crates.io | |
| needs: [create-release, build] | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Install Rust | |
| uses: dtolnay/rust-toolchain@1.92.0 | |
| - name: Install protobuf compiler | |
| uses: arduino/setup-protoc@v3 | |
| with: | |
| repo-token: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Cache dependencies | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| ~/.cargo/registry | |
| ~/.cargo/git | |
| target | |
| key: ${{ runner.os }}-cargo-publish-${{ hashFiles('**/Cargo.lock') }} | |
| restore-keys: | | |
| ${{ runner.os }}-cargo-publish- | |
| ${{ runner.os }}-cargo- | |
| - name: Validate version matches tag | |
| run: | | |
| VERSION="${{ needs.create-release.outputs.version }}" | |
| CARGO_VERSION=$(grep '^version = ' Cargo.toml | head -1 | sed 's/version = "\(.*\)"/\1/') | |
| if [ "$VERSION" != "$CARGO_VERSION" ]; then | |
| echo "❌ Version mismatch!" | |
| echo "Git tag: $VERSION" | |
| echo "Cargo.toml: $CARGO_VERSION" | |
| echo "Please update Cargo.toml version to match the git tag" | |
| exit 1 | |
| fi | |
| echo "✅ Version validation passed: $VERSION" | |
| - name: Check if version already published | |
| id: check_published | |
| run: | | |
| VERSION="${{ needs.create-release.outputs.version }}" | |
| # Check if this version already exists on crates.io | |
| if cargo search octocode --limit 1 | grep -q "octocode = \"$VERSION\""; then | |
| echo "already_published=true" >> $GITHUB_OUTPUT | |
| echo "⚠️ Version $VERSION already published to crates.io" | |
| else | |
| echo "already_published=false" >> $GITHUB_OUTPUT | |
| echo "✅ Version $VERSION not yet published" | |
| fi | |
| - name: Dry run publish | |
| if: steps.check_published.outputs.already_published == 'false' | |
| run: | | |
| echo "🔍 Running dry-run publish to validate package..." | |
| cargo publish --dry-run --no-default-features | |
| env: | |
| CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} | |
| - name: Publish to crates.io | |
| if: steps.check_published.outputs.already_published == 'false' | |
| run: | | |
| echo "🚀 Publishing to crates.io..." | |
| cargo publish --no-default-features | |
| echo "✅ Successfully published to crates.io!" | |
| env: | |
| CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} | |
| - name: Skip publishing | |
| if: steps.check_published.outputs.already_published == 'true' | |
| run: | | |
| echo "⏭️ Skipping crates.io publish - version already exists" | |
| finalize-release: | |
| name: Finalize Release | |
| needs: [create-release, build, publish-crate] | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Publish release | |
| run: | | |
| VERSION="${{ needs.create-release.outputs.version }}" | |
| # Update release notes with download links | |
| cat > release_notes.md << EOF | |
| ## 🚀 What's Changed | |
| ${{ needs.create-release.outputs.changelog }} | |
| ## 📦 Installation | |
| ### Quick Install Script (Universal) | |
| \`\`\`bash | |
| curl -fsSL https://raw.githubusercontent.com/${{ github.repository }}/main/install.sh | sh | |
| \`\`\` | |
| **Works on:** Linux, macOS, Windows (Git Bash/WSL/MSYS2), and any Unix-like system | |
| ### Manual Download | |
| | Platform | Architecture | Download | | |
| |----------|--------------|----------| | |
| | Linux | x86_64 (static) | [octocode-${VERSION}-x86_64-unknown-linux-musl.tar.gz](https://github.com/${{ github.repository }}/releases/download/${VERSION}/octocode-${VERSION}-x86_64-unknown-linux-musl.tar.gz) | | |
| | Linux | ARM64 (static) | [octocode-${VERSION}-aarch64-unknown-linux-musl.tar.gz](https://github.com/${{ github.repository }}/releases/download/${VERSION}/octocode-${VERSION}-aarch64-unknown-linux-musl.tar.gz) | | |
| | Windows | x86_64 | [octocode-${VERSION}-x86_64-pc-windows-msvc.zip](https://github.com/${{ github.repository }}/releases/download/${VERSION}/octocode-${VERSION}-x86_64-pc-windows-msvc.zip) | | |
| | Windows | ARM64 | [octocode-${VERSION}-aarch64-pc-windows-msvc.zip](https://github.com/${{ github.repository }}/releases/download/${VERSION}/octocode-${VERSION}-aarch64-pc-windows-msvc.zip) | | |
| | macOS | x86_64 | [octocode-${VERSION}-x86_64-apple-darwin.tar.gz](https://github.com/${{ github.repository }}/releases/download/${VERSION}/octocode-${VERSION}-x86_64-apple-darwin.tar.gz) | | |
| | macOS | ARM64 | [octocode-${VERSION}-aarch64-apple-darwin.tar.gz](https://github.com/${{ github.repository }}/releases/download/${VERSION}/octocode-${VERSION}-aarch64-apple-darwin.tar.gz) | | |
| ### Using Cargo (from crates.io) | |
| \`\`\`bash | |
| cargo install octocode | |
| \`\`\` | |
| ### Using Cargo (from Git) | |
| \`\`\`bash | |
| cargo install --git https://github.com/${{ github.repository }} | |
| \`\`\` | |
| ### Verify Installation | |
| \`\`\`bash | |
| octocode --version | |
| \`\`\` | |
| EOF | |
| # Publish the release (remove draft status) | |
| gh release edit "$VERSION" --draft=false --notes-file release_notes.md | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| notify-homebrew: | |
| name: Notify Homebrew Tap | |
| needs: [create-release, finalize-release] | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Dispatch update to homebrew-tap | |
| run: | | |
| curl -X POST \ | |
| -H "Accept: application/vnd.github+json" \ | |
| -H "Authorization: Bearer ${{ secrets.TAP_GITHUB_TOKEN }}" \ | |
| https://api.github.com/repos/muvon/homebrew-tap/dispatches \ | |
| -d "{\"event_type\":\"octocode-release\",\"client_payload\":{\"version\":\"${{ needs.create-release.outputs.version }}\"}}" | |
| docker: | |
| name: Build and Push Docker Images | |
| needs: [create-release, build, publish-crate] | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - 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: Set lowercase repository name | |
| id: repo | |
| run: echo "repository=$(echo '${{ github.repository }}' | tr '[:upper:]' '[:lower:]')" >> $GITHUB_OUTPUT | |
| - name: Build and push Docker image | |
| uses: docker/build-push-action@v5 | |
| with: | |
| context: . | |
| platforms: linux/amd64,linux/arm64 | |
| push: true | |
| tags: | | |
| ghcr.io/${{ steps.repo.outputs.repository }}:latest | |
| ghcr.io/${{ steps.repo.outputs.repository }}:${{ needs.create-release.outputs.version }} | |
| cache-from: type=gha | |
| cache-to: type=gha,mode=max |