From 476375963ac294375881f2a8dc3c9755f5d1640a Mon Sep 17 00:00:00 2001 From: Phil Leggetter Date: Tue, 7 Oct 2025 22:42:55 +0100 Subject: [PATCH 1/7] feat: implement Homebrew cask migration with dual distribution Migrated from Homebrew formula to cask following GoReleaser best practices for distributing pre-built binaries. Maintained backward compatibility with a dual distribution strategy during the transition period. Changes: - Added scripts/completions.sh to pre-generate shell completions - Updated .goreleaser/mac.yml with dual distribution (formula + cask) - Formula (brews): Added deprecation warning directing users to cask - Cask (homebrew_casks): Configured with automatic completion installation - Added completions/ to .gitignore Key improvements: - Completions now auto-installed for both formula and cask users - Conflict prevention between formula and cask installations - Clear migration path with 3-6 month deprecation timeline - Follows GoReleaser v2.10+ recommendations for binary distribution The formula will be removed after a 3-6 month transition period, giving users time to migrate to the cask installation method. Ref: https://goreleaser.com/blog/goreleaser-v2.10/\#homebrew-casks --- .gitignore | 1 + .goreleaser/mac.yml | 69 ++++++++++++++++++++++++++++++------------ scripts/completions.sh | 30 ++++++++++++++++++ 3 files changed, 80 insertions(+), 20 deletions(-) create mode 100755 scripts/completions.sh diff --git a/.gitignore b/.gitignore index ec677969..fcd590ee 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ +completions/ dist/ bin/ coverage.txt diff --git a/.goreleaser/mac.yml b/.goreleaser/mac.yml index 4752a63f..5006f584 100644 --- a/.goreleaser/mac.yml +++ b/.goreleaser/mac.yml @@ -5,6 +5,7 @@ before: hooks: - go mod download - go generate ./... + - ./scripts/completions.sh project_name: hookdeck builds: - id: hookdeck-darwin @@ -37,29 +38,57 @@ checksum: name_template: "{{ .ProjectName }}-checksums.txt" snapshot: name_template: "{{ .Tag }}-next" +archives: + - id: hookdeck + files: + - completions/* + - LICENSE* + - README* + - CHANGELOG* +brews: + - name: hookdeck + ids: + - hookdeck + repository: + owner: hookdeck + name: homebrew-hookdeck + directory: Formula + homepage: https://hookdeck.com + description: Alternative to ngrok for localhost webhook development + + install: | + bin.install "hookdeck" + + # Install completions from pre-generated files + bash_completion.install "completions/hookdeck.bash" => "hookdeck" + zsh_completion.install "completions/_hookdeck" + + caveats: | + ⚠️ WARNING: This formula is deprecated! + + Please migrate to the cask version for the latest updates: + brew uninstall hookdeck + brew install --cask hookdeck/hookdeck/hookdeck + + The formula distribution will be removed in approximately 3-6 months. + Learn more: https://goreleaser.com/blog/goreleaser-v2.10/#homebrew-casks + homebrew_casks: - name: hookdeck + ids: + - hookdeck repository: owner: hookdeck name: homebrew-hookdeck - commit_author: - name: hookdeck - email: support@hookdeck.com homepage: https://hookdeck.com - description: Hookdeck CLI utility - caveats: "❤ Thanks for installing the Hookdeck CLI! If this is your first time using the CLI, be sure to run `hookdeck login` first." - custom_block: | - postflight do - system "hookdeck", "completion", "--shell", "bash" - system "hookdeck", "completion", "--shell", "zsh" - bash_completion.install "hookdeck-completion.bash" - zsh_completion.install "hookdeck-completion.zsh" - (zsh_completion/"_hookdeck").write <<~EOS - #compdef hookdeck - _hookdeck () { - local e - e=$(dirname ${funcsourcetrace[1]%:*})/hookdeck-completion.zsh - if [[ -f $e ]]; then source $e; fi - } - EOS - end + description: Alternative to ngrok for localhost webhook development + # Install shell completions automatically + completions: + bash: "completions/hookdeck.bash" + zsh: "completions/_hookdeck" + + caveats: | + Thanks for installing the Hookdeck CLI! + + If this is your first time using the CLI, run: + hookdeck login diff --git a/scripts/completions.sh b/scripts/completions.sh new file mode 100755 index 00000000..e735cc52 --- /dev/null +++ b/scripts/completions.sh @@ -0,0 +1,30 @@ +#!/bin/sh +set -e + +# Generate shell completions for Hookdeck CLI +# This script is run during the GoReleaser build process to pre-generate +# completion files that will be included in the release archives. + +rm -rf completions +mkdir completions + +# Use 'go run .' to compile and run the CLI to generate completions +# This works on any platform that can build Go code +# The completion command writes files to the current directory, so we cd into completions/ +echo "Generating bash completion..." +(cd completions && go run .. completion --shell bash) + +echo "Generating zsh completion..." +(cd completions && go run .. completion --shell zsh) + +# Rename the generated files to match GoReleaser expectations +mv completions/hookdeck-completion.bash completions/hookdeck.bash +mv completions/hookdeck-completion.zsh completions/_hookdeck + +# Fish completion is not currently supported by the CLI +# If it gets added in the future, uncomment this: +# echo "Generating fish completion..." +# go run . completion --shell fish > completions/hookdeck.fish + +echo "✅ Completions generated successfully in completions/" +ls -lh completions/ \ No newline at end of file From 3d297c8bea5314bfdfc773108ffd7eaa882c6094 Mon Sep 17 00:00:00 2001 From: Phil Leggetter Date: Tue, 7 Oct 2025 23:52:27 +0100 Subject: [PATCH 2/7] feat: Implement Homebrew cask migration with dual distribution - Update GoReleaser config with both formula (deprecated) and cask - Add conflicts_with directive to prevent formula/cask conflicts - Create comprehensive Homebrew build validation script - Add GitHub Actions workflow for automated testing - Move acceptance tests to test-scripts/ directory The migration provides a transition period where both formula and cask work, with clear deprecation warnings guiding users to migrate from formula to cask distribution. Following GoReleaser best practices for distributing pre-built binaries. --- ...cceptance-test.yml => test-acceptance.yml} | 6 +- .github/workflows/test-homebrew-build.yml | 63 ++ .goreleaser/mac.yml | 5 + .../test-acceptance.sh | 0 test-scripts/test-homebrew-build.sh | 659 ++++++++++++++++++ 5 files changed, 730 insertions(+), 3 deletions(-) rename .github/workflows/{acceptance-test.yml => test-acceptance.yml} (76%) create mode 100644 .github/workflows/test-homebrew-build.yml rename scripts/acceptance-test.sh => test-scripts/test-acceptance.sh (100%) create mode 100755 test-scripts/test-homebrew-build.sh diff --git a/.github/workflows/acceptance-test.yml b/.github/workflows/test-acceptance.yml similarity index 76% rename from .github/workflows/acceptance-test.yml rename to .github/workflows/test-acceptance.yml index 3da9f1ed..1487f070 100644 --- a/.github/workflows/acceptance-test.yml +++ b/.github/workflows/test-acceptance.yml @@ -17,10 +17,10 @@ jobs: - name: Set up Go uses: actions/setup-go@v3 with: - go-version: '1.18' + go-version: "1.18" - name: Make script executable - run: chmod +x scripts/acceptance-test.sh + run: chmod +x test-scripts/test-acceptance.sh - name: Run acceptance tests - run: ./scripts/acceptance-test.sh \ No newline at end of file + run: ./test-scripts/test-acceptance.sh diff --git a/.github/workflows/test-homebrew-build.yml b/.github/workflows/test-homebrew-build.yml new file mode 100644 index 00000000..0a6f8074 --- /dev/null +++ b/.github/workflows/test-homebrew-build.yml @@ -0,0 +1,63 @@ +name: Homebrew Build Validation + +on: + pull_request: + branches: + - main + paths: + - ".goreleaser/**" + - "scripts/completions.sh" + - "test-scripts/test-homebrew-build.sh" + - ".github/workflows/test-homebrew-build.yml" + + workflow_dispatch: + inputs: + run_install_tests: + description: "Run installation tests" + required: false + default: "false" + type: choice + options: + - "false" + - "true" + +jobs: + test-homebrew-build: + runs-on: macos-latest + + steps: + - name: Check out code + uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version-file: "go.mod" + + - name: Verify Homebrew is installed + run: | + echo "Homebrew version:" + brew --version + + - name: Install GoReleaser + run: brew install goreleaser + + - name: Make test script executable + run: chmod +x test-scripts/test-homebrew-build.sh + + - name: Run build validation + run: ./test-scripts/test-homebrew-build.sh + + - name: Run installation tests + if: github.event.inputs.run_install_tests == 'true' + run: ./test-scripts/test-homebrew-build.sh --install + + - name: Upload generated Homebrew files + if: always() + uses: actions/upload-artifact@v4 + with: + name: homebrew-files + path: | + dist/homebrew/Formula/hookdeck.rb + dist/homebrew/Casks/hookdeck.rb + retention-days: 7 diff --git a/.goreleaser/mac.yml b/.goreleaser/mac.yml index 5006f584..486f4a10 100644 --- a/.goreleaser/mac.yml +++ b/.goreleaser/mac.yml @@ -87,6 +87,11 @@ homebrew_casks: bash: "completions/hookdeck.bash" zsh: "completions/_hookdeck" + # Prevent installation alongside the deprecated formula + # Note: The deprecated 'conflicts' field no longer works, so we use custom_block + custom_block: | + conflicts_with formula: "hookdeck" + caveats: | Thanks for installing the Hookdeck CLI! diff --git a/scripts/acceptance-test.sh b/test-scripts/test-acceptance.sh similarity index 100% rename from scripts/acceptance-test.sh rename to test-scripts/test-acceptance.sh diff --git a/test-scripts/test-homebrew-build.sh b/test-scripts/test-homebrew-build.sh new file mode 100755 index 00000000..db4dfa69 --- /dev/null +++ b/test-scripts/test-homebrew-build.sh @@ -0,0 +1,659 @@ +#!/bin/bash + +# Homebrew Build Validation Test Script for Hookdeck CLI +# -------------------------------------------------------- +# This script validates that GoReleaser generates correct Homebrew files +# for the Hookdeck CLI without attempting to install them. +# +# It validates that: +# - GoReleaser snapshot build completes successfully +# - Homebrew formula and cask files are generated +# - Formula contains deprecation warning +# - Formula references completion files correctly +# - Cask has proper bash_completion directive +# - Cask has proper zsh_completion directive +# - Cask has conflicts with formula +# - Completion files are bundled in the tarball +# +# Usage: +# ./test-scripts/test-homebrew-build.sh # Build validation only +# ./test-scripts/test-homebrew-build.sh --install # Build + installation testing +# ./test-scripts/test-homebrew-build.sh --install --bypass-gatekeeper # Full testing with binary execution +# +# Prerequisites: +# - Go installed +# - GoReleaser installed (brew install goreleaser) +# - Homebrew installed (for --install testing) +# +# Note: Without --install, this script only validates BUILD outputs. +# With --install, it also tests actual installation from local tap. +# For CLI functionality testing, use test-scripts/test-acceptance.sh instead. +# +# Unsigned Binary Note: +# Local snapshot builds produce unsigned binaries which macOS Gatekeeper blocks. +# To test binary execution locally, the script can remove the quarantine attribute. +# This requires manual approval for security reasons. + +set -e + +# Parse command line arguments +RUN_INSTALL_TESTS=false +BYPASS_GATEKEEPER=false + +while [[ $# -gt 0 ]]; do + case $1 in + --install) + RUN_INSTALL_TESTS=true + shift + ;; + --bypass-gatekeeper) + BYPASS_GATEKEEPER=true + shift + ;; + *) + echo "Unknown option: $1" + echo "Usage: $0 [--install] [--bypass-gatekeeper]" + exit 1 + ;; + esac +done + +# Global variables for cleanup +LOCAL_TAP_PATH="" +FORMULA_INSTALLED=false +CASK_INSTALLED=false + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# Utility functions +echo_success() { + echo -e "${GREEN}✓ $1${NC}" +} + +echo_error() { + echo -e "${RED}✗ $1${NC}" +} + +echo_info() { + echo -e "${YELLOW}→ $1${NC}" +} + +echo_section() { + echo "" + echo -e "${BLUE}============================================${NC}" + echo -e "${BLUE}$1${NC}" + echo -e "${BLUE}============================================${NC}" + echo "" +} + +# Cleanup function for trap +cleanup_installations() { + if [ "$RUN_INSTALL_TESTS" = false ]; then + return 0 + fi + + echo "" + echo_section "Cleaning Up Test Installations" + + # Uninstall formula if installed + if [ "$FORMULA_INSTALLED" = true ]; then + echo_info "Uninstalling formula..." + if brew uninstall hookdeck 2>/dev/null || true; then + echo_success "Formula uninstalled" + fi + fi + + # Uninstall cask if installed + if [ "$CASK_INSTALLED" = true ]; then + echo_info "Uninstalling cask..." + if brew uninstall --cask hookdeck 2>/dev/null || true; then + echo_success "Cask uninstalled" + fi + fi + + # Remove local tap + if [ -n "$LOCAL_TAP_PATH" ] && [ -d "$LOCAL_TAP_PATH" ]; then + echo_info "Removing local test tap..." + rm -rf "$LOCAL_TAP_PATH" + echo_success "Local tap removed" + fi + + echo_success "Cleanup completed" +} + +# Set up trap for cleanup on exit +trap cleanup_installations EXIT + +# Check prerequisites +check_prerequisites() { + echo_section "Checking Prerequisites" + + if ! command -v go &> /dev/null; then + echo_error "Go is not installed. Please install Go first." + exit 1 + fi + echo_success "Go is installed: $(go version)" + + if ! command -v goreleaser &> /dev/null; then + echo_error "GoReleaser is not installed. Install with: brew install goreleaser" + exit 1 + fi + echo_success "GoReleaser is installed: $(goreleaser --version | head -n1)" + + if [ "$RUN_INSTALL_TESTS" = true ]; then + if ! command -v brew &> /dev/null; then + echo_error "Homebrew is not installed. Homebrew is required for --install testing." + exit 1 + fi + echo_success "Homebrew is installed: $(brew --version | head -n1)" + fi +} + +# Clean previous build artifacts +clean_dist() { + echo_section "Cleaning Previous Build Artifacts" + + if [ -d "dist" ]; then + echo_info "Removing existing dist/ directory..." + rm -rf dist + echo_success "Cleaned dist/ directory" + else + echo_info "No dist/ directory to clean" + fi +} + +# Run GoReleaser snapshot build +run_goreleaser_build() { + echo_section "Running GoReleaser Snapshot Build" + + echo_info "Building with: goreleaser release --snapshot --clean --config .goreleaser/mac.yml" + if goreleaser release --snapshot --clean --config .goreleaser/mac.yml; then + echo_success "GoReleaser build completed successfully" + else + echo_error "GoReleaser build failed" + exit 1 + fi +} + +# Validate Homebrew formula file +validate_formula() { + echo_section "Validating Formula (dist/homebrew/Formula/hookdeck.rb)" + + local formula_file="dist/homebrew/Formula/hookdeck.rb" + + if [ ! -f "$formula_file" ]; then + echo_error "Formula file not found at $formula_file" + return 1 + fi + echo_success "Formula file exists" + + # Check for deprecation warning + if grep -q "WARNING: This formula is deprecated" "$formula_file"; then + echo_success "Formula contains deprecation warning" + else + echo_error "Formula missing deprecation warning" + return 1 + fi + + # Check for bash completion reference + if grep -q 'bash_completion.install' "$formula_file"; then + echo_success "Formula contains bash_completion directive" + else + echo_error "Formula missing bash_completion directive" + return 1 + fi + + # Check for zsh completion reference + if grep -q 'zsh_completion.install' "$formula_file"; then + echo_success "Formula contains zsh_completion directive" + else + echo_error "Formula missing zsh_completion directive" + return 1 + fi + + # Check for completion files in install block + if grep -q 'completions/hookdeck.bash' "$formula_file"; then + echo_success "Formula references completions/hookdeck.bash" + else + echo_error "Formula missing reference to completions/hookdeck.bash" + return 1 + fi + + if grep -q 'completions/_hookdeck' "$formula_file"; then + echo_success "Formula references completions/_hookdeck" + else + echo_error "Formula missing reference to completions/_hookdeck" + return 1 + fi + + echo_success "Formula validation passed" + return 0 +} + +# Validate Homebrew cask file +validate_cask() { + echo_section "Validating Cask (dist/homebrew/Casks/hookdeck.rb)" + + local cask_file="dist/homebrew/Casks/hookdeck.rb" + + if [ ! -f "$cask_file" ]; then + echo_error "Cask file not found at $cask_file" + return 1 + fi + echo_success "Cask file exists" + + # Check for bash completion + if grep -q 'bash.*completion.*hookdeck\.bash' "$cask_file"; then + echo_success "Cask contains bash_completion directive" + else + echo_error "Cask missing bash_completion directive" + return 1 + fi + + # Check for zsh completion + if grep -q 'zsh.*completion.*_hookdeck' "$cask_file"; then + echo_success "Cask contains zsh_completion directive" + else + echo_error "Cask missing zsh_completion directive" + return 1 + fi + + # Check for conflicts with formula + if grep -q 'conflicts.*formula.*hookdeck' "$cask_file"; then + echo_success "Cask contains conflicts with formula directive" + else + echo_error "Cask missing conflicts with formula directive" + return 1 + fi + + echo_success "Cask validation passed" + return 0 +} + +# Validate completion files in tarball +validate_completions_in_tarball() { + echo_section "Validating Completion Files in Tarball" + + # Find the darwin tarball (there should be one for amd64 or arm64) + local tarball=$(find dist -name "hookdeck_*_darwin_*.tar.gz" | head -n1) + + if [ -z "$tarball" ]; then + echo_error "No darwin tarball found in dist/" + return 1 + fi + echo_success "Found tarball: $tarball" + + # Check if tarball contains bash completion + if tar -tzf "$tarball" | grep -q "completions/hookdeck.bash"; then + echo_success "Tarball contains completions/hookdeck.bash" + else + echo_error "Tarball missing completions/hookdeck.bash" + return 1 + fi + + # Check if tarball contains zsh completion + if tar -tzf "$tarball" | grep -q "completions/_hookdeck"; then + echo_success "Tarball contains completions/_hookdeck" + else + echo_error "Tarball missing completions/_hookdeck" + return 1 + fi + + echo_success "Completion files validation passed" + return 0 +} + +# Set up local Homebrew tap for testing +setup_local_tap() { + echo_section "Setting Up Local Test Tap" + + local tap_name="hookdeck-test/hookdeck-test" + LOCAL_TAP_PATH="$(brew --repository)/Library/Taps/hookdeck-test/homebrew-hookdeck-test" + + echo_info "Creating local tap at: $LOCAL_TAP_PATH" + mkdir -p "$LOCAL_TAP_PATH" + + echo_info "Copying Homebrew files to local tap..." + cp -r dist/homebrew/* "$LOCAL_TAP_PATH/" + + # Patch formula to use local file:// URLs for testing + echo_info "Patching formula to use local file URLs for testing..." + local formula_file="$LOCAL_TAP_PATH/Formula/hookdeck.rb" + local current_dir="$(pwd)" + + # Replace GitHub URLs with local file:// URLs + sed -i '' "s|https://github.com/hookdeck/hookdeck-cli/releases/download/v[^/]*/|file://$current_dir/dist/|g" "$formula_file" + + # Patch cask to use local file:// URLs for testing + echo_info "Patching cask to use local file URLs for testing..." + local cask_file="$LOCAL_TAP_PATH/Casks/hookdeck.rb" + + # Replace GitHub URLs with local file:// URLs + sed -i '' "s|https://github.com/hookdeck/hookdeck-cli/releases/download/v[^/]*/|file://$current_dir/dist/|g" "$cask_file" + + echo_success "Local tap created and patched successfully" + echo_info "Tap name: $tap_name" +} + +# Test formula installation +test_formula_installation() { + echo_section "Testing Formula Installation" + + local tap_name="hookdeck-test/hookdeck-test/hookdeck" + + echo_info "Installing formula: brew install $tap_name" + if brew install "$tap_name"; then + echo_success "Formula installed successfully" + FORMULA_INSTALLED=true + else + echo_error "Formula installation failed" + return 1 + fi + + # Verify binary works (may fail on macOS due to unsigned binary) + echo_info "Testing binary: hookdeck version" + + # Try to run the binary + if hookdeck version 2>/dev/null; then + echo_success "Binary is functional" + else + # Binary execution failed - likely Gatekeeper + if [ "$BYPASS_GATEKEEPER" = true ]; then + echo_info "Binary blocked by Gatekeeper - attempting to bypass..." + local binary_path="$(which hookdeck)" + echo_info "Removing quarantine attribute from: $binary_path" + + if sudo xattr -d com.apple.quarantine "$binary_path" 2>/dev/null; then + echo_success "Quarantine attribute removed" + + # Try again + if hookdeck version; then + echo_success "Binary is functional after Gatekeeper bypass" + else + echo_error "Binary still failed to execute after bypass" + return 1 + fi + else + echo_error "Failed to remove quarantine attribute (sudo required)" + return 1 + fi + else + echo_info "Binary test skipped (unsigned binaries are blocked by macOS Gatekeeper)" + echo_info "Use --bypass-gatekeeper flag to remove quarantine attribute and test binary" + echo_info "Formula installation succeeded (binary path and completions verified)" + fi + fi + + # Verify bash completion is installed + local bash_completion_path="$(brew --prefix)/etc/bash_completion.d/hookdeck" + echo_info "Checking bash completion at: $bash_completion_path" + if [ -f "$bash_completion_path" ]; then + echo_success "Bash completion installed" + else + echo_error "Bash completion not found at $bash_completion_path" + return 1 + fi + + # Verify zsh completion is installed + local zsh_completion_path="$(brew --prefix)/share/zsh/site-functions/_hookdeck" + echo_info "Checking zsh completion at: $zsh_completion_path" + if [ -f "$zsh_completion_path" ]; then + echo_success "Zsh completion installed" + else + echo_error "Zsh completion not found at $zsh_completion_path" + return 1 + fi + + echo_success "Formula installation validation passed" + return 0 +} + +# Test cask installation +test_cask_installation() { + echo_section "Testing Cask Installation" + + local tap_name="hookdeck-test/hookdeck-test/hookdeck" + + echo_info "Installing cask: brew install --cask $tap_name" + if brew install --cask "$tap_name"; then + echo_success "Cask installed successfully" + CASK_INSTALLED=true + else + echo_error "Cask installation failed" + return 1 + fi + + # Verify binary works (may fail on macOS due to unsigned binary) + echo_info "Testing binary: hookdeck version" + + # Try to run the binary + if hookdeck version 2>/dev/null; then + echo_success "Binary is functional" + else + # Binary execution failed - likely Gatekeeper + if [ "$BYPASS_GATEKEEPER" = true ]; then + echo_info "Binary blocked by Gatekeeper - attempting to bypass..." + local binary_path="$(which hookdeck)" + echo_info "Removing quarantine attribute from: $binary_path" + + if sudo xattr -d com.apple.quarantine "$binary_path" 2>/dev/null; then + echo_success "Quarantine attribute removed" + + # Try again + if hookdeck version; then + echo_success "Binary is functional after Gatekeeper bypass" + else + echo_error "Binary still failed to execute after bypass" + return 1 + fi + else + echo_error "Failed to remove quarantine attribute (sudo required)" + return 1 + fi + else + echo_info "Binary test skipped (unsigned binaries are blocked by macOS Gatekeeper)" + echo_info "Use --bypass-gatekeeper flag to remove quarantine attribute and test binary" + echo_info "Cask installation succeeded (binary path and completions verified)" + fi + fi + + # Verify bash completion is installed + local bash_completion_path="$(brew --prefix)/etc/bash_completion.d/hookdeck" + echo_info "Checking bash completion at: $bash_completion_path" + if [ -f "$bash_completion_path" ]; then + echo_success "Bash completion installed" + else + echo_error "Bash completion not found at $bash_completion_path" + return 1 + fi + + # Verify zsh completion is installed + local zsh_completion_path="$(brew --prefix)/share/zsh/site-functions/_hookdeck" + echo_info "Checking zsh completion at: $zsh_completion_path" + if [ -f "$zsh_completion_path" ]; then + echo_success "Zsh completion installed" + else + echo_error "Zsh completion not found at $zsh_completion_path" + return 1 + fi + + echo_success "Cask installation validation passed" + return 0 +} + +# Test conflict detection +test_conflict_detection() { + echo_section "Testing Conflict Detection" + + local tap_name="hookdeck-test/hookdeck-test/hookdeck" + + # First, install formula with --formula flag to ensure it's installed as formula + echo_info "Installing formula first: brew install --formula $tap_name" + if brew install --formula "$tap_name"; then + echo_success "Formula installed successfully" + FORMULA_INSTALLED=true + else + echo_error "Formula installation failed" + return 1 + fi + + # Ensure the formula is linked + echo_info "Ensuring formula is linked..." + if brew link hookdeck 2>/dev/null || true; then + echo_success "Formula linked" + fi + + # Try to install cask - should fail with conflict + echo_info "Attempting to install cask (should fail with conflict)..." + local output + if output=$(brew install --cask "$tap_name" 2>&1); then + # Check if it's actually refusing to upgrade/install due to formula being present + if echo "$output" | grep -qi "already installed"; then + echo_success "Conflict implicitly detected (formula already installed, cask refused to overwrite)" + echo_info "Message: $(echo "$output" | grep -i "already installed")" + else + echo_error "Cask installation succeeded (should have failed with conflict!)" + echo_error "Output: $output" + return 1 + fi + else + # Check if the error is about conflicts + if echo "$output" | grep -qi "conflict"; then + echo_success "Conflict properly detected" + echo_info "Conflict message: $(echo "$output" | grep -i conflict)" + else + echo_error "Installation failed, but NOT due to conflict" + echo_error "Output: $output" + return 1 + fi + fi + + echo_success "Conflict detection validation passed" + return 0 +} + +# Run installation tests +run_installation_tests() { + echo_section "Running Installation Tests" + + if [ "$RUN_INSTALL_TESTS" = false ]; then + echo_info "Installation tests skipped (use --install flag to enable)" + return 0 + fi + + local all_passed=true + + # Set up local tap + if ! setup_local_tap; then + echo_error "Failed to set up local tap" + return 1 + fi + + # Test 1: Formula installation + if ! test_formula_installation; then + all_passed=false + fi + + # Clean up formula before cask test + if [ "$FORMULA_INSTALLED" = true ]; then + echo_info "Uninstalling formula before cask test..." + brew uninstall hookdeck 2>/dev/null || true + FORMULA_INSTALLED=false + echo_success "Formula uninstalled" + fi + + # Test 2: Cask installation + if ! test_cask_installation; then + all_passed=false + fi + + # Clean up cask before conflict test + if [ "$CASK_INSTALLED" = true ]; then + echo_info "Uninstalling cask before conflict test..." + brew uninstall --cask hookdeck 2>/dev/null || true + CASK_INSTALLED=false + echo_success "Cask uninstalled" + fi + + # Test 3: Conflict detection + if ! test_conflict_detection; then + all_passed=false + fi + + if [ "$all_passed" = true ]; then + echo_success "All installation tests passed!" + return 0 + else + echo_error "Some installation tests failed" + return 1 + fi +} + +# Main test execution +main() { + echo_section "Hookdeck CLI Homebrew Build Validation" + + check_prerequisites + clean_dist + run_goreleaser_build + + local all_passed=true + + if ! validate_formula; then + all_passed=false + fi + + if ! validate_cask; then + all_passed=false + fi + + if ! validate_completions_in_tarball; then + all_passed=false + fi + + # Run installation tests if requested + if [ "$RUN_INSTALL_TESTS" = true ]; then + if ! run_installation_tests; then + all_passed=false + fi + fi + + echo "" + echo_section "Validation Summary" + + if [ "$all_passed" = true ]; then + echo_success "All validations passed!" + echo "" + echo_info "What was validated:" + echo " ✓ GoReleaser configuration generates correct Homebrew files" + echo " ✓ Completion files are bundled in archives" + echo " ✓ Formula has deprecation warnings" + echo " ✓ Formula has proper completion directives" + echo " ✓ Cask has proper completion directives" + echo " ✓ Cask has conflicts with formula" + + if [ "$RUN_INSTALL_TESTS" = true ]; then + echo " ✓ Formula installs correctly from local tap" + echo " ✓ Cask installs correctly from local tap" + echo " ✓ Conflict detection works (formula vs cask)" + echo " ✓ Completions are installed in correct locations" + echo " ✓ Binary is functional after installation" + else + echo "" + echo_info "Note: Installation tests not run (use --install flag to enable)" + fi + echo "" + return 0 + else + echo_error "Some validations failed" + return 1 + fi +} + +# Run main function +main \ No newline at end of file From a14181a1ac6fb9f28b3d971442d71c50ffedc9d3 Mon Sep 17 00:00:00 2001 From: Phil Leggetter Date: Wed, 8 Oct 2025 00:01:08 +0100 Subject: [PATCH 3/7] chore: Update description for Homebrew formula and cask to clarify functionality --- .goreleaser/mac.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.goreleaser/mac.yml b/.goreleaser/mac.yml index 486f4a10..e861b46d 100644 --- a/.goreleaser/mac.yml +++ b/.goreleaser/mac.yml @@ -54,7 +54,7 @@ brews: name: homebrew-hookdeck directory: Formula homepage: https://hookdeck.com - description: Alternative to ngrok for localhost webhook development + description: Receive events (e.g. webhooks) on your localhost with event history, replay, and team collaboration install: | bin.install "hookdeck" @@ -81,7 +81,7 @@ homebrew_casks: owner: hookdeck name: homebrew-hookdeck homepage: https://hookdeck.com - description: Alternative to ngrok for localhost webhook development + description: Receive events (e.g. webhooks) on your localhost with event history, replay, and team collaboration # Install shell completions automatically completions: bash: "completions/hookdeck.bash" From 75bc13ca89d1f1550ade456e534de5186b489e20 Mon Sep 17 00:00:00 2001 From: Phil Leggetter Date: Wed, 8 Oct 2025 00:06:27 +0100 Subject: [PATCH 4/7] chore: always run install on PRs --- .github/workflows/test-homebrew-build.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/test-homebrew-build.yml b/.github/workflows/test-homebrew-build.yml index 0a6f8074..8a7ee7ef 100644 --- a/.github/workflows/test-homebrew-build.yml +++ b/.github/workflows/test-homebrew-build.yml @@ -1,4 +1,4 @@ -name: Homebrew Build Validation +name: Homebrew Build and Installation Tests on: pull_request: @@ -49,7 +49,6 @@ jobs: run: ./test-scripts/test-homebrew-build.sh - name: Run installation tests - if: github.event.inputs.run_install_tests == 'true' run: ./test-scripts/test-homebrew-build.sh --install - name: Upload generated Homebrew files From 6bf08a59b0c80eabcdc69d4af595dbbff5433053 Mon Sep 17 00:00:00 2001 From: Phil Leggetter Date: Wed, 8 Oct 2025 00:21:30 +0100 Subject: [PATCH 5/7] chore: Remove deprecated conflicts_with directive and update migration approach - Remove custom_block with conflicts_with from Homebrew cask config - Update cask caveats with migration instructions for binary conflict errors - Remove conflict detection test from test-homebrew-build.sh - Remove cask validation check for conflicts_with directive - Add troubleshooting section to README for binary already exists error - Focus tests on clean installs rather than conflict detection Homebrew deprecated conflicts_with with no replacement. Instead of trying to enforce conflicts, we now rely on Homebrew's native warnings and provide clear migration instructions in both the cask caveats and README. --- .goreleaser/mac.yml | 16 +++--- README.md | 18 +++++++ test-scripts/test-homebrew-build.sh | 75 ----------------------------- 3 files changed, 27 insertions(+), 82 deletions(-) diff --git a/.goreleaser/mac.yml b/.goreleaser/mac.yml index e861b46d..1da65705 100644 --- a/.goreleaser/mac.yml +++ b/.goreleaser/mac.yml @@ -87,13 +87,15 @@ homebrew_casks: bash: "completions/hookdeck.bash" zsh: "completions/_hookdeck" - # Prevent installation alongside the deprecated formula - # Note: The deprecated 'conflicts' field no longer works, so we use custom_block - custom_block: | - conflicts_with formula: "hookdeck" - - caveats: | + caveats: |- Thanks for installing the Hookdeck CLI! - If this is your first time using the CLI, run: + ⚠️ If you see an error about a binary already existing: + brew uninstall hookdeck + brew install --cask hookdeck/hookdeck/hookdeck + + Shell completions have been installed automatically. + You may need to restart your shell for them to take effect. + + First time using the CLI? Run: hookdeck login diff --git a/README.md b/README.md index 64ee197c..376b0fd9 100644 --- a/README.md +++ b/README.md @@ -430,6 +430,24 @@ There are also some hidden flags that are mainly used for development and debugg * `--console-base`: Sets the web console base URL. * `--ws-base`: Sets the Websocket base URL. +## Troubleshooting + +### Homebrew: Binary Already Exists Error + +If you previously installed Hookdeck via the Homebrew formula and are upgrading to the cask version, you may see: + +``` +Warning: It seems there is already a Binary at '/opt/homebrew/bin/hookdeck' +from formula hookdeck; skipping link. +``` + +To resolve this, uninstall the old formula version first, then install the cask: + +```sh +brew uninstall hookdeck +brew install --cask hookdeck/hookdeck/hookdeck +``` + ## Developing diff --git a/test-scripts/test-homebrew-build.sh b/test-scripts/test-homebrew-build.sh index db4dfa69..adaff4e3 100755 --- a/test-scripts/test-homebrew-build.sh +++ b/test-scripts/test-homebrew-build.sh @@ -12,7 +12,6 @@ # - Formula references completion files correctly # - Cask has proper bash_completion directive # - Cask has proper zsh_completion directive -# - Cask has conflicts with formula # - Completion files are bundled in the tarball # # Usage: @@ -263,14 +262,6 @@ validate_cask() { return 1 fi - # Check for conflicts with formula - if grep -q 'conflicts.*formula.*hookdeck' "$cask_file"; then - echo_success "Cask contains conflicts with formula directive" - else - echo_error "Cask missing conflicts with formula directive" - return 1 - fi - echo_success "Cask validation passed" return 0 } @@ -486,57 +477,6 @@ test_cask_installation() { return 0 } -# Test conflict detection -test_conflict_detection() { - echo_section "Testing Conflict Detection" - - local tap_name="hookdeck-test/hookdeck-test/hookdeck" - - # First, install formula with --formula flag to ensure it's installed as formula - echo_info "Installing formula first: brew install --formula $tap_name" - if brew install --formula "$tap_name"; then - echo_success "Formula installed successfully" - FORMULA_INSTALLED=true - else - echo_error "Formula installation failed" - return 1 - fi - - # Ensure the formula is linked - echo_info "Ensuring formula is linked..." - if brew link hookdeck 2>/dev/null || true; then - echo_success "Formula linked" - fi - - # Try to install cask - should fail with conflict - echo_info "Attempting to install cask (should fail with conflict)..." - local output - if output=$(brew install --cask "$tap_name" 2>&1); then - # Check if it's actually refusing to upgrade/install due to formula being present - if echo "$output" | grep -qi "already installed"; then - echo_success "Conflict implicitly detected (formula already installed, cask refused to overwrite)" - echo_info "Message: $(echo "$output" | grep -i "already installed")" - else - echo_error "Cask installation succeeded (should have failed with conflict!)" - echo_error "Output: $output" - return 1 - fi - else - # Check if the error is about conflicts - if echo "$output" | grep -qi "conflict"; then - echo_success "Conflict properly detected" - echo_info "Conflict message: $(echo "$output" | grep -i conflict)" - else - echo_error "Installation failed, but NOT due to conflict" - echo_error "Output: $output" - return 1 - fi - fi - - echo_success "Conflict detection validation passed" - return 0 -} - # Run installation tests run_installation_tests() { echo_section "Running Installation Tests" @@ -572,19 +512,6 @@ run_installation_tests() { all_passed=false fi - # Clean up cask before conflict test - if [ "$CASK_INSTALLED" = true ]; then - echo_info "Uninstalling cask before conflict test..." - brew uninstall --cask hookdeck 2>/dev/null || true - CASK_INSTALLED=false - echo_success "Cask uninstalled" - fi - - # Test 3: Conflict detection - if ! test_conflict_detection; then - all_passed=false - fi - if [ "$all_passed" = true ]; then echo_success "All installation tests passed!" return 0 @@ -635,12 +562,10 @@ main() { echo " ✓ Formula has deprecation warnings" echo " ✓ Formula has proper completion directives" echo " ✓ Cask has proper completion directives" - echo " ✓ Cask has conflicts with formula" if [ "$RUN_INSTALL_TESTS" = true ]; then echo " ✓ Formula installs correctly from local tap" echo " ✓ Cask installs correctly from local tap" - echo " ✓ Conflict detection works (formula vs cask)" echo " ✓ Completions are installed in correct locations" echo " ✓ Binary is functional after installation" else From 36a9929eb5eb3a64338a398306a56812c79e957e Mon Sep 17 00:00:00 2001 From: Phil Leggetter Date: Wed, 8 Oct 2025 15:17:49 +0100 Subject: [PATCH 6/7] chore: temporarily disable cask distribution until code signing - Comment out homebrew_casks section in .goreleaser/mac.yml - Remove deprecation warning from formula caveats (formula is now primary) - Update test script to focus on formula-only testing - Remove cask-related tests and validations - Update CI workflow to test only formula distribution - Enforce Gatekeeper compliance testing (removed bypass flag) All tests pass including installation validation and binary execution. Will re-enable cask distribution once Apple Developer certificate is in place. --- .github/workflows/test-homebrew-build.yml | 2 +- .goreleaser/mac.yml | 66 ++--- test-scripts/test-homebrew-build.sh | 327 ++++++++++------------ 3 files changed, 181 insertions(+), 214 deletions(-) diff --git a/.github/workflows/test-homebrew-build.yml b/.github/workflows/test-homebrew-build.yml index 8a7ee7ef..045a2a58 100644 --- a/.github/workflows/test-homebrew-build.yml +++ b/.github/workflows/test-homebrew-build.yml @@ -51,6 +51,7 @@ jobs: - name: Run installation tests run: ./test-scripts/test-homebrew-build.sh --install + # Upload generated Homebrew formula (cask distribution has been disabled) - name: Upload generated Homebrew files if: always() uses: actions/upload-artifact@v4 @@ -58,5 +59,4 @@ jobs: name: homebrew-files path: | dist/homebrew/Formula/hookdeck.rb - dist/homebrew/Casks/hookdeck.rb retention-days: 7 diff --git a/.goreleaser/mac.yml b/.goreleaser/mac.yml index 1da65705..63f6aff6 100644 --- a/.goreleaser/mac.yml +++ b/.goreleaser/mac.yml @@ -64,38 +64,38 @@ brews: zsh_completion.install "completions/_hookdeck" caveats: | - ⚠️ WARNING: This formula is deprecated! + ❤ Thanks for installing the Hookdeck CLI! - Please migrate to the cask version for the latest updates: - brew uninstall hookdeck - brew install --cask hookdeck/hookdeck/hookdeck - - The formula distribution will be removed in approximately 3-6 months. - Learn more: https://goreleaser.com/blog/goreleaser-v2.10/#homebrew-casks - -homebrew_casks: - - name: hookdeck - ids: - - hookdeck - repository: - owner: hookdeck - name: homebrew-hookdeck - homepage: https://hookdeck.com - description: Receive events (e.g. webhooks) on your localhost with event history, replay, and team collaboration - # Install shell completions automatically - completions: - bash: "completions/hookdeck.bash" - zsh: "completions/_hookdeck" - - caveats: |- - Thanks for installing the Hookdeck CLI! - - ⚠️ If you see an error about a binary already existing: - brew uninstall hookdeck - brew install --cask hookdeck/hookdeck/hookdeck - - Shell completions have been installed automatically. - You may need to restart your shell for them to take effect. - - First time using the CLI? Run: + If this is your first time using the CLI, run: hookdeck login + +# TODO: Temporarily disabled until we implement code signing +# Cask distribution causes Gatekeeper issues with unsigned binaries +# Will re-enable once Apple Developer certificate is in place +# +# homebrew_casks: +# - name: hookdeck +# ids: +# - hookdeck +# repository: +# owner: hookdeck +# name: homebrew-hookdeck +# homepage: https://hookdeck.com +# description: Receive events (e.g. webhooks) on your localhost with event history, replay, and team collaboration +# # Install shell completions automatically +# completions: +# bash: "completions/hookdeck.bash" +# zsh: "completions/_hookdeck" +# +# caveats: |- +# Thanks for installing the Hookdeck CLI! +# +# ⚠️ If you see an error about a binary already existing: +# brew uninstall hookdeck +# brew install --cask hookdeck/hookdeck/hookdeck +# +# Shell completions have been installed automatically. +# You may need to restart your shell for them to take effect. +# +# First time using the CLI? Run: +# hookdeck login diff --git a/test-scripts/test-homebrew-build.sh b/test-scripts/test-homebrew-build.sh index adaff4e3..78bd9f4d 100755 --- a/test-scripts/test-homebrew-build.sh +++ b/test-scripts/test-homebrew-build.sh @@ -7,17 +7,15 @@ # # It validates that: # - GoReleaser snapshot build completes successfully -# - Homebrew formula and cask files are generated +# - Homebrew formula file is generated # - Formula contains deprecation warning # - Formula references completion files correctly -# - Cask has proper bash_completion directive -# - Cask has proper zsh_completion directive # - Completion files are bundled in the tarball +# NOTE: Cask validation is currently commented out - focusing on formula only # # Usage: -# ./test-scripts/test-homebrew-build.sh # Build validation only -# ./test-scripts/test-homebrew-build.sh --install # Build + installation testing -# ./test-scripts/test-homebrew-build.sh --install --bypass-gatekeeper # Full testing with binary execution +# ./test-scripts/test-homebrew-build.sh # Build validation only +# ./test-scripts/test-homebrew-build.sh --install # Build + installation testing # # Prerequisites: # - Go installed @@ -27,17 +25,11 @@ # Note: Without --install, this script only validates BUILD outputs. # With --install, it also tests actual installation from local tap. # For CLI functionality testing, use test-scripts/test-acceptance.sh instead. -# -# Unsigned Binary Note: -# Local snapshot builds produce unsigned binaries which macOS Gatekeeper blocks. -# To test binary execution locally, the script can remove the quarantine attribute. -# This requires manual approval for security reasons. set -e # Parse command line arguments RUN_INSTALL_TESTS=false -BYPASS_GATEKEEPER=false while [[ $# -gt 0 ]]; do case $1 in @@ -45,13 +37,9 @@ while [[ $# -gt 0 ]]; do RUN_INSTALL_TESTS=true shift ;; - --bypass-gatekeeper) - BYPASS_GATEKEEPER=true - shift - ;; *) echo "Unknown option: $1" - echo "Usage: $0 [--install] [--bypass-gatekeeper]" + echo "Usage: $0 [--install]" exit 1 ;; esac @@ -60,7 +48,7 @@ done # Global variables for cleanup LOCAL_TAP_PATH="" FORMULA_INSTALLED=false -CASK_INSTALLED=false +# CASK_INSTALLED=false # Commented out - not testing cask currently # Colors for output RED='\033[0;31m' @@ -108,12 +96,13 @@ cleanup_installations() { fi # Uninstall cask if installed - if [ "$CASK_INSTALLED" = true ]; then - echo_info "Uninstalling cask..." - if brew uninstall --cask hookdeck 2>/dev/null || true; then - echo_success "Cask uninstalled" - fi - fi + # NOTE: Cask testing is currently commented out + # if [ "$CASK_INSTALLED" = true ]; then + # echo_info "Uninstalling cask..." + # if brew uninstall --cask hookdeck 2>/dev/null || true; then + # echo_success "Cask uninstalled" + # fi + # fi # Remove local tap if [ -n "$LOCAL_TAP_PATH" ] && [ -d "$LOCAL_TAP_PATH" ]; then @@ -191,13 +180,7 @@ validate_formula() { fi echo_success "Formula file exists" - # Check for deprecation warning - if grep -q "WARNING: This formula is deprecated" "$formula_file"; then - echo_success "Formula contains deprecation warning" - else - echo_error "Formula missing deprecation warning" - return 1 - fi + # Note: Deprecation warning removed since cask is disabled, formula is now the official method # Check for bash completion reference if grep -q 'bash_completion.install' "$formula_file"; then @@ -235,36 +218,37 @@ validate_formula() { } # Validate Homebrew cask file -validate_cask() { - echo_section "Validating Cask (dist/homebrew/Casks/hookdeck.rb)" - - local cask_file="dist/homebrew/Casks/hookdeck.rb" - - if [ ! -f "$cask_file" ]; then - echo_error "Cask file not found at $cask_file" - return 1 - fi - echo_success "Cask file exists" - - # Check for bash completion - if grep -q 'bash.*completion.*hookdeck\.bash' "$cask_file"; then - echo_success "Cask contains bash_completion directive" - else - echo_error "Cask missing bash_completion directive" - return 1 - fi - - # Check for zsh completion - if grep -q 'zsh.*completion.*_hookdeck' "$cask_file"; then - echo_success "Cask contains zsh_completion directive" - else - echo_error "Cask missing zsh_completion directive" - return 1 - fi - - echo_success "Cask validation passed" - return 0 -} +# NOTE: Cask validation is currently commented out - focusing on formula only +# validate_cask() { +# echo_section "Validating Cask (dist/homebrew/Casks/hookdeck.rb)" +# +# local cask_file="dist/homebrew/Casks/hookdeck.rb" +# +# if [ ! -f "$cask_file" ]; then +# echo_error "Cask file not found at $cask_file" +# return 1 +# fi +# echo_success "Cask file exists" +# +# # Check for bash completion +# if grep -q 'bash.*completion.*hookdeck\.bash' "$cask_file"; then +# echo_success "Cask contains bash_completion directive" +# else +# echo_error "Cask missing bash_completion directive" +# return 1 +# fi +# +# # Check for zsh completion +# if grep -q 'zsh.*completion.*_hookdeck' "$cask_file"; then +# echo_success "Cask contains zsh_completion directive" +# else +# echo_error "Cask missing zsh_completion directive" +# return 1 +# fi +# +# echo_success "Cask validation passed" +# return 0 +# } # Validate completion files in tarball validate_completions_in_tarball() { @@ -321,11 +305,12 @@ setup_local_tap() { sed -i '' "s|https://github.com/hookdeck/hookdeck-cli/releases/download/v[^/]*/|file://$current_dir/dist/|g" "$formula_file" # Patch cask to use local file:// URLs for testing - echo_info "Patching cask to use local file URLs for testing..." - local cask_file="$LOCAL_TAP_PATH/Casks/hookdeck.rb" - - # Replace GitHub URLs with local file:// URLs - sed -i '' "s|https://github.com/hookdeck/hookdeck-cli/releases/download/v[^/]*/|file://$current_dir/dist/|g" "$cask_file" + # NOTE: Cask patching is currently commented out - focusing on formula only + # echo_info "Patching cask to use local file URLs for testing..." + # local cask_file="$LOCAL_TAP_PATH/Casks/hookdeck.rb" + # + # # Replace GitHub URLs with local file:// URLs + # sed -i '' "s|https://github.com/hookdeck/hookdeck-cli/releases/download/v[^/]*/|file://$current_dir/dist/|g" "$cask_file" echo_success "Local tap created and patched successfully" echo_info "Tap name: $tap_name" @@ -346,38 +331,17 @@ test_formula_installation() { return 1 fi - # Verify binary works (may fail on macOS due to unsigned binary) + # Verify binary works (must not be blocked by Gatekeeper) echo_info "Testing binary: hookdeck version" # Try to run the binary if hookdeck version 2>/dev/null; then echo_success "Binary is functional" else - # Binary execution failed - likely Gatekeeper - if [ "$BYPASS_GATEKEEPER" = true ]; then - echo_info "Binary blocked by Gatekeeper - attempting to bypass..." - local binary_path="$(which hookdeck)" - echo_info "Removing quarantine attribute from: $binary_path" - - if sudo xattr -d com.apple.quarantine "$binary_path" 2>/dev/null; then - echo_success "Quarantine attribute removed" - - # Try again - if hookdeck version; then - echo_success "Binary is functional after Gatekeeper bypass" - else - echo_error "Binary still failed to execute after bypass" - return 1 - fi - else - echo_error "Failed to remove quarantine attribute (sudo required)" - return 1 - fi - else - echo_info "Binary test skipped (unsigned binaries are blocked by macOS Gatekeeper)" - echo_info "Use --bypass-gatekeeper flag to remove quarantine attribute and test binary" - echo_info "Formula installation succeeded (binary path and completions verified)" - fi + echo_error "Binary execution failed" + echo_error "This indicates the binary is unsigned or improperly signed" + echo_error "Gatekeeper is blocking execution - build must fail" + return 1 fi # Verify bash completion is installed @@ -405,77 +369,78 @@ test_formula_installation() { } # Test cask installation -test_cask_installation() { - echo_section "Testing Cask Installation" - - local tap_name="hookdeck-test/hookdeck-test/hookdeck" - - echo_info "Installing cask: brew install --cask $tap_name" - if brew install --cask "$tap_name"; then - echo_success "Cask installed successfully" - CASK_INSTALLED=true - else - echo_error "Cask installation failed" - return 1 - fi - - # Verify binary works (may fail on macOS due to unsigned binary) - echo_info "Testing binary: hookdeck version" - - # Try to run the binary - if hookdeck version 2>/dev/null; then - echo_success "Binary is functional" - else - # Binary execution failed - likely Gatekeeper - if [ "$BYPASS_GATEKEEPER" = true ]; then - echo_info "Binary blocked by Gatekeeper - attempting to bypass..." - local binary_path="$(which hookdeck)" - echo_info "Removing quarantine attribute from: $binary_path" - - if sudo xattr -d com.apple.quarantine "$binary_path" 2>/dev/null; then - echo_success "Quarantine attribute removed" - - # Try again - if hookdeck version; then - echo_success "Binary is functional after Gatekeeper bypass" - else - echo_error "Binary still failed to execute after bypass" - return 1 - fi - else - echo_error "Failed to remove quarantine attribute (sudo required)" - return 1 - fi - else - echo_info "Binary test skipped (unsigned binaries are blocked by macOS Gatekeeper)" - echo_info "Use --bypass-gatekeeper flag to remove quarantine attribute and test binary" - echo_info "Cask installation succeeded (binary path and completions verified)" - fi - fi - - # Verify bash completion is installed - local bash_completion_path="$(brew --prefix)/etc/bash_completion.d/hookdeck" - echo_info "Checking bash completion at: $bash_completion_path" - if [ -f "$bash_completion_path" ]; then - echo_success "Bash completion installed" - else - echo_error "Bash completion not found at $bash_completion_path" - return 1 - fi - - # Verify zsh completion is installed - local zsh_completion_path="$(brew --prefix)/share/zsh/site-functions/_hookdeck" - echo_info "Checking zsh completion at: $zsh_completion_path" - if [ -f "$zsh_completion_path" ]; then - echo_success "Zsh completion installed" - else - echo_error "Zsh completion not found at $zsh_completion_path" - return 1 - fi - - echo_success "Cask installation validation passed" - return 0 -} +# NOTE: Cask installation testing is currently commented out - focusing on formula only +# test_cask_installation() { +# echo_section "Testing Cask Installation" +# +# local tap_name="hookdeck-test/hookdeck-test/hookdeck" +# +# echo_info "Installing cask: brew install --cask $tap_name" +# if brew install --cask "$tap_name"; then +# echo_success "Cask installed successfully" +# CASK_INSTALLED=true +# else +# echo_error "Cask installation failed" +# return 1 +# fi +# +# # Verify binary works (may fail on macOS due to unsigned binary) +# echo_info "Testing binary: hookdeck version" +# +# # Try to run the binary +# if hookdeck version 2>/dev/null; then +# echo_success "Binary is functional" +# else +# # Binary execution failed - likely Gatekeeper +# if [ "$BYPASS_GATEKEEPER" = true ]; then +# echo_info "Binary blocked by Gatekeeper - attempting to bypass..." +# local binary_path="$(which hookdeck)" +# echo_info "Removing quarantine attribute from: $binary_path" +# +# if sudo xattr -d com.apple.quarantine "$binary_path" 2>/dev/null; then +# echo_success "Quarantine attribute removed" +# +# # Try again +# if hookdeck version; then +# echo_success "Binary is functional after Gatekeeper bypass" +# else +# echo_error "Binary still failed to execute after bypass" +# return 1 +# fi +# else +# echo_error "Failed to remove quarantine attribute (sudo required)" +# return 1 +# fi +# else +# echo_info "Binary test skipped (unsigned binaries are blocked by macOS Gatekeeper)" +# echo_info "Use --bypass-gatekeeper flag to remove quarantine attribute and test binary" +# echo_info "Cask installation succeeded (binary path and completions verified)" +# fi +# fi +# +# # Verify bash completion is installed +# local bash_completion_path="$(brew --prefix)/etc/bash_completion.d/hookdeck" +# echo_info "Checking bash completion at: $bash_completion_path" +# if [ -f "$bash_completion_path" ]; then +# echo_success "Bash completion installed" +# else +# echo_error "Bash completion not found at $bash_completion_path" +# return 1 +# fi +# +# # Verify zsh completion is installed +# local zsh_completion_path="$(brew --prefix)/share/zsh/site-functions/_hookdeck" +# echo_info "Checking zsh completion at: $zsh_completion_path" +# if [ -f "$zsh_completion_path" ]; then +# echo_success "Zsh completion installed" +# else +# echo_error "Zsh completion not found at $zsh_completion_path" +# return 1 +# fi +# +# echo_success "Cask installation validation passed" +# return 0 +# } # Run installation tests run_installation_tests() { @@ -500,17 +465,18 @@ run_installation_tests() { fi # Clean up formula before cask test - if [ "$FORMULA_INSTALLED" = true ]; then - echo_info "Uninstalling formula before cask test..." - brew uninstall hookdeck 2>/dev/null || true - FORMULA_INSTALLED=false - echo_success "Formula uninstalled" - fi - - # Test 2: Cask installation - if ! test_cask_installation; then - all_passed=false - fi + # NOTE: Cask testing is currently commented out - focusing on formula only + # if [ "$FORMULA_INSTALLED" = true ]; then + # echo_info "Uninstalling formula before cask test..." + # brew uninstall hookdeck 2>/dev/null || true + # FORMULA_INSTALLED=false + # echo_success "Formula uninstalled" + # fi + # + # # Test 2: Cask installation + # if ! test_cask_installation; then + # all_passed=false + # fi if [ "$all_passed" = true ]; then echo_success "All installation tests passed!" @@ -535,9 +501,10 @@ main() { all_passed=false fi - if ! validate_cask; then - all_passed=false - fi + # NOTE: Cask validation is currently commented out - focusing on formula only + # if ! validate_cask; then + # all_passed=false + # fi if ! validate_completions_in_tarball; then all_passed=false @@ -557,15 +524,15 @@ main() { echo_success "All validations passed!" echo "" echo_info "What was validated:" - echo " ✓ GoReleaser configuration generates correct Homebrew files" + echo " ✓ GoReleaser configuration generates correct Homebrew formula" echo " ✓ Completion files are bundled in archives" echo " ✓ Formula has deprecation warnings" echo " ✓ Formula has proper completion directives" - echo " ✓ Cask has proper completion directives" + # echo " ✓ Cask has proper completion directives" # Commented out - not testing cask if [ "$RUN_INSTALL_TESTS" = true ]; then echo " ✓ Formula installs correctly from local tap" - echo " ✓ Cask installs correctly from local tap" + # echo " ✓ Cask installs correctly from local tap" # Commented out - not testing cask echo " ✓ Completions are installed in correct locations" echo " ✓ Binary is functional after installation" else From 4c74b10a41002546dfd7f08298f4c4527402f4d0 Mon Sep 17 00:00:00 2001 From: Phil Leggetter Date: Wed, 8 Oct 2025 15:25:33 +0100 Subject: [PATCH 7/7] fix: remove paths filter from homebrew test workflow to ensure it runs on all PRs to main --- .github/workflows/test-homebrew-build.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.github/workflows/test-homebrew-build.yml b/.github/workflows/test-homebrew-build.yml index 045a2a58..5595b8e6 100644 --- a/.github/workflows/test-homebrew-build.yml +++ b/.github/workflows/test-homebrew-build.yml @@ -4,11 +4,6 @@ on: pull_request: branches: - main - paths: - - ".goreleaser/**" - - "scripts/completions.sh" - - "test-scripts/test-homebrew-build.sh" - - ".github/workflows/test-homebrew-build.yml" workflow_dispatch: inputs: