From 76d406c29f4e248b240bc23f43c9ee93fb916f02 Mon Sep 17 00:00:00 2001 From: Carlos Granados Date: Thu, 15 Oct 2026 19:22:22 +0200 Subject: [PATCH] Add install scripts for Linux, macOS and Windows --- .github/workflows/release.yml | 3 ++ .github/workflows/tests.yml | 35 +++++++++++++ README.md | 27 +++++++++- scripts/install.ps1 | 73 ++++++++++++++++++++++++++ scripts/install.sh | 99 +++++++++++++++++++++++++++++++++++ 5 files changed, 235 insertions(+), 2 deletions(-) create mode 100644 scripts/install.ps1 create mode 100755 scripts/install.sh diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6826daa..b3999bf 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -48,6 +48,9 @@ jobs: fi rm -f "build/$bin" done + # Attach the install scripts too, so releases/latest/download/install.sh + # is a stable URL that serves the released version of the script. + cp scripts/install.sh scripts/install.ps1 dist/ ls -la dist - name: Attach binaries to the release diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index e21ff58..d8362b1 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -64,3 +64,38 @@ jobs: env: DBGP_REQUIRE_ENGINE: '1' run: go test -race -count=1 -v ./... + + # The install scripts, run as users run them, against the latest release. + install: + name: install script (${{ matrix.os }}) + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + steps: + - uses: actions/checkout@v7 + - name: install.sh + if: runner.os != 'Windows' + run: | + cat scripts/install.sh | sh + dbgp -version + # A chosen release, to a directory of its own. + INSTALL_DIR="$RUNNER_TEMP/dbgp" VERSION=0.1.0 sh scripts/install.sh + test "$("$RUNNER_TEMP/dbgp/dbgp" -version)" = "dbgp 0.1.0" + - name: install.ps1 + if: runner.os == 'Windows' + shell: pwsh + run: | + Get-Content -Raw scripts/install.ps1 | Invoke-Expression + $dir = Join-Path $env:ProgramFiles "dbgp" + & (Join-Path $dir "dbgp.exe") -version + if (-not (([Environment]::GetEnvironmentVariable("Path", "Machine") -split ';') -contains $dir)) { + throw "$dir is not on the system PATH" + } + # A chosen release, to a directory of its own. + $env:INSTALL_DIR = Join-Path $env:RUNNER_TEMP "dbgp" + $env:VERSION = "0.1.0" + ./scripts/install.ps1 + $got = & (Join-Path $env:INSTALL_DIR "dbgp.exe") -version + if ($got -ne "dbgp 0.1.0") { throw "installed $got, want dbgp 0.1.0" } diff --git a/README.md b/README.md index e8f9742..7058f84 100644 --- a/README.md +++ b/README.md @@ -30,12 +30,35 @@ out, next, _ := sess.Output(0) // program output, read incrementally notes, _ := sess.Notifications(0) // PHP warnings and other notifications ``` -## Command-line debugger +## Installation + +The `dbgp` command, both the command-line debugger and the MCP server, installs system-wide with a script that downloads the latest [release](https://github.com/php-debugger/dbgp-client/releases) for your OS and architecture. + +macOS / Linux (installs to `/usr/local/bin`, using `sudo` if needed): + +```sh +curl -fsSL https://github.com/php-debugger/dbgp-client/releases/latest/download/install.sh | sh +``` + +Windows (PowerShell as administrator; installs to `%ProgramFiles%\dbgp` and adds it to the system PATH): + +```powershell +powershell -c "irm https://github.com/php-debugger/dbgp-client/releases/latest/download/install.ps1 | iex" +``` -`cmd/dbgp` is an interactive debugger built on the library. Each [release](https://github.com/php-debugger/dbgp-client/releases) has it built for Linux and macOS (amd64 and arm64) and Windows (amd64); or install it with Go: +Set `INSTALL_DIR` to install elsewhere, and `VERSION` (e.g. `0.1.0`) to install a release other than the latest. Run the script again to update; if the background MCP server is running, restart it afterwards with `dbgp mcp stop` and `dbgp mcp start`. Builds are published for Linux and macOS (amd64 and arm64) and Windows (amd64, which also runs on ARM); elsewhere, install with Go: ```sh go install github.com/php-debugger/dbgp-client/cmd/dbgp@latest +``` + +`dbgp -version` shows the installed version. + +## Command-line debugger + +`cmd/dbgp` is an interactive debugger built on the library: + +```sh dbgp -break src/Controller/HomeController.php:25 ``` diff --git a/scripts/install.ps1 b/scripts/install.ps1 new file mode 100644 index 0000000..6b460f9 --- /dev/null +++ b/scripts/install.ps1 @@ -0,0 +1,73 @@ +<# +.SYNOPSIS + dbgp installer (Windows). + +.DESCRIPTION + Downloads the latest dbgp release for Windows, installs it system-wide and + adds it to the system PATH. Run it from PowerShell as administrator. + + Usage: + powershell -c "irm https://github.com/php-debugger/dbgp-client/releases/latest/download/install.ps1 | iex" + + Environment overrides: + $env:INSTALL_DIR where to place the binary (default: %ProgramFiles%\dbgp) + $env:VERSION the release to install (default: the latest) +#> + +$ErrorActionPreference = "Stop" + +$Repo = "php-debugger/dbgp-client" +$Bin = "dbgp.exe" +$InstallDir = if ($env:INSTALL_DIR) { $env:INSTALL_DIR } else { Join-Path $env:ProgramFiles "dbgp" } +$IsAdmin = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole( + [Security.Principal.WindowsBuiltInRole]::Administrator) + +# The default location and the system PATH need administrator rights. +if (-not $env:INSTALL_DIR -and -not $IsAdmin) { + throw "Installing to $InstallDir needs administrator rights: run PowerShell as administrator, or set `$env:INSTALL_DIR" +} + +# Only one Windows build is published (x64); Windows-on-ARM runs it via emulation. +$Asset = "dbgp-windows-amd64.zip" + +if ($env:VERSION) { + $Url = "https://github.com/$Repo/releases/download/$($env:VERSION)/$Asset" + Write-Host "Installing dbgp $($env:VERSION) (windows/amd64)" +} else { + # The releases/latest/download URL always resolves to the newest release. + $Url = "https://github.com/$Repo/releases/latest/download/$Asset" + Write-Host "Installing dbgp (latest, windows/amd64)" +} + +$tmp = Join-Path $env:TEMP ("dbgp-" + [System.Guid]::NewGuid().ToString()) +New-Item -ItemType Directory -Path $tmp | Out-Null +try { + $zip = Join-Path $tmp $Asset + Invoke-WebRequest -UseBasicParsing -Uri $Url -OutFile $zip + Expand-Archive -Path $zip -DestinationPath $tmp -Force + + $src = Join-Path $tmp $Bin + if (-not (Test-Path $src)) { throw "archive did not contain $Bin" } + + New-Item -ItemType Directory -Force -Path $InstallDir | Out-Null + $dest = Join-Path $InstallDir $Bin + try { + Move-Item -Force $src $dest + } catch { + throw "Cannot replace $dest, probably because dbgp is running: stop it (dbgp mcp stop for the background MCP server) and run this again" + } +} finally { + Remove-Item -Recurse -Force $tmp -ErrorAction SilentlyContinue +} + +Write-Host "" +Write-Host "Installed $(& $dest -version) to $dest" + +# Add the directory to the PATH: the system one when installing system-wide. +$scope = if ($IsAdmin) { "Machine" } else { "User" } +$path = [Environment]::GetEnvironmentVariable("Path", $scope) +if (-not $path) { $path = "" } +if (-not (($path -split ';') -contains $InstallDir)) { + [Environment]::SetEnvironmentVariable("Path", ((@($path.TrimEnd(';'), $InstallDir) | Where-Object { $_ }) -join ';'), $scope) + Write-Host "Added $InstallDir to the $($scope.ToLower()) PATH: open a new terminal to use dbgp." +} diff --git a/scripts/install.sh b/scripts/install.sh new file mode 100755 index 0000000..0c5234e --- /dev/null +++ b/scripts/install.sh @@ -0,0 +1,99 @@ +#!/bin/sh +# dbgp installer (Linux/macOS). Installs the latest release system-wide. +# +# Usage: +# curl -fsSL https://github.com/php-debugger/dbgp-client/releases/latest/download/install.sh | sh +# wget -qO- https://github.com/php-debugger/dbgp-client/releases/latest/download/install.sh | sh +# +# Environment overrides: +# INSTALL_DIR=/path where to place the binary (default: /usr/local/bin) +# VERSION=0.1.0 the release to install (default: the latest) +# +# sudo is used only when INSTALL_DIR is not writable. +# +# Because the archive is fetched with curl/wget (not a browser), the binary is +# not quarantined, so macOS Gatekeeper does not block it. As a safety net the +# script also clears the quarantine attribute if present. +set -eu + +REPO="php-debugger/dbgp-client" +BIN="dbgp" +INSTALL_DIR="${INSTALL_DIR:-/usr/local/bin}" +VERSION="${VERSION:-}" + +info() { printf '%s\n' "$*"; } +fail() { printf 'error: %s\n' "$*" >&2; exit 1; } +have() { command -v "$1" >/dev/null 2>&1; } + +download() { # download URL FILE + if have curl; then curl -fsSL "$1" -o "$2" + elif have wget; then wget -q "$1" -O "$2" + else fail "need curl or wget to download"; fi +} + +# --- detect OS --- +kernel="$(uname -s)" +case "$kernel" in + Linux) OS="linux" ;; + Darwin) OS="macos" ;; + *) fail "unsupported operating system: $kernel (install with: go install github.com/$REPO/cmd/dbgp@latest)" ;; +esac + +# --- detect architecture --- +machine="$(uname -m)" +case "$machine" in + x86_64 | amd64) ARCH="amd64" ;; + arm64 | aarch64) ARCH="arm64" ;; + *) fail "unsupported architecture: $machine (install with: go install github.com/$REPO/cmd/dbgp@latest)" ;; +esac + +ASSET="dbgp-${OS}-${ARCH}.tar.gz" +if [ -n "$VERSION" ]; then + URL="https://github.com/$REPO/releases/download/$VERSION/$ASSET" + info "Installing $BIN $VERSION ($OS/$ARCH)" +else + # The releases/latest/download URL always resolves to the newest release. + URL="https://github.com/$REPO/releases/latest/download/$ASSET" + info "Installing $BIN (latest, $OS/$ARCH)" +fi + +# --- download + extract --- +tmp="$(mktemp -d)" +trap 'rm -rf "$tmp"' EXIT INT TERM +download "$URL" "$tmp/$ASSET" || fail "failed to download $URL" +tar -xzf "$tmp/$ASSET" -C "$tmp" || fail "failed to extract $ASSET" +[ -f "$tmp/$BIN" ] || fail "archive did not contain $BIN" +chmod +x "$tmp/$BIN" +# Clear any quarantine attribute so macOS Gatekeeper does not block it. +if [ "$OS" = "macos" ]; then + xattr -d com.apple.quarantine "$tmp/$BIN" 2>/dev/null || true +fi + +# --- install --- +# sudo only if the directory, or the one it would be created in, is not writable. +target="$INSTALL_DIR" +while [ ! -d "$target" ]; do target="$(dirname "$target")"; done +SUDO="" +if [ ! -w "$target" ]; then + have sudo || fail "cannot write to $INSTALL_DIR: run as root, or set INSTALL_DIR" + info "Installing to $INSTALL_DIR needs administrator rights: using sudo" + SUDO="sudo" +fi +$SUDO mkdir -p "$INSTALL_DIR" || fail "cannot create $INSTALL_DIR" +# mv replaces the file rather than writing into it, so a running dbgp (such +# as the background MCP server) keeps running the old one. +$SUDO mv -f "$tmp/$BIN" "$INSTALL_DIR/$BIN" || fail "cannot write to $INSTALL_DIR" + +info "" +info "Installed $("$INSTALL_DIR/$BIN" -version) to $INSTALL_DIR/$BIN" +case ":$PATH:" in + *":$INSTALL_DIR:"*) ;; + *) + info "Add $INSTALL_DIR to your PATH, e.g. in your shell's profile:" + info " export PATH=\"$INSTALL_DIR:\$PATH\"" + ;; +esac +if "$INSTALL_DIR/$BIN" mcp status >/dev/null 2>&1; then + info "The background MCP server still runs the previous version: restart it with" + info " dbgp mcp stop && dbgp mcp start" +fi