Release #86
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: | |
| - v[0-9]+.[0-9]+.[0-9]+ | |
| workflow_dispatch: | |
| permissions: | |
| contents: write | |
| jobs: | |
| # --- publish-cargo job remains the same --- | |
| publish-cargo: | |
| name: Publish to Cargo | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Setup Rust | |
| uses: actions-rs/toolchain@v1 | |
| with: | |
| toolchain: stable | |
| - name: Publish to crates.io | |
| run: cargo publish --token ${{ secrets.CARGO_REGISTRY_TOKEN }} | |
| # --- publish-binaries job: Refactored Python Steps --- | |
| publish-binaries: | |
| name: Publish binaries | |
| runs-on: ${{ matrix.build.OS }} | |
| needs: publish-cargo | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| build: | |
| - { NAME: linux-x64-glibc, OS: ubuntu-22.04, TOOLCHAIN: stable, TARGET: x86_64-unknown-linux-gnu, NPM_PUBLISH: true, PYPI_PUBLISH: true } | |
| - { NAME: linux-x64-musl, OS: ubuntu-22.04, TOOLCHAIN: stable, TARGET: x86_64-unknown-linux-musl, NPM_PUBLISH: false, PYPI_PUBLISH: true } | |
| - { NAME: linux-x86-glibc, OS: ubuntu-22.04, TOOLCHAIN: stable, TARGET: i686-unknown-linux-gnu, NPM_PUBLISH: false, PYPI_PUBLISH: false } | |
| - { NAME: linux-x86-musl, OS: ubuntu-22.04, TOOLCHAIN: stable, TARGET: i686-unknown-linux-musl, NPM_PUBLISH: false, PYPI_PUBLISH: true } | |
| - { NAME: linux-arm64-glibc, OS: ubuntu-22.04, TOOLCHAIN: stable, TARGET: aarch64-unknown-linux-gnu, NPM_PUBLISH: true, PYPI_PUBLISH: true } | |
| - { NAME: linux-arm64-musl, OS: ubuntu-22.04, TOOLCHAIN: stable, TARGET: aarch64-unknown-linux-musl, NPM_PUBLISH: false, PYPI_PUBLISH: true } | |
| - { NAME: win32-x64-msvc, OS: windows-latest, TOOLCHAIN: stable, TARGET: x86_64-pc-windows-msvc, NPM_PUBLISH: true, PYPI_PUBLISH: true } | |
| - { NAME: win32-x86-msvc, OS: windows-latest, TOOLCHAIN: stable, TARGET: i686-pc-windows-msvc, NPM_PUBLISH: false, PYPI_PUBLISH: true } | |
| - { NAME: win32-arm64-msvc, OS: windows-latest, TOOLCHAIN: stable, TARGET: aarch64-pc-windows-msvc, NPM_PUBLISH: true, PYPI_PUBLISH: false } | |
| - { NAME: darwin-x64, OS: macos-15, TOOLCHAIN: stable, TARGET: x86_64-apple-darwin, NPM_PUBLISH: true, PYPI_PUBLISH: true } | |
| - { NAME: darwin-arm64, OS: macos-15, TOOLCHAIN: stable, TARGET: aarch64-apple-darwin, NPM_PUBLISH: true, PYPI_PUBLISH: true } | |
| steps: | |
| - uses: actions/checkout@v4 | |
| # Ensure pyproject.toml and Rust Cargo.toml are present and correct | |
| - name: Setup Rust | |
| uses: actions-rs/toolchain@v1 | |
| with: | |
| toolchain: ${{ matrix.build.TOOLCHAIN }} | |
| target: ${{ matrix.build.TARGET }} | |
| # Optional: Use 'cross' for more reliable cross-compilation on Linux | |
| - name: Install cross | |
| if: runner.os == 'Linux' | |
| run: cargo install cross --git https://github.com/cross-rs/cross | |
| - name: Set release version | |
| shell: bash | |
| run: echo "RELEASE_VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_ENV | |
| - name: Build Rust Binary | |
| shell: bash | |
| run: | | |
| # Build the Rust binary for the target platform using cross or cargo | |
| if [[ "${{ runner.os }}" == "Linux" ]]; then | |
| # Use cross for Linux cross-compilation if installed | |
| cross build --release --target ${{ matrix.build.TARGET }} | |
| else | |
| # Use standard cargo build for native compilation (macOS, Windows) | |
| cargo build --release --target ${{ matrix.build.TARGET }} | |
| fi | |
| # Verify the binary was built | |
| BINARY_PATH="target/${{ matrix.build.TARGET }}/release/gitforge" # Adjust if your binary name is different | |
| if [[ "${{ matrix.build.TARGET }}" == *"windows"* ]]; then | |
| BINARY_PATH="${BINARY_PATH}.exe" | |
| fi | |
| if [ ! -f "$BINARY_PATH" ]; then | |
| echo "Error: Binary not found at $BINARY_PATH" | |
| exit 1 | |
| fi | |
| echo "Built binary: $BINARY_PATH" | |
| # --- NEW: Python Wheel Build using Maturin --- | |
| - name: Setup Python for Maturin | |
| if: matrix.build.PYPI_PUBLISH == true | |
| uses: actions/setup-python@v5 # Use latest setup-python action | |
| with: | |
| python-version: '3.11' # Use a version compatible with your minimum requirement | |
| - name: Install Maturin | |
| if: matrix.build.PYPI_PUBLISH == true | |
| run: | | |
| python -m pip install --upgrade pip | |
| pip install maturin | |
| - name: Build Python Wheel with Maturin | |
| if: matrix.build.PYPI_PUBLISH == true | |
| shell: bash | |
| run: | | |
| # Run maturin build for the current target platform | |
| # It will use the pyproject.toml in the repo root | |
| # Ensure your pyproject.toml has: bindings = "bin" and module-name = "gitforge" (or correct name) | |
| maturin build --release --target ${{ matrix.build.TARGET }} | |
| # Verify the wheel was created | |
| WHEEL_DIR="target/wheels" | |
| if [ ! -d "$WHEEL_DIR" ] || [ -z "$(ls -A $WHEEL_DIR/*.whl 2>/dev/null)" ]; then | |
| echo "Error: No wheel file found in $WHEEL_DIR" | |
| ls -la $WHEEL_DIR # List directory contents for debugging | |
| exit 1 | |
| fi | |
| echo "Built wheel in $WHEEL_DIR:" | |
| ls -la $WHEEL_DIR/*.whl | |
| # --- Upload Wheels as Artifacts for later PyPI publish --- | |
| - name: Upload Python Wheels as Artifacts | |
| if: matrix.build.PYPI_PUBLISH == true | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: python-wheels-${{ matrix.build.NAME }} # Unique name per platform | |
| path: target/wheels/*.whl | |
| if-no-files-found: error # Fail if no wheels are found | |
| # --- OLD NPM Steps (Assuming they work correctly) --- | |
| # Prepare npm package structure (this part can stay if needed for npm) | |
| - name: Prepare NPM Package Structure (if needed) | |
| if: matrix.build.NPM_PUBLISH == true | |
| shell: bash | |
| run: | | |
| # This step prepares files specifically for the npm package, separate from PyPI wheels | |
| VERSION=$(echo "${{ github.ref_name }}" | sed 's/^v//') | |
| mkdir -p npm/bin | |
| # Copy the built binary to npm/bin (adjust path if different) | |
| BINARY_NAME="gitforge" | |
| if [[ "${{ matrix.build.TARGET }}" == *"windows"* ]]; then | |
| BINARY_NAME="${BINARY_NAME}.exe" | |
| fi | |
| cp target/${{ matrix.build.TARGET }}/release/$BINARY_NAME npm/bin/ | |
| # Create npm package.json (adjust as needed for your npm package structure) | |
| cat > npm/package.json << EOF | |
| { | |
| "name": "@rafaeljohn9/gitforge-${{ matrix.build.NAME }}", | |
| "version": "$VERSION", | |
| "bin": { | |
| "gitforge": "./bin/$BINARY_NAME" | |
| }, | |
| "files": ["bin/"], | |
| "license": "APACHE-2.0" | |
| } | |
| EOF | |
| echo "Prepared NPM package structure in npm/ directory." | |
| - name: Setup Node.js for NPM | |
| if: matrix.build.NPM_PUBLISH == true | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '18' | |
| registry-url: 'https://registry.npmjs.org' | |
| - name: Publish platform-specific NPM package | |
| if: matrix.build.NPM_PUBLISH == true | |
| shell: bash | |
| run: | | |
| cd npm # Enter the npm directory prepared earlier | |
| npm publish | |
| env: | |
| NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} | |
| # --- OLD GitHub Release Upload (for raw binaries) --- | |
| - name: Prepare Binary for GitHub Release | |
| shell: bash | |
| run: | | |
| # Copy the built binary with a platform-specific name for GitHub Release | |
| BINARY_NAME="gitforge-${{ matrix.build.NAME }}" | |
| if [[ "${{ matrix.build.TARGET }}" == *"windows"* ]]; then | |
| BINARY_NAME="${BINARY_NAME}.exe" | |
| cp target/${{ matrix.build.TARGET }}/release/gitforge.exe $BINARY_NAME | |
| else | |
| cp target/${{ matrix.build.TARGET }}/release/gitforge $BINARY_NAME | |
| chmod +x $BINARY_NAME | |
| fi | |
| echo "BINARY=$BINARY_NAME" >> $GITHUB_ENV | |
| - name: Upload to GitHub Release | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| files: ${{ env.BINARY }} | |
| overwrite: true | |
| fail_on_unmatched_files: false | |
| # --- NEW: Single Job to Publish ALL Wheels to PyPI --- | |
| publish-python-wheels-to-pypi: | |
| name: Publish Python Wheels to PyPI | |
| needs: publish-binaries # Wait for all publish-binaries jobs to finish and upload artifacts | |
| runs-on: ubuntu-latest | |
| environment: pypi # Use a GitHub environment for PyPI secrets | |
| permissions: | |
| id-token: write # IMPORTANT: mandatory for trusted publishing | |
| steps: | |
| - name: Download all Python wheel artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| pattern: python-wheels-* # Download all artifacts matching the pattern | |
| path: dist # Download to a 'dist' directory | |
| merge-multiple: true # Combine artifacts from different matrix runs into one 'dist' folder | |
| - name: Publish all wheels to PyPI | |
| # Uses the PYPI_API_TOKEN secret configured in the GitHub environment | |
| # Recommended: Use trusted publishing (OIDC) for security | |
| uses: pypa/gh-action-pypi-publish@release/v1 | |
| # Ensure you have configured trusted publishing on PyPI for your project | |
| # --- publish-npm-unified job remains the same --- | |
| publish-npm-unified: | |
| name: Publish unified NPM package | |
| runs-on: ubuntu-latest | |
| needs: publish-binaries | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '18' | |
| registry-url: 'https://registry.npmjs.org' | |
| - name: Set release version | |
| run: echo "RELEASE_VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_ENV | |
| - name: Create unified package | |
| run: | | |
| mkdir -p gitforge-unified/bin | |
| # Create package.json | |
| cat > gitforge-unified/package.json << EOF | |
| { | |
| "name": "gitforge", | |
| "version": "${{ env.RELEASE_VERSION }}", | |
| "description": "GitHub Templates CLI tool (platform-aware wrapper)", | |
| "bin": { | |
| "gitforge": "./bin/gitforge" | |
| }, | |
| "license": "Apache-2.0", | |
| "repository": { | |
| "type": "git", | |
| "url": "git+https://github.com/${{ github.repository }}.git" | |
| }, | |
| "optionalDependencies": { | |
| "gitforge-linux-x64": "${{ env.RELEASE_VERSION }}", | |
| "gitforge-linux-arm64": "${{ env.RELEASE_VERSION }}", | |
| "gitforge-darwin-x64": "${{ env.RELEASE_VERSION }}", | |
| "gitforge-darwin-arm64": "${{ env.RELEASE_VERSION }}", | |
| "gitforge-windows-x64": "${{ env.RELEASE_VERSION }}", | |
| "gitforge-windows-arm64": "${{ env.RELEASE_VERSION }}" | |
| }, | |
| "files": ["bin/"], | |
| "engines": { | |
| "node": ">=14" | |
| } | |
| } | |
| EOF | |
| # Create the smart wrapper | |
| cat > gitforge-unified/bin/gitforge << 'EOF' | |
| #!/usr/bin/env node | |
| const { execFileSync } = require('child_process'); | |
| const path = require('path'); | |
| const os = require('os'); | |
| const platform = os.platform(); | |
| const arch = os.arch(); | |
| let packageName; | |
| if (platform === 'win32') { | |
| if (arch === 'arm64') { | |
| packageName = 'gitforge-windows-arm64'; | |
| } else { | |
| packageName = 'gitforge-windows-x64'; | |
| } | |
| } else if (platform === 'darwin') { | |
| packageName = arch === 'arm64' ? 'gitforge-darwin-arm64' : 'gitforge-darwin-x64'; | |
| } else if (platform === 'linux') { | |
| packageName = arch === 'arm64' ? 'gitforge-linux-arm64' : 'gitforge-linux-x64'; | |
| } else { | |
| console.error(`Unsupported platform: ${platform}-${arch}`); | |
| process.exit(1); | |
| } | |
| try { | |
| const pkgPath = require.resolve(`${packageName}/package.json`); | |
| const binPath = path.join(path.dirname(pkgPath), 'bin', platform === 'win32' ? 'gitforge.exe' : 'gitforge'); | |
| execFileSync(binPath, process.argv.slice(2), { stdio: 'inherit' }); | |
| } catch (err) { | |
| console.error(`Platform-specific package not found: ${packageName}`); | |
| console.error(`Install it directly:`); | |
| console.error(` npm install -g ${packageName}`); | |
| console.error(` OR`); | |
| console.error(` npm install -g gitforge-${platform}-${arch}`); | |
| process.exit(1); | |
| } | |
| EOF | |
| chmod +x gitforge-unified/bin/gitforge | |
| cd gitforge-unified | |
| npm publish | |
| env: | |
| NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} | |
| # --- publish-homebrew job remains the same --- | |
| publish-homebrew: | |
| name: Update Homebrew formula | |
| runs-on: macos-latest | |
| needs: publish-binaries | |
| steps: | |
| - name: Set release version | |
| run: echo "RELEASE_VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_ENV | |
| - name: Update Homebrew formula | |
| run: | | |
| VERSION=$(echo "${{ github.ref_name }}" | sed 's/^v//') | |
| # Get release assets | |
| LINUX_URL="https://github.com/${{ github.repository }}/releases/download/${{ github.ref_name }}/gitforge-linux-x64-glibc" | |
| DARWIN_ARM_URL="https://github.com/${{ github.repository }}/releases/download/${{ github.ref_name }}/gitforge-darwin-arm64" | |
| DARWIN_X64_URL="https://github.com/${{ github.repository }}/releases/download/${{ github.ref_name }}/gitforge-darwin-x64" | |
| # Calculate checksums | |
| wget -q $LINUX_URL -O linux-binary | |
| wget -q $DARWIN_ARM_URL -O darwin-arm-binary | |
| wget -q $DARWIN_X64_URL -O darwin-x64-binary | |
| LINUX_SHA=$(shasum -a 256 linux-binary | cut -d' ' -f1) | |
| DARWIN_ARM_SHA=$(shasum -a 256 darwin-arm-binary | cut -d' ' -f1) | |
| DARWIN_X64_SHA=$(shasum -a 256 darwin-x64-binary | cut -d' ' -f1) | |
| # Clone homebrew tap | |
| git clone https://github.com/rafaeljohn9/homebrew-tap.git | |
| cd homebrew-tap | |
| # Create/update formula | |
| cat > Formula/gitforge.rb << EOF | |
| class GhTemplates < Formula | |
| desc "GitHub Templates CLI tool" | |
| homepage "https://github.com/${{ github.repository }}" | |
| version "$VERSION" | |
| on_macos do | |
| if Hardware::CPU.arm? | |
| url "$DARWIN_ARM_URL" | |
| sha256 "$DARWIN_ARM_SHA" | |
| else | |
| url "$DARWIN_X64_URL" | |
| sha256 "$DARWIN_X64_SHA" | |
| end | |
| end | |
| on_linux do | |
| url "$LINUX_URL" | |
| sha256 "$LINUX_SHA" | |
| end | |
| def install | |
| bin.install "gitforge" | |
| end | |
| test do | |
| system "#{bin}/gitforge", "--version" | |
| end | |
| end | |
| EOF | |
| # Commit and push | |
| git config user.name "github-actions[bot]" | |
| git config user.email "github-actions[bot]@users.noreply.github.com" | |
| git add Formula/gitforge.rb | |
| git commit -m "Update gitforge to $VERSION" | |
| git push https://x-access-token:${{ secrets.HOMEBREW_GITHUB_TOKEN }}@github.com/rafaeljohn9/homebrew-tap.git |