name: Create GitHub Pre-Release on: workflow_dispatch: inputs: version_type: description: "Version bump type" required: true default: "patch" type: choice options: - patch - minor - major - prerelease concurrency: group: release-${{ github.ref_name }} # allow concurrent prerelease from different branches cancel-in-progress: true permissions: contents: write packages: write pull-requests: write env: NODE_VERSION: 22 PNPM_VERSION: 10.8.1 RUST_TOOLCHAIN: nightly-2025-05-18 jobs: bump-version: runs-on: ubuntu-22.04 outputs: new_tag: ${{ steps.version.outputs.new_tag }} new_version: ${{ steps.version.outputs.new_version }} branch_suffix: ${{ steps.branch.outputs.suffix }} steps: - uses: actions/checkout@v4 with: token: ${{ secrets.GITHUB_TOKEN }} ssh-key: ${{ secrets.DEPLOY_KEY }} - name: Setup Node uses: ./.github/actions/setup-node - name: Cache cargo-edit uses: actions/cache@v3 id: cache-cargo-edit with: path: ~/.cargo/bin/cargo-set-version key: cargo-edit-${{ runner.os }}-${{ env.RUST_TOOLCHAIN }} - name: Install cargo-edit if: steps.cache-cargo-edit.outputs.cache-hit != 'true' run: cargo install cargo-edit - name: Generate branch suffix id: branch run: | branch_name="${{ github.ref_name }}" # Get last 6 characters of branch name, remove all special chars (including dashes) suffix=$(echo "$branch_name" | tail -c 7 | sed 's/[^a-zA-Z0-9]//g' | tr '[:upper:]' '[:lower:]') echo "Branch: $branch_name" echo "Suffix: $suffix" echo "suffix=$suffix" >> $GITHUB_OUTPUT - name: Determine and update versions id: version run: | # Get the latest version from npm registry latest_npm_version=$(npm view vibe-kanban version 2>/dev/null || echo "0.0.0") echo "Latest npm version: $latest_npm_version" timestamp=$(date +%Y%m%d%H%M%S) # Update root package.json based on npm version, not current package.json if [[ "${{ github.event.inputs.version_type }}" == "prerelease" ]]; then # For prerelease, use current package.json version and add branch suffix npm version prerelease --preid="${{ steps.branch.outputs.suffix }}" --no-git-tag-version new_version=$(node -p "require('./package.json').version") new_tag="v${new_version}.${timestamp}" else # For regular releases, use npm version and bump it npm version $latest_npm_version --no-git-tag-version --allow-same-version npm version ${{ github.event.inputs.version_type }} --no-git-tag-version new_version=$(node -p "require('./package.json').version") new_tag="v${new_version}-${timestamp}" fi # Update npx-cli package.json to match cd npx-cli npm version $new_version --no-git-tag-version --allow-same-version cd .. cargo set-version --workspace "$new_version" echo "New version: $new_version" echo "new_version=$new_version" >> $GITHUB_OUTPUT echo "new_tag=$new_tag" >> $GITHUB_OUTPUT - name: Commit changes and create tag run: | git config --local user.email "action@github.com" git config --local user.name "GitHub Action" git add package.json pnpm-lock.yaml npx-cli/package.json git add $(find . -name Cargo.toml) git commit -m "chore: bump version to ${{ steps.version.outputs.new_version }}" git tag -a ${{ steps.version.outputs.new_tag }} -m "Release ${{ steps.version.outputs.new_tag }}" git push git push --tags build-frontend: needs: bump-version runs-on: ubuntu-22.04 env: VITE_PUBLIC_REACT_VIRTUOSO_LICENSE_KEY: ${{ secrets.PUBLIC_REACT_VIRTUOSO_LICENSE_KEY }} steps: - uses: actions/checkout@v4 with: ref: ${{ needs.bump-version.outputs.new_tag }} - name: Setup Node uses: ./.github/actions/setup-node - name: Install dependencies run: pnpm install - name: Lint frontend run: cd frontend && npm run lint - name: Type check frontend run: cd frontend && npx tsc --noEmit - name: Build frontend run: cd frontend && npm run build env: SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} - name: Create Sentry release uses: getsentry/action-release@v3 env: SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} SENTRY_ORG: ${{ secrets.SENTRY_ORG }} SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }} with: release: ${{ needs.bump-version.outputs.new_version }} environment: production sourcemaps: "./frontend/dist" ignore_missing: true - name: Upload frontend artifact uses: actions/upload-artifact@v4 with: name: frontend-dist path: frontend/dist/ retention-days: 1 build-backend: needs: [bump-version, build-frontend] runs-on: ${{ matrix.os }} strategy: # Platform matrix - keep target/name in sync with package-npx-cli job matrix: include: - target: x86_64-unknown-linux-gnu os: ubuntu-22.04 name: linux-x64 - target: x86_64-pc-windows-msvc os: windows-latest-l name: windows-x64 - target: x86_64-apple-darwin os: macos-13 name: macos-x64 - target: aarch64-apple-darwin os: macos-14 name: macos-arm64 - target: aarch64-pc-windows-msvc os: windows-latest-l name: windows-arm64 - target: aarch64-unknown-linux-gnu os: ubuntu-22.04 name: linux-arm64 steps: - uses: actions/checkout@v4 with: ref: ${{ needs.bump-version.outputs.new_tag }} - name: Install Rust toolchain uses: dtolnay/rust-toolchain@stable with: toolchain: ${{ env.RUST_TOOLCHAIN }} targets: ${{ matrix.target }} components: rustfmt, clippy - name: Cache Rust dependencies uses: Swatinem/rust-cache@v2 with: workspaces: "." prefix-key: "cache-v1.0" key: ${{ matrix.target }}_${{ matrix.os }} cache-on-failure: true shared-key: "shared" cache-all-crates: true - name: Download frontend artifact uses: actions/download-artifact@v4 with: name: frontend-dist path: frontend/dist/ - name: Install system dependencies (Linux) if: matrix.os == 'ubuntu-22.04' run: | sudo apt-get update sudo apt-get install -y pkg-config libssl-dev - name: Install ARM64 cross-compilation dependencies (Linux) if: matrix.os == 'ubuntu-22.04' && matrix.target == 'aarch64-unknown-linux-gnu' run: | sudo apt-get install -y gcc-aarch64-linux-gnu g++-aarch64-linux-gnu libc6-dev-arm64-cross - name: Build backend for target run: | cargo build --release --target ${{ matrix.target }} -p server cargo build --release --target ${{ matrix.target }} --bin mcp_task_server env: CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: ${{ matrix.target == 'aarch64-unknown-linux-gnu' && 'aarch64-linux-gnu-gcc' || '' }} POSTHOG_API_KEY: ${{ secrets.POSTHOG_API_KEY }} POSTHOG_API_ENDPOINT: ${{ secrets.POSTHOG_API_ENDPOINT }} - name: Setup Sentry CLI uses: matbour/setup-sentry-cli@v2 with: token: ${{ secrets.SENTRY_AUTH_TOKEN }} organization: ${{ secrets.SENTRY_ORG }} project: ${{ secrets.SENTRY_PROJECT }} version: 2.21.2 - name: Upload source maps to Sentry run: sentry-cli debug-files upload --include-sources target/${{ matrix.target }}/release - name: Prepare binaries (non-macOS) if: runner.os != 'macOS' shell: bash run: | mkdir -p dist if [[ "${{ matrix.os }}" == "windows-latest-l" ]]; then cp target/${{ matrix.target }}/release/server.exe dist/vibe-kanban-${{ matrix.name }}.exe cp target/${{ matrix.target }}/release/mcp_task_server.exe dist/vibe-kanban-mcp-${{ matrix.name }}.exe else cp target/${{ matrix.target }}/release/server dist/vibe-kanban-${{ matrix.name }} cp target/${{ matrix.target }}/release/mcp_task_server dist/vibe-kanban-mcp-${{ matrix.name }} fi # Code signing for macOS only - name: Prepare Apple certificate (macOS) if: runner.os == 'macOS' run: | echo "${{ secrets.APPLE_CERTIFICATE_P12_BASE64 }}" | base64 --decode > certificate.p12 - name: Write API Key to file if: runner.os == 'macOS' env: API_KEY: ${{ secrets.APP_STORE_API_KEY }} run: echo $API_KEY > app_store_key.json - name: Sign main binary (macOS) if: runner.os == 'macOS' uses: indygreg/apple-code-sign-action@v1 with: input_path: target/${{ matrix.target }}/release/server output_path: vibe-kanban p12_file: certificate.p12 p12_password: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} sign: true sign_args: "--code-signature-flags=runtime" - name: Package main binary (macOS) if: runner.os == 'macOS' run: zip vibe-kanban.zip vibe-kanban - name: Notarize signed binary (macOS) if: runner.os == 'macOS' uses: indygreg/apple-code-sign-action@v1 continue-on-error: true with: input_path: vibe-kanban.zip sign: false notarize: true app_store_connect_api_key_json_file: app_store_key.json - name: Sign MCP binary (macOS) if: runner.os == 'macOS' uses: indygreg/apple-code-sign-action@v1 with: input_path: target/${{ matrix.target }}/release/mcp_task_server output_path: vibe-kanban-mcp p12_file: certificate.p12 p12_password: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} sign: true sign_args: "--code-signature-flags=runtime" - name: Package MCP binary (macOS) if: runner.os == 'macOS' run: zip vibe-kanban-mcp.zip vibe-kanban-mcp - name: Notarize signed MCP binary (macOS) if: runner.os == 'macOS' uses: indygreg/apple-code-sign-action@v1 continue-on-error: true with: input_path: vibe-kanban-mcp.zip sign: false notarize: true app_store_connect_api_key_json_file: app_store_key.json - name: Prepare signed binaries (macOS) if: runner.os == 'macOS' run: | mkdir -p dist cp vibe-kanban.zip dist/vibe-kanban-${{ matrix.name }}.zip cp vibe-kanban-mcp.zip dist/vibe-kanban-mcp-${{ matrix.name }}.zip - name: Clean up certificates (macOS) if: runner.os == 'macOS' run: | rm -f certificate.p12 rm -rf private_keys/ - name: Upload binary artifact uses: actions/upload-artifact@v4 with: name: backend-binary-${{ matrix.name }} path: dist/ retention-days: 1 package-npx-cli: needs: [bump-version, build-frontend, build-backend] runs-on: ubuntu-22.04 strategy: # NOTE: This matrix must be kept in sync with build-backend job above # GitHub Actions doesn't support YAML anchors, so duplication is unavoidable matrix: include: - target: x86_64-unknown-linux-gnu name: linux-x64 binary: vibe-kanban mcp_binary: vibe-kanban-mcp - target: x86_64-pc-windows-msvc name: windows-x64 binary: vibe-kanban.exe mcp_binary: vibe-kanban-mcp.exe - target: x86_64-apple-darwin name: macos-x64 binary: vibe-kanban mcp_binary: vibe-kanban-mcp - target: aarch64-apple-darwin name: macos-arm64 binary: vibe-kanban mcp_binary: vibe-kanban-mcp - target: aarch64-pc-windows-msvc name: windows-arm64 binary: vibe-kanban.exe mcp_binary: vibe-kanban-mcp.exe - target: aarch64-unknown-linux-gnu name: linux-arm64 binary: vibe-kanban mcp_binary: vibe-kanban-mcp steps: - uses: actions/checkout@v4 with: ref: ${{ needs.bump-version.outputs.new_tag }} - name: Download frontend artifact uses: actions/download-artifact@v4 with: name: frontend-dist path: frontend/dist/ - name: Download backend binary artifact uses: actions/download-artifact@v4 with: name: backend-binary-${{ matrix.name }} path: dist/ - name: List downloaded artifacts run: | echo "Downloaded backend binaries:" find dist/ - name: Create platform package if: matrix.name != 'macos-arm64' && matrix.name != 'macos-x64' run: | mkdir -p npx-cli/dist/${{ matrix.name }} mkdir vibe-kanban-${{ matrix.name }} mkdir vibe-kanban-mcp-${{ matrix.name }} cp dist/vibe-kanban-${{ matrix.name }}* vibe-kanban-${{ matrix.name }}/${{ matrix.binary }} cp dist/vibe-kanban-mcp-${{ matrix.name }}* vibe-kanban-mcp-${{ matrix.name }}/${{ matrix.mcp_binary }} zip -j npx-cli/dist/${{ matrix.name }}/vibe-kanban.zip vibe-kanban-${{ matrix.name }}/${{ matrix.binary }} zip -j npx-cli/dist/${{ matrix.name }}/vibe-kanban-mcp.zip vibe-kanban-mcp-${{ matrix.name }}/${{ matrix.mcp_binary }} - name: Create platform package (macOS) if: matrix.name == 'macos-arm64' || matrix.name == 'macos-x64' run: | mkdir -p npx-cli/dist/${{ matrix.name }} mkdir vibe-kanban-${{ matrix.name }} cp dist/vibe-kanban-${{ matrix.name }}* npx-cli/dist/${{ matrix.name }}/vibe-kanban.zip cp dist/vibe-kanban-mcp-${{ matrix.name }}* npx-cli/dist/${{ matrix.name }}/vibe-kanban-mcp.zip - name: Upload platform package artifact uses: actions/upload-artifact@v4 with: name: npx-platform-${{ matrix.name }} path: npx-cli/dist/ retention-days: 1 create-prerelease: needs: [bump-version, build-frontend, build-backend, package-npx-cli] runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 with: ref: ${{ needs.bump-version.outputs.new_tag }} - name: Download frontend artifact uses: actions/download-artifact@v4 with: name: frontend-dist path: frontend/dist/ - name: Download backend npx-cli zips uses: actions/download-artifact@v4 with: pattern: npx-platform-* path: npx-cli/dist/ merge-multiple: true - name: List downloaded artifacts run: | echo "Backend dist:" find npx-cli/dist echo "Frontend dist:" find frontend/dist - name: Zip frontend run: | mkdir vibe-kanban-${{ needs.bump-version.outputs.new_tag }} mv frontend/dist vibe-kanban-${{ needs.bump-version.outputs.new_tag }} zip -r vibe-kanban-${{ needs.bump-version.outputs.new_tag }}.zip vibe-kanban-${{ needs.bump-version.outputs.new_tag }} - name: Setup Node for npm pack uses: ./.github/actions/setup-node - name: Pack run: | cd npx-cli npm pack - name: Create GitHub Pre-Release uses: softprops/action-gh-release@v2 with: tag_name: ${{ needs.bump-version.outputs.new_tag }} name: Pre-release ${{ needs.bump-version.outputs.new_tag }} prerelease: true generate_release_notes: true files: | vibe-kanban-${{ needs.bump-version.outputs.new_tag }}.zip npx-cli/vibe-kanban-*.tgz