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 828c6fd718..e8d52a7334 100644 --- a/.github/workflows/build-cli-binaries.yml +++ b/.github/workflows/build-cli-binaries.yml @@ -14,10 +14,23 @@ # # SPDX-License-Identifier: Apache-2.0 -name: Build CLI Binaries +name: Build CLI Binaries (RC) on: workflow_dispatch: + inputs: + version: + description: 'Version tag to build (e.g., v1.0.0, v1.0.0-rc.1)' + required: true + type: string + create_rc: + description: 'Create release candidate with unsigned binaries' + required: false + type: boolean + default: false + +permissions: + contents: write jobs: build: @@ -34,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 }} @@ -46,6 +54,16 @@ jobs: - name: Checkout code uses: actions/checkout@v4 + - name: Validate version format + shell: bash + run: | + VERSION="${{ inputs.version }}" + 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: Setup Bun uses: oven-sh/setup-bun@v2 with: @@ -101,7 +119,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 test: needs: build @@ -253,4 +271,112 @@ 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-rc: + needs: [build, test] + runs-on: ubuntu-latest + if: inputs.create_rc + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Generate changelog + id: changelog + run: | + # 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 + 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: Download all binary artifacts + uses: actions/download-artifact@v4 + with: + path: release-assets + + - name: Create Release Candidate + id: create_rc + uses: softprops/action-gh-release@v2 + with: + tag_name: ${{ inputs.version }} + 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/${{ 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 + + ${{ steps.changelog.outputs.changelog }} + + ## Next Steps + + After testing, these binaries will be promoted to the final release. + + ## Installation (Testing Only) + + ```bash + # Download and test the RC binary + 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 + + create-rc-summary: + needs: [create-rc] + runs-on: ubuntu-latest + if: inputs.create_rc + + steps: + - name: Create job summary + run: | + 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. 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 new file mode 100644 index 0000000000..ca47fdec78 --- /dev/null +++ b/.github/workflows/promote-cli-release.yml @@ -0,0 +1,327 @@ +# 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 (Unsigned) + +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 + +permissions: + contents: write + +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 binaries exist + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + RC_VERSION="${{ inputs.rc_version }}" + + echo "Checking for 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 binaries + EXPECTED_BINARIES=( + "genkit-linux-x64" + "genkit-linux-arm64" + "genkit-darwin-x64" + "genkit-darwin-arm64" + "genkit-win32-x64.exe" + ) + + # Check for binaries + MISSING_BINARIES=() + for platform in "${EXPECTED_BINARIES[@]}"; do + if ! echo "$ASSETS" | grep -q "^$platform$"; then + MISSING_BINARIES+=("$platform") + else + echo "✓ Found binary: $platform" + fi + done + + if [[ ${#MISSING_BINARIES[@]} -gt 0 ]]; then + echo "" + echo "❌ Missing binaries:" + printf ' - %s\n' "${MISSING_BINARIES[@]}" + exit 1 + fi + + echo "" + echo "✅ All expected binaries found" + + - name: Create release directory + run: mkdir -p release-assets + + - name: Download binaries from RC + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + RC_VERSION="${{ inputs.rc_version }}" + + echo "Downloading binaries from RC release..." + + # Expected binaries + BINARIES=( + "genkit-linux-x64" + "genkit-linux-arm64" + "genkit-darwin-x64" + "genkit-darwin-arm64" + "genkit-win32-x64.exe" + ) + + # 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 == \"$binary\") | .url") + + if [[ -z "$DOWNLOAD_URL" || "$DOWNLOAD_URL" == "null" ]]; then + 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/$binary" + + echo "✓ Downloaded $binary" + done + + echo "" + echo "All binaries downloaded successfully" + ls -la release-assets/ + + - name: Generate changelog + id: changelog + run: | + # 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 + 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: softprops/action-gh-release@v2 + with: + tag_name: ${{ inputs.final_version }} + 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 + 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 # This is a final release, so it should be the latest + + - 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 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 the 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/bin/install_cli b/bin/install_cli old mode 100644 new mode 100755 index 7c01e3f132..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 : ========================================== @@ -250,12 +250,49 @@ 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 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/') + +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 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 +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") diff --git a/scripts/sign-and-upload-binaries.sh b/scripts/sign-and-upload-binaries.sh new file mode 100755 index 0000000000..f3c3901c30 --- /dev/null +++ b/scripts/sign-and-upload-binaries.sh @@ -0,0 +1,188 @@ +#!/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 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" + + # 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" + + # 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_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 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_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 "" +done + +echo "=== Signing simulation complete ===" +echo "" +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