diff --git a/.github/workflows/homebrew-dev-cask.yml b/.github/workflows/homebrew-dev-cask.yml new file mode 100644 index 000000000..fea137fe1 --- /dev/null +++ b/.github/workflows/homebrew-dev-cask.yml @@ -0,0 +1,127 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +name: Publish Homebrew Dev Cask Bundle + +on: + workflow_dispatch: + workflow_run: + workflows: + - Release Dev + - Release VM Dev + types: + - completed + +permissions: + contents: write + +concurrency: + group: homebrew-dev-cask + cancel-in-progress: false + +defaults: + run: + shell: bash + +jobs: + publish: + name: Publish Dev Cask Bundle + if: github.repository == 'NVIDIA/OpenShell' && (github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success') + runs-on: ubuntu-latest + timeout-minutes: 10 + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + BUNDLE_ASSET: openshell-homebrew-dev-aarch64-apple-darwin.tar.gz + steps: + - name: Download release assets + run: | + set -euo pipefail + mkdir -p downloads + + gh release download dev \ + --repo "${GITHUB_REPOSITORY}" \ + --pattern openshell-aarch64-apple-darwin.tar.gz \ + --pattern openshell-gateway-aarch64-apple-darwin.tar.gz \ + --dir downloads \ + --clobber + + gh release download vm-dev \ + --repo "${GITHUB_REPOSITORY}" \ + --pattern openshell-driver-vm-aarch64-apple-darwin.tar.gz \ + --dir downloads \ + --clobber + + test -f downloads/openshell-aarch64-apple-darwin.tar.gz + test -f downloads/openshell-gateway-aarch64-apple-darwin.tar.gz + test -f downloads/openshell-driver-vm-aarch64-apple-darwin.tar.gz + + - name: Build cask bundle + run: | + set -euo pipefail + mkdir -p bundle/libexec/openshell artifacts + + tar -xzf downloads/openshell-aarch64-apple-darwin.tar.gz \ + -C bundle openshell + tar -xzf downloads/openshell-gateway-aarch64-apple-darwin.tar.gz \ + -C bundle/libexec openshell-gateway + tar -xzf downloads/openshell-driver-vm-aarch64-apple-darwin.tar.gz \ + -C bundle/libexec/openshell openshell-driver-vm + + cat > bundle/openshell-gateway <<'SH' + #!/bin/sh + set -e + + path=$0 + while [ -L "$path" ]; do + link=$(readlink "$path") + case "$link" in + /*) path=$link ;; + *) path=$(dirname "$path")/$link ;; + esac + done + + dir=$(CDPATH= cd -- "$(dirname -- "$path")" && pwd) + export OPENSHELL_DRIVER_DIR="${dir}/libexec/openshell" + exec "${dir}/libexec/openshell-gateway" "$@" + SH + + cat > bundle/openshell-driver-vm <<'SH' + #!/bin/sh + set -e + + path=$0 + while [ -L "$path" ]; do + link=$(readlink "$path") + case "$link" in + /*) path=$link ;; + *) path=$(dirname "$path")/$link ;; + esac + done + + dir=$(CDPATH= cd -- "$(dirname -- "$path")" && pwd) + exec "${dir}/libexec/openshell/openshell-driver-vm" "$@" + SH + + cat > bundle/libexec/openshell/openshell-driver-vm-entitlements.plist <<'XML' + + + + + com.apple.security.hypervisor + + + + XML + + chmod 0555 bundle/openshell bundle/openshell-gateway bundle/openshell-driver-vm + chmod 0555 bundle/libexec/openshell-gateway bundle/libexec/openshell/openshell-driver-vm + + tar -czf "artifacts/${BUNDLE_ASSET}" -C bundle . + tar -tzf "artifacts/${BUNDLE_ASSET}" + + - name: Upload cask bundle to dev release + run: | + set -euo pipefail + gh release upload dev "artifacts/${BUNDLE_ASSET}" \ + --repo "${GITHUB_REPOSITORY}" \ + --clobber diff --git a/.github/workflows/release-vm-dev.yml b/.github/workflows/release-vm-dev.yml index 6619d2a6b..7d04defb4 100644 --- a/.github/workflows/release-vm-dev.yml +++ b/.github/workflows/release-vm-dev.yml @@ -689,8 +689,8 @@ jobs: done vm_count=$(ls release-final/openshell-vm-*.tar.gz 2>/dev/null | wc -l) driver_count=$(ls release-final/openshell-driver-vm-*.tar.gz 2>/dev/null | wc -l) - if [ "$vm_count" -eq 0 ] || [ "$driver_count" -eq 0 ]; then - echo "ERROR: Missing binary tarballs (openshell-vm=${vm_count}, openshell-driver-vm=${driver_count})" >&2 + if [ "$vm_count" -ne 3 ] || [ "$driver_count" -ne 3 ]; then + echo "ERROR: Expected 3 openshell-vm and 3 openshell-driver-vm binary tarballs (openshell-vm=${vm_count}, openshell-driver-vm=${driver_count})" >&2 ls -la release/ || true exit 1 fi diff --git a/Casks/openshell-dev.rb b/Casks/openshell-dev.rb new file mode 100644 index 000000000..f18d8939d --- /dev/null +++ b/Casks/openshell-dev.rb @@ -0,0 +1,28 @@ +cask "openshell-dev" do + version :latest + sha256 :no_check + + url "https://github.com/NVIDIA/OpenShell/releases/download/dev/openshell-homebrew-dev-aarch64-apple-darwin.tar.gz" + name "OpenShell Dev" + desc "Development build of the safe private runtime for autonomous AI agents" + homepage "https://github.com/NVIDIA/OpenShell" + + depends_on arch: :arm64 + depends_on macos: ">= :big_sur" + + binary "openshell" + binary "openshell-gateway" + binary "openshell-driver-vm" + + postflight do + system_command "/usr/bin/codesign", + args: [ + "--entitlements", + "#{staged_path}/libexec/openshell/openshell-driver-vm-entitlements.plist", + "--force", + "-s", + "-", + "#{staged_path}/libexec/openshell/openshell-driver-vm", + ] + end +end diff --git a/README.md b/README.md index aaa851452..b9863ee3a 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,19 @@ OpenShell is built agent-first. The project ships with agent skills for everythi curl -LsSf https://raw.githubusercontent.com/NVIDIA/OpenShell/main/install.sh | sh ``` +**Homebrew dev cask (macOS Apple Silicon):** + +```bash +curl -LsSf https://raw.githubusercontent.com/NVIDIA/OpenShell/main/install-dev.sh | sh +``` + +Or install the cask directly: + +```bash +brew tap nvidia/openshell https://github.com/NVIDIA/OpenShell.git +brew install --cask nvidia/openshell/openshell-dev +``` + **From PyPI (requires [uv](https://docs.astral.sh/uv/)):** ```bash diff --git a/architecture/build-containers.md b/architecture/build-containers.md index 7886c766c..78ac421e4 100644 --- a/architecture/build-containers.md +++ b/architecture/build-containers.md @@ -35,10 +35,25 @@ OpenShell also publishes a standalone `openshell-gateway` binary as a GitHub rel - **Artifact name**: `openshell-gateway-.tar.gz` - **Targets**: `x86_64-unknown-linux-gnu`, `aarch64-unknown-linux-gnu`, `aarch64-apple-darwin` - **Release workflows**: `.github/workflows/release-dev.yml`, `.github/workflows/release-tag.yml` -- **Installer**: None yet. The binary is a manual-download asset. +- **Installers**: `install-vm.sh` for the rolling development gateway, and the Homebrew dev cask under `Casks/`. Both the standalone artifact and the deployed container image use the `openshell-gateway` binary. +## Homebrew Dev Cask + +This repository also acts as the Homebrew tap for the rolling macOS Apple Silicon dev build. Users must tap it with the full Git URL because the repository is not named `homebrew-*`: + +```bash +brew tap nvidia/openshell https://github.com/NVIDIA/OpenShell.git +brew install --cask nvidia/openshell/openshell-dev +``` + +`Casks/openshell-dev.rb` uses `version :latest` and `sha256 :no_check` because it installs a rolling development archive from the `dev` GitHub release. `.github/workflows/homebrew-dev-cask.yml` runs after the dev CLI/gateway release or VM dev driver release completes. It downloads the macOS ARM64 CLI, gateway, and VM driver assets, repacks them into `openshell-homebrew-dev-aarch64-apple-darwin.tar.gz`, and uploads that archive back to the `dev` release. + +The archive exposes `openshell`, `openshell-gateway`, and `openshell-driver-vm` through cask `binary` stanzas. The gateway entrypoint is a wrapper that sets `OPENSHELL_DRIVER_DIR` to the bundled driver directory before executing the real gateway binary. The cask signs the VM driver with the Hypervisor entitlement during `postflight`. + +`install-dev.sh` installs the same dev cask automatically on macOS Apple Silicon. On Linux amd64 and arm64, it installs the rolling development Debian package. + ## Python Wheels OpenShell also publishes Python wheels for `linux/amd64`, `linux/arm64`, and macOS ARM64. diff --git a/install-dev.sh b/install-dev.sh index dc0666cd6..d98de09cc 100755 --- a/install-dev.sh +++ b/install-dev.sh @@ -5,7 +5,8 @@ # Install the OpenShell development build from the rolling GitHub `dev` release. # # This script is intended as a convenient installer for development builds. It -# currently supports Debian packages on Linux amd64 and arm64 only. +# supports Debian packages on Linux amd64/arm64 and the Homebrew dev cask on +# macOS Apple Silicon. # set -e @@ -14,6 +15,9 @@ REPO="NVIDIA/OpenShell" GITHUB_URL="https://github.com/${REPO}" RELEASE_TAG="dev" CHECKSUMS_NAME="openshell-checksums-sha256.txt" +HOMEBREW_TAP="nvidia/openshell" +HOMEBREW_TAP_URL="${GITHUB_URL}.git" +HOMEBREW_CASK="${HOMEBREW_TAP}/openshell-dev" info() { printf '%s: %s\n' "$APP_NAME" "$*" >&2 @@ -26,7 +30,7 @@ error() { usage() { cat </dev/null 2>&1; then + info "reinstalling ${HOMEBREW_CASK}..." + brew reinstall --cask "${HOMEBREW_CASK}" + else + info "installing ${HOMEBREW_CASK}..." + brew install --cask "${HOMEBREW_CASK}" + fi + + _brew_prefix="$(brew --prefix)" + _openshell_bin="${_brew_prefix}/bin/openshell" + if [ -x "$_openshell_bin" ]; then + _installed_version="$("$_openshell_bin" --version 2>/dev/null || true)" + if [ -n "$_installed_version" ]; then + info "installed ${_installed_version} via Homebrew" + else + info "installed OpenShell development cask via Homebrew" + fi + else + info "installed OpenShell development cask via Homebrew" + fi +} + main() { - while [ "$#" -gt 0 ]; do - case "$1" in + for arg in "$@"; do + case "$arg" in --help) usage exit 0 ;; *) - error "unknown option: $1" + error "unknown option: $arg" ;; esac - shift done require_cmd curl + + if [ "$(uname -s)" = "Darwin" ]; then + install_homebrew_cask + return 0 + fi + check_platform TARGET_USER="$(target_user)"