diff --git a/.github/workflows/build-cli.yml b/.github/workflows/build-cli.yml new file mode 100644 index 0000000000..9f812d959f --- /dev/null +++ b/.github/workflows/build-cli.yml @@ -0,0 +1,69 @@ +# This is a **reuseable** workflow that bundles the Desktop App for macOS. +# It doesn't get triggered on its own. It gets used in multiple workflows: +# - release.yml +# - canary.yml +on: + workflow_call: + inputs: + # Let's allow overriding the OSes and architectures in JSON array form: + # e.g. '["ubuntu-latest","macos-latest"]' + # If no input is provided, these defaults apply. + operating-systems: + type: string + required: false + default: '["ubuntu-latest","macos-latest"]' + architectures: + type: string + required: false + default: '["x86_64","aarch64"]' + +name: "Reusable workflow to build CLI" + +jobs: + build-cli: + name: Build CLI + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: ${{ fromJson(inputs.operating-systems) }} + architecture: ${{ fromJson(inputs.architectures) }} + include: + - os: ubuntu-latest + target-suffix: unknown-linux-gnu + - os: macos-latest + target-suffix: apple-darwin + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Rust + uses: dtolnay/rust-toolchain@stable + with: + toolchain: stable + target: ${{ matrix.architecture }}-${{ matrix.target-suffix }} + + - name: Install cross + run: cargo install cross --git https://github.com/cross-rs/cross + + - name: Build CLI + env: + CROSS_NO_WARNINGS: 0 + run: | + export TARGET="${{ matrix.architecture }}-${{ matrix.target-suffix }}" + rustup target add "${TARGET}" + + # 'cross' is used to cross-compile for different architectures (see Cross.toml) + cross build --release --target ${TARGET} -p goose-cli + + # tar the goose binary as goose-.tar.bz2 + cd target/${TARGET}/release + tar -cjf goose-${TARGET}.tar.bz2 goose + echo "ARTIFACT=target/${TARGET}/release/goose-${TARGET}.tar.bz2" >> $GITHUB_ENV + + - name: Upload CLI artifact + uses: actions/upload-artifact@v4 + with: + name: goose-${{ matrix.architecture }}-${{ matrix.target-suffix }} + path: ${{ env.ARTIFACT }} diff --git a/.github/workflows/bundle-desktop.yml b/.github/workflows/bundle-desktop.yml new file mode 100644 index 0000000000..a36a6e787d --- /dev/null +++ b/.github/workflows/bundle-desktop.yml @@ -0,0 +1,141 @@ +# This is a **reuseable** workflow that bundles the Desktop App for macOS. +# It doesn't get triggered on its own. It gets used in multiple workflows: +# - release.yml +# - canary.yml +# - pr-comment-bundle-desktop.yml +on: + workflow_call: + secrets: + CERTIFICATE_OSX_APPLICATION: + required: true + CERTIFICATE_PASSWORD: + required: true + APPLE_ID: + required: true + APPLE_ID_PASSWORD: + required: true + APPLE_TEAM_ID: + required: true + +name: Reusable workflow to bundle desktop app + +jobs: + bundle-desktop: + runs-on: macos-latest + name: Bundle Desktop App on macOS + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Rust + uses: dtolnay/rust-toolchain@stable + with: + toolchain: stable + + - name: Cache Cargo registry + uses: actions/cache@v3 + with: + path: ~/.cargo/registry + key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }} + restore-keys: | + ${{ runner.os }}-cargo-registry- + + - name: Cache Cargo index + uses: actions/cache@v3 + with: + path: ~/.cargo/index + key: ${{ runner.os }}-cargo-index + restore-keys: | + ${{ runner.os }}-cargo-index + + - name: Cache Cargo build + uses: actions/cache@v3 + with: + path: target + key: ${{ runner.os }}-cargo-build-${{ hashFiles('**/Cargo.lock') }} + restore-keys: | + ${{ runner.os }}-cargo-build- + + - name: Build goosed + run: cargo build --release -p goose-server + + - name: Copy binary into Electron folder + run: cp target/release/goosed ui/desktop/src/bin/goosed + + - name: Add MacOS certs for signing and notarization + run: ./add-macos-cert.sh + working-directory: ui/desktop + env: + CERTIFICATE_OSX_APPLICATION: ${{ secrets.CERTIFICATE_OSX_APPLICATION }} + CERTIFICATE_PASSWORD: ${{ secrets.CERTIFICATE_PASSWORD }} + + - name: Set up Node.js + uses: actions/setup-node@v2 + with: + node-version: 'lts/*' + + - name: Install dependencies + run: npm ci + working-directory: ui/desktop + + - name: Make default Goose App + run: | + attempt=0 + max_attempts=2 + until [ $attempt -ge $max_attempts ]; do + npm run bundle:default && break + attempt=$((attempt + 1)) + echo "Attempt $attempt failed. Retrying..." + sleep 5 + done + if [ $attempt -ge $max_attempts ]; then + echo "Action failed after $max_attempts attempts." + exit 1 + fi + working-directory: ui/desktop + env: + APPLE_ID: ${{ secrets.APPLE_ID }} + APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }} + APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} + + - name: Upload Desktop artifact + uses: actions/upload-artifact@v4 + with: + name: Goose-darwin-arm64 + path: ui/desktop/out/Goose-darwin-arm64/Goose.zip + + - name: Quick launch test (macOS) + run: | + # Ensure no quarantine attributes (if needed) + xattr -cr "ui/desktop/out/Goose-darwin-arm64/Goose.app" + echo "Opening Goose.app..." + open -g "ui/desktop/out/Goose-darwin-arm64/Goose.app" + + # Give the app a few seconds to start and write logs + sleep 5 + + # Check if it's running + if pgrep -f "Goose.app/Contents/MacOS/Goose" > /dev/null; then + echo "App appears to be running." + else + echo "App did not stay open. Possible crash or startup error." + exit 1 + fi + LOGFILE="$HOME/Library/Application Support/Goose/logs/main.log" + # Print the log and verify "ChatWindow loaded" is in the logs + if [ -f "$LOGFILE" ]; then + echo "===== Log file contents =====" + cat "$LOGFILE" + echo "=============================" + if grep -F "ChatWindow loaded" "$LOGFILE"; then + echo "Confirmed: 'ChatWindow loaded' found in logs!" + else + echo "Did not find 'ChatWindow loaded' in logs. Failing..." + exit 1 + fi + else + echo "No log file found at $LOGFILE. Exiting with failure." + exit 1 + fi + # Kill the app to clean up + pkill -f "Goose.app/Contents/MacOS/Goose" \ No newline at end of file diff --git a/.github/workflows/canary.yml b/.github/workflows/canary.yml new file mode 100644 index 0000000000..18bab669c9 --- /dev/null +++ b/.github/workflows/canary.yml @@ -0,0 +1,80 @@ +# This workflow is for canary releases, automatically triggered by push to v1.0 branch. +# This workflow is identical to "release.yml" with these exceptions: +# - Triggered by push to v1.0 branch +# - Github Release tagged as "canary" +on: + push: + paths-ignore: + - 'docs/**' + branches: + - v1.0 + +name: Canary + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + # ------------------------------------ + # 1) Build CLI for multiple OS/Arch + # ------------------------------------ + build-cli: + uses: ./.github/workflows/build-cli.yml + + # ------------------------------------ + # 2) Upload Install CLI Script (we only need to do this once) + # ------------------------------------ + install-script: + name: Upload Install Script + runs-on: ubuntu-latest + needs: [ build-cli ] + steps: + - uses: actions/checkout@v4 + - uses: actions/upload-artifact@v4 + with: + name: download_cli.sh + path: download_cli.sh + + # ------------------------------------------------------------ + # 3) Bundle Desktop App (macOS only) - builds goosed and Electron app + # ------------------------------------------------------------ + bundle-desktop: + uses: ./.github/workflows/bundle-desktop.yml + secrets: + CERTIFICATE_OSX_APPLICATION: ${{ secrets.CERTIFICATE_OSX_APPLICATION }} + CERTIFICATE_PASSWORD: ${{ secrets.CERTIFICATE_PASSWORD }} + APPLE_ID: ${{ secrets.APPLE_ID }} + APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }} + APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} + + # ------------------------------------ + # 4) Create/Update GitHub Release + # ------------------------------------ + release: + name: Release + runs-on: ubuntu-latest + needs: [ build-cli, install-script, bundle-desktop ] + permissions: + contents: write + + steps: + - name: Download all artifacts + uses: actions/download-artifact@v4 + with: + merge-multiple: true + + # Create/update the canary release + - name: Release canary + uses: ncipollo/release-action@v1 + with: + tag: canary + name: Canary + token: ${{ secrets.GITHUB_TOKEN }} + artifacts: | + goose-*.tar.bz2 + Goose*.zip + download_cli.sh + allowUpdates: true + omitBody: true + omitPrereleaseDuringUpdate: true diff --git a/.github/workflows/ci-desktop.yml b/.github/workflows/ci-desktop.yml deleted file mode 100644 index 82905d1802..0000000000 --- a/.github/workflows/ci-desktop.yml +++ /dev/null @@ -1,30 +0,0 @@ -name: Desktop App Lint - -on: - push: - branches: - - v1.0 - pull_request: - branches: - - v1.0 - -jobs: - build: - runs-on: macos-latest - - steps: - - name: Checkout repository - uses: actions/checkout@v2 - - - name: Set up Node.js - uses: actions/setup-node@v2 - with: - node-version: 'lts/*' - - - name: Install dependencies - run: npm ci - working-directory: ui/desktop - - - name: Run Lint check - run: npm run lint:check - working-directory: ui/desktop \ No newline at end of file diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml deleted file mode 100644 index 8926d2d6e3..0000000000 --- a/.github/workflows/ci.yaml +++ /dev/null @@ -1,103 +0,0 @@ -name: Rust Build and Test - -on: - push: - branches: - - v1.0 - pull_request: - branches: - - v1.0 - -jobs: - format: - runs-on: ubuntu-latest - - steps: - - name: Checkout Code - uses: actions/checkout@v3 - - - name: Set up Rust - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - profile: minimal - override: true - - - name: check format - run: | - cargo fmt --check - - build-and-test: - runs-on: ubuntu-latest - - steps: - - name: Checkout Code - uses: actions/checkout@v3 - - - name: Install Libs - run: | - sudo apt update -y - sudo apt install -y libdbus-1-dev gnome-keyring libxcb1-dev - - - name: Start gnome-keyring - # run gnome-keyring with 'foobar' as password for the login keyring - # this will create a new login keyring and unlock it - # the login password doesn't matter, but the keyring must be unlocked for the tests to work - run: gnome-keyring-daemon --components=secrets --daemonize --unlock <<< 'foobar' - - - name: Set up Rust - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - profile: minimal - override: true - - - name: Cache Cargo registry - uses: actions/cache@v3 - with: - path: ~/.cargo/registry - key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }} - restore-keys: | - ${{ runner.os }}-cargo-registry- - - - name: Cache Cargo index - uses: actions/cache@v3 - with: - path: ~/.cargo/index - key: ${{ runner.os }}-cargo-index - restore-keys: | - ${{ runner.os }}-cargo-index - - - name: Cache Cargo build - uses: actions/cache@v3 - with: - path: target - key: ${{ runner.os }}-cargo-build-${{ hashFiles('**/Cargo.lock') }} - restore-keys: | - ${{ runner.os }}-cargo-build- - - - name: Install Ollama - run: curl -fsSL https://ollama.com/install.sh | sh - - - name: Start Ollama - run: | - # Run the background, in a way that survives to the next step - nohup ollama serve > ollama.log 2>&1 & - - # Block using the ready endpoint - time curl --retry 5 --retry-connrefused --retry-delay 1 -sf http://localhost:11434 - - - name: Test Ollama model - run: ollama run qwen2.5 hello || cat ollama.log - - - name: Build the Rust project - run: cargo build - - - name: Run Tests - run: cargo test --verbose - env: - OLLAMA_MODEL: "qwen2.5" - - - name: check lint - run: | - cargo clippy diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000000..4c04a6e424 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,122 @@ +on: + push: + paths-ignore: + - 'docs/**' + branches: + - v1.0 + pull_request: + paths-ignore: + - 'docs/**' + branches: + - v1.0 + +name: CI + +jobs: + rust-format: + name: Check Rust Code Format + runs-on: ubuntu-latest + steps: + - name: Checkout Code + uses: actions/checkout@v4 + + - name: Setup Rust + uses: dtolnay/rust-toolchain@stable + with: + toolchain: stable + + - name: Run cargo fmt + run: cargo fmt --check + + rust-build-and-test: + name: Build and Test Rust Project + runs-on: ubuntu-latest + steps: + - name: Checkout Code + uses: actions/checkout@v4 + + - name: Install Dependencies + run: | + sudo apt update -y + sudo apt install -y libdbus-1-dev gnome-keyring libxcb1-dev + + - name: Start gnome-keyring + # run gnome-keyring with 'foobar' as password for the login keyring + # this will create a new login keyring and unlock it + # the login password doesn't matter, but the keyring must be unlocked for the tests to work + run: | + gnome-keyring-daemon --components=secrets --daemonize --unlock <<< 'foobar' + + - name: Setup Rust + uses: dtolnay/rust-toolchain@stable + with: + toolchain: stable + + - name: Cache Cargo Registry + uses: actions/cache@v3 + with: + path: ~/.cargo/registry + key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }} + restore-keys: | + ${{ runner.os }}-cargo-registry- + + - name: Cache Cargo Index + uses: actions/cache@v3 + with: + path: ~/.cargo/index + key: ${{ runner.os }}-cargo-index + restore-keys: | + ${{ runner.os }}-cargo-index + + - name: Cache Cargo Build + uses: actions/cache@v3 + with: + path: target + key: ${{ runner.os }}-cargo-build-${{ hashFiles('**/Cargo.lock') }} + restore-keys: | + ${{ runner.os }}-cargo-build- + + - name: Install Ollama + run: curl -fsSL https://ollama.com/install.sh | sh + + - name: Start Ollama + run: | + # Run the background, in a way that survives to the next step + nohup ollama serve > ollama.log 2>&1 & + # Block using the ready endpoint + time curl --retry 5 --retry-connrefused --retry-delay 1 -sf http://localhost:11434 + + - name: Test Ollama Model + run: ollama run qwen2.5 hello || cat ollama.log + + - name: Build Rust Project + run: cargo build + + - name: Run Tests + run: cargo test --verbose + env: + OLLAMA_MODEL: "qwen2.5" + + ## TODO: Need to decide if we wanna error out on clippy warnings. It was not being used before. + # - name: Run Cargo Clippy (Lint) + # run: cargo clippy -- -D warnings + + desktop-lint: + name: Lint Electron Desktop App + runs-on: macos-latest + steps: + - name: Checkout Code + uses: actions/checkout@v4 + + - name: Set up Node.js + uses: actions/setup-node@v2 + with: + node-version: 'lts/*' + + - name: Install Dependencies + run: npm ci + working-directory: ui/desktop + + - name: Run Lint + run: npm run lint:check + working-directory: ui/desktop diff --git a/.github/workflows/cli-release.yml b/.github/workflows/cli-release.yml deleted file mode 100644 index 4ae606433a..0000000000 --- a/.github/workflows/cli-release.yml +++ /dev/null @@ -1,71 +0,0 @@ -on: - push: - tags: - - "v1.*" - workflow_dispatch: - -concurrency: - group: ${{ github.workflow }} - cancel-in-progress: true - -name: Release CLI - -jobs: - build: - name: Build ${{ matrix.os }}-${{ matrix.architecture }} - runs-on: ${{ matrix.os }} - strategy: - fail-fast: false - matrix: - os: [ ubuntu-latest, macos-latest ] - architecture: [ aarch64, x86_64 ] - include: - - os: ubuntu-latest - target-suffix: unknown-linux-gnu - - os: macos-latest - target-suffix: apple-darwin - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Set up Rust toolchain - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - - - name: build - run: | - export TARGET=${{ matrix.architecture }}-${{ matrix.target-suffix }} - rustup target add "${TARGET}" - cargo install cross --git https://github.com/cross-rs/cross - CROSS_NO_WARNINGS=0 cross build --release --target ${TARGET} - cd target/${TARGET}/release - tar -cjf goose-${TARGET}.tar.bz2 goose goosed - echo "ARTIFACT=target/${TARGET}/release/goose-${TARGET}.tar.bz2" >> $GITHUB_ENV - - - name: Upload artifact - uses: actions/upload-artifact@v4 - with: - name: goose-${{ matrix.architecture }}-${{ matrix.target-suffix }} - path: ${{ env.ARTIFACT }} - - release: - name: Release - runs-on: ubuntu-latest - needs: [ build ] - permissions: - contents: write - steps: - # Step 1: Download all build artifacts - - name: Download all artifacts - uses: actions/download-artifact@v4 - with: - merge-multiple: true - - # Step 2: Create GitHub release with artifacts - - name: Create GitHub release - uses: ncipollo/release-action@v1 - with: - artifacts: "goose-*.tar.bz2" - token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/desktop-app-release.yaml b/.github/workflows/desktop-app-release.yaml deleted file mode 100644 index 67ecc6e10c..0000000000 --- a/.github/workflows/desktop-app-release.yaml +++ /dev/null @@ -1,214 +0,0 @@ -name: Desktop App Build - -on: - push: - tags: - - "v1.*" - pull_request: - branches: - - v1.0 - workflow_dispatch: - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -jobs: - build: - runs-on: macos-latest - permissions: - contents: write - pull-requests: write - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Set up Rust - uses: actions-rust-lang/setup-rust-toolchain@v1 - with: - toolchain: stable - - - name: Cache Cargo registry - uses: actions/cache@v3 - with: - path: ~/.cargo/registry - key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }} - restore-keys: | - ${{ runner.os }}-cargo-registry- - - - name: Cache Cargo index - uses: actions/cache@v3 - with: - path: ~/.cargo/index - key: ${{ runner.os }}-cargo-index - restore-keys: | - ${{ runner.os }}-cargo-index - - - name: Cache Cargo build - uses: actions/cache@v3 - with: - path: target - key: ${{ runner.os }}-cargo-build-${{ hashFiles('**/Cargo.lock') }} - restore-keys: | - ${{ runner.os }}-cargo-build- - - # Build Rust Binary - - name: Build Release Binary - run: cargo build --release - - - name: copy binary - run: cp target/release/goosed ui/desktop/src/bin/goosed - - # Desktop App Steps - - name: Add MacOS certs for signing and notarization - run: ./add-macos-cert.sh - working-directory: ui/desktop - env: - CERTIFICATE_OSX_APPLICATION: ${{ secrets.CERTIFICATE_OSX_APPLICATION }} - CERTIFICATE_PASSWORD: ${{ secrets.CERTIFICATE_PASSWORD }} - - - name: Set up Node.js - uses: actions/setup-node@v2 - with: - node-version: 'lts/*' - - - name: Install dependencies - run: npm ci - working-directory: ui/desktop - - - name: Make default Goose App - run: | - attempt=0 - max_attempts=2 - until [ $attempt -ge $max_attempts ]; do - npm run bundle:default && break - attempt=$((attempt + 1)) - echo "Attempt $attempt failed. Retrying..." - sleep 5 - done - if [ $attempt -ge $max_attempts ]; then - echo "Action failed after $max_attempts attempts." - exit 1 - fi - working-directory: ui/desktop - env: - APPLE_ID: ${{ secrets.APPLE_ID }} - APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }} - APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} - - - name: Upload artifact - uses: actions/upload-artifact@v4 - with: - name: Goose-darwin-arm64 - path: ui/desktop/out/Goose-darwin-arm64/Goose.zip - - - name: Quick launch test (macOS) - run: | - # Ensure no quarantine attributes (if needed) - xattr -cr "ui/desktop/out/Goose-darwin-arm64/Goose.app" - - - echo "Opening Goose.app..." - open -g "ui/desktop/out/Goose-darwin-arm64/Goose.app" - - # Give the app a few seconds to start and write logs - sleep 5 - - # Check if it's running - if pgrep -f "Goose.app/Contents/MacOS/Goose" > /dev/null; then - echo "App appears to be running." - else - echo "App did not stay open. Possible crash or startup error." - exit 1 - fi - - LOGFILE="$HOME/Library/Application Support/Goose/logs/main.log" - - # Print the log and verify "Starting goosed" - if [ -f "$LOGFILE" ]; then - echo "===== Log file contents =====" - cat "$LOGFILE" - echo "=============================" - - # Check for evidence it ran in the logs: - if grep -F "ChatWindow loaded" "$LOGFILE"; then - echo "Confirmed: 'Starting goosed' found in logs!" - else - echo "Did not find 'Starting goosed' in logs. Failing..." - exit 1 - fi - else - echo "No log file found at $LOGFILE. Exiting with failure." - exit 1 - fi - - # Kill the app to clean up - pkill -f "Goose.app/Contents/MacOS/Goose" - - - - release: - name: Release - runs-on: ubuntu-latest - needs: [build] - permissions: - contents: write - pull-requests: write - if: github.event_name != 'pull_request' - steps: - # Download all artifacts - - name: Download all artifacts - uses: actions/download-artifact@v4 - with: - merge-multiple: true - - # Create or update release - - name: Create/Update Release - uses: ncipollo/release-action@v1 - with: - artifacts: "*.zip" - token: ${{ secrets.GITHUB_TOKEN }} - allowUpdates: true - omitBody: true - omitPrereleaseDuringUpdate: true - - pr-comment: - name: Add PR Comment - runs-on: ubuntu-latest - needs: [build] - permissions: - pull-requests: write - if: github.event_name == 'pull_request' - steps: - # Download all artifacts - - name: Download all artifacts - uses: actions/download-artifact@v4 - with: - merge-multiple: true - - # Create comment with download links - - name: Find Comment - uses: peter-evans/find-comment@v2 - id: fc - with: - issue-number: ${{ github.event.pull_request.number }} - comment-author: 'github-actions[bot]' - body-includes: Desktop App build artifacts - - - name: Create or update comment - uses: peter-evans/create-or-update-comment@v3 - with: - comment-id: ${{ steps.fc.outputs.comment-id }} - issue-number: ${{ github.event.pull_request.number }} - body: | - ### Desktop App for this PR - - The following build is available for testing: - - - [📱 macOS Desktop App (Universal, signed)](https://nightly.link/${{ github.repository }}/actions/runs/${{ github.run_id }}/Goose-darwin-arm64.zip) - - The app is signed and notarized for macOS. After downloading, unzip the file and drag the Goose.app to your Applications folder. - - This link is provided by nightly.link and will work even if you're not logged into GitHub. - edit-mode: replace \ No newline at end of file diff --git a/.github/workflows/pr-comment-bundle-desktop.yml b/.github/workflows/pr-comment-bundle-desktop.yml new file mode 100644 index 0000000000..b5b9237cef --- /dev/null +++ b/.github/workflows/pr-comment-bundle-desktop.yml @@ -0,0 +1,71 @@ +# This workflow is triggered by a comment on an issue or PR with the text "/bundle-desktop". +# It bundles the Desktop App, then creates a PR comment with a link to download the app. +on: + issue_comment: + types: [created] + workflow_dispatch: + +# permissions needed for reacting to IssueOps commands on issues and PRs +permissions: + pull-requests: write + issues: write + checks: read + +name: Workflow to Bundle Desktop App + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + trigger-on-command: + name: Trigger on "/bundle-desktop" PR comment + runs-on: ubuntu-latest + steps: + - uses: github/command@v1.3.0 + id: command + with: + command: "/bundle-desktop" + reaction: "eyes" + allowed_contexts: pull_request + + bundle-desktop: + # Only run this if "/bundle-desktop" command is detected. + if: ${{ steps.command.outputs.continue == 'true' }} + uses: ./.github/workflows/bundle-desktop.yml + secrets: + CERTIFICATE_OSX_APPLICATION: ${{ secrets.CERTIFICATE_OSX_APPLICATION }} + CERTIFICATE_PASSWORD: ${{ secrets.CERTIFICATE_PASSWORD }} + APPLE_ID: ${{ secrets.APPLE_ID }} + APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }} + APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} + + pr-comment: + name: PR Comment with Desktop App + runs-on: ubuntu-latest + needs: [ bundle-desktop ] + permissions: + pull-requests: write + + steps: + - name: Download all artifacts + uses: actions/download-artifact@v4 + with: + merge-multiple: true + + - name: Comment on PR with download link + uses: peter-evans/create-or-update-comment@v3 + with: + comment-id: ${{ steps.command.outputs.comment_id }} + issue-number: ${{ github.event.pull_request.number }} + body: | + ### Desktop App for this PR + + The following build is available for testing: + + - [📱 macOS Desktop App (arm64, signed)](https://nightly.link/${{ github.repository }}/actions/runs/${{ github.run_id }}/Goose-darwin-arm64.zip) + + After downloading, unzip the file and drag the Goose.app to your Applications folder. The app is signed and notarized for macOS. + + This link is provided by nightly.link and will work even if you're not logged into GitHub. + edit-mode: replace diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000000..66fc51944c --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,89 @@ +# This workflow is main release, needs to be manually tagged & pushed. +on: + push: + tags: + - "v1.*" + +name: Release + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + # ------------------------------------ + # 1) Build CLI for multiple OS/Arch + # ------------------------------------ + build-cli: + uses: ./.github/workflows/build-cli.yml + + # ------------------------------------ + # 2) Upload Install CLI Script (we only need to do this once) + # ------------------------------------ + install-script: + name: Upload Install Script + runs-on: ubuntu-latest + needs: [ build-cli ] + steps: + - uses: actions/checkout@v4 + - uses: actions/upload-artifact@v4 + with: + name: download_cli.sh + path: download_cli.sh + + # ------------------------------------------------------------ + # 3) Bundle Desktop App (macOS only) - builds goosed and Electron app + # ------------------------------------------------------------ + bundle-desktop: + uses: ./.github/workflows/bundle-desktop.yml + secrets: + CERTIFICATE_OSX_APPLICATION: ${{ secrets.CERTIFICATE_OSX_APPLICATION }} + CERTIFICATE_PASSWORD: ${{ secrets.CERTIFICATE_PASSWORD }} + APPLE_ID: ${{ secrets.APPLE_ID }} + APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }} + APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} + + # ------------------------------------ + # 4) Create/Update GitHub Release + # ------------------------------------ + release: + name: Release + runs-on: ubuntu-latest + needs: [ build-cli, install-script, bundle-desktop ] + permissions: + contents: write + + steps: + - name: Download all artifacts + uses: actions/download-artifact@v4 + with: + merge-multiple: true + + # Create/update the versioned release + - name: Release versioned + uses: ncipollo/release-action@v1 + with: + token: ${{ secrets.GITHUB_TOKEN }} + # This pattern will match both goose tar.bz2 artifacts and the Goose.zip + artifacts: | + goose-*.tar.bz2 + Goose*.zip + download_cli.sh + allowUpdates: true + omitBody: true + omitPrereleaseDuringUpdate: true + + # Create/update the stable release + - name: Release stable + uses: ncipollo/release-action@v1 + with: + tag: stable + name: Stable + token: ${{ secrets.GITHUB_TOKEN }} + artifacts: | + goose-*.tar.bz2 + Goose*.zip + download_cli.sh + allowUpdates: true + omitBody: true + omitPrereleaseDuringUpdate: true diff --git a/Cross.toml b/Cross.toml index 3c37eeeb17..3469859802 100644 --- a/Cross.toml +++ b/Cross.toml @@ -1,19 +1,29 @@ +# Configuration for cross-compiling using cross [target.aarch64-unknown-linux-gnu] xargo = false pre-build = [ - "dpkg --add-architecture $CROSS_DEB_ARCH && apt-get update --fix-missing && apt-get install --assume-yes libxcb1-dev:$CROSS_DEB_ARCH libdbus-1-dev:$CROSS_DEB_ARCH", + # Add the ARM64 architecture and install necessary dependencies + "dpkg --add-architecture arm64", + """\ + apt-get update --fix-missing && apt-get install -y \ + pkg-config \ + libssl-dev:arm64 \ + libdbus-1-dev:arm64 \ + libxcb1-dev:arm64 + """ ] +env = { PKG_CONFIG_PATH = "/usr/lib/aarch64-linux-gnu/pkgconfig" } -# If you run the build on your local machine, -# This is a workaround for the missing pkg-config path on aarch64 -# You also need to add pkg-config:$CROSS_DEB_ARCH to the apt-get install command above -#[target.aarch64-unknown-linux-gnu.env] -#passthrough = ["PKG_CONFIG_PATH=/usr/lib/aarch64-linux-gnu/pkgconfig"] - -# If you run the build on your local machine, -# You need to add pkg-config:$CROSS_DEB_ARCH to the apt-get install command below [target.x86_64-unknown-linux-gnu] xargo = false pre-build = [ - "dpkg --add-architecture $CROSS_DEB_ARCH && apt-get update --fix-missing && apt-get install --assume-yes libxcb1-dev:$CROSS_DEB_ARCH libdbus-1-dev:$CROSS_DEB_ARCH", + # Install necessary dependencies for x86_64 + # We don't need architecture-specific flags because x86_64 dependencies are installable on Ubuntu system + """\ + apt-get update && apt-get install -y \ + pkg-config \ + libssl-dev \ + libdbus-1-dev \ + libxcb1-dev \ + """ ] diff --git a/download_cli.sh b/download_cli.sh index e5e3aae420..bd09ed703c 100755 --- a/download_cli.sh +++ b/download_cli.sh @@ -1,39 +1,76 @@ -#!/usr/bin/env sh +#!/usr/bin/env bash set -euo pipefail +############################################################################## +# Goose CLI Install Script +# +# This script downloads the latest 'goose' CLI binary from GitHub releases +# and installs it to your system. +# +# Supported OS: macOS (darwin), Linux +# Supported Architectures: x86_64, arm64 +# +# Usage: +# curl -H 'Accept: application/vnd.github.v3.raw' "https://api.github.com/repos/block/goose/contents/download_cli.sh?ref=v1.0" | bash +# +# Environment variables: +# GOOSE_BIN_DIR - Directory to which Goose will be installed (default: $HOME/.local/bin) +# GOOSE_PROVIDER - Optional: provider for goose (passed to "goose configure") +# GOOSE_MODEL - Optional: model for goose (passed to "goose configure") +############################################################################## + +# --- 1) Check for curl --- +if ! command -v curl >/dev/null 2>&1; then + echo "Error: 'curl' is required to download Goose. Please install curl and try again." + exit 1 +fi + +# --- 2) Variables --- REPO="block/goose" OUT_FILE="goose" GITHUB_API_ENDPOINT="api.github.com" +GOOSE_BIN_DIR="${GOOSE_BIN_DIR:-"$HOME/.local/bin"}" -function gh_curl() { - curl -sL -H "Accept: application/vnd.github.v3.raw" $@ +# Helper function to fetch JSON from GitHub +gh_curl() { + curl -sL -H "Accept: application/vnd.github.v3.raw" "$@" } -# Determine the operating system and architecture -OS=$(uname | tr '[:upper:]' '[:lower:]') +# --- 3) Detect OS/Architecture --- +OS=$(uname -s | tr '[:upper:]' '[:lower:]') ARCH=$(uname -m) +case "$OS" in + linux|darwin) ;; + *) + echo "Error: Unsupported OS '$OS'. Goose only supports Linux and macOS." + exit 1 + ;; +esac + case "$ARCH" in x86_64) ARCH="x86_64" ;; - arm64) + arm64|aarch64) + # Some systems use 'arm64' and some 'aarch64' – standardize to 'aarch64' ARCH="aarch64" ;; *) - echo "ERROR: Unsupported architecture: $ARCH" + echo "Error: Unsupported architecture '$ARCH'." exit 1 ;; esac -FILE="goose-$ARCH-unknown-linux-gnu.tar.bz2" +# Build the filename we expect in the release assets if [ "$OS" = "darwin" ]; then FILE="goose-$ARCH-apple-darwin.tar.bz2" +else + FILE="goose-$ARCH-unknown-linux-gnu.tar.bz2" fi -# Find the goose binary asset id +# --- 4) Fetch GitHub Releases and locate the correct asset ID --- echo "Looking up the most recent goose binary release..." -echo "" RELEASES=$(gh_curl https://$GITHUB_API_ENDPOINT/repos/$REPO/releases) # Parse JSON to find the asset ID @@ -52,58 +89,57 @@ ASSET_ID=$(echo "$RELEASES" | awk -v file="$FILE" ' ') if [ -z "$ASSET_ID" ]; then - echo "ERROR: $FILE asset not found" + echo "Error: Could not find a release asset named '$FILE' in the latest releases." exit 1 fi -# Download the goose binary +# --- 5) Download & extract 'goose' binary --- echo "Downloading $FILE..." -echo "" -curl -sL --header 'Accept: application/octet-stream' https://$GITHUB_API_ENDPOINT/repos/$REPO/releases/assets/$ASSET_ID > $FILE -tar -xjf $FILE -echo "Cleaning up $FILE..." -rm $FILE -chmod +x goose goosed +curl -sL --header 'Accept: application/octet-stream' \ + "https://$GITHUB_API_ENDPOINT/repos/$REPO/releases/assets/$ASSET_ID" \ + --output "$FILE" -LOCAL_BIN="$HOME/.local/bin" -if [ ! -d "$LOCAL_BIN" ]; then - echo "Directory $LOCAL_BIN does not exist. Creating it now..." - mkdir -p "$LOCAL_BIN" - echo "Directory $LOCAL_BIN created successfully." +echo "Extracting $FILE..." +tar -xjf "$FILE" +rm "$FILE" # clean up the downloaded tarball + +# Make binaries executable +chmod +x goose + +# --- 6) Install to $GOOSE_BIN_DIR --- +if [ ! -d "$GOOSE_BIN_DIR" ]; then + echo "Creating directory: $GOOSE_BIN_DIR" + mkdir -p "$GOOSE_BIN_DIR" +fi + +echo "Moving goose to $GOOSE_BIN_DIR/$OUT_FILE" +mv goose "$GOOSE_BIN_DIR/$OUT_FILE" + + +# --- 7) Check PATH and give instructions if needed --- +if [[ ":$PATH:" != *":$GOOSE_BIN_DIR:"* ]]; then + echo "" + echo "Warning: $GOOSE_BIN_DIR is not in your PATH." + echo "Add it to your PATH by editing ~/.bashrc, ~/.zshrc, or similar:" + echo " export PATH=\"$GOOSE_BIN_DIR:\$PATH\"" + echo "Then reload your shell (e.g. 'source ~/.bashrc', 'source ~/.zshrc') to apply changes." echo "" fi -echo "Sending goose to $LOCAL_BIN/$OUT_FILE" -echo "" -mv goose $LOCAL_BIN/$OUT_FILE -mv goosed $LOCAL_BIN - -# Check if the directory is in the PATH -if [[ ":$PATH:" != *":$LOCAL_BIN:"* ]]; then - echo "The directory $LOCAL_BIN is not in your PATH." - echo "To add it, append the following line to your shell configuration file (e.g., ~/.bashrc or ~/.zshrc):" - echo "" - echo " export PATH=\"$LOCAL_BIN:\$PATH\"" - echo "" - echo "Then reload your shell configuration file by running:" - echo "" - echo " source ~/.bashrc # or source ~/.zshrc" -fi - -# Initialize config args with the default name +# --- 8) Auto-configure Goose (Optional) --- CONFIG_ARGS="-n default" - -# Check for GOOSE_PROVIDER environment variable if [ -n "${GOOSE_PROVIDER:-}" ]; then - CONFIG_ARGS="$CONFIG_ARGS -p $GOOSE_PROVIDER" + CONFIG_ARGS="$CONFIG_ARGS -p $GOOSE_PROVIDER" fi - -# Check for GOOSE_MODEL environment variable if [ -n "${GOOSE_MODEL:-}" ]; then - CONFIG_ARGS="$CONFIG_ARGS -m $GOOSE_MODEL" + CONFIG_ARGS="$CONFIG_ARGS -m $GOOSE_MODEL" fi -$LOCAL_BIN/$OUT_FILE configure $CONFIG_ARGS - echo "" -echo "You can now run Goose using: $OUT_FILE session" +echo "Configuring Goose with: '$CONFIG_ARGS'" +echo "" +"$GOOSE_BIN_DIR/$OUT_FILE" configure $CONFIG_ARGS + +echo "" +echo "Goose installed successfully! Run '$OUT_FILE session' to get started." +echo ""