mirror of
https://github.com/diodeinc/pcb.git
synced 2026-09-14 20:17:19 +08:00
6efd247cae
* feat(pcb): add native KiCad URL launcher * fix(pcb): harden launcher recovery choices * fix(pcb): prevent concurrent recovery sessions * fix(pcb): guard restored editor sessions * refactor(pcb): extract shared pcb-diode-uri and pcb-native-dialog crates pcb-launcher's URI validation re-implemented pcb-diode-api's SandboxFileUri parsing; move that parser into a tiny pcb-diode-uri crate both can use, and give the trusted-host allowlist a single home there. Likewise collapse the duplicated osascript/zenity/kdialog/PowerShell dialog machinery from pcb-launcher and pcbc's recovery dialog into a pcb-native-dialog crate. Also drop the launcher's redundant staged-app cleanup (the Drop guard covers it), the duplicate log chmod, the Debug-formatted log header, and a clippy for_kv_map lint in the interposer pattern test. * fix(pcb): block recovery while a recoverable session's editor is open A recoverable stop now deliberately leaves KiCad running, so the still-open-editor guard must cover Recoverable sessions too — otherwise Restore reuses the same local layout dir while the first KiCad instance still has it open. Also match the tracked pid's process name (open / pcbnew / kicad) instead of bare liveness, so a recycled pid cannot permanently wedge the board. * refactor(pcb): simplify launcher install and open plumbing Split install_shim_launcher into fetch / stage / replace helpers so the Windows backup-rename dance is a reusable replace_executable_with_backup, fold pcb-kicad's duplicated per-OS pcbnew command construction into one builder, share a single Download-style helper in install.ps1 the way install.sh already does, dedupe the recovery-cancel wind-down, and fix the README's launcher wording. * refactor(pcb): re-export diode URI types without a module alias Nothing consumed the pcb_diode_api::diode_uri path; re-export the types directly from pcb-diode-uri instead of aliasing the whole crate. * refactor(pcb): trust *.diode.computer in release, all hosts in debug Replaces the enumerated host allowlist: release launchers accept any *.diode.computer subdomain (portless, so preview and gov endpoints work without listing them), while debug builds trust every host so local API stacks need no special-casing.
221 lines
6.9 KiB
Bash
Executable File
221 lines
6.9 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
base_url="https://pcb.api.diode.computer/pcb"
|
|
install_dir="${PCB_INSTALL_DIR:-$HOME/.local/bin}"
|
|
mode="release"
|
|
|
|
usage() {
|
|
cat <<EOF
|
|
Usage: install.sh [--local]
|
|
|
|
Options:
|
|
--local Build pcb and pcbc from this checkout and install a local toolchain.
|
|
-h, --help Show this help.
|
|
EOF
|
|
}
|
|
|
|
while [ "$#" -gt 0 ]; do
|
|
case "$1" in
|
|
--local) mode="local" ;;
|
|
-h|--help) usage; exit 0 ;;
|
|
*) echo "unknown option: $1" >&2; usage >&2; exit 1 ;;
|
|
esac
|
|
shift
|
|
done
|
|
|
|
add_install_dir_to_path() {
|
|
case ":$PATH:" in *":$install_dir:"*) return 0 ;; esac
|
|
|
|
if [ -n "${GITHUB_PATH:-}" ]; then
|
|
echo "$install_dir" >> "$GITHUB_PATH"
|
|
fi
|
|
|
|
env_script="$install_dir/env"
|
|
cat > "$env_script" <<EOF
|
|
case ":\${PATH}:" in
|
|
*:"$install_dir":*) ;;
|
|
*) export PATH="$install_dir:\$PATH" ;;
|
|
esac
|
|
EOF
|
|
|
|
source_line=". \"$env_script\""
|
|
for rc in "$HOME/.profile" "$HOME/.bashrc" "$HOME/.zshrc"; do
|
|
[ -e "$rc" ] || [ "$rc" = "$HOME/.profile" ] || continue
|
|
touch "$rc"
|
|
grep -Fqx "$source_line" "$rc" || printf '\n%s\n' "$source_line" >> "$rc"
|
|
done
|
|
|
|
fish_dir="$HOME/.config/fish/conf.d"
|
|
if [ -d "$HOME/.config/fish" ]; then
|
|
mkdir -p "$fish_dir"
|
|
printf 'fish_add_path "%s"\n' "$install_dir" > "$fish_dir/pcb.env.fish"
|
|
fi
|
|
|
|
echo "Added $install_dir to PATH. Restart your shell or run: $source_line"
|
|
}
|
|
|
|
register_url_launcher() {
|
|
launcher_toolchain="$1"
|
|
if ! "$install_dir/pcb-launcher" --install --toolchain "$launcher_toolchain"; then
|
|
echo "Warning: installed pcb, but could not register the Diode URL launcher; see $HOME/.pcb/pcb-launcher.log" >&2
|
|
fi
|
|
}
|
|
|
|
install_local() {
|
|
command -v cargo >/dev/null || { echo "missing required command: cargo" >&2; exit 1; }
|
|
|
|
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
if [ -f "$script_dir/Cargo.toml" ] && [ -d "$script_dir/crates/pcb" ] && [ -d "$script_dir/crates/pcbc" ]; then
|
|
source_dir="$script_dir"
|
|
elif [ -f "Cargo.toml" ] && [ -d "crates/pcb" ] && [ -d "crates/pcbc" ]; then
|
|
source_dir="$(pwd)"
|
|
else
|
|
echo "could not find pcb checkout; run ./install.sh --local from the repository root" >&2
|
|
exit 1
|
|
fi
|
|
|
|
target_dir="$source_dir/target"
|
|
cargo build --release -p pcb -p pcb-launcher -p pcbc -p rectify --manifest-path "$source_dir/Cargo.toml" --target-dir "$target_dir"
|
|
|
|
local_target_dir="$data_dir/toolchains/local/$target"
|
|
stdlib_dir="$local_target_dir/lib/std"
|
|
|
|
[ -d "$source_dir/lib/std" ] || { echo "missing stdlib: $source_dir/lib/std" >&2; exit 1; }
|
|
|
|
mkdir -p "$install_dir"
|
|
install -m 755 "$target_dir/release/pcb" "$install_dir/pcb"
|
|
launcher_installed=""
|
|
if install -m 755 "$target_dir/release/pcb-launcher" "$install_dir/pcb-launcher"; then
|
|
launcher_installed="true"
|
|
else
|
|
echo "Warning: installed pcb, but could not install the Diode URL launcher" >&2
|
|
fi
|
|
|
|
mkdir -p "$local_target_dir/lib"
|
|
install -m 755 "$target_dir/release/pcbc" "$local_target_dir/pcbc"
|
|
install -m 755 "$target_dir/release/pcb-rectify" "$local_target_dir/pcb-rectify"
|
|
rm -f "$install_dir/pcbc"
|
|
rm -rf "$stdlib_dir"
|
|
cp -R "$source_dir/lib/std" "$stdlib_dir"
|
|
|
|
add_install_dir_to_path
|
|
if [ -n "$launcher_installed" ]; then
|
|
register_url_launcher local
|
|
fi
|
|
|
|
echo "Installed local pcb to $install_dir/pcb"
|
|
if [ -n "$launcher_installed" ]; then
|
|
echo "Installed Diode URL launcher to $install_dir/pcb-launcher"
|
|
fi
|
|
echo "Installed local pcbc to $local_target_dir/pcbc"
|
|
echo "Installed local pcb-rectify to $local_target_dir/pcb-rectify"
|
|
echo "Installed local stdlib to $stdlib_dir"
|
|
}
|
|
|
|
platform="$(uname -s)-$(uname -m)"
|
|
download_targets=""
|
|
case "$platform" in
|
|
Darwin-arm64) target="aarch64-apple-darwin"; download_targets="$target"; data_dir="$HOME/Library/Application Support/pcb" ;;
|
|
Linux-aarch64|Linux-arm64) target="aarch64-unknown-linux-gnu"; download_targets="aarch64-unknown-linux-musl $target"; data_dir="${XDG_DATA_HOME:-$HOME/.local/share}/pcb" ;;
|
|
Linux-x86_64) target="x86_64-unknown-linux-gnu"; download_targets="x86_64-unknown-linux-musl $target"; data_dir="${XDG_DATA_HOME:-$HOME/.local/share}/pcb" ;;
|
|
*) echo "unsupported platform: $platform" >&2; exit 1 ;;
|
|
esac
|
|
|
|
if [ "$mode" = "local" ]; then
|
|
install_local
|
|
exit 0
|
|
fi
|
|
|
|
command -v curl >/dev/null || { echo "missing required command: curl" >&2; exit 1; }
|
|
|
|
json="$(curl -fsSL "$base_url/pcb-latest.json")"
|
|
tag="$(printf '%s' "$json" | sed -n 's/.*"tag"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p')"
|
|
[ -n "$tag" ] || { echo "could not read latest pcb release" >&2; exit 1; }
|
|
|
|
tmp="$(mktemp -d)"
|
|
trap 'rm -rf "$tmp"' EXIT
|
|
|
|
download_binary() {
|
|
binary_name="$1"
|
|
downloaded=""
|
|
for artifact_target in $download_targets; do
|
|
output="$tmp/$binary_name"
|
|
compressed="$output.zst"
|
|
checksum="$output.sha256"
|
|
artifact="$binary_name-$artifact_target"
|
|
rm -f "$output" "$compressed" "$checksum"
|
|
if ! curl -fsSL "$base_url/$tag/$artifact.sha256" -o "$checksum" 2>/dev/null; then
|
|
continue
|
|
fi
|
|
if command -v zstd >/dev/null \
|
|
&& curl -fsSL "$base_url/$tag/$artifact.zst" -o "$compressed" 2>/dev/null; then
|
|
if ! zstd -q -d -f "$compressed" -o "$output"; then
|
|
rm -f "$output"
|
|
if ! curl -fsSL "$base_url/$tag/$artifact" -o "$output" 2>/dev/null; then
|
|
continue
|
|
fi
|
|
fi
|
|
elif ! curl -fsSL "$base_url/$tag/$artifact" -o "$output" 2>/dev/null; then
|
|
continue
|
|
fi
|
|
|
|
expected="$(sed 's/[[:space:]].*//' "$checksum")"
|
|
if command -v shasum >/dev/null; then
|
|
actual="$(shasum -a 256 "$output" | sed 's/[[:space:]].*//')"
|
|
elif command -v sha256sum >/dev/null; then
|
|
actual="$(sha256sum "$output" | sed 's/[[:space:]].*//')"
|
|
else
|
|
echo "missing shasum or sha256sum" >&2
|
|
exit 1
|
|
fi
|
|
[ "$actual" = "$expected" ] || { echo "checksum mismatch for $artifact" >&2; return 2; }
|
|
downloaded="true"
|
|
break
|
|
done
|
|
[ -n "$downloaded" ]
|
|
}
|
|
|
|
if download_binary pcb; then
|
|
:
|
|
else
|
|
pcb_status="$?"
|
|
if [ "$pcb_status" -eq 1 ]; then
|
|
echo "could not find pcb release artifact for $target" >&2
|
|
fi
|
|
exit "$pcb_status"
|
|
fi
|
|
|
|
launcher_downloaded=""
|
|
if download_binary pcb-launcher; then
|
|
launcher_downloaded="true"
|
|
else
|
|
launcher_status="$?"
|
|
if [ "$launcher_status" -eq 1 ]; then
|
|
echo "Warning: pcb-launcher is not available for $tag; skipping Diode URL launcher install" >&2
|
|
else
|
|
echo "Warning: pcb-launcher failed integrity verification; skipping Diode URL launcher install" >&2
|
|
fi
|
|
fi
|
|
|
|
mkdir -p "$install_dir"
|
|
chmod +x "$tmp/pcb"
|
|
mv "$tmp/pcb" "$install_dir/pcb"
|
|
|
|
if [ -n "$launcher_downloaded" ]; then
|
|
if ! chmod +x "$tmp/pcb-launcher" || ! mv "$tmp/pcb-launcher" "$install_dir/pcb-launcher"; then
|
|
echo "Warning: installed pcb, but could not install the Diode URL launcher" >&2
|
|
launcher_downloaded=""
|
|
fi
|
|
fi
|
|
|
|
add_install_dir_to_path
|
|
if [ -n "$launcher_downloaded" ]; then
|
|
register_url_launcher latest
|
|
fi
|
|
|
|
echo "Installed pcb to $install_dir/pcb"
|
|
if [ -n "$launcher_downloaded" ]; then
|
|
echo "Installed Diode URL launcher to $install_dir/pcb-launcher"
|
|
fi
|