From 69ac8a4efacbb78fe1ba073c7a0e8eb2e3512144 Mon Sep 17 00:00:00 2001 From: Jacob Cable Date: Thu, 10 Jul 2025 10:13:29 +0100 Subject: [PATCH 01/10] feat: automatically update latest tag for cli releases --- .github/workflows/build-cli-binaries.yml | 152 ++++++++++++++++++++++- bin/install_cli | 3 +- 2 files changed, 153 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-cli-binaries.yml b/.github/workflows/build-cli-binaries.yml index 828c6fd718..0366162cce 100644 --- a/.github/workflows/build-cli-binaries.yml +++ b/.github/workflows/build-cli-binaries.yml @@ -18,6 +18,16 @@ name: Build CLI Binaries on: workflow_dispatch: + inputs: + version: + description: 'Version tag to build (e.g., v1.0.0)' + required: true + type: string + upload_to_release: + description: 'Upload binaries to GitHub release and update latest tag' + required: false + type: boolean + default: false jobs: build: @@ -46,6 +56,15 @@ jobs: - name: Checkout code uses: actions/checkout@v4 + - name: Validate version format + run: | + VERSION="${{ inputs.version }}" + if [[ ! "$VERSION" =~ ^v[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.-]+)?(\+[a-zA-Z0-9.-]+)?$ ]]; then + echo "Error: Version '$VERSION' does not follow semantic versioning format (e.g., v1.0.0, v1.0.0-beta.1)" + exit 1 + fi + echo "✓ Version format is valid: $VERSION" + - name: Setup Bun uses: oven-sh/setup-bun@v2 with: @@ -253,4 +272,135 @@ jobs: } # Clean up any remaining genkit processes - Get-Process | Where-Object { $_.ProcessName -match "genkit" } | Stop-Process -Force -ErrorAction SilentlyContinue \ No newline at end of file + Get-Process | Where-Object { $_.ProcessName -match "genkit" } | Stop-Process -Force -ErrorAction SilentlyContinue + + create-release: + needs: [build, test] + runs-on: ubuntu-latest + if: inputs.upload_to_release == 'true' + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Generate changelog + id: changelog + run: | + # Get the previous release tag + PREVIOUS_TAG=$(git describe --tags --abbrev=0 HEAD~1 2>/dev/null || echo "") + + if [[ -n "$PREVIOUS_TAG" ]]; then + # Generate changelog from previous tag to current + CHANGELOG=$(git log --pretty=format:"- %s" $PREVIOUS_TAG..HEAD | head -20) + echo "changelog<> $GITHUB_OUTPUT + echo "$CHANGELOG" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + else + # First release + echo "changelog<> $GITHUB_OUTPUT + echo "- Initial release" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + fi + + - name: Create Release + id: create_release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ inputs.version }} + release_name: Genkit CLI ${{ inputs.version }} + body: | + # Genkit CLI ${{ inputs.version }} + + ## Downloads + + - [Linux x64](https://github.com/firebase/genkit/releases/download/${{ inputs.version }}/genkit-linux-x64) + - [Linux ARM64](https://github.com/firebase/genkit/releases/download/${{ inputs.version }}/genkit-linux-arm64) + - [macOS x64](https://github.com/firebase/genkit/releases/download/${{ inputs.version }}/genkit-darwin-x64) + - [macOS ARM64](https://github.com/firebase/genkit/releases/download/${{ inputs.version }}/genkit-darwin-arm64) + - [Windows x64](https://github.com/firebase/genkit/releases/download/${{ inputs.version }}/genkit-win32-x64.exe) + + ## Changes + + ${{ steps.changelog.outputs.changelog }} + + ## Installation + + ```bash + TODO: Add installation instructions + ``` + draft: false + prerelease: false + + upload-assets: + needs: [build, test, create-release] + runs-on: ubuntu-latest + if: inputs.upload_to_release == 'true' + strategy: + matrix: + include: + - target: linux-x64 + - target: linux-arm64 + - target: darwin-x64 + - target: darwin-arm64 + - target: win32-x64 + + steps: + - name: Set binary extension + id: binary + shell: bash + run: | + if [[ "${{ matrix.target }}" == win32-* ]]; then + echo "ext=.exe" >> $GITHUB_OUTPUT + else + echo "ext=" >> $GITHUB_OUTPUT + fi + + - name: Download binary artifact + uses: actions/download-artifact@v4 + with: + name: genkit-${{ matrix.target }} + path: ./ + + - name: Upload to GitHub Release + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ needs.create-release.outputs.upload_url }} + asset_path: ./genkit-${{ matrix.target }}${{ steps.binary.outputs.ext }} + asset_name: genkit-${{ matrix.target }} + asset_content_type: application/octet-stream + + update-latest-tag: + needs: [create-release, upload-assets] + runs-on: ubuntu-latest + if: inputs.upload_to_release == 'true' + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Update latest tag + run: | + # Check if latest tag already points to the correct version + CURRENT_LATEST=$(git rev-parse latest 2>/dev/null || echo "") + TARGET_COMMIT=$(git rev-parse ${{ inputs.version }} 2>/dev/null || echo "") + + if [[ "$CURRENT_LATEST" == "$TARGET_COMMIT" ]]; then + echo "Latest tag already points to ${{ inputs.version }}" + exit 0 + fi + + # Delete the existing "latest" tag if it exists + git tag -d latest 2>/dev/null || true + git push origin :refs/tags/latest 2>/dev/null || true + + # Create new "latest" tag pointing to the release tag + git tag latest ${{ inputs.version }} + git push origin latest + + echo "Updated 'latest' tag to point to ${{ inputs.version }}" \ No newline at end of file diff --git a/bin/install_cli b/bin/install_cli index 7c01e3f132..c454e4444d 100644 --- a/bin/install_cli +++ b/bin/install_cli @@ -250,7 +250,8 @@ if [[ -z "$MACHINE" ]]; then fi # We have enough information to generate the binary's download URL. -DOWNLOAD_URL="https://$DOMAIN/bin/$MACHINE/latest" +# Use GitHub releases with "latest" tag that gets updated by the workflow +DOWNLOAD_URL="https://github.com/firebase/genkit/releases/download/latest/genkit-$MACHINE" echo "-- Downloading binary from $DOWNLOAD_URL" # We use "curl" to download the binary with a flag set to follow redirects From ee2a789c1f823e5c8de42dc8f64f614f3fe231df Mon Sep 17 00:00:00 2001 From: Jacob Cable Date: Thu, 10 Jul 2025 10:43:32 +0100 Subject: [PATCH 02/10] feat: CI for binaries allowing signing via kokoro --- .github/workflows/build-cli-binaries.yml | 230 +++++++++++++++++++++-- 1 file changed, 215 insertions(+), 15 deletions(-) diff --git a/.github/workflows/build-cli-binaries.yml b/.github/workflows/build-cli-binaries.yml index 0366162cce..dabb697853 100644 --- a/.github/workflows/build-cli-binaries.yml +++ b/.github/workflows/build-cli-binaries.yml @@ -20,11 +20,16 @@ on: workflow_dispatch: inputs: version: - description: 'Version tag to build (e.g., v1.0.0)' + description: 'Version tag to build (e.g., v1.0.0, v1.0.0-rc.1)' required: true type: string - upload_to_release: - description: 'Upload binaries to GitHub release and update latest tag' + create_rc: + description: 'Create release candidate with unsigned binaries' + required: false + type: boolean + default: false + promote_rc: + description: 'Promote RC to final release (requires signed binaries)' required: false type: boolean default: false @@ -60,7 +65,7 @@ jobs: run: | VERSION="${{ inputs.version }}" if [[ ! "$VERSION" =~ ^v[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.-]+)?(\+[a-zA-Z0-9.-]+)?$ ]]; then - echo "Error: Version '$VERSION' does not follow semantic versioning format (e.g., v1.0.0, v1.0.0-beta.1)" + echo "Error: Version '$VERSION' does not follow semantic versioning format (e.g., v1.0.0, v1.0.0-rc.1)" exit 1 fi echo "✓ Version format is valid: $VERSION" @@ -120,7 +125,7 @@ jobs: with: name: genkit-${{ matrix.target }} path: genkit-tools/cli/dist/bin/genkit-${{ matrix.target }}${{ steps.binary.outputs.ext }} - retention-days: 1 # TODO: Consider increasing to 7 days for better debugging capability + retention-days: 7 # Increased for better debugging and signing workflow test: needs: build @@ -274,15 +279,178 @@ jobs: # Clean up any remaining genkit processes Get-Process | Where-Object { $_.ProcessName -match "genkit" } | Stop-Process -Force -ErrorAction SilentlyContinue - create-release: + create-rc: + needs: [build, test] + runs-on: ubuntu-latest + if: inputs.create_rc == 'true' + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Generate changelog + id: changelog + run: | + # Get the previous release tag + PREVIOUS_TAG=$(git describe --tags --abbrev=0 HEAD~1 2>/dev/null || echo "") + + if [[ -n "$PREVIOUS_TAG" ]]; then + # Generate changelog from previous tag to current + CHANGELOG=$(git log --pretty=format:"- %s" $PREVIOUS_TAG..HEAD | head -20) + echo "changelog<> $GITHUB_OUTPUT + echo "$CHANGELOG" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + else + # First release + echo "changelog<> $GITHUB_OUTPUT + echo "- Initial release" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + fi + + - name: Create Release Candidate + id: create_rc + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ inputs.version }} + release_name: Genkit CLI ${{ inputs.version }} (Release Candidate) + body: | + # Genkit CLI ${{ inputs.version }} - Release Candidate + + ⚠️ **This is a release candidate with unsigned binaries for testing purposes.** + + ## Downloads (Unsigned - For Testing Only) + + - [Linux x64](https://github.com/firebase/genkit/releases/download/${{ inputs.version }}/genkit-linux-x64) + - [Linux ARM64](https://github.com/firebase/genkit/releases/download/${{ inputs.version }}/genkit-linux-arm64) + - [macOS x64](https://github.com/firebase/genkit/releases/download/${{ inputs.version }}/genkit-darwin-x64) + - [macOS ARM64](https://github.com/firebase/genkit/releases/download/${{ inputs.version }}/genkit-darwin-arm64) + - [Windows x64](https://github.com/firebase/genkit/releases/download/${{ inputs.version }}/genkit-win32-x64.exe) + + ## Changes + + ${{ steps.changelog.outputs.changelog }} + + ## Next Steps + + After testing, these binaries will be signed and promoted to the final release. + + ## Installation (Testing Only) + + ```bash + # Download and test the RC binary + curl -Lo genkit https://github.com/firebase/genkit/releases/download/${{ inputs.version }}/genkit-$(uname -s | tr '[:upper:]' '[:lower:]')-$(uname -m | sed 's/x86_64/x64/;s/aarch64/arm64/') + chmod +x genkit + ./genkit --version + ``` + draft: false + prerelease: true + + upload-rc-assets: + needs: [build, test, create-rc] + runs-on: ubuntu-latest + if: inputs.create_rc == 'true' + strategy: + matrix: + include: + - target: linux-x64 + - target: linux-arm64 + - target: darwin-x64 + - target: darwin-arm64 + - target: win32-x64 + + steps: + - name: Set binary extension + id: binary + shell: bash + run: | + if [[ "${{ matrix.target }}" == win32-* ]]; then + echo "ext=.exe" >> $GITHUB_OUTPUT + else + echo "ext=" >> $GITHUB_OUTPUT + fi + + - name: Download binary artifact + uses: actions/download-artifact@v4 + with: + name: genkit-${{ matrix.target }} + path: ./ + + - name: Upload to GitHub Release Candidate + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ needs.create-rc.outputs.upload_url }} + asset_path: ./genkit-${{ matrix.target }}${{ steps.binary.outputs.ext }} + asset_name: genkit-${{ matrix.target }} + asset_content_type: application/octet-stream + + promote-to-release: needs: [build, test] runs-on: ubuntu-latest - if: inputs.upload_to_release == 'true' + if: inputs.promote_rc == 'true' steps: - name: Checkout code uses: actions/checkout@v4 + # Note: After RC creation, signed binaries should be uploaded via Kokoro + # before running promote_rc. The promotion assumes signed binaries are + # available for the final release. + - name: Validate signed binaries exist + run: | + RC_VERSION="${{ inputs.version }}" + FINAL_VERSION="${RC_VERSION%-rc*}" + + echo "Validating signed binaries exist for RC: $RC_VERSION" + echo "Will promote to final version: $FINAL_VERSION" + + # Expected platforms that should have signed binaries + EXPECTED_PLATFORMS=("linux-x64" "linux-arm64" "darwin-x64" "darwin-arm64" "win32-x64") + + # Check if RC release exists and get its assets + RELEASE_INFO=$(curl -s -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \ + "https://api.github.com/repos/firebase/genkit/releases/tags/$RC_VERSION") + + if [[ $(echo "$RELEASE_INFO" | jq -r '.id') == "null" ]]; then + echo "❌ Error: RC release $RC_VERSION not found" + exit 1 + fi + + echo "✓ RC release $RC_VERSION found" + + # Get list of assets from the RC release + ASSETS=$(echo "$RELEASE_INFO" | jq -r '.assets[].name' | sort) + + # Check for expected signed binaries + MISSING_BINARIES=() + for platform in "${EXPECTED_PLATFORMS[@]}"; do + if [[ "$platform" == win32-* ]]; then + expected_file="genkit-$platform.exe" + else + expected_file="genkit-$platform" + fi + + if ! echo "$ASSETS" | grep -q "^$expected_file$"; then + MISSING_BINARIES+=("$expected_file") + else + echo "✓ Found signed binary: $expected_file" + fi + done + + if [[ ${#MISSING_BINARIES[@]} -gt 0 ]]; then + echo "❌ Missing signed binaries:" + printf ' - %s\n' "${MISSING_BINARIES[@]}" + echo "" + echo "Please ensure all binaries are signed before promoting to final release." + echo "You can trigger the signing workflow or manually upload signed binaries." + exit 1 + fi + + echo "✅ All expected signed binaries found in RC release" + - name: Generate changelog id: changelog run: | @@ -302,7 +470,7 @@ jobs: echo "EOF" >> $GITHUB_OUTPUT fi - - name: Create Release + - name: Create Final Release id: create_release uses: actions/create-release@v1 env: @@ -327,16 +495,48 @@ jobs: ## Installation + ### Quick Install (Recommended) + ```bash - TODO: Add installation instructions + curl -sL https://genkit.tools | bash ``` + + ### Manual Installation + + ```bash + # Download the appropriate binary for your platform + curl -Lo genkit https://github.com/firebase/genkit/releases/download/${{ inputs.version }}/genkit-$(uname -s | tr '[:upper:]' '[:lower:]')-$(uname -m | sed 's/x86_64/x64/;s/aarch64/arm64/') + + # Make it executable + chmod +x genkit + + # Move to a directory in your PATH + sudo mv genkit /usr/local/bin/ + + # Verify installation + genkit --version + ``` + + ### Windows Installation + + ```powershell + # Download the Windows binary + Invoke-WebRequest -Uri "https://github.com/firebase/genkit/releases/download/${{ inputs.version }}/genkit-win32-x64.exe" -OutFile "genkit.exe" + + # Add to PATH or run from current directory + .\genkit.exe --version + ``` + + ## Documentation + + For more information, visit [https://firebase.google.com/docs/genkit/](https://firebase.google.com/docs/genkit/) draft: false prerelease: false - upload-assets: - needs: [build, test, create-release] + upload-release-assets: + needs: [build, test, promote-to-release] runs-on: ubuntu-latest - if: inputs.upload_to_release == 'true' + if: inputs.promote_rc == 'true' strategy: matrix: include: @@ -368,15 +568,15 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: - upload_url: ${{ needs.create-release.outputs.upload_url }} + upload_url: ${{ needs.promote-to-release.outputs.upload_url }} asset_path: ./genkit-${{ matrix.target }}${{ steps.binary.outputs.ext }} asset_name: genkit-${{ matrix.target }} asset_content_type: application/octet-stream update-latest-tag: - needs: [create-release, upload-assets] + needs: [promote-to-release, upload-release-assets] runs-on: ubuntu-latest - if: inputs.upload_to_release == 'true' + if: inputs.promote_rc == 'true' steps: - name: Checkout code From bd566d20a5117e965fd10ef884b150633945c2f8 Mon Sep 17 00:00:00 2001 From: Jacob Cable Date: Tue, 15 Jul 2025 16:05:24 +0100 Subject: [PATCH 03/10] chore(ci): remove unnecessary comment --- .github/workflows/build-cli-binaries.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-cli-binaries.yml b/.github/workflows/build-cli-binaries.yml index dabb697853..8ee956a6ab 100644 --- a/.github/workflows/build-cli-binaries.yml +++ b/.github/workflows/build-cli-binaries.yml @@ -125,7 +125,7 @@ jobs: with: name: genkit-${{ matrix.target }} path: genkit-tools/cli/dist/bin/genkit-${{ matrix.target }}${{ steps.binary.outputs.ext }} - retention-days: 7 # Increased for better debugging and signing workflow + retention-days: 7 test: needs: build From ae57a0239bcd87e51e921874b338fa6c2140c3c9 Mon Sep 17 00:00:00 2001 From: Jacob Cable Date: Tue, 15 Jul 2025 16:40:06 +0100 Subject: [PATCH 04/10] fix(cli): error handling in install script --- bin/install_cli | 42 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/bin/install_cli b/bin/install_cli index c454e4444d..6ced7fb39a 100644 --- a/bin/install_cli +++ b/bin/install_cli @@ -250,13 +250,49 @@ if [[ -z "$MACHINE" ]]; then fi # We have enough information to generate the binary's download URL. -# Use GitHub releases with "latest" tag that gets updated by the workflow -DOWNLOAD_URL="https://github.com/firebase/genkit/releases/download/latest/genkit-$MACHINE" +# Use GitHub API to find the latest non-prerelease (which will contain signed binaries) +echo "-- Finding latest release..." +LATEST_RELEASE=$(curl -sfL https://api.github.com/repos/firebase/genkit/releases/latest | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/') + +if [[ -z "$LATEST_RELEASE" ]]; then + echo "Error: Unable to find the latest genkit release." + echo "This might be because no releases have been published yet." + echo "Please check https://github.com/firebase/genkit/releases for available releases." + exit 1 +fi + +echo "-- Latest release: $LATEST_RELEASE" + +# Construct the download URL for the signed binary from the latest release +DOWNLOAD_URL="https://github.com/firebase/genkit/releases/download/$LATEST_RELEASE/genkit-$MACHINE" + +# Check if the binary exists at this URL +if ! curl -sfI "$DOWNLOAD_URL" >/dev/null 2>&1; then + echo "Error: Binary not found at $DOWNLOAD_URL" + echo "This might be because:" + echo " - The release doesn't include binaries for your platform ($MACHINE)" + echo " - The binaries haven't been uploaded yet" + echo "Please check https://github.com/firebase/genkit/releases for available downloads." + exit 1 +fi + echo "-- Downloading binary from $DOWNLOAD_URL" # We use "curl" to download the binary with a flag set to follow redirects # (GitHub download URLs redirect to CDNs) and a flag to show a progress bar. -curl -o "/tmp/genkit_standalone.tmp" -L --progress-bar $DOWNLOAD_URL +if ! curl -o "/tmp/genkit_standalone.tmp" -L --progress-bar $DOWNLOAD_URL; then + echo "" + echo "Error: Failed to download the genkit binary." + echo "Please check your internet connection and try again." + exit 1 +fi + +# Verify the downloaded file is not empty +if [[ ! -s "/tmp/genkit_standalone.tmp" ]]; then + echo "Error: Downloaded file is empty." + echo "This might indicate a problem with the release or network connection." + exit 1 +fi GENKIT_BINARY=${GENKIT_BINARY:-$GLOBAL_BINARY} INSTALL_DIR=$(dirname -- "$GENKIT_BINARY") From 568217c4fe00f15e8ec8757eb9db20d119fc39c9 Mon Sep 17 00:00:00 2001 From: Jacob Cable Date: Tue, 15 Jul 2025 16:48:50 +0100 Subject: [PATCH 05/10] fix(CI): specify bash in validate step of cli workflow --- .github/workflows/build-cli-binaries.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build-cli-binaries.yml b/.github/workflows/build-cli-binaries.yml index 8ee956a6ab..74411d2185 100644 --- a/.github/workflows/build-cli-binaries.yml +++ b/.github/workflows/build-cli-binaries.yml @@ -62,6 +62,7 @@ jobs: uses: actions/checkout@v4 - name: Validate version format + shell: bash run: | VERSION="${{ inputs.version }}" if [[ ! "$VERSION" =~ ^v[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.-]+)?(\+[a-zA-Z0-9.-]+)?$ ]]; then From cca643821301264415c3c0d3dc189c1e655b5ccd Mon Sep 17 00:00:00 2001 From: Jacob Cable Date: Thu, 17 Jul 2025 10:31:31 +0100 Subject: [PATCH 06/10] fix(ci): fix version check validation --- .github/workflows/build-cli-binaries.yml | 257 +++------------ .github/workflows/promote-cli-release.yml | 345 +++++++++++++++++++++ .github/workflows/sign-binaries-manual.yml | 103 ++++++ scripts/sign-and-upload-binaries.sh | 144 +++++++++ 4 files changed, 627 insertions(+), 222 deletions(-) create mode 100644 .github/workflows/promote-cli-release.yml create mode 100644 .github/workflows/sign-binaries-manual.yml create mode 100755 scripts/sign-and-upload-binaries.sh diff --git a/.github/workflows/build-cli-binaries.yml b/.github/workflows/build-cli-binaries.yml index 74411d2185..65ef097022 100644 --- a/.github/workflows/build-cli-binaries.yml +++ b/.github/workflows/build-cli-binaries.yml @@ -14,7 +14,7 @@ # # SPDX-License-Identifier: Apache-2.0 -name: Build CLI Binaries +name: Build CLI Binaries (RC) on: workflow_dispatch: @@ -28,11 +28,6 @@ on: required: false type: boolean default: false - promote_rc: - description: 'Promote RC to final release (requires signed binaries)' - required: false - type: boolean - default: false jobs: build: @@ -65,7 +60,8 @@ jobs: shell: bash run: | VERSION="${{ inputs.version }}" - if [[ ! "$VERSION" =~ ^v[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.-]+)?(\+[a-zA-Z0-9.-]+)?$ ]]; then + # Use grep for cross-platform regex matching + if ! echo "$VERSION" | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.-]+)?(\+[a-zA-Z0-9.-]+)?$' > /dev/null; then echo "Error: Version '$VERSION' does not follow semantic versioning format (e.g., v1.0.0, v1.0.0-rc.1)" exit 1 fi @@ -283,7 +279,9 @@ jobs: create-rc: needs: [build, test] runs-on: ubuntu-latest - if: inputs.create_rc == 'true' + if: inputs.create_rc + outputs: + upload_url: ${{ steps.create_rc.outputs.upload_url }} steps: - name: Checkout code @@ -351,7 +349,7 @@ jobs: upload-rc-assets: needs: [build, test, create-rc] runs-on: ubuntu-latest - if: inputs.create_rc == 'true' + if: inputs.create_rc strategy: matrix: include: @@ -385,223 +383,38 @@ jobs: with: upload_url: ${{ needs.create-rc.outputs.upload_url }} asset_path: ./genkit-${{ matrix.target }}${{ steps.binary.outputs.ext }} - asset_name: genkit-${{ matrix.target }} + asset_name: genkit-${{ matrix.target }}${{ steps.binary.outputs.ext }} asset_content_type: application/octet-stream - promote-to-release: - needs: [build, test] - runs-on: ubuntu-latest - if: inputs.promote_rc == 'true' - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - # Note: After RC creation, signed binaries should be uploaded via Kokoro - # before running promote_rc. The promotion assumes signed binaries are - # available for the final release. - - name: Validate signed binaries exist - run: | - RC_VERSION="${{ inputs.version }}" - FINAL_VERSION="${RC_VERSION%-rc*}" - - echo "Validating signed binaries exist for RC: $RC_VERSION" - echo "Will promote to final version: $FINAL_VERSION" - - # Expected platforms that should have signed binaries - EXPECTED_PLATFORMS=("linux-x64" "linux-arm64" "darwin-x64" "darwin-arm64" "win32-x64") - - # Check if RC release exists and get its assets - RELEASE_INFO=$(curl -s -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \ - "https://api.github.com/repos/firebase/genkit/releases/tags/$RC_VERSION") - - if [[ $(echo "$RELEASE_INFO" | jq -r '.id') == "null" ]]; then - echo "❌ Error: RC release $RC_VERSION not found" - exit 1 - fi - - echo "✓ RC release $RC_VERSION found" - - # Get list of assets from the RC release - ASSETS=$(echo "$RELEASE_INFO" | jq -r '.assets[].name' | sort) - - # Check for expected signed binaries - MISSING_BINARIES=() - for platform in "${EXPECTED_PLATFORMS[@]}"; do - if [[ "$platform" == win32-* ]]; then - expected_file="genkit-$platform.exe" - else - expected_file="genkit-$platform" - fi - - if ! echo "$ASSETS" | grep -q "^$expected_file$"; then - MISSING_BINARIES+=("$expected_file") - else - echo "✓ Found signed binary: $expected_file" - fi - done - - if [[ ${#MISSING_BINARIES[@]} -gt 0 ]]; then - echo "❌ Missing signed binaries:" - printf ' - %s\n' "${MISSING_BINARIES[@]}" - echo "" - echo "Please ensure all binaries are signed before promoting to final release." - echo "You can trigger the signing workflow or manually upload signed binaries." - exit 1 - fi - - echo "✅ All expected signed binaries found in RC release" - - - name: Generate changelog - id: changelog - run: | - # Get the previous release tag - PREVIOUS_TAG=$(git describe --tags --abbrev=0 HEAD~1 2>/dev/null || echo "") - - if [[ -n "$PREVIOUS_TAG" ]]; then - # Generate changelog from previous tag to current - CHANGELOG=$(git log --pretty=format:"- %s" $PREVIOUS_TAG..HEAD | head -20) - echo "changelog<> $GITHUB_OUTPUT - echo "$CHANGELOG" >> $GITHUB_OUTPUT - echo "EOF" >> $GITHUB_OUTPUT - else - # First release - echo "changelog<> $GITHUB_OUTPUT - echo "- Initial release" >> $GITHUB_OUTPUT - echo "EOF" >> $GITHUB_OUTPUT - fi - - - name: Create Final Release - id: create_release - uses: actions/create-release@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - tag_name: ${{ inputs.version }} - release_name: Genkit CLI ${{ inputs.version }} - body: | - # Genkit CLI ${{ inputs.version }} - - ## Downloads - - - [Linux x64](https://github.com/firebase/genkit/releases/download/${{ inputs.version }}/genkit-linux-x64) - - [Linux ARM64](https://github.com/firebase/genkit/releases/download/${{ inputs.version }}/genkit-linux-arm64) - - [macOS x64](https://github.com/firebase/genkit/releases/download/${{ inputs.version }}/genkit-darwin-x64) - - [macOS ARM64](https://github.com/firebase/genkit/releases/download/${{ inputs.version }}/genkit-darwin-arm64) - - [Windows x64](https://github.com/firebase/genkit/releases/download/${{ inputs.version }}/genkit-win32-x64.exe) - - ## Changes - - ${{ steps.changelog.outputs.changelog }} - - ## Installation - - ### Quick Install (Recommended) - - ```bash - curl -sL https://genkit.tools | bash - ``` - - ### Manual Installation - - ```bash - # Download the appropriate binary for your platform - curl -Lo genkit https://github.com/firebase/genkit/releases/download/${{ inputs.version }}/genkit-$(uname -s | tr '[:upper:]' '[:lower:]')-$(uname -m | sed 's/x86_64/x64/;s/aarch64/arm64/') - - # Make it executable - chmod +x genkit - - # Move to a directory in your PATH - sudo mv genkit /usr/local/bin/ - - # Verify installation - genkit --version - ``` - - ### Windows Installation - - ```powershell - # Download the Windows binary - Invoke-WebRequest -Uri "https://github.com/firebase/genkit/releases/download/${{ inputs.version }}/genkit-win32-x64.exe" -OutFile "genkit.exe" - - # Add to PATH or run from current directory - .\genkit.exe --version - ``` - - ## Documentation - - For more information, visit [https://firebase.google.com/docs/genkit/](https://firebase.google.com/docs/genkit/) - draft: false - prerelease: false - - upload-release-assets: - needs: [build, test, promote-to-release] + create-rc-summary: + needs: [upload-rc-assets] runs-on: ubuntu-latest - if: inputs.promote_rc == 'true' - strategy: - matrix: - include: - - target: linux-x64 - - target: linux-arm64 - - target: darwin-x64 - - target: darwin-arm64 - - target: win32-x64 + if: inputs.create_rc steps: - - name: Set binary extension - id: binary - shell: bash + - name: Create job summary run: | - if [[ "${{ matrix.target }}" == win32-* ]]; then - echo "ext=.exe" >> $GITHUB_OUTPUT - else - echo "ext=" >> $GITHUB_OUTPUT - fi - - - name: Download binary artifact - uses: actions/download-artifact@v4 - with: - name: genkit-${{ matrix.target }} - path: ./ - - - name: Upload to GitHub Release - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ needs.promote-to-release.outputs.upload_url }} - asset_path: ./genkit-${{ matrix.target }}${{ steps.binary.outputs.ext }} - asset_name: genkit-${{ matrix.target }} - asset_content_type: application/octet-stream + echo "# 🎉 Genkit CLI Release Candidate Created" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Version:** \`${{ inputs.version }}\`" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "## ✅ Build Status" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "All binaries have been successfully built and uploaded!" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "### 📦 Available Binaries:" >> $GITHUB_STEP_SUMMARY + echo "- ✓ Linux x64 (\`genkit-linux-x64\`)" >> $GITHUB_STEP_SUMMARY + echo "- ✓ Linux ARM64 (\`genkit-linux-arm64\`)" >> $GITHUB_STEP_SUMMARY + echo "- ✓ macOS x64 (\`genkit-darwin-x64\`)" >> $GITHUB_STEP_SUMMARY + echo "- ✓ macOS ARM64 (\`genkit-darwin-arm64\`)" >> $GITHUB_STEP_SUMMARY + echo "- ✓ Windows x64 (\`genkit-win32-x64.exe\`)" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "### 🔗 Release Links" >> $GITHUB_STEP_SUMMARY + echo "- [View Release](https://github.com/${{ github.repository }}/releases/tag/${{ inputs.version }})" >> $GITHUB_STEP_SUMMARY + echo "- [Download Binaries](https://github.com/${{ github.repository }}/releases/tag/${{ inputs.version }}#assets)" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "### 📝 Next Steps" >> $GITHUB_STEP_SUMMARY + echo "1. Test the RC binaries" >> $GITHUB_STEP_SUMMARY + echo "2. Sign the binaries using the manual workflow" >> $GITHUB_STEP_SUMMARY + echo "3. Promote to final release using the 'Promote CLI Release' workflow" >> $GITHUB_STEP_SUMMARY - update-latest-tag: - needs: [promote-to-release, upload-release-assets] - runs-on: ubuntu-latest - if: inputs.promote_rc == 'true' - - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - token: ${{ secrets.GITHUB_TOKEN }} - - - name: Update latest tag - run: | - # Check if latest tag already points to the correct version - CURRENT_LATEST=$(git rev-parse latest 2>/dev/null || echo "") - TARGET_COMMIT=$(git rev-parse ${{ inputs.version }} 2>/dev/null || echo "") - - if [[ "$CURRENT_LATEST" == "$TARGET_COMMIT" ]]; then - echo "Latest tag already points to ${{ inputs.version }}" - exit 0 - fi - - # Delete the existing "latest" tag if it exists - git tag -d latest 2>/dev/null || true - git push origin :refs/tags/latest 2>/dev/null || true - - # Create new "latest" tag pointing to the release tag - git tag latest ${{ inputs.version }} - git push origin latest - - echo "Updated 'latest' tag to point to ${{ inputs.version }}" \ No newline at end of file diff --git a/.github/workflows/promote-cli-release.yml b/.github/workflows/promote-cli-release.yml new file mode 100644 index 0000000000..ecf9ff0630 --- /dev/null +++ b/.github/workflows/promote-cli-release.yml @@ -0,0 +1,345 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +name: Promote CLI Release + +on: + workflow_dispatch: + inputs: + rc_version: + description: 'RC version to promote (e.g., v1.0.0-rc.1)' + required: true + type: string + final_version: + description: 'Final version tag (e.g., v1.0.0)' + required: true + type: string + +jobs: + validate-and-promote: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Validate version formats + shell: bash + run: | + RC_VERSION="${{ inputs.rc_version }}" + FINAL_VERSION="${{ inputs.final_version }}" + + # Validate RC version format + if ! echo "$RC_VERSION" | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+(-rc\.[0-9]+)$' > /dev/null; then + echo "Error: RC version '$RC_VERSION' does not follow expected format (e.g., v1.0.0-rc.1)" + exit 1 + fi + + # Validate final version format + if ! echo "$FINAL_VERSION" | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' > /dev/null; then + echo "Error: Final version '$FINAL_VERSION' does not follow expected format (e.g., v1.0.0)" + exit 1 + fi + + # Extract base versions for comparison + RC_BASE=$(echo "$RC_VERSION" | sed 's/-rc\.[0-9]*//') + + if [[ "$RC_BASE" != "$FINAL_VERSION" ]]; then + echo "Error: RC base version ($RC_BASE) does not match final version ($FINAL_VERSION)" + exit 1 + fi + + echo "✓ Version formats are valid" + echo " RC Version: $RC_VERSION" + echo " Final Version: $FINAL_VERSION" + + - name: Validate signed binaries exist + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + RC_VERSION="${{ inputs.rc_version }}" + + echo "Checking for signed binaries in RC release: $RC_VERSION" + + # Get RC release information + RELEASE_INFO=$(curl -s -H "Authorization: token $GITHUB_TOKEN" \ + "https://api.github.com/repos/${{ github.repository }}/releases/tags/$RC_VERSION") + + if [[ $(echo "$RELEASE_INFO" | jq -r '.id') == "null" ]]; then + echo "❌ Error: RC release $RC_VERSION not found" + exit 1 + fi + + # Get list of assets + ASSETS=$(echo "$RELEASE_INFO" | jq -r '.assets[].name' | sort) + + echo "Found assets:" + echo "$ASSETS" + echo "" + + # Expected signed binaries + EXPECTED_SIGNED=( + "genkit-linux-x64-signed" + "genkit-linux-arm64-signed" + "genkit-darwin-x64-signed" + "genkit-darwin-arm64-signed" + "genkit-win32-x64-signed.exe" + ) + + # Check for signed binaries + MISSING_SIGNED=() + for platform in "${EXPECTED_SIGNED[@]}"; do + if ! echo "$ASSETS" | grep -q "^$platform$"; then + MISSING_SIGNED+=("$platform") + else + echo "✓ Found signed binary: $platform" + fi + done + + if [[ ${#MISSING_SIGNED[@]} -gt 0 ]]; then + echo "" + echo "❌ Missing signed binaries:" + printf ' - %s\n' "${MISSING_SIGNED[@]}" + echo "" + echo "Please ensure all binaries are signed before promoting to final release." + exit 1 + fi + + echo "" + echo "✅ All expected signed binaries found" + + - name: Create release directory + run: mkdir -p release-assets + + - name: Download signed binaries from RC + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + RC_VERSION="${{ inputs.rc_version }}" + + echo "Downloading signed binaries from RC release..." + + # Expected signed binaries with their final names + declare -A BINARY_MAP=( + ["genkit-linux-x64-signed"]="genkit-linux-x64" + ["genkit-linux-arm64-signed"]="genkit-linux-arm64" + ["genkit-darwin-x64-signed"]="genkit-darwin-x64" + ["genkit-darwin-arm64-signed"]="genkit-darwin-arm64" + ["genkit-win32-x64-signed.exe"]="genkit-win32-x64.exe" + ) + + # Download each signed binary + for signed_name in "${!BINARY_MAP[@]}"; do + final_name="${BINARY_MAP[$signed_name]}" + + echo "Downloading $signed_name as $final_name..." + + # Get download URL for the asset + DOWNLOAD_URL=$(curl -s -H "Authorization: token $GITHUB_TOKEN" \ + "https://api.github.com/repos/${{ github.repository }}/releases/tags/$RC_VERSION" | \ + jq -r ".assets[] | select(.name == \"$signed_name\") | .url") + + if [[ -z "$DOWNLOAD_URL" || "$DOWNLOAD_URL" == "null" ]]; then + echo "❌ Failed to get download URL for $signed_name" + exit 1 + fi + + # Download the asset + curl -L -H "Authorization: token $GITHUB_TOKEN" \ + -H "Accept: application/octet-stream" \ + "$DOWNLOAD_URL" -o "release-assets/$final_name" + + echo "✓ Downloaded $final_name" + done + + echo "" + echo "All signed binaries downloaded successfully" + ls -la release-assets/ + + - name: Generate changelog + id: changelog + run: | + # Get the previous release tag + PREVIOUS_TAG=$(git describe --tags --abbrev=0 HEAD~1 2>/dev/null || echo "") + + if [[ -n "$PREVIOUS_TAG" ]]; then + # Generate changelog from previous tag to current + CHANGELOG=$(git log --pretty=format:"- %s" $PREVIOUS_TAG..HEAD | head -20) + echo "changelog<> $GITHUB_OUTPUT + echo "$CHANGELOG" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + else + # First release + echo "changelog<> $GITHUB_OUTPUT + echo "- Initial release" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + fi + + - name: Check if release already exists + id: check_release + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + RELEASE_INFO=$(curl -s -H "Authorization: token $GITHUB_TOKEN" \ + "https://api.github.com/repos/${{ github.repository }}/releases/tags/${{ inputs.final_version }}") + + if [[ $(echo "$RELEASE_INFO" | jq -r '.id') != "null" ]]; then + echo "❌ Error: Release ${{ inputs.final_version }} already exists" + echo "Please choose a different version or delete the existing release first" + exit 1 + fi + + echo "✓ Release ${{ inputs.final_version }} does not exist, proceeding..." + + - name: Create final release + id: create_release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ inputs.final_version }} + release_name: Genkit CLI ${{ inputs.final_version }} + body: | + # Genkit CLI ${{ inputs.final_version }} + + ## Downloads + + - [Linux x64](https://github.com/${{ github.repository }}/releases/download/${{ inputs.final_version }}/genkit-linux-x64) + - [Linux ARM64](https://github.com/${{ github.repository }}/releases/download/${{ inputs.final_version }}/genkit-linux-arm64) + - [macOS x64](https://github.com/${{ github.repository }}/releases/download/${{ inputs.final_version }}/genkit-darwin-x64) + - [macOS ARM64](https://github.com/${{ github.repository }}/releases/download/${{ inputs.final_version }}/genkit-darwin-arm64) + - [Windows x64](https://github.com/${{ github.repository }}/releases/download/${{ inputs.final_version }}/genkit-win32-x64.exe) + + ## Changes + + ${{ steps.changelog.outputs.changelog }} + + ## Installation + + ### Quick Install (Recommended) + + ```bash + curl -sL https://genkit.tools | bash + ``` + + ### Manual Installation + + ```bash + # Download the appropriate binary for your platform + curl -Lo genkit https://github.com/${{ github.repository }}/releases/download/${{ inputs.final_version }}/genkit-$(uname -s | tr '[:upper:]' '[:lower:]')-$(uname -m | sed 's/x86_64/x64/;s/aarch64/arm64/') + + # Make it executable + chmod +x genkit + + # Move to a directory in your PATH + sudo mv genkit /usr/local/bin/ + + # Verify installation + genkit --version + ``` + + ### Windows Installation + + ```powershell + # Download the Windows binary + Invoke-WebRequest -Uri "https://github.com/${{ github.repository }}/releases/download/${{ inputs.final_version }}/genkit-win32-x64.exe" -OutFile "genkit.exe" + + # Add to PATH or run from current directory + .\genkit.exe --version + ``` + + ## Documentation + + For more information, visit [https://firebase.google.com/docs/genkit/](https://firebase.google.com/docs/genkit/) + draft: false + prerelease: false + + - name: Upload release assets + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + UPLOAD_URL="${{ steps.create_release.outputs.upload_url }}" + + echo "Uploading binaries to final release..." + + for file in release-assets/*; do + filename=$(basename "$file") + echo "Uploading $filename..." + + curl -X POST \ + -H "Authorization: token $GITHUB_TOKEN" \ + -H "Content-Type: application/octet-stream" \ + --data-binary "@$file" \ + "${UPLOAD_URL}?name=$filename" | jq -r '.browser_download_url' + done + + echo "" + echo "✅ All binaries uploaded successfully" + + - name: Update latest tag + run: | + # Configure git + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + + # The create-release action should have created the tag + # Let's fetch it first to make sure we have it + git fetch --tags + + # Verify the tag exists + if ! git rev-parse ${{ inputs.final_version }} >/dev/null 2>&1; then + echo "Warning: Tag ${{ inputs.final_version }} was not created by create-release action" + echo "Creating it manually..." + git tag ${{ inputs.final_version }} + git push origin ${{ inputs.final_version }} + fi + + # Delete the existing "latest" tag if it exists + git tag -d latest 2>/dev/null || true + git push origin :refs/tags/latest 2>/dev/null || true + + # Create new "latest" tag pointing to the final release tag + git tag latest ${{ inputs.final_version }} + git push origin latest + + echo "Updated 'latest' tag to point to ${{ inputs.final_version }}" + + - name: Create job summary + run: | + echo "# 🚀 Genkit CLI Release Published" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Promoted from:** \`${{ inputs.rc_version }}\`" >> $GITHUB_STEP_SUMMARY + echo "**Final Version:** \`${{ inputs.final_version }}\`" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "## ✅ Release Status" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "The release has been successfully promoted from RC to final!" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "### 📦 Published Binaries:" >> $GITHUB_STEP_SUMMARY + echo "- ✓ Linux x64 (\`genkit-linux-x64\`)" >> $GITHUB_STEP_SUMMARY + echo "- ✓ Linux ARM64 (\`genkit-linux-arm64\`)" >> $GITHUB_STEP_SUMMARY + echo "- ✓ macOS x64 (\`genkit-darwin-x64\`)" >> $GITHUB_STEP_SUMMARY + echo "- ✓ macOS ARM64 (\`genkit-darwin-arm64\`)" >> $GITHUB_STEP_SUMMARY + echo "- ✓ Windows x64 (\`genkit-win32-x64.exe\`)" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "### 🔗 Release Links" >> $GITHUB_STEP_SUMMARY + echo "- [View Release](https://github.com/${{ github.repository }}/releases/tag/${{ inputs.final_version }})" >> $GITHUB_STEP_SUMMARY + echo "- [Download Latest](https://github.com/${{ github.repository }}/releases/latest)" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "### 📥 Quick Install" >> $GITHUB_STEP_SUMMARY + echo "\`\`\`bash" >> $GITHUB_STEP_SUMMARY + echo "curl -sL https://genkit.tools | bash" >> $GITHUB_STEP_SUMMARY + echo "\`\`\`" >> $GITHUB_STEP_SUMMARY \ No newline at end of file diff --git a/.github/workflows/sign-binaries-manual.yml b/.github/workflows/sign-binaries-manual.yml new file mode 100644 index 0000000000..033ec8bdc9 --- /dev/null +++ b/.github/workflows/sign-binaries-manual.yml @@ -0,0 +1,103 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +name: Sign Binaries (Manual) + +on: + workflow_dispatch: + inputs: + version: + description: 'Version tag to sign binaries for (e.g., v1.0.0-rc.1)' + required: true + type: string + +jobs: + sign-binaries: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Validate version format + shell: bash + run: | + VERSION="${{ inputs.version }}" + # Use grep for cross-platform regex matching + if ! echo "$VERSION" | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.-]+)?(\+[a-zA-Z0-9.-]+)?$' > /dev/null; then + echo "Error: Version '$VERSION' does not follow semantic versioning format (e.g., v1.0.0, v1.0.0-rc.1)" + exit 1 + fi + echo "✓ Version format is valid: $VERSION" + + - name: Install dependencies + run: | + # Install jq if not available + if ! command -v jq &> /dev/null; then + sudo apt-get update + sudo apt-get install -y jq + fi + + - name: Sign and upload binaries + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + echo "Starting binary signing simulation for version ${{ inputs.version }}" + ./scripts/sign-and-upload-binaries.sh "${{ inputs.version }}" + + - name: Verify signed binaries + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + VERSION="${{ inputs.version }}" + + echo "Verifying signed binaries for release $VERSION..." + + # Get release assets + ASSETS=$(curl -s -H "Authorization: token $GITHUB_TOKEN" \ + "https://api.github.com/repos/${{ github.repository }}/releases/tags/$VERSION" | \ + jq -r '.assets[].name' | sort) + + # Expected signed binaries + EXPECTED_SIGNED=( + "genkit-linux-x64-signed" + "genkit-linux-arm64-signed" + "genkit-darwin-x64-signed" + "genkit-darwin-arm64-signed" + "genkit-win32-x64-signed.exe" + ) + + # Check for each expected signed binary + MISSING=() + for expected in "${EXPECTED_SIGNED[@]}"; do + if ! echo "$ASSETS" | grep -q "^$expected$"; then + MISSING+=("$expected") + else + echo "✓ Found: $expected" + fi + done + + if [ ${#MISSING[@]} -gt 0 ]; then + echo "" + echo "❌ Missing signed binaries:" + printf ' - %s\n' "${MISSING[@]}" + exit 1 + fi + + echo "" + echo "✅ All signed binaries uploaded successfully!" + echo "" + echo "View the release at: https://github.com/${{ github.repository }}/releases/tag/$VERSION" \ No newline at end of file diff --git a/scripts/sign-and-upload-binaries.sh b/scripts/sign-and-upload-binaries.sh new file mode 100755 index 0000000000..93adb8cedb --- /dev/null +++ b/scripts/sign-and-upload-binaries.sh @@ -0,0 +1,144 @@ +#!/bin/bash +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +set -euo pipefail + +# This script simulates signing binaries and uploads them to a GitHub release +# Usage: ./sign-and-upload-binaries.sh +# Example: ./sign-and-upload-binaries.sh v1.0.0-rc.1 + +if [ $# -ne 1 ]; then + echo "Usage: $0 " + echo "Example: $0 v1.0.0-rc.1" + exit 1 +fi + +VERSION="$1" + +# Extract repo owner and name from GITHUB_REPOSITORY env var or use defaults +if [ -n "${GITHUB_REPOSITORY:-}" ]; then + REPO_OWNER=$(echo "$GITHUB_REPOSITORY" | cut -d'/' -f1) + REPO_NAME=$(echo "$GITHUB_REPOSITORY" | cut -d'/' -f2) +else + # Fallback for local testing + REPO_OWNER="${REPO_OWNER:-firebase}" + REPO_NAME="${REPO_NAME:-genkit}" +fi + +# Check if GITHUB_TOKEN is set +if [ -z "${GITHUB_TOKEN:-}" ]; then + echo "Error: GITHUB_TOKEN environment variable is not set" + exit 1 +fi + +# Platform configurations +PLATFORMS=( + "linux-x64" + "linux-arm64" + "darwin-x64" + "darwin-arm64" + "win32-x64" +) + +echo "=== Genkit Binary Signing Simulation ===" +echo "Version: $VERSION" +echo "Repository: $REPO_OWNER/$REPO_NAME" +echo "" + +# Get release information +echo "Fetching release information..." +RELEASE_INFO=$(curl -s -H "Authorization: token $GITHUB_TOKEN" \ + "https://api.github.com/repos/$REPO_OWNER/$REPO_NAME/releases/tags/$VERSION") + +RELEASE_ID=$(echo "$RELEASE_INFO" | jq -r '.id') +UPLOAD_URL=$(echo "$RELEASE_INFO" | jq -r '.upload_url' | sed 's/{?name,label}//') + +if [ "$RELEASE_ID" == "null" ]; then + echo "Error: Release $VERSION not found" + exit 1 +fi + +echo "✓ Found release ID: $RELEASE_ID" +echo "" + +# Create temporary directory for downloads +TEMP_DIR=$(mktemp -d) +trap "rm -rf $TEMP_DIR" EXIT + +echo "Working directory: $TEMP_DIR" +echo "" + +# Process each platform +for platform in "${PLATFORMS[@]}"; do + echo "Processing $platform..." + + # Set file extension + if [[ "$platform" == win32-* ]]; then + ext=".exe" + else + ext="" + fi + + # Original and signed file names + original_name="genkit-$platform$ext" + signed_name="genkit-$platform-signed$ext" + + # Download URL + download_url="https://github.com/$REPO_OWNER/$REPO_NAME/releases/download/$VERSION/$original_name" + + # Download the binary + echo " Downloading $original_name..." + if curl -sL -o "$TEMP_DIR/$original_name" "$download_url"; then + echo " ✓ Downloaded successfully" + else + echo " ✗ Failed to download $original_name - skipping" + continue + fi + + # Simulate signing by renaming + echo " Simulating signing process..." + cp "$TEMP_DIR/$original_name" "$TEMP_DIR/$signed_name" + + # Add a simple signature marker to the file + echo "SIGNED:$VERSION:$(date -u +%Y%m%d%H%M%S)" >> "$TEMP_DIR/$signed_name" + + echo " ✓ Signing simulated" + + # Upload the signed binary + echo " Uploading $signed_name..." + + upload_response=$(curl -s -X POST \ + -H "Authorization: token $GITHUB_TOKEN" \ + -H "Content-Type: application/octet-stream" \ + --data-binary "@$TEMP_DIR/$signed_name" \ + "$UPLOAD_URL?name=$signed_name") + + # Check if upload was successful + if echo "$upload_response" | jq -e '.id' > /dev/null; then + asset_url=$(echo "$upload_response" | jq -r '.browser_download_url') + echo " ✓ Uploaded successfully: $asset_url" + else + echo " ✗ Failed to upload $signed_name" + echo " Error: $(echo "$upload_response" | jq -r '.message // "Unknown error"')" + fi + + echo "" +done + +echo "=== Signing simulation complete ===" +echo "" +echo "View the release at: https://github.com/$REPO_OWNER/$REPO_NAME/releases/tag/$VERSION" \ No newline at end of file From 1235e3a381b3e03863837e39950b921a5ff9aa43 Mon Sep 17 00:00:00 2001 From: Jacob Cable Date: Mon, 21 Jul 2025 10:10:19 +0100 Subject: [PATCH 07/10] fix: apply improvements to cli workflows --- .github/workflows/build-cli-binaries.yml | 76 +++++++---------------- .github/workflows/promote-cli-release.yml | 38 ++++-------- scripts/sign-and-upload-binaries.sh | 66 ++++++++++++++++---- 3 files changed, 92 insertions(+), 88 deletions(-) diff --git a/.github/workflows/build-cli-binaries.yml b/.github/workflows/build-cli-binaries.yml index 65ef097022..6f9022b0f3 100644 --- a/.github/workflows/build-cli-binaries.yml +++ b/.github/workflows/build-cli-binaries.yml @@ -29,6 +29,9 @@ on: type: boolean default: false +permissions: + contents: write + jobs: build: strategy: @@ -280,8 +283,6 @@ jobs: needs: [build, test] runs-on: ubuntu-latest if: inputs.create_rc - outputs: - upload_url: ${{ steps.create_rc.outputs.upload_url }} steps: - name: Checkout code @@ -306,14 +307,17 @@ jobs: echo "EOF" >> $GITHUB_OUTPUT fi + - name: Download all binary artifacts + uses: actions/download-artifact@v4 + with: + path: release-assets + - name: Create Release Candidate id: create_rc - uses: actions/create-release@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + uses: softprops/action-gh-release@v2 with: tag_name: ${{ inputs.version }} - release_name: Genkit CLI ${{ inputs.version }} (Release Candidate) + name: Genkit CLI ${{ inputs.version }} (Release Candidate) body: | # Genkit CLI ${{ inputs.version }} - Release Candidate @@ -321,11 +325,11 @@ jobs: ## Downloads (Unsigned - For Testing Only) - - [Linux x64](https://github.com/firebase/genkit/releases/download/${{ inputs.version }}/genkit-linux-x64) - - [Linux ARM64](https://github.com/firebase/genkit/releases/download/${{ inputs.version }}/genkit-linux-arm64) - - [macOS x64](https://github.com/firebase/genkit/releases/download/${{ inputs.version }}/genkit-darwin-x64) - - [macOS ARM64](https://github.com/firebase/genkit/releases/download/${{ inputs.version }}/genkit-darwin-arm64) - - [Windows x64](https://github.com/firebase/genkit/releases/download/${{ inputs.version }}/genkit-win32-x64.exe) + - [Linux x64](https://github.com/${{ github.repository }}/releases/download/${{ inputs.version }}/genkit-linux-x64) + - [Linux ARM64](https://github.com/${{ github.repository }}/releases/download/${{ inputs.version }}/genkit-linux-arm64) + - [macOS x64](https://github.com/${{ github.repository }}/releases/download/${{ inputs.version }}/genkit-darwin-x64) + - [macOS ARM64](https://github.com/${{ github.repository }}/releases/download/${{ inputs.version }}/genkit-darwin-arm64) + - [Windows x64](https://github.com/${{ github.repository }}/releases/download/${{ inputs.version }}/genkit-win32-x64.exe) ## Changes @@ -339,55 +343,23 @@ jobs: ```bash # Download and test the RC binary - curl -Lo genkit https://github.com/firebase/genkit/releases/download/${{ inputs.version }}/genkit-$(uname -s | tr '[:upper:]' '[:lower:]')-$(uname -m | sed 's/x86_64/x64/;s/aarch64/arm64/') + curl -Lo genkit https://github.com/${{ github.repository }}/releases/download/${{ inputs.version }}/genkit-$(uname -s | tr '[:upper:]' '[:lower:]')-$(uname -m | sed 's/x86_64/x64/;s/aarch64/arm64/') chmod +x genkit ./genkit --version ``` draft: false prerelease: true + files: | + release-assets/genkit-linux-x64/genkit-linux-x64 + release-assets/genkit-linux-arm64/genkit-linux-arm64 + release-assets/genkit-darwin-x64/genkit-darwin-x64 + release-assets/genkit-darwin-arm64/genkit-darwin-arm64 + release-assets/genkit-win32-x64/genkit-win32-x64.exe + - upload-rc-assets: - needs: [build, test, create-rc] - runs-on: ubuntu-latest - if: inputs.create_rc - strategy: - matrix: - include: - - target: linux-x64 - - target: linux-arm64 - - target: darwin-x64 - - target: darwin-arm64 - - target: win32-x64 - - steps: - - name: Set binary extension - id: binary - shell: bash - run: | - if [[ "${{ matrix.target }}" == win32-* ]]; then - echo "ext=.exe" >> $GITHUB_OUTPUT - else - echo "ext=" >> $GITHUB_OUTPUT - fi - - - name: Download binary artifact - uses: actions/download-artifact@v4 - with: - name: genkit-${{ matrix.target }} - path: ./ - - - name: Upload to GitHub Release Candidate - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ needs.create-rc.outputs.upload_url }} - asset_path: ./genkit-${{ matrix.target }}${{ steps.binary.outputs.ext }} - asset_name: genkit-${{ matrix.target }}${{ steps.binary.outputs.ext }} - asset_content_type: application/octet-stream create-rc-summary: - needs: [upload-rc-assets] + needs: [create-rc] runs-on: ubuntu-latest if: inputs.create_rc diff --git a/.github/workflows/promote-cli-release.yml b/.github/workflows/promote-cli-release.yml index ecf9ff0630..a9efc58d51 100644 --- a/.github/workflows/promote-cli-release.yml +++ b/.github/workflows/promote-cli-release.yml @@ -28,6 +28,9 @@ on: required: true type: string +permissions: + contents: write + jobs: validate-and-promote: runs-on: ubuntu-latest @@ -206,12 +209,10 @@ jobs: - name: Create final release id: create_release - uses: actions/create-release@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + uses: softprops/action-gh-release@v2 with: tag_name: ${{ inputs.final_version }} - release_name: Genkit CLI ${{ inputs.final_version }} + name: Genkit CLI ${{ inputs.final_version }} body: | # Genkit CLI ${{ inputs.final_version }} @@ -266,28 +267,15 @@ jobs: For more information, visit [https://firebase.google.com/docs/genkit/](https://firebase.google.com/docs/genkit/) draft: false prerelease: false + files: | + release-assets/genkit-linux-x64 + release-assets/genkit-linux-arm64 + release-assets/genkit-darwin-x64 + release-assets/genkit-darwin-arm64 + release-assets/genkit-win32-x64.exe + make_latest: true - - name: Upload release assets - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - UPLOAD_URL="${{ steps.create_release.outputs.upload_url }}" - - echo "Uploading binaries to final release..." - - for file in release-assets/*; do - filename=$(basename "$file") - echo "Uploading $filename..." - - curl -X POST \ - -H "Authorization: token $GITHUB_TOKEN" \ - -H "Content-Type: application/octet-stream" \ - --data-binary "@$file" \ - "${UPLOAD_URL}?name=$filename" | jq -r '.browser_download_url' - done - - echo "" - echo "✅ All binaries uploaded successfully" + - name: Update latest tag run: | diff --git a/scripts/sign-and-upload-binaries.sh b/scripts/sign-and-upload-binaries.sh index 93adb8cedb..f3c3901c30 100755 --- a/scripts/sign-and-upload-binaries.sh +++ b/scripts/sign-and-upload-binaries.sh @@ -109,31 +109,69 @@ for platform in "${PLATFORMS[@]}"; do continue fi - # Simulate signing by renaming + # Simulate signing process (preserve binary integrity) echo " Simulating signing process..." + + # Method 1: Create a copy with signed name (binary remains unchanged) cp "$TEMP_DIR/$original_name" "$TEMP_DIR/$signed_name" - # Add a simple signature marker to the file - echo "SIGNED:$VERSION:$(date -u +%Y%m%d%H%M%S)" >> "$TEMP_DIR/$signed_name" + # Method 2: Create a separate signature file + signature_name="$signed_name.sig" + cat > "$TEMP_DIR/$signature_name" << EOF +-----BEGIN GENKIT SIGNATURE----- +Version: $VERSION +Platform: $platform +Signed: $(date -u -Iseconds) +Signature: $(sha256sum "$TEMP_DIR/$original_name" | cut -d' ' -f1) +Signer: Genkit Signing Service (Simulation) +-----END GENKIT SIGNATURE----- +EOF + + echo " ✓ Signing simulated (binary integrity preserved)" + echo " ✓ Signature file created: $signature_name" - echo " ✓ Signing simulated" + # Verify binary integrity after "signing" + if cmp -s "$TEMP_DIR/$original_name" "$TEMP_DIR/$signed_name"; then + echo " ✓ Binary integrity verified (no corruption)" + else + echo " ✗ Binary integrity check failed!" + exit 1 + fi # Upload the signed binary echo " Uploading $signed_name..." - upload_response=$(curl -s -X POST \ + upload_binary_response=$(curl -s -X POST \ -H "Authorization: token $GITHUB_TOKEN" \ -H "Content-Type: application/octet-stream" \ --data-binary "@$TEMP_DIR/$signed_name" \ "$UPLOAD_URL?name=$signed_name") - # Check if upload was successful - if echo "$upload_response" | jq -e '.id' > /dev/null; then - asset_url=$(echo "$upload_response" | jq -r '.browser_download_url') - echo " ✓ Uploaded successfully: $asset_url" + # Check if binary upload was successful + if echo "$upload_binary_response" | jq -e '.id' > /dev/null; then + asset_url=$(echo "$upload_binary_response" | jq -r '.browser_download_url') + echo " ✓ Binary uploaded successfully: $asset_url" else echo " ✗ Failed to upload $signed_name" - echo " Error: $(echo "$upload_response" | jq -r '.message // "Unknown error"')" + echo " Error: $(echo "$upload_binary_response" | jq -r '.message // "Unknown error"')" + fi + + # Upload the signature file + echo " Uploading $signature_name..." + + upload_signature_response=$(curl -s -X POST \ + -H "Authorization: token $GITHUB_TOKEN" \ + -H "Content-Type: text/plain" \ + --data-binary "@$TEMP_DIR/$signature_name" \ + "$UPLOAD_URL?name=$signature_name") + + # Check if signature upload was successful + if echo "$upload_signature_response" | jq -e '.id' > /dev/null; then + sig_asset_url=$(echo "$upload_signature_response" | jq -r '.browser_download_url') + echo " ✓ Signature file uploaded successfully: $sig_asset_url" + else + echo " ✗ Failed to upload signature file" + echo " Error: $(echo "$upload_signature_response" | jq -r '.message // "Unknown error"')" fi echo "" @@ -141,4 +179,10 @@ done echo "=== Signing simulation complete ===" echo "" -echo "View the release at: https://github.com/$REPO_OWNER/$REPO_NAME/releases/tag/$VERSION" \ No newline at end of file +echo "✓ All binaries signed and uploaded successfully" +echo "✓ Binary integrity preserved (no corruption)" +echo "✓ Separate signature files created for verification" +echo "" +echo "View the release at: https://github.com/$REPO_OWNER/$REPO_NAME/releases/tag/$VERSION" +echo "" +echo "Note: This is a simulation. In production, you would use proper code signing tools." \ No newline at end of file From 8ac6b4d273a2fdd01462b81d8f281f78cebd71e6 Mon Sep 17 00:00:00 2001 From: Jacob Cable Date: Mon, 21 Jul 2025 13:16:23 +0100 Subject: [PATCH 08/10] fix(ci): get previous release tag by version ordering --- .github/workflows/build-cli-binaries.yml | 4 ++-- .github/workflows/promote-cli-release.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build-cli-binaries.yml b/.github/workflows/build-cli-binaries.yml index 6f9022b0f3..433e2fcca0 100644 --- a/.github/workflows/build-cli-binaries.yml +++ b/.github/workflows/build-cli-binaries.yml @@ -291,8 +291,8 @@ jobs: - name: Generate changelog id: changelog run: | - # Get the previous release tag - PREVIOUS_TAG=$(git describe --tags --abbrev=0 HEAD~1 2>/dev/null || echo "") + # Get the previous release tag by version ordering + PREVIOUS_TAG=$(git tag --sort=-version:refname | head -2 | tail -1 2>/dev/null || echo "") if [[ -n "$PREVIOUS_TAG" ]]; then # Generate changelog from previous tag to current diff --git a/.github/workflows/promote-cli-release.yml b/.github/workflows/promote-cli-release.yml index a9efc58d51..86001de85f 100644 --- a/.github/workflows/promote-cli-release.yml +++ b/.github/workflows/promote-cli-release.yml @@ -175,8 +175,8 @@ jobs: - name: Generate changelog id: changelog run: | - # Get the previous release tag - PREVIOUS_TAG=$(git describe --tags --abbrev=0 HEAD~1 2>/dev/null || echo "") + # Get the previous release tag by version ordering + PREVIOUS_TAG=$(git tag --sort=-version:refname | head -2 | tail -1 2>/dev/null || echo "") if [[ -n "$PREVIOUS_TAG" ]]; then # Generate changelog from previous tag to current From c30a5d8c99e35683c70949855a2ed97f2481d3e2 Mon Sep 17 00:00:00 2001 From: Jacob Cable Date: Wed, 23 Jul 2025 14:18:20 +0100 Subject: [PATCH 09/10] feat(ci): add unsigned version, and disabled signed flow --- .github/workflows/README.md | 75 ++++++++++++++++ .../workflows/build-cli-binaries-signed.yml | 46 ++++++++++ .github/workflows/build-cli-binaries.yml | 14 +-- .../workflows/promote-cli-release-signed.yml | 45 ++++++++++ .github/workflows/promote-cli-release.yml | 86 +++++++++---------- bin/install_cli | 4 +- 6 files changed, 210 insertions(+), 60 deletions(-) create mode 100644 .github/workflows/README.md create mode 100644 .github/workflows/build-cli-binaries-signed.yml create mode 100644 .github/workflows/promote-cli-release-signed.yml diff --git a/.github/workflows/README.md b/.github/workflows/README.md new file mode 100644 index 0000000000..65defb8a60 --- /dev/null +++ b/.github/workflows/README.md @@ -0,0 +1,75 @@ +# CLI Release Workflows + +This directory contains GitHub Actions workflows for building and releasing the Genkit CLI. + +## Current Workflows (Unsigned) + +### `build-cli-binaries.yml` - Build CLI Binaries (RC) +- **Purpose**: Build and release unsigned CLI binaries +- **Trigger**: Manual workflow dispatch +- **Inputs**: + - `version`: Version tag to build (e.g., `v1.0.0`, `v1.0.0-rc.1`) + - `create_rc`: Create release candidate with unsigned binaries (optional, default: false) +- **Outputs**: + - Binary artifacts for all platforms (Linux x64/ARM64, macOS x64/ARM64, Windows x64) + - Optional: GitHub release with unsigned binaries for testing + +### `promote-cli-release.yml` - Promote CLI Release (Unsigned) +- **Purpose**: Promote RC releases to final releases +- **Trigger**: Manual workflow dispatch +- **Inputs**: + - `rc_version`: RC version to promote (e.g., `v1.0.0-rc.1`) + - `final_version`: Final version tag (e.g., `v1.0.0`) +- **Outputs**: Final GitHub release with unsigned binaries + +## Preserved Workflows (Signed - Disabled) + +### `build-cli-binaries-signed.yml` - Build CLI Binaries (SIGNED - DISABLED) +- **Purpose**: Preserved for future code signing implementation +- **Status**: Disabled - shows error message directing users to unsigned workflow +- **Future**: Will be re-enabled when code signing is implemented + +### `promote-cli-release-signed.yml` - Promote CLI Release (SIGNED - DISABLED) +- **Purpose**: Preserved for future code signing implementation +- **Status**: Disabled - shows error message directing users to unsigned workflow +- **Future**: Will be re-enabled when code signing is implemented + +## Usage + +### For RC Releases: +1. Run "Build CLI Binaries (RC)" workflow +2. Set version (e.g., `v1.0.0-rc.1`) +3. Check "Create release" to publish RC with unsigned binaries + +### For Final Releases: +1. Run "Promote CLI Release (Unsigned)" workflow +2. Set RC version (e.g., `v1.0.0-rc.1`) +3. Set final version (e.g., `v1.0.0`) + +## Binary Naming Convention + +The workflows generate binaries with the following naming convention: +- `genkit-linux-x64` - Linux x64 +- `genkit-linux-arm64` - Linux ARM64 +- `genkit-darwin-x64` - macOS x64 (Intel) +- `genkit-darwin-arm64` - macOS ARM64 (Apple Silicon) +- `genkit-win32-x64.exe` - Windows x64 + +## Future Code Signing + +When code signing is implemented: +1. Rename workflows back to original names +2. Re-enable signed workflows +3. Update install script to use signed binaries +4. Update binary naming to include `-signed` suffix + +## Installation Script + +The `bin/install_cli` script has been updated to work with unsigned releases. It downloads the latest non-prerelease binaries from GitHub releases. + +## Notes + +- All current releases use unsigned binaries +- The install script (`genkit.tools`) works with unsigned binaries +- When code signing is ready, the signed workflows will be re-enabled +- The disabled workflows prevent accidental use of incomplete signing processes \ No newline at end of file diff --git a/.github/workflows/build-cli-binaries-signed.yml b/.github/workflows/build-cli-binaries-signed.yml new file mode 100644 index 0000000000..6bdce5cf70 --- /dev/null +++ b/.github/workflows/build-cli-binaries-signed.yml @@ -0,0 +1,46 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +name: Build CLI Binaries (SIGNED - DISABLED) + +on: + workflow_dispatch: + inputs: + _disabled: + description: 'This workflow is disabled - use the unsigned workflow instead' + required: true + type: boolean + default: false + +permissions: + contents: write + +jobs: + disabled: + runs-on: ubuntu-latest + steps: + - name: Workflow Disabled + run: | + echo "❌ This signed workflow is disabled!" + echo "" + echo "Please use the unsigned workflow instead:" + echo " - Workflow: 'Build CLI Binaries (RC)'" + echo " - File: .github/workflows/build-cli-binaries.yml" + echo "" + echo "This workflow is preserved for future code signing implementation." + echo "When code signing is ready, this workflow will be re-enabled." + exit 1 + diff --git a/.github/workflows/build-cli-binaries.yml b/.github/workflows/build-cli-binaries.yml index 433e2fcca0..e8d52a7334 100644 --- a/.github/workflows/build-cli-binaries.yml +++ b/.github/workflows/build-cli-binaries.yml @@ -47,11 +47,6 @@ jobs: target: darwin-arm64 - os: windows-latest target: win32-x64 - # Note: Windows ARM64 currently runs x64 binaries through emulation - # Native ARM64 support is not yet available in Bun - # See: https://github.com/oven-sh/bun/pull/11430 - # - os: windows-11-arm - # target: win32-arm64 runs-on: ${{ matrix.os }} @@ -63,7 +58,6 @@ jobs: shell: bash run: | VERSION="${{ inputs.version }}" - # Use grep for cross-platform regex matching if ! echo "$VERSION" | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.-]+)?(\+[a-zA-Z0-9.-]+)?$' > /dev/null; then echo "Error: Version '$VERSION' does not follow semantic versioning format (e.g., v1.0.0, v1.0.0-rc.1)" exit 1 @@ -337,7 +331,7 @@ jobs: ## Next Steps - After testing, these binaries will be signed and promoted to the final release. + After testing, these binaries will be promoted to the final release. ## Installation (Testing Only) @@ -356,8 +350,6 @@ jobs: release-assets/genkit-darwin-arm64/genkit-darwin-arm64 release-assets/genkit-win32-x64/genkit-win32-x64.exe - - create-rc-summary: needs: [create-rc] runs-on: ubuntu-latest @@ -387,6 +379,4 @@ jobs: echo "" >> $GITHUB_STEP_SUMMARY echo "### 📝 Next Steps" >> $GITHUB_STEP_SUMMARY echo "1. Test the RC binaries" >> $GITHUB_STEP_SUMMARY - echo "2. Sign the binaries using the manual workflow" >> $GITHUB_STEP_SUMMARY - echo "3. Promote to final release using the 'Promote CLI Release' workflow" >> $GITHUB_STEP_SUMMARY - + echo "2. Promote to final release using the 'Promote CLI Release (Unsigned)' workflow" >> $GITHUB_STEP_SUMMARY \ No newline at end of file diff --git a/.github/workflows/promote-cli-release-signed.yml b/.github/workflows/promote-cli-release-signed.yml new file mode 100644 index 0000000000..41bfad776c --- /dev/null +++ b/.github/workflows/promote-cli-release-signed.yml @@ -0,0 +1,45 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +name: Promote CLI Release (SIGNED - DISABLED) + +on: + workflow_dispatch: + inputs: + _disabled: + description: 'This workflow is disabled - use the unsigned workflow instead' + required: true + type: boolean + default: false + +permissions: + contents: write + +jobs: + disabled: + runs-on: ubuntu-latest + steps: + - name: Workflow Disabled + run: | + echo "❌ This signed workflow is disabled!" + echo "" + echo "Please use the unsigned workflow instead:" + echo " - Workflow: 'Promote CLI Release (Unsigned)'" + echo " - File: .github/workflows/promote-cli-release.yml" + echo "" + echo "This workflow is preserved for future code signing implementation." + echo "When code signing is ready, this workflow will be re-enabled." + exit 1 \ No newline at end of file diff --git a/.github/workflows/promote-cli-release.yml b/.github/workflows/promote-cli-release.yml index 86001de85f..ca47fdec78 100644 --- a/.github/workflows/promote-cli-release.yml +++ b/.github/workflows/promote-cli-release.yml @@ -14,7 +14,7 @@ # # SPDX-License-Identifier: Apache-2.0 -name: Promote CLI Release +name: Promote CLI Release (Unsigned) on: workflow_dispatch: @@ -69,13 +69,13 @@ jobs: echo " RC Version: $RC_VERSION" echo " Final Version: $FINAL_VERSION" - - name: Validate signed binaries exist + - name: Validate binaries exist env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | RC_VERSION="${{ inputs.rc_version }}" - echo "Checking for signed binaries in RC release: $RC_VERSION" + echo "Checking for binaries in RC release: $RC_VERSION" # Get RC release information RELEASE_INFO=$(curl -s -H "Authorization: token $GITHUB_TOKEN" \ @@ -93,83 +93,79 @@ jobs: echo "$ASSETS" echo "" - # Expected signed binaries - EXPECTED_SIGNED=( - "genkit-linux-x64-signed" - "genkit-linux-arm64-signed" - "genkit-darwin-x64-signed" - "genkit-darwin-arm64-signed" - "genkit-win32-x64-signed.exe" + # Expected binaries + EXPECTED_BINARIES=( + "genkit-linux-x64" + "genkit-linux-arm64" + "genkit-darwin-x64" + "genkit-darwin-arm64" + "genkit-win32-x64.exe" ) - # Check for signed binaries - MISSING_SIGNED=() - for platform in "${EXPECTED_SIGNED[@]}"; do + # Check for binaries + MISSING_BINARIES=() + for platform in "${EXPECTED_BINARIES[@]}"; do if ! echo "$ASSETS" | grep -q "^$platform$"; then - MISSING_SIGNED+=("$platform") + MISSING_BINARIES+=("$platform") else - echo "✓ Found signed binary: $platform" + echo "✓ Found binary: $platform" fi done - if [[ ${#MISSING_SIGNED[@]} -gt 0 ]]; then + if [[ ${#MISSING_BINARIES[@]} -gt 0 ]]; then echo "" - echo "❌ Missing signed binaries:" - printf ' - %s\n' "${MISSING_SIGNED[@]}" - echo "" - echo "Please ensure all binaries are signed before promoting to final release." + echo "❌ Missing binaries:" + printf ' - %s\n' "${MISSING_BINARIES[@]}" exit 1 fi echo "" - echo "✅ All expected signed binaries found" + echo "✅ All expected binaries found" - name: Create release directory run: mkdir -p release-assets - - name: Download signed binaries from RC + - name: Download binaries from RC env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | RC_VERSION="${{ inputs.rc_version }}" - echo "Downloading signed binaries from RC release..." + echo "Downloading binaries from RC release..." - # Expected signed binaries with their final names - declare -A BINARY_MAP=( - ["genkit-linux-x64-signed"]="genkit-linux-x64" - ["genkit-linux-arm64-signed"]="genkit-linux-arm64" - ["genkit-darwin-x64-signed"]="genkit-darwin-x64" - ["genkit-darwin-arm64-signed"]="genkit-darwin-arm64" - ["genkit-win32-x64-signed.exe"]="genkit-win32-x64.exe" + # Expected binaries + BINARIES=( + "genkit-linux-x64" + "genkit-linux-arm64" + "genkit-darwin-x64" + "genkit-darwin-arm64" + "genkit-win32-x64.exe" ) - # Download each signed binary - for signed_name in "${!BINARY_MAP[@]}"; do - final_name="${BINARY_MAP[$signed_name]}" - - echo "Downloading $signed_name as $final_name..." + # Download each binary + for binary in "${BINARIES[@]}"; do + echo "Downloading $binary..." # Get download URL for the asset DOWNLOAD_URL=$(curl -s -H "Authorization: token $GITHUB_TOKEN" \ "https://api.github.com/repos/${{ github.repository }}/releases/tags/$RC_VERSION" | \ - jq -r ".assets[] | select(.name == \"$signed_name\") | .url") + jq -r ".assets[] | select(.name == \"$binary\") | .url") if [[ -z "$DOWNLOAD_URL" || "$DOWNLOAD_URL" == "null" ]]; then - echo "❌ Failed to get download URL for $signed_name" + echo "❌ Failed to get download URL for $binary" exit 1 fi # Download the asset curl -L -H "Authorization: token $GITHUB_TOKEN" \ -H "Accept: application/octet-stream" \ - "$DOWNLOAD_URL" -o "release-assets/$final_name" + "$DOWNLOAD_URL" -o "release-assets/$binary" - echo "✓ Downloaded $final_name" + echo "✓ Downloaded $binary" done echo "" - echo "All signed binaries downloaded successfully" + echo "All binaries downloaded successfully" ls -la release-assets/ - name: Generate changelog @@ -273,9 +269,7 @@ jobs: release-assets/genkit-darwin-x64 release-assets/genkit-darwin-arm64 release-assets/genkit-win32-x64.exe - make_latest: true - - + make_latest: true # This is a final release, so it should be the latest - name: Update latest tag run: | @@ -283,13 +277,13 @@ jobs: git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" - # The create-release action should have created the tag + # The action should have created the tag # Let's fetch it first to make sure we have it git fetch --tags # Verify the tag exists if ! git rev-parse ${{ inputs.final_version }} >/dev/null 2>&1; then - echo "Warning: Tag ${{ inputs.final_version }} was not created by create-release action" + echo "Warning: Tag ${{ inputs.final_version }} was not created by the release action" echo "Creating it manually..." git tag ${{ inputs.final_version }} git push origin ${{ inputs.final_version }} @@ -330,4 +324,4 @@ jobs: echo "### 📥 Quick Install" >> $GITHUB_STEP_SUMMARY echo "\`\`\`bash" >> $GITHUB_STEP_SUMMARY echo "curl -sL https://genkit.tools | bash" >> $GITHUB_STEP_SUMMARY - echo "\`\`\`" >> $GITHUB_STEP_SUMMARY \ No newline at end of file + echo "\`\`\`" >> $GITHUB_STEP_SUMMARY \ No newline at end of file diff --git a/bin/install_cli b/bin/install_cli index 6ced7fb39a..917e7f7ee3 100644 --- a/bin/install_cli +++ b/bin/install_cli @@ -250,7 +250,7 @@ if [[ -z "$MACHINE" ]]; then fi # We have enough information to generate the binary's download URL. -# Use GitHub API to find the latest non-prerelease (which will contain signed binaries) +# Use GitHub API to find the latest non-prerelease (which will contain unsigned binaries) echo "-- Finding latest release..." LATEST_RELEASE=$(curl -sfL https://api.github.com/repos/firebase/genkit/releases/latest | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/') @@ -263,7 +263,7 @@ fi echo "-- Latest release: $LATEST_RELEASE" -# Construct the download URL for the signed binary from the latest release +# Construct the download URL for the unsigned binary from the latest release DOWNLOAD_URL="https://github.com/firebase/genkit/releases/download/$LATEST_RELEASE/genkit-$MACHINE" # Check if the binary exists at this URL From daa13c564a5ceef7c71cfb291bd207f26cbd2f31 Mon Sep 17 00:00:00 2001 From: Jacob Cable Date: Fri, 25 Jul 2025 11:57:27 +0100 Subject: [PATCH 10/10] fix: improve rendering of script in html, and correct domain --- bin/install_cli | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) mode change 100644 => 100755 bin/install_cli diff --git a/bin/install_cli b/bin/install_cli old mode 100644 new mode 100755 index 917e7f7ee3..77362d1948 --- a/bin/install_cli +++ b/bin/install_cli @@ -15,7 +15,7 @@ # # SPDX-License-Identifier: Apache-2.0 - +# HTML rendering helpers (these are treated as HTML comments when served as web page) ## ## ## @@ -23,7 +23,7 @@ ## # Configuration variables -DOMAIN="genkit.tools" +DOMAIN="cli.genkit.dev" TRACKING_ID="UA-XXXXXXXXX-X" # Not used when analytics is commented out : ==========================================