diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 220ff2f..961065d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -20,8 +20,12 @@ jobs: go-version-file: go.mod - name: Build and package all targets + env: + TAG: ${{ github.event.release.tag_name }} run: | mkdir -p dist build + ver="${TAG#v}" # strip a leading "v" if present + ldflags="-s -w -X github.com/php-debugger/installer/internal/version.Version=${ver}" # Windows-on-ARM runs x64 under emulation, so one Windows build suffices. for target in linux/amd64 linux/arm64 darwin/amd64 darwin/arm64 windows/amd64; do goos="${target%/*}"; arch="${target#*/}" @@ -31,7 +35,7 @@ jobs: echo "building $osname/$arch" CGO_ENABLED=0 GOOS="$goos" GOARCH="$arch" \ - go build -trimpath -ldflags "-s -w" -o "build/$bin" . + go build -trimpath -ldflags "$ldflags" -o "build/$bin" . chmod +x "build/$bin" # Package into an archive so the executable bit survives download @@ -44,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/README.md b/README.md index 66f7d42..b7e1094 100644 --- a/README.md +++ b/README.md @@ -7,9 +7,28 @@ pulls the **latest release** and auto-detects your OS and architecture. ## Installation +### Install script (recommended) + +macOS / Linux: + +```bash +curl -fsSL https://github.com/php-debugger/installer/releases/latest/download/install.sh | sh +``` + +Windows (PowerShell): + +```powershell +powershell -c "irm https://github.com/php-debugger/installer/releases/latest/download/install.ps1 | iex" +``` + +The script detects your OS/arch, downloads the right archive, and installs the +binary (into the current directory by default; set `INSTALL_DIR` to change it, or +`VERSION` to pin a release). Because it fetches with `curl`/`wget` rather than a +browser, the binary is **not quarantined**, so macOS Gatekeeper doesn't block it. + ### Download a prebuilt binary -Grab the archive for your platform from the +Alternatively, grab the archive for your platform from the [Releases page](https://github.com/php-debugger/installer/releases): | Platform | Asset | @@ -20,9 +39,7 @@ Grab the archive for your platform from the | Linux (arm64) | `php-debugger-linux-arm64.tar.gz` | | Windows (x64) | `php-debugger-windows-amd64.zip` | -On macOS/Linux, extract **in the terminal** (not by double-clicking in Finder). -Command-line `tar` keeps the executable bit and avoids macOS Gatekeeper flagging -the binary — no `chmod` or "allow anyway" needed: +Extract it (command-line `tar` keeps the executable bit) and put it on your PATH: ```bash tar -xzf php-debugger-macos-arm64.tar.gz @@ -30,6 +47,14 @@ mv php-debugger /usr/local/bin/ # or ~/.local/bin php-debugger --help ``` +On macOS, a binary downloaded through a **browser** is quarantined, so Gatekeeper +shows "Apple could not verify…". These builds are ad-hoc signed but not notarized, +so clear the quarantine flag once (the install script above avoids this entirely): + +```bash +xattr -d com.apple.quarantine ./php-debugger +``` + On Windows, unzip the archive and put `php-debugger.exe` somewhere on your PATH. ### Build from source diff --git a/internal/cli/root.go b/internal/cli/root.go index c5fe3c0..c492949 100644 --- a/internal/cli/root.go +++ b/internal/cli/root.go @@ -4,6 +4,7 @@ import ( "fmt" "os" + "github.com/php-debugger/installer/internal/version" "github.com/spf13/cobra" ) @@ -27,6 +28,7 @@ func newRootCmd() *cobra.Command { Long: "php-debugger installs the PHP debugger from the php-debugger/php-debugger\n" + "GitHub releases: either a self-contained PHP interpreter with the debugger\n" + "compiled in (default), or the debugger extension for an existing PHP.", + Version: version.Version, SilenceUsage: true, SilenceErrors: true, } @@ -45,6 +47,7 @@ func newRootCmd() *cobra.Command { newUninstallCmd(), newSwitchCmd(), newResolveCmd(), + newVersionCmd(), ) return rootCmd diff --git a/internal/cli/version.go b/internal/cli/version.go new file mode 100644 index 0000000..4418e58 --- /dev/null +++ b/internal/cli/version.go @@ -0,0 +1,26 @@ +package cli + +import ( + "fmt" + "runtime" + + "github.com/php-debugger/installer/internal/platform" + "github.com/php-debugger/installer/internal/version" + "github.com/spf13/cobra" +) + +func newVersionCmd() *cobra.Command { + return &cobra.Command{ + Use: "version", + Short: "Print the installer version", + Args: cobra.NoArgs, + RunE: func(cmd *cobra.Command, args []string) error { + plat := runtime.GOOS + "/" + runtime.GOARCH + if p, err := platform.Detect(); err == nil { + plat = p.String() + } + fmt.Fprintf(cmd.OutOrStdout(), "php-debugger %s (%s)\n", version.Version, plat) + return nil + }, + } +} diff --git a/internal/version/version.go b/internal/version/version.go new file mode 100644 index 0000000..91ba11e --- /dev/null +++ b/internal/version/version.go @@ -0,0 +1,8 @@ +// Package version holds the installer's build version. +package version + +// Version is the installer version. It is "dev" for local builds; release builds +// override it with the release tag via: +// +// -ldflags "-X github.com/php-debugger/installer/internal/version.Version=" +var Version = "dev" diff --git a/scripts/install.ps1 b/scripts/install.ps1 new file mode 100644 index 0000000..c3c43da --- /dev/null +++ b/scripts/install.ps1 @@ -0,0 +1,51 @@ +<# +.SYNOPSIS + php-debugger installer bootstrap (Windows). + +.DESCRIPTION + Downloads the latest php-debugger release for Windows and installs the binary. + + Usage: + powershell -c "irm https://github.com/php-debugger/installer/releases/latest/download/install.ps1 | iex" + + Environment overrides: + $env:INSTALL_DIR where to place the binary (default: current directory) +#> + +$ErrorActionPreference = "Stop" + +$Repo = "php-debugger/installer" +$Bin = "php-debugger.exe" +$InstallDir = if ($env:INSTALL_DIR) { $env:INSTALL_DIR } else { (Get-Location).Path } + +# Only one Windows build is published (x64); Windows-on-ARM runs it via emulation. +$Asset = "php-debugger-windows-amd64.zip" + +# The releases/latest/download URL always resolves to the newest release. +$Url = "https://github.com/$Repo/releases/latest/download/$Asset" +Write-Host "Installing $Bin (latest, windows/amd64)" + +$tmp = Join-Path $env:TEMP ("php-debugger-" + [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 + Move-Item -Force $src (Join-Path $InstallDir $Bin) +} finally { + Remove-Item -Recurse -Force $tmp -ErrorAction SilentlyContinue +} + +Write-Host "" +Write-Host "Installed to $InstallDir\$Bin" +$onPath = ($env:PATH -split ';') -contains $InstallDir +if ($onPath) { + Write-Host "Run: php-debugger install" +} else { + Write-Host "Add $InstallDir to your PATH, then run 'php-debugger install'." +} diff --git a/scripts/install.sh b/scripts/install.sh new file mode 100755 index 0000000..1570c45 --- /dev/null +++ b/scripts/install.sh @@ -0,0 +1,78 @@ +#!/bin/sh +# php-debugger installer bootstrap (Linux/macOS). Always installs the latest release. +# +# Usage: +# curl -fsSL https://github.com/php-debugger/installer/releases/latest/download/install.sh | sh +# wget -qO- https://github.com/php-debugger/installer/releases/latest/download/install.sh | sh +# +# Environment overrides: +# INSTALL_DIR=/path where to place the binary (default: current directory) +# +# 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/installer" +BIN="php-debugger" +INSTALL_DIR="${INSTALL_DIR:-$(pwd)}" + +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 (see the build-from-source instructions)" ;; +esac + +# --- detect architecture --- +machine="$(uname -m)" +case "$machine" in + x86_64 | amd64) ARCH="amd64" ;; + arm64 | aarch64) ARCH="arm64" ;; + *) fail "unsupported architecture: $machine" ;; +esac + +ASSET="php-debugger-${OS}-${ARCH}.tar.gz" +# 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)" + +# --- 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" + +# --- install --- +mkdir -p "$INSTALL_DIR" +mv "$tmp/$BIN" "$INSTALL_DIR/$BIN" +chmod +x "$INSTALL_DIR/$BIN" +# Clear any quarantine attribute so macOS Gatekeeper does not block it. +if [ "$OS" = "macos" ]; then + xattr -d com.apple.quarantine "$INSTALL_DIR/$BIN" 2>/dev/null || true +fi + +info "" +info "Installed to $INSTALL_DIR/$BIN" +case ":$PATH:" in + *":$INSTALL_DIR:"*) + info "Run: $BIN install" + ;; + *) + info "Add $INSTALL_DIR to your PATH, then run '$BIN install':" + info " export PATH=\"$INSTALL_DIR:\$PATH\"" + ;; +esac