mirror of
https://github.com/webinstall/webi-installers.git
synced 2026-05-28 11:33:09 +00:00
Compare commits
4 Commits
v1.3.1
...
doc-instal
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a5c265d8ee | ||
|
|
bd330897c1 | ||
|
|
80fc00de21 | ||
|
|
734455a6c5 |
442
_skills/installer/SKILL.md
Normal file
442
_skills/installer/SKILL.md
Normal file
@@ -0,0 +1,442 @@
|
||||
---
|
||||
name: installer
|
||||
description: >
|
||||
Create or update install.sh and install.ps1 scripts for a webi package.
|
||||
Use when adding a new package to webi-installers, or when an existing
|
||||
install script needs to be updated to match a changed archive structure.
|
||||
Covers discovering archive layout from GitHub releases, identifying the
|
||||
right install pattern (A–I), and writing both the POSIX shell and
|
||||
PowerShell scripts that the webi framework calls.
|
||||
Note: this skill covers install scripts only — writing releases.js /
|
||||
releases.conf (the release-fetcher config) is a separate concern.
|
||||
license: MIT
|
||||
compatibility: Requires git, curl, tar. GitHub API access needed for
|
||||
discovery phase. Designed for Claude Code in the webi-installers repo.
|
||||
metadata:
|
||||
author: AJ ONeal
|
||||
version: "1.1"
|
||||
---
|
||||
|
||||
# Webi Installer Skill
|
||||
|
||||
Write `install.sh` and `install.ps1` for a webi package. These scripts are
|
||||
called by the webi framework **after** it has already downloaded and verified
|
||||
the archive — your job is only to unpack and place the files.
|
||||
|
||||
> **Scope:** This skill covers `install.sh` and `install.ps1` only. A
|
||||
> separate `releases.js` / `releases.conf` file is needed to tell webi where
|
||||
> to fetch releases from. That config must already exist (or be written
|
||||
> separately) before these install scripts are useful.
|
||||
|
||||
## Quick overview
|
||||
|
||||
1. [Discover the archive layout](#1-discover-the-archive-layout) — inspect
|
||||
GitHub releases with `curl` + `tar -t` to understand what's inside.
|
||||
2. [Choose the install pattern](#2-choose-the-install-pattern) — nine
|
||||
patterns (A–I) cover almost every real-world case.
|
||||
3. [Write `install.sh`](#3-write-installsh) — POSIX shell, ~20–40 lines.
|
||||
4. [Write `install.ps1`](#4-write-installps1) — PowerShell, ~40–60 lines.
|
||||
5. [Check for classification issues](#5-check-for-classification-issues) —
|
||||
look for variant assets, non-standard OS/arch naming, or installer .exe
|
||||
files that need special handling.
|
||||
|
||||
Full reference: [`references/PATTERNS.md`](references/PATTERNS.md)
|
||||
Archive layout details: [`references/ARCHIVE-LAYOUTS.md`](references/ARCHIVE-LAYOUTS.md)
|
||||
Classification guide: [`references/CLASSIFICATION.md`](references/CLASSIFICATION.md)
|
||||
|
||||
---
|
||||
|
||||
## 1. Discover the archive layout
|
||||
|
||||
### Use the webi releases API (fastest, if the package already exists)
|
||||
|
||||
```sh
|
||||
# JSON with all releases for a package
|
||||
curl -s https://webinstall.dev/api/releases/bat.json | jq '.releases[:3]'
|
||||
```
|
||||
|
||||
Each entry has `name` (filename), `version`, `os`, `arch`, `ext`, `download`.
|
||||
|
||||
### Or inspect GitHub releases directly
|
||||
|
||||
```sh
|
||||
# List asset filenames for the latest release
|
||||
curl -s "https://api.github.com/repos/sharkdp/bat/releases?per_page=3" \
|
||||
| jq '.[0].assets[] | .name'
|
||||
```
|
||||
|
||||
### Inspect what's inside an archive
|
||||
|
||||
Download one representative asset and list its contents **without extracting**:
|
||||
|
||||
```sh
|
||||
# tar.gz / tar.xz
|
||||
curl -fsSL "$DOWNLOAD_URL" | tar -tz
|
||||
|
||||
# tar.zst (modern systems — GNU tar / bsdtar both support this)
|
||||
curl -fsSL "$DOWNLOAD_URL" | tar --zstd -tz
|
||||
|
||||
# zip
|
||||
curl -fsSL "$DOWNLOAD_URL" -o /tmp/pkg.zip && unzip -l /tmp/pkg.zip
|
||||
|
||||
# bare binary (no archive extension, e.g. jq-linux-amd64)
|
||||
# The file IS the binary — no unpacking needed. Set WEBI_SINGLE=true.
|
||||
```
|
||||
|
||||
Look for:
|
||||
- Is the binary at the top level or inside a subdirectory?
|
||||
- Does the subdirectory name include the version and/or triplet?
|
||||
- Are there completions (`completions/`, `autocomplete/`, `complete/`)?
|
||||
- Are there man pages (`*.1`, `doc/*.1`, `man/man1/`)?
|
||||
- Are there shared libraries (`.so`, `.dylib`, `.dll`) alongside the binary?
|
||||
- Is the binary name different from the package command name?
|
||||
|
||||
See [`references/ARCHIVE-LAYOUTS.md`](references/ARCHIVE-LAYOUTS.md) for
|
||||
what each pattern looks like, with real examples.
|
||||
|
||||
---
|
||||
|
||||
## 2. Choose the install pattern
|
||||
|
||||
| Pattern | Description | Examples |
|
||||
|---------|-------------|---------|
|
||||
| **A** | Bare binary (or binary+docs) at archive root | caddy, fzf, k9s, terraform |
|
||||
| **B** | Binary inside a version/triplet-named subdirectory | delta, shellcheck, trip, xsv |
|
||||
| **C** | Like B, plus shell completions and/or man pages | bat, fd, rg, sd, watchexec, zoxide |
|
||||
| **D** | Binary + shared libraries (bundled) | ollama (Linux), psql, sass, syncthing |
|
||||
| **E** | FHS-like layout (`bin/`, `share/man/`) | gh, pandoc |
|
||||
| **F** | Renamed binary needing install-time rename | pathman, yq |
|
||||
| **G** | Full SDK/toolchain (many files) | go, node, zig, flutter, julia |
|
||||
| **H** | .NET runtime bundle | pwsh |
|
||||
| **I** | Multi-binary distribution | dashcore, mutagen |
|
||||
|
||||
**Pattern A** is by far the most common (~28 packages). When in doubt,
|
||||
download the archive and `tar -tz` it before writing a single line of code.
|
||||
|
||||
---
|
||||
|
||||
## 3. Write `install.sh`
|
||||
|
||||
The framework (`_webi/package-install.tpl.sh`) handles: user-agent detection,
|
||||
version resolution, download, checksum verification, and PATH management.
|
||||
Your script is **injected into** the framework and provides the
|
||||
package-specific part: where to find the binary and how to move it.
|
||||
|
||||
### Script structure
|
||||
|
||||
Every `install.sh` wraps its definitions in an `__init_pkgname()` function
|
||||
and immediately calls it. This prevents variable leakage when the script is
|
||||
sourced by the framework:
|
||||
|
||||
```sh
|
||||
#!/bin/sh
|
||||
|
||||
__init_toolname() {
|
||||
set -e
|
||||
set -u
|
||||
|
||||
####################
|
||||
# Install toolname #
|
||||
####################
|
||||
|
||||
pkg_cmd_name="toolname"
|
||||
WEBI_SINGLE=true # if applicable — see below
|
||||
|
||||
pkg_dst_cmd="$HOME/.local/bin/toolname"
|
||||
pkg_dst="$pkg_dst_cmd"
|
||||
|
||||
pkg_src_cmd="$HOME/.local/opt/toolname-v$WEBI_VERSION/bin/toolname"
|
||||
pkg_src_dir="$HOME/.local/opt/toolname-v$WEBI_VERSION"
|
||||
pkg_src="$pkg_src_cmd"
|
||||
|
||||
pkg_install() {
|
||||
# ...
|
||||
}
|
||||
|
||||
pkg_get_current_version() {
|
||||
# ...
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
__init_toolname
|
||||
```
|
||||
|
||||
### Variables
|
||||
|
||||
| Variable | Description |
|
||||
|----------|-------------|
|
||||
| `pkg_cmd_name` | The command name that ends up on `$PATH` |
|
||||
| `pkg_dst_cmd` | Final destination: `~/.local/bin/<cmd>` (the symlink) |
|
||||
| `pkg_dst` | Same as `pkg_dst_cmd` for single-binary packages; `~/.local/opt/<cmd>` for SDKs |
|
||||
| `pkg_src_cmd` | Versioned binary: `~/.local/opt/<pkg>-v<ver>/bin/<cmd>` |
|
||||
| `pkg_src_dir` | Versioned install dir: `~/.local/opt/<pkg>-v<ver>` |
|
||||
| `pkg_src` | Same as `pkg_src_cmd` for single-binary packages; same as `pkg_src_dir` for SDKs |
|
||||
|
||||
**Framework-derived (set by the framework before calling `pkg_install` — do not set manually):**
|
||||
- `pkg_src_bin` — `$(dirname "$pkg_src_cmd")` — the versioned `bin/` dir
|
||||
- `pkg_dst_bin` — `$(dirname "$pkg_dst_cmd")` — `~/.local/bin`
|
||||
|
||||
### `WEBI_SINGLE`
|
||||
|
||||
`WEBI_SINGLE=true` affects the default values the framework uses for
|
||||
`pkg_src` and `pkg_dst`, and how `webi_link()` creates the symlink:
|
||||
|
||||
- **With `WEBI_SINGLE=true`**: links the binary file directly:
|
||||
`~/.local/bin/cmd → ~/.local/opt/cmd-vX.Y.Z/bin/cmd`
|
||||
- **Without it (default)**: links the directory:
|
||||
`~/.local/opt/cmd → ~/.local/opt/cmd-vX.Y.Z`
|
||||
|
||||
Set `WEBI_SINGLE=true` when using the conventional Pattern A skeleton
|
||||
(where `pkg_src` and `pkg_dst` are not set to custom values). When you
|
||||
explicitly assign all six variables yourself (as in Patterns B–F),
|
||||
`WEBI_SINGLE` is not strictly required but can still be set for clarity.
|
||||
|
||||
Pattern G (SDKs) and Pattern H (.NET bundles) do NOT use `WEBI_SINGLE` —
|
||||
they define `pkg_link()` manually because the whole directory tree must
|
||||
be linked, not just a single binary.
|
||||
|
||||
### Required function: `pkg_install`
|
||||
|
||||
Moves files from the extracted archive into the versioned opt directory.
|
||||
The framework has already extracted the archive into a temp directory and
|
||||
`cd`'d into it before calling `pkg_install`.
|
||||
|
||||
```sh
|
||||
pkg_install() {
|
||||
mkdir -p "$pkg_src_bin"
|
||||
mv ./tool-*/tool "$pkg_src_cmd"
|
||||
chmod a+x "$pkg_src_cmd"
|
||||
}
|
||||
```
|
||||
|
||||
### Recommended function: `pkg_get_current_version`
|
||||
|
||||
Used to detect whether the package is already installed at the right version:
|
||||
|
||||
```sh
|
||||
pkg_get_current_version() {
|
||||
# 'tool --version' output: "tool 1.2.3 (rev abc)"
|
||||
# trim to just the version number
|
||||
tool --version 2>/dev/null | head -n 1 | cut -d' ' -f2
|
||||
}
|
||||
```
|
||||
|
||||
### Skeletons by pattern
|
||||
|
||||
**Pattern A** — binary at archive root (`WEBI_SINGLE=true`):
|
||||
```sh
|
||||
WEBI_SINGLE=true
|
||||
pkg_install() {
|
||||
mkdir -p "$pkg_src_bin"
|
||||
mv ./"$pkg_cmd_name"* "$pkg_src_cmd"
|
||||
chmod a+x "$pkg_src_cmd"
|
||||
}
|
||||
```
|
||||
Use `$pkg_cmd_name*` as the glob — it matches the binary and avoids
|
||||
accidentally moving LICENSE or README into the binary path.
|
||||
|
||||
**Pattern B** — binary inside a `tool-{ver}-{triplet}/` subdirectory:
|
||||
```sh
|
||||
WEBI_SINGLE=true
|
||||
pkg_install() {
|
||||
mkdir -p "$pkg_src_bin"
|
||||
mv ./tool-*/tool "$pkg_src_cmd"
|
||||
chmod a+x "$pkg_src_cmd"
|
||||
}
|
||||
```
|
||||
|
||||
**Pattern C** — like B, plus completions and man pages.
|
||||
The completion directory and filename vary per package — always check
|
||||
`tar -tz` output first. Common variants: `completions/`, `autocomplete/`,
|
||||
`complete/`. See [`references/PATTERNS.md`](references/PATTERNS.md) for
|
||||
a full example with guards:
|
||||
```sh
|
||||
WEBI_SINGLE=true
|
||||
pkg_install() {
|
||||
mkdir -p "$pkg_src_bin"
|
||||
mv ./tool-*/tool "$pkg_src_cmd"
|
||||
chmod a+x "$pkg_src_cmd"
|
||||
|
||||
# bash completion (directory name varies — check tar -tz)
|
||||
if test -e ./tool-*/completions/tool.bash; then
|
||||
mkdir -p "$pkg_src_dir/share/bash-completion/completions"
|
||||
mv ./tool-*/completions/tool.bash \
|
||||
"$pkg_src_dir/share/bash-completion/completions/tool"
|
||||
fi
|
||||
if test -e ./tool-*/completions/tool.fish; then
|
||||
mkdir -p "$pkg_src_dir/share/fish/vendor_completions.d"
|
||||
mv ./tool-*/completions/tool.fish \
|
||||
"$pkg_src_dir/share/fish/vendor_completions.d/tool.fish"
|
||||
fi
|
||||
if test -e ./tool-*/completions/_tool; then
|
||||
mkdir -p "$pkg_src_dir/share/zsh/site-functions"
|
||||
mv ./tool-*/completions/_tool \
|
||||
"$pkg_src_dir/share/zsh/site-functions/_tool"
|
||||
fi
|
||||
if test -e ./tool-*/tool.1; then
|
||||
mkdir -p "$pkg_src_dir/share/man/man1"
|
||||
mv ./tool-*/tool.1 "$pkg_src_dir/share/man/man1/tool.1"
|
||||
fi
|
||||
}
|
||||
```
|
||||
|
||||
**Pattern D** — binary + shared libraries. The entire directory structure
|
||||
must be preserved. See [`references/PATTERNS.md`](references/PATTERNS.md)
|
||||
for the ollama and psql examples.
|
||||
|
||||
**Pattern E** — FHS layout (archive already has `bin/` and `share/`):
|
||||
```sh
|
||||
WEBI_SINGLE=true
|
||||
pkg_install() {
|
||||
mkdir -p "$(dirname "$pkg_src_dir")"
|
||||
mv ./tool-*/ "$pkg_src_dir"
|
||||
}
|
||||
```
|
||||
|
||||
**Pattern F** — binary needs rename (archive name ≠ command name).
|
||||
Use when the binary in the archive cannot be matched by `$pkg_cmd_name*`
|
||||
— e.g., `yq_linux_amd64` for a command named `yq`:
|
||||
```sh
|
||||
WEBI_SINGLE=true
|
||||
pkg_install() {
|
||||
mkdir -p "$pkg_src_bin"
|
||||
mv ./yq_* "$pkg_src_cmd"
|
||||
chmod a+x "$pkg_src_cmd"
|
||||
}
|
||||
```
|
||||
|
||||
**Pattern G** — full SDK (do NOT set `WEBI_SINGLE`):
|
||||
```sh
|
||||
# pkg_src = directory, not a binary
|
||||
pkg_src="$pkg_src_dir"
|
||||
pkg_dst="$HOME/.local/opt/tool"
|
||||
|
||||
pkg_install() {
|
||||
mkdir -p "$(dirname "$pkg_src_dir")"
|
||||
mv ./tool-*/ "$pkg_src_dir"
|
||||
}
|
||||
|
||||
pkg_link() {
|
||||
rm -f "$pkg_dst"
|
||||
ln -s "$pkg_src" "$pkg_dst"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Write `install.ps1`
|
||||
|
||||
A PowerShell framework template exists (`_webi/package-install.tpl.ps1`)
|
||||
and injects the `install.ps1` script at the `# {{ installer }}` placeholder.
|
||||
The template provides: error handling, directory setup, `Invoke-DownloadUrl`
|
||||
helper, and PATH management via `webi_path_add`. However, unlike the shell
|
||||
side, the PS1 framework does **not** download or extract the archive — the
|
||||
package script must handle that itself. The same path conventions apply
|
||||
(opt/bin layout), but Windows uses `Copy-Item` instead of symlinks for
|
||||
the final `bin/` step.
|
||||
|
||||
### Variable block (always at top)
|
||||
|
||||
```powershell
|
||||
$pkg_cmd_name = "tool"
|
||||
|
||||
$pkg_dst_cmd = "$Env:USERPROFILE\.local\bin\tool.exe"
|
||||
$pkg_dst_bin = "$Env:USERPROFILE\.local\bin"
|
||||
$pkg_dst = "$pkg_dst_cmd"
|
||||
|
||||
$pkg_src_cmd = "$Env:USERPROFILE\.local\opt\tool-v$Env:WEBI_VERSION\bin\tool.exe"
|
||||
$pkg_src_bin = "$Env:USERPROFILE\.local\opt\tool-v$Env:WEBI_VERSION\bin"
|
||||
$pkg_src_dir = "$Env:USERPROFILE\.local\opt\tool-v$Env:WEBI_VERSION"
|
||||
$pkg_src = "$pkg_src_cmd"
|
||||
```
|
||||
|
||||
### Standard body
|
||||
|
||||
```powershell
|
||||
New-Item "$Env:USERPROFILE\Downloads\webi" -ItemType Directory -Force | Out-Null
|
||||
$pkg_download = "$Env:USERPROFILE\Downloads\webi\$Env:WEBI_PKG_FILE"
|
||||
|
||||
# Fetch archive
|
||||
if (!(Test-Path -Path "$pkg_download")) {
|
||||
Write-Output "Downloading tool from $Env:WEBI_PKG_URL to $pkg_download"
|
||||
& curl.exe -A "$Env:WEBI_UA" -fsSL "$Env:WEBI_PKG_URL" -o "$pkg_download.part"
|
||||
& Move-Item "$pkg_download.part" "$pkg_download"
|
||||
}
|
||||
|
||||
if (!(Test-Path -Path "$pkg_src_cmd")) {
|
||||
Write-Output "Installing tool"
|
||||
|
||||
Push-Location .local\tmp
|
||||
Remove-Item -Path ".\tool-v*" -Recurse -ErrorAction Ignore
|
||||
|
||||
# Unpack — Windows BSD-tar handles zip too
|
||||
Write-Output "Unpacking $pkg_download"
|
||||
& tar xf "$pkg_download"
|
||||
|
||||
# Move binary into place — adjust glob for your archive structure
|
||||
Write-Output "Install Location: $pkg_src_cmd"
|
||||
New-Item "$pkg_src_bin" -ItemType Directory -Force | Out-Null
|
||||
Move-Item -Path ".\tool-*\tool.exe" -Destination "$pkg_src_bin"
|
||||
Pop-Location
|
||||
}
|
||||
|
||||
# Windows has no symlinks in the webi sense — copy to bin/
|
||||
Write-Output "Copying into '$pkg_dst_cmd' from '$pkg_src_cmd'"
|
||||
Remove-Item -Path "$pkg_dst_cmd" -Recurse -ErrorAction Ignore | Out-Null
|
||||
New-Item "$pkg_dst_bin" -ItemType Directory -Force | Out-Null
|
||||
Copy-Item -Path "$pkg_src" -Destination "$pkg_dst" -Recurse
|
||||
```
|
||||
|
||||
For Pattern A (binary at archive root), change the `Move-Item` line to:
|
||||
```powershell
|
||||
Move-Item -Path ".\tool.exe" -Destination "$pkg_src_bin"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Check for classification issues
|
||||
|
||||
Before writing any scripts, scan the asset list for red flags:
|
||||
|
||||
### Non-standard OS/arch names in filenames
|
||||
|
||||
The webi classifier recognises most patterns automatically. Watch for:
|
||||
- `darwin` vs `macos` — both recognised; output normalised to `macos`
|
||||
- `x86_64` vs `amd64` — both recognised; output normalised to `amd64`
|
||||
- `aarch64` vs `arm64` — both recognised; output normalised to `arm64`
|
||||
- `armv7` (missing trailing `l`) — normalised to `armv7l`
|
||||
|
||||
These are handled automatically. Only flag them if the asset list contains
|
||||
something genuinely unusual that the classifier would not recognise.
|
||||
|
||||
### Variant assets needing tags
|
||||
|
||||
Flag if you see multiple assets for the same OS/arch that serve different
|
||||
hardware or runtime requirements:
|
||||
- **GPU variants**: `*-rocm*`, `*-cuda*`, `*-vulkan*` alongside a baseline build
|
||||
- **Windows installer**: `*Setup.exe` or `*Install.exe` alongside a bare `*.exe`
|
||||
- **Framework-dependent .NET**: `*-fxdependent*` vs self-contained
|
||||
- **AppImage**: `*.AppImage` — not supported by the webi installer
|
||||
- **Electron/GUI app**: `*.dmg` or `*.AppImage` that is a full GUI app, not a CLI
|
||||
|
||||
If you find variants, see [`references/CLASSIFICATION.md`](references/CLASSIFICATION.md)
|
||||
for how to write a variant tagger.
|
||||
|
||||
### Formats to drop
|
||||
|
||||
These are automatically filtered by the framework — no action needed:
|
||||
- `.deb`, `.rpm`, `.snap`, `.AppImage`
|
||||
- Checksums (`*.sha256`, `*.sha512`, `*.asc`, `*.sig`)
|
||||
- Source archives (`*-src.tar.gz`, `*.tar.gz` with no OS in name)
|
||||
|
||||
---
|
||||
|
||||
## Reference files
|
||||
|
||||
- [`references/PATTERNS.md`](references/PATTERNS.md) — detailed pattern
|
||||
descriptions with real package examples and complete install script snippets
|
||||
- [`references/ARCHIVE-LAYOUTS.md`](references/ARCHIVE-LAYOUTS.md) — actual
|
||||
`tar -t` output for representative packages in each pattern
|
||||
- [`references/CLASSIFICATION.md`](references/CLASSIFICATION.md) — when and
|
||||
how to write variant taggers; non-standard filename conventions
|
||||
289
_skills/installer/references/ARCHIVE-LAYOUTS.md
Normal file
289
_skills/installer/references/ARCHIVE-LAYOUTS.md
Normal file
@@ -0,0 +1,289 @@
|
||||
# Archive Layouts — Real Package Examples
|
||||
|
||||
Actual `tar -t` / `unzip -l` output for representative packages.
|
||||
Use these to calibrate your eye for what each pattern looks like.
|
||||
|
||||
---
|
||||
|
||||
## Pattern A — Flat archive (no subdirectory)
|
||||
|
||||
### caddy 2.9.1 — linux/amd64 tar.gz
|
||||
```
|
||||
caddy
|
||||
LICENSE
|
||||
README.md
|
||||
```
|
||||
Binary `caddy` is at the top level. Set `WEBI_SINGLE=true`.
|
||||
|
||||
### fzf 0.70.0 — linux/amd64 tar.gz
|
||||
```
|
||||
fzf
|
||||
```
|
||||
Minimal — just the binary.
|
||||
|
||||
### terraform 1.9.8 — linux/amd64 zip
|
||||
```
|
||||
terraform
|
||||
LICENSE.txt
|
||||
```
|
||||
Zip archive but same flat layout.
|
||||
|
||||
### k9s — linux/amd64 tar.gz
|
||||
```
|
||||
k9s
|
||||
LICENSE
|
||||
README.md
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Pattern B — Named subdirectory, binary only
|
||||
|
||||
### delta 0.18.2 — linux/amd64 tar.gz
|
||||
```
|
||||
delta-0.18.2-x86_64-unknown-linux-musl/
|
||||
delta-0.18.2-x86_64-unknown-linux-musl/delta
|
||||
delta-0.18.2-x86_64-unknown-linux-musl/LICENSE
|
||||
delta-0.18.2-x86_64-unknown-linux-musl/README.md
|
||||
```
|
||||
Glob to move: `./delta-*/delta`
|
||||
|
||||
### shellcheck 0.10.0 — linux/x86_64 tar.xz
|
||||
```
|
||||
shellcheck-v0.10.0/
|
||||
shellcheck-v0.10.0/shellcheck
|
||||
shellcheck-v0.10.0/LICENSE.txt
|
||||
shellcheck-v0.10.0/README.txt
|
||||
```
|
||||
Glob to move: `./shellcheck-*/shellcheck`
|
||||
|
||||
### xsv 0.13.0 — linux/x86_64 tar.gz
|
||||
```
|
||||
xsv-0.13.0-x86_64-unknown-linux-musl/
|
||||
xsv-0.13.0-x86_64-unknown-linux-musl/xsv
|
||||
xsv-0.13.0-x86_64-unknown-linux-musl/UNLICENSE
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Pattern C — Subdirectory + completions + man pages
|
||||
|
||||
### rg/ripgrep 14.1.1 — linux/amd64 tar.gz
|
||||
```
|
||||
ripgrep-14.1.1-x86_64-unknown-linux-musl/
|
||||
ripgrep-14.1.1-x86_64-unknown-linux-musl/rg
|
||||
ripgrep-14.1.1-x86_64-unknown-linux-musl/complete/
|
||||
ripgrep-14.1.1-x86_64-unknown-linux-musl/complete/_rg
|
||||
ripgrep-14.1.1-x86_64-unknown-linux-musl/complete/_rg.ps1
|
||||
ripgrep-14.1.1-x86_64-unknown-linux-musl/complete/rg.bash
|
||||
ripgrep-14.1.1-x86_64-unknown-linux-musl/complete/rg.fish
|
||||
ripgrep-14.1.1-x86_64-unknown-linux-musl/doc/
|
||||
ripgrep-14.1.1-x86_64-unknown-linux-musl/doc/rg.1
|
||||
ripgrep-14.1.1-x86_64-unknown-linux-musl/doc/FAQ.md
|
||||
ripgrep-14.1.1-x86_64-unknown-linux-musl/doc/GUIDE.md
|
||||
ripgrep-14.1.1-x86_64-unknown-linux-musl/CHANGELOG.md
|
||||
ripgrep-14.1.1-x86_64-unknown-linux-musl/LICENSE-MIT
|
||||
ripgrep-14.1.1-x86_64-unknown-linux-musl/README.md
|
||||
```
|
||||
Note: completions are in `complete/` (not `completions/`). Man page is `doc/rg.1`.
|
||||
|
||||
### sd 1.1.0 — linux/x86_64 tar.gz
|
||||
```
|
||||
sd-v1.1.0-x86_64-unknown-linux-musl/
|
||||
sd-v1.1.0-x86_64-unknown-linux-musl/sd
|
||||
sd-v1.1.0-x86_64-unknown-linux-musl/sd.1
|
||||
sd-v1.1.0-x86_64-unknown-linux-musl/completions/
|
||||
sd-v1.1.0-x86_64-unknown-linux-musl/completions/sd.bash
|
||||
sd-v1.1.0-x86_64-unknown-linux-musl/completions/sd.elv
|
||||
sd-v1.1.0-x86_64-unknown-linux-musl/completions/sd.fish
|
||||
sd-v1.1.0-x86_64-unknown-linux-musl/completions/_sd
|
||||
sd-v1.1.0-x86_64-unknown-linux-musl/completions/_sd.ps1
|
||||
sd-v1.1.0-x86_64-unknown-linux-musl/CHANGELOG.md
|
||||
sd-v1.1.0-x86_64-unknown-linux-musl/LICENSE
|
||||
sd-v1.1.0-x86_64-unknown-linux-musl/README.md
|
||||
```
|
||||
Note: man page `sd.1` is at subdirectory root. Completions in `completions/`.
|
||||
|
||||
### bat 0.26.1 — linux/amd64 tar.gz
|
||||
```
|
||||
bat-v0.26.1-x86_64-unknown-linux-musl/
|
||||
bat-v0.26.1-x86_64-unknown-linux-musl/bat
|
||||
bat-v0.26.1-x86_64-unknown-linux-musl/bat.1
|
||||
bat-v0.26.1-x86_64-unknown-linux-musl/autocomplete/
|
||||
bat-v0.26.1-x86_64-unknown-linux-musl/autocomplete/bat.bash
|
||||
bat-v0.26.1-x86_64-unknown-linux-musl/autocomplete/bat.fish
|
||||
bat-v0.26.1-x86_64-unknown-linux-musl/autocomplete/bat.zsh
|
||||
bat-v0.26.1-x86_64-unknown-linux-musl/LICENSE-APACHE
|
||||
bat-v0.26.1-x86_64-unknown-linux-musl/LICENSE-MIT
|
||||
bat-v0.26.1-x86_64-unknown-linux-musl/README.md
|
||||
```
|
||||
Note: completions in `autocomplete/` (not `completions/`). Zsh file is `bat.zsh` not `_bat`.
|
||||
|
||||
### goreleaser — linux/amd64 tar.gz
|
||||
```
|
||||
goreleaser
|
||||
completions/
|
||||
completions/goreleaser.bash
|
||||
completions/goreleaser.fish
|
||||
completions/goreleaser.zsh
|
||||
manpages/
|
||||
manpages/goreleaser.1.gz
|
||||
LICENSE.md
|
||||
README.md
|
||||
```
|
||||
Note: goreleaser uses Pattern A layout (binary at root, no subdirectory)
|
||||
but includes completions and a gzipped man page. Set `WEBI_SINGLE=true`;
|
||||
move completions and man page after the binary.
|
||||
|
||||
---
|
||||
|
||||
## Pattern D — Binary + shared libraries
|
||||
|
||||
### ollama 0.17.7 — linux/amd64 tar.zst
|
||||
```
|
||||
bin/
|
||||
bin/ollama
|
||||
lib/
|
||||
lib/ollama/
|
||||
lib/ollama/libggml-base.so
|
||||
lib/ollama/libggml-cpu-alderlake.so
|
||||
lib/ollama/libggml-cpu-haswell.so
|
||||
lib/ollama/libggml-cpu-icelake.so
|
||||
lib/ollama/libggml-cpu-sandybridge.so
|
||||
lib/ollama/libggml-cpu-skylakex.so
|
||||
lib/ollama/libggml-cpu-sse42.so
|
||||
lib/ollama/libggml-cpu-x64.so
|
||||
lib/ollama/cuda_v12/
|
||||
lib/ollama/cuda_v12/libcublas.so.12
|
||||
lib/ollama/cuda_v12/libcublasLt.so.12
|
||||
lib/ollama/cuda_v12/libcudart.so.12
|
||||
lib/ollama/cuda_v12/libggml-cuda.so
|
||||
... (66 files total)
|
||||
```
|
||||
Extract bin/ and lib/ directories separately or together.
|
||||
|
||||
### psql (postgres client) — linux/amd64 tar.gz
|
||||
```
|
||||
psql-17.2-linux-x86_64/
|
||||
psql-17.2-linux-x86_64/bin/
|
||||
psql-17.2-linux-x86_64/bin/psql
|
||||
psql-17.2-linux-x86_64/lib/
|
||||
psql-17.2-linux-x86_64/lib/libpq.so.5
|
||||
psql-17.2-linux-x86_64/lib/libz.so.1
|
||||
psql-17.2-linux-x86_64/lib/libzstd.so.1
|
||||
psql-17.2-linux-x86_64/lib/libssl.so.3
|
||||
psql-17.2-linux-x86_64/lib/libcrypto.so.3
|
||||
psql-17.2-linux-x86_64/include/
|
||||
... (75 files total)
|
||||
```
|
||||
Move the entire `psql-{ver}-{triplet}/` directory: `mv ./psql-*/ "$pkg_src_dir"`
|
||||
|
||||
---
|
||||
|
||||
## Pattern E — FHS layout
|
||||
|
||||
### gh 2.67.0 — linux/amd64 tar.gz
|
||||
```
|
||||
gh_2.67.0_linux_amd64/
|
||||
gh_2.67.0_linux_amd64/bin/
|
||||
gh_2.67.0_linux_amd64/bin/gh
|
||||
gh_2.67.0_linux_amd64/share/
|
||||
gh_2.67.0_linux_amd64/share/man/
|
||||
gh_2.67.0_linux_amd64/share/man/man1/
|
||||
gh_2.67.0_linux_amd64/share/man/man1/gh-actions-cache-delete.1
|
||||
gh_2.67.0_linux_amd64/share/man/man1/gh-actions-cache-list.1
|
||||
... (129 man pages)
|
||||
gh_2.67.0_linux_amd64/LICENSE
|
||||
```
|
||||
Move the entire `gh_*/` directory: `mv ./gh_*/ "$pkg_src_dir"`
|
||||
|
||||
---
|
||||
|
||||
## Pattern F — Binary needs rename
|
||||
|
||||
### yq — linux/amd64 tar.gz (WEBI_SINGLE=true)
|
||||
```
|
||||
yq_linux_amd64
|
||||
yq.1
|
||||
```
|
||||
Binary is `yq_linux_amd64` — must rename to `yq` during install.
|
||||
|
||||
### pathman 0.6.0 — linux/amd64 tar.gz (WEBI_SINGLE=true)
|
||||
```
|
||||
pathman-v0.6.0-linux-amd64_v1
|
||||
```
|
||||
Binary name includes the full release tag. Rename to `pathman`.
|
||||
|
||||
---
|
||||
|
||||
## Pattern G — Full SDK
|
||||
|
||||
### node 24.14.0 — linux/amd64 tar.xz
|
||||
```
|
||||
node-v24.14.0-linux-x64/
|
||||
node-v24.14.0-linux-x64/bin/
|
||||
node-v24.14.0-linux-x64/bin/node
|
||||
node-v24.14.0-linux-x64/bin/npm -> ../lib/node_modules/npm/bin/npm-cli.js
|
||||
node-v24.14.0-linux-x64/bin/npx -> ../lib/node_modules/npm/bin/npx-cli.js
|
||||
node-v24.14.0-linux-x64/include/
|
||||
node-v24.14.0-linux-x64/lib/
|
||||
node-v24.14.0-linux-x64/lib/node_modules/
|
||||
node-v24.14.0-linux-x64/share/
|
||||
... (thousands of files)
|
||||
```
|
||||
Move entire directory: `mv ./node-*/ "$pkg_src_dir"`
|
||||
|
||||
### go 1.24.1 — linux/amd64 tar.gz
|
||||
```
|
||||
go/
|
||||
go/bin/
|
||||
go/bin/go
|
||||
go/bin/gofmt
|
||||
go/src/
|
||||
go/pkg/
|
||||
... (thousands of files)
|
||||
```
|
||||
Note: go's archive root directory is literally `go/` with no version in the name.
|
||||
|
||||
---
|
||||
|
||||
## Pattern H — .NET runtime bundle
|
||||
|
||||
### pwsh 7.4.6 — linux/amd64 tar.gz
|
||||
```
|
||||
pwsh
|
||||
Accessibility.dll
|
||||
clrcompression.dll
|
||||
clrjit.dll
|
||||
createdump
|
||||
cs/
|
||||
cs/System.Private.CoreLib.resources.dll
|
||||
de/
|
||||
de/System.Private.CoreLib.resources.dll
|
||||
... (727 files, all in same flat directory)
|
||||
```
|
||||
No subdirectory. Move all files into `$pkg_src_bin/`.
|
||||
|
||||
---
|
||||
|
||||
## Inspecting archives yourself
|
||||
|
||||
```sh
|
||||
# tar.gz / tar.xz / tar.zst — list contents only (no extraction)
|
||||
curl -fsSL "$URL" | tar -tz | head -20
|
||||
|
||||
# zip
|
||||
curl -fsSL "$URL" -o /tmp/pkg.zip
|
||||
unzip -l /tmp/pkg.zip | head -20
|
||||
|
||||
# For a .zst file when tar doesn't support zstd natively:
|
||||
curl -fsSL "$URL" -o /tmp/pkg.tar.zst && zstd -dc /tmp/pkg.tar.zst | tar -tz | head -20
|
||||
```
|
||||
|
||||
**What to look for**:
|
||||
1. Is there a top-level directory? (Pattern B/C/D/E/G) or no directory? (Pattern A/F/H)
|
||||
2. What is the directory named? Does it contain version? triplet?
|
||||
3. Are there `completions/`, `autocomplete/`, `complete/` subdirs? (Pattern C)
|
||||
4. Are there `.so`/`.dylib`/`.dll` files? (Pattern D or H)
|
||||
5. Does the binary name match the command you want on PATH? (Pattern F if not)
|
||||
6. Is there a `bin/` directory at the top level? (Pattern E or G)
|
||||
183
_skills/installer/references/CLASSIFICATION.md
Normal file
183
_skills/installer/references/CLASSIFICATION.md
Normal file
@@ -0,0 +1,183 @@
|
||||
# Classification Reference
|
||||
|
||||
When to flag classification issues, what the webi classifier does automatically,
|
||||
and what needs manual annotation.
|
||||
|
||||
---
|
||||
|
||||
## What the classifier handles automatically
|
||||
|
||||
The webi classifier (`internal/classify/classify.go`) parses asset filenames
|
||||
using regex patterns and produces canonical `os`, `arch`, `libc`, and `ext`
|
||||
values. It handles the vast majority of packages with no configuration needed.
|
||||
|
||||
### OS recognition
|
||||
Filenames containing these terms are classified automatically:
|
||||
- `darwin`, `macos`, `osx`, `apple` → `macos` in legacy cache
|
||||
- `linux` → `linux`
|
||||
- `windows`, `win`, `win32`, `win64` → `windows`
|
||||
- `freebsd`, `openbsd`, `netbsd`, `dragonfly` → respective values
|
||||
- `.deb`, `.rpm`, `.snap` → `linux` (but dropped from legacy cache)
|
||||
- `.dmg`, `.app.zip` → `macos`
|
||||
|
||||
### Arch recognition
|
||||
Filenames containing these terms are classified automatically:
|
||||
- `x86_64`, `amd64`, `64bit`, `x64` → `amd64`
|
||||
- `aarch64`, `arm64` → `arm64`
|
||||
- `armv7`, `armv7l`, `armhf`, `gnueabihf` → `armv7l`
|
||||
- `armv6`, `armv6l` → `armv6l`
|
||||
- `i386`, `i686`, `386`, `x86` → `x86`
|
||||
- `universal`, `universal2` → `amd64` (fat binary; arm64 falls back to this)
|
||||
|
||||
### Format recognition
|
||||
- `.tar.gz`, `.tar.xz`, `.tar.zst`, `.tar.bz2`, `.zip`, `.7z` → compressed archive
|
||||
- `.pkg`, `.msi`, `.dmg` → platform installer
|
||||
- `.exe` → either bare binary or GUI installer (see below)
|
||||
- No extension in filename → bare binary (ext = `exe` in cache)
|
||||
|
||||
### Automatically dropped
|
||||
These asset types are recognised and excluded without any configuration:
|
||||
- Checksums: `*.sha256`, `*.sha512`, `*.md5`, `*.sha256sum`
|
||||
- Signatures: `*.asc`, `*.sig`, `*.cosign`, `*.sbom`
|
||||
- Source archives: files with `source`, `src` in the name but no OS
|
||||
- Package formats not supported by the Node installer: `.deb`, `.rpm`, `.snap`,
|
||||
`.AppImage`, `.apk`
|
||||
|
||||
---
|
||||
|
||||
## When you need to add configuration
|
||||
|
||||
### Variant assets
|
||||
|
||||
A **variant** is a secondary build that serves the same OS/arch as a baseline
|
||||
build but requires different hardware or runtime support. The Node.js installer
|
||||
can't choose between variants — it only knows OS, arch, and libc. Variants
|
||||
must be tagged and then excluded at export time.
|
||||
|
||||
**Common variants and how to identify them**:
|
||||
|
||||
| Variant | Filename pattern | Notes |
|
||||
|---------|-----------------|-------|
|
||||
| CUDA (GPU) | `*-cuda*`, `*cuda12*` | NVIDIA GPU support |
|
||||
| ROCm (GPU) | `*-rocm*` | AMD GPU support |
|
||||
| Vulkan | `*-vulkan*` | Cross-vendor GPU |
|
||||
| AppImage | `*.AppImage` | Linux sandboxed app |
|
||||
| .NET fxdependent | `*-fxdependent*` | Requires .NET runtime |
|
||||
| Windows installer | `*Setup.exe`, `*Install.exe` | GUI installer, not the binary |
|
||||
|
||||
**Rule**: if there are multiple assets for the same OS/arch combination and
|
||||
they serve the same users differently, they need variant tags. The baseline
|
||||
(most widely compatible) build should be kept; variants should be tagged and
|
||||
excluded.
|
||||
|
||||
**Example**: ollama publishes for linux/amd64:
|
||||
- `ollama-linux-amd64.tar.zst` — baseline (CPU + any GPU auto-detected)
|
||||
- `ollama-linux-amd64-rocm.tar.zst` — ROCm variant
|
||||
- `ollama-linux-amd64-jetpack6.tar.zst` — NVIDIA Jetson variant
|
||||
|
||||
Only the baseline is useful via webi. The ROCm and Jetpack builds should be
|
||||
tagged as variants and excluded.
|
||||
|
||||
---
|
||||
|
||||
### Windows .exe: bare binary vs GUI installer
|
||||
|
||||
`.exe` assets are ambiguous — they could be:
|
||||
1. A bare binary (the tool itself, run from command line)
|
||||
2. A GUI installer (runs a setup wizard, not useful for webi)
|
||||
|
||||
**How to tell**:
|
||||
- GUI installer: filename contains `Setup`, `Install`, `Installer`, `inno`, `nsis`
|
||||
- GUI installer: the tool also has a `.zip` or `.tar.gz` for Windows
|
||||
- Bare binary: filename matches the tool name with minimal decoration
|
||||
|
||||
**When you see both**, the `.zip`/archive build is what webi uses. The `.exe`
|
||||
installer should be tagged as a variant (`installer`) so it's excluded.
|
||||
|
||||
**When there's only a `.exe`** (no archive), it's probably the bare binary.
|
||||
Test by downloading and running it — a bare binary runs immediately.
|
||||
|
||||
---
|
||||
|
||||
### Packages with no OS/arch in filenames
|
||||
|
||||
Some packages (rare) release with minimal filename decoration. Examples:
|
||||
- `tool-v1.2.3.tar.gz` — no OS, no arch
|
||||
- `tool.tar.gz` — version not even in filename
|
||||
|
||||
These are usually source archives (not compiled binaries) and should be
|
||||
dropped entirely from the release list. If they are compiled binaries for a
|
||||
specific OS, the releases.js config needs an `asset_filter` key to match the
|
||||
right file, plus OS/arch metadata added.
|
||||
|
||||
---
|
||||
|
||||
### Non-standard OS naming in filenames
|
||||
|
||||
A few upstreams use unusual OS names:
|
||||
- `sunos` — should map to `solaris` (the webi classifier does this)
|
||||
- `osx` or `macosx` — recognised as `macos`
|
||||
- `apple-darwin` (Rust triplet) — recognised as `macos`
|
||||
|
||||
If a package uses a genuinely unknown OS string, the classifier will produce
|
||||
`os = ""` for that asset. Those entries are dropped from the legacy cache.
|
||||
|
||||
---
|
||||
|
||||
### Asset filter configuration
|
||||
|
||||
If GitHub releases for a package include multiple builds that would otherwise
|
||||
collide (e.g. `extended` vs non-extended for hugo, or specific project builds
|
||||
in a monorepo), add to the package's `releases.conf`:
|
||||
|
||||
```ini
|
||||
# Only include assets containing "extended" in the name
|
||||
asset_filter = extended
|
||||
|
||||
# Exclude assets containing "legacy" in the name
|
||||
asset_exclude = legacy
|
||||
```
|
||||
|
||||
These filters run before classification.
|
||||
|
||||
---
|
||||
|
||||
## Quick checklist when inspecting a new package
|
||||
|
||||
1. **Look at the latest 2–3 releases** on GitHub. Note all asset filenames.
|
||||
2. **Find the "standard" builds** — the ones a normal user would download for
|
||||
their OS. Usually there are ≤4 per OS (amd64, arm64, x86, armv7l).
|
||||
3. **Check for extras**:
|
||||
- Are there GPU-specific builds for the same OS/arch? → variant
|
||||
- Are there `.exe` installer files alongside a `.zip`? → variant
|
||||
- Are there `.deb`/`.rpm`/`.AppImage`? → auto-dropped, no action needed
|
||||
- Does the Windows build have no archive and only a bare `.exe`? → fine
|
||||
4. **Check OS/arch naming** — does the filename use standard terms, or
|
||||
something unusual that might confuse the classifier?
|
||||
5. **Check format changes** — do old releases use a different archive type
|
||||
or directory layout than recent ones? The install script may need to
|
||||
handle both.
|
||||
|
||||
---
|
||||
|
||||
## Canonical vocabulary reference
|
||||
|
||||
All cache output must use exactly these values.
|
||||
|
||||
**OS**: `macos`, `linux`, `windows`, `freebsd`, `openbsd`, `netbsd`,
|
||||
`dragonfly`, `aix`, `illumos`, `plan9`, `solaris`
|
||||
|
||||
**Arch**:
|
||||
- `amd64` (not `x86_64`)
|
||||
- `arm64` (not `aarch64`)
|
||||
- `armv7l` (not `armv7` — the `l` stands for little-endian; `uname -m` reports `armv7l`)
|
||||
- `armv6l` (not `armv6`)
|
||||
- `x86` (not `i386`, `i686`, `386`)
|
||||
- `mipsle` (not `mipsel`)
|
||||
- `mips64le` (not `mips64el`)
|
||||
- Other: `arm`, `ppc64le`, `ppc64`, `loong64`, `riscv64`, `s390x`, `mips`, `mips64`
|
||||
|
||||
**Libc**: `none` (static/Go/Zig — never empty), `gnu`, `musl`, `msvc`
|
||||
|
||||
**Ext**: `tar.gz`, `tar.xz`, `zip`, `exe`, `7z`, `pkg`, `msi`
|
||||
(no leading dot; `exe` for bare binaries with no file extension)
|
||||
388
_skills/installer/references/PATTERNS.md
Normal file
388
_skills/installer/references/PATTERNS.md
Normal file
@@ -0,0 +1,388 @@
|
||||
# Install Patterns Reference
|
||||
|
||||
Nine patterns cover the full range of webi packages. Pattern A is by far
|
||||
the most common. Check `tar -tz $ARCHIVE` before writing any code.
|
||||
|
||||
---
|
||||
|
||||
## Pattern A — Bare binary at archive root
|
||||
|
||||
The archive extracts directly to the current directory with no wrapper
|
||||
subdirectory. Binary (and optional LICENSE/README) is at the top level.
|
||||
|
||||
**Set `WEBI_SINGLE=true`** — tells the framework to link the binary file
|
||||
directly (`~/.local/bin/cmd → ~/.local/opt/cmd-vX/bin/cmd`) rather than
|
||||
linking the versioned directory.
|
||||
|
||||
Representative packages: caddy, fzf, k9s, terraform, sttr, lf, monorel,
|
||||
awless, bun, cilium, curlie, dashmsg, dotenv, dotenv-linter, ffuf,
|
||||
gitdeploy, gprox, grype, hugo, keypairs, koji, ots, runzip, sclient,
|
||||
sqlc, sqlpkg, uuidv7, xcaddy, deno
|
||||
|
||||
**install.sh**:
|
||||
```sh
|
||||
pkg_cmd_name="caddy"
|
||||
WEBI_SINGLE=true
|
||||
|
||||
pkg_dst_cmd="$HOME/.local/bin/caddy"
|
||||
pkg_dst="$pkg_dst_cmd"
|
||||
pkg_src_cmd="$HOME/.local/opt/caddy-v$WEBI_VERSION/bin/caddy"
|
||||
pkg_src_dir="$HOME/.local/opt/caddy-v$WEBI_VERSION"
|
||||
pkg_src="$pkg_src_cmd"
|
||||
|
||||
pkg_install() {
|
||||
mkdir -p "$pkg_src_bin"
|
||||
mv ./"$pkg_cmd_name"* "$pkg_src_cmd"
|
||||
chmod a+x "$pkg_src_cmd"
|
||||
}
|
||||
|
||||
pkg_get_current_version() {
|
||||
caddy version 2>/dev/null | head -n 1 | cut -d' ' -f1 | sed 's:^v::'
|
||||
}
|
||||
```
|
||||
|
||||
**install.ps1** key lines:
|
||||
```powershell
|
||||
# No subdirectory — binary is at the top level of the archive
|
||||
Move-Item -Path ".\caddy.exe" -Destination "$pkg_src_bin"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Pattern B — Binary inside a version/triplet subdirectory
|
||||
|
||||
Archive extracts to a single directory named with the version and/or
|
||||
platform triplet. Binary (and docs) live inside that directory.
|
||||
|
||||
Representative packages: delta, hexyl, shellcheck, trip, xsv, kubectx, kubens
|
||||
|
||||
**Subdirectory naming conventions seen in the wild**:
|
||||
- `tool-{ver}-{triplet}/` — most Rust tools (delta, shellcheck, xsv)
|
||||
- `tool-{ver}/` — simpler version-only dirs
|
||||
- flat (no dir) — kubectx/kubens use flat archives despite being "B-ish"
|
||||
|
||||
**install.sh**:
|
||||
```sh
|
||||
pkg_cmd_name="delta"
|
||||
# WEBI_SINGLE not set (or false)
|
||||
|
||||
pkg_dst_cmd="$HOME/.local/bin/delta"
|
||||
pkg_dst="$pkg_dst_cmd"
|
||||
pkg_src_cmd="$HOME/.local/opt/delta-v$WEBI_VERSION/bin/delta"
|
||||
pkg_src_dir="$HOME/.local/opt/delta-v$WEBI_VERSION"
|
||||
pkg_src="$pkg_src_cmd"
|
||||
|
||||
pkg_install() {
|
||||
mkdir -p "$pkg_src_bin"
|
||||
mv ./delta-*/delta "$pkg_src_cmd"
|
||||
chmod a+x "$pkg_src_cmd"
|
||||
}
|
||||
|
||||
pkg_get_current_version() {
|
||||
delta --version 2>/dev/null | head -n 1 | cut -d' ' -f2
|
||||
}
|
||||
```
|
||||
|
||||
**install.ps1** key lines:
|
||||
```powershell
|
||||
Move-Item -Path ".\delta-*\delta.exe" -Destination "$pkg_src_bin"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Pattern C — Subdirectory with binary + completions and/or man pages
|
||||
|
||||
Same as B but the archive also contains shell completions and/or man pages
|
||||
worth installing.
|
||||
|
||||
Representative packages: bat, fd, lsd, rg/ripgrep, sd, watchexec, zoxide
|
||||
|
||||
Note: goreleaser has a flat archive (Pattern A layout) but with completions at
|
||||
the archive root. See the goreleaser entry in ARCHIVE-LAYOUTS.md.
|
||||
|
||||
**Completion directory name varies by package**:
|
||||
- `completions/` — sd, watchexec, zoxide
|
||||
- `autocomplete/` — bat, fd, lsd
|
||||
- `complete/` — rg/ripgrep
|
||||
|
||||
**Completion filename conventions**:
|
||||
- Bash: `tool.bash`, `tool.bash-completion`, `_tool.bash`
|
||||
- Fish: `tool.fish`
|
||||
- Zsh: `_tool`
|
||||
- PowerShell: `_tool.ps1`, `tool.ps1`
|
||||
|
||||
**Man page location varies**:
|
||||
- `tool.1` at subdirectory root — sd, bat, fd, lsd
|
||||
- `doc/tool.1` — rg/ripgrep
|
||||
- `man/man1/tool.1` — zoxide (deepest path)
|
||||
|
||||
**install.sh** (rg as example):
|
||||
```sh
|
||||
pkg_cmd_name="rg"
|
||||
|
||||
pkg_dst_cmd="$HOME/.local/bin/rg"
|
||||
pkg_dst="$pkg_dst_cmd"
|
||||
pkg_src_cmd="$HOME/.local/opt/rg-v$WEBI_VERSION/bin/rg"
|
||||
pkg_src_dir="$HOME/.local/opt/rg-v$WEBI_VERSION"
|
||||
pkg_src="$pkg_src_cmd"
|
||||
|
||||
pkg_install() {
|
||||
mkdir -p "$pkg_src_bin"
|
||||
mv ./ripgrep-*/rg "$pkg_src_cmd"
|
||||
chmod a+x "$pkg_src_cmd"
|
||||
|
||||
# bash completion
|
||||
if test -e ./ripgrep-*/complete/rg.bash; then
|
||||
mkdir -p "$pkg_src_dir/share/bash-completion/completions"
|
||||
mv ./ripgrep-*/complete/rg.bash \
|
||||
"$pkg_src_dir/share/bash-completion/completions/rg"
|
||||
fi
|
||||
# fish completion
|
||||
if test -e ./ripgrep-*/complete/rg.fish; then
|
||||
mkdir -p "$pkg_src_dir/share/fish/vendor_completions.d"
|
||||
mv ./ripgrep-*/complete/rg.fish \
|
||||
"$pkg_src_dir/share/fish/vendor_completions.d/rg.fish"
|
||||
fi
|
||||
# zsh completion
|
||||
if test -e ./ripgrep-*/complete/_rg; then
|
||||
mkdir -p "$pkg_src_dir/share/zsh/site-functions"
|
||||
mv ./ripgrep-*/complete/_rg \
|
||||
"$pkg_src_dir/share/zsh/site-functions/_rg"
|
||||
fi
|
||||
# man page
|
||||
if test -e ./ripgrep-*/doc/rg.1; then
|
||||
mkdir -p "$pkg_src_dir/share/man/man1"
|
||||
mv ./ripgrep-*/doc/rg.1 "$pkg_src_dir/share/man/man1/rg.1"
|
||||
fi
|
||||
}
|
||||
|
||||
pkg_get_current_version() {
|
||||
rg --version 2>/dev/null | head -n 1 | cut -d' ' -f2
|
||||
}
|
||||
```
|
||||
|
||||
**Note**: Completion paths in completions/man install are best-effort
|
||||
— use `if test -e ...` guards so the script still works on older releases
|
||||
that didn't include them.
|
||||
|
||||
---
|
||||
|
||||
## Pattern D — Binary + shared libraries
|
||||
|
||||
The package bundles shared libraries alongside the binary. The entire
|
||||
directory tree must be preserved.
|
||||
|
||||
Representative packages: ollama (Linux), psql/postgres, sass (Dart VM),
|
||||
syncthing, xz
|
||||
|
||||
**install.sh**:
|
||||
```sh
|
||||
pkg_cmd_name="ollama"
|
||||
|
||||
pkg_dst_cmd="$HOME/.local/bin/ollama"
|
||||
pkg_dst="$pkg_dst_cmd"
|
||||
pkg_src_cmd="$HOME/.local/opt/ollama-v$WEBI_VERSION/bin/ollama"
|
||||
pkg_src_dir="$HOME/.local/opt/ollama-v$WEBI_VERSION"
|
||||
pkg_src="$pkg_src_cmd"
|
||||
|
||||
pkg_install() {
|
||||
mkdir -p "$(dirname "$pkg_src_dir")"
|
||||
# Archive already has bin/ and lib/ layout
|
||||
mv ./bin "$pkg_src_dir/bin"
|
||||
mv ./lib "$pkg_src_dir/lib"
|
||||
}
|
||||
```
|
||||
|
||||
For psql (archive has a `psql-{ver}-{triplet}/` wrapper dir):
|
||||
```sh
|
||||
pkg_install() {
|
||||
mkdir -p "$(dirname "$pkg_src_dir")"
|
||||
mv ./psql-*/ "$pkg_src_dir"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Pattern E — FHS-like layout
|
||||
|
||||
Archive already follows `bin/`, `share/man/`, `share/doc/` hierarchy.
|
||||
Extract the whole thing directly into the versioned opt directory.
|
||||
|
||||
Representative packages: gh (GitHub CLI), pandoc
|
||||
|
||||
**install.sh**:
|
||||
```sh
|
||||
pkg_cmd_name="gh"
|
||||
|
||||
pkg_dst_cmd="$HOME/.local/bin/gh"
|
||||
pkg_dst="$pkg_dst_cmd"
|
||||
pkg_src_cmd="$HOME/.local/opt/gh-v$WEBI_VERSION/bin/gh"
|
||||
pkg_src_dir="$HOME/.local/opt/gh-v$WEBI_VERSION"
|
||||
pkg_src="$pkg_src_cmd"
|
||||
|
||||
pkg_install() {
|
||||
mkdir -p "$(dirname "$pkg_src_dir")"
|
||||
mv ./gh_*/ "$pkg_src_dir"
|
||||
}
|
||||
|
||||
pkg_get_current_version() {
|
||||
gh --version 2>/dev/null | head -n 1 | cut -d' ' -f3
|
||||
}
|
||||
```
|
||||
|
||||
No `chmod` needed — binary is already executable inside the archive.
|
||||
|
||||
---
|
||||
|
||||
## Pattern F — Binary needs rename
|
||||
|
||||
Binary in the archive doesn't match the expected command name.
|
||||
|
||||
Representative packages: pathman (`pathman-v0.6.0-linux-amd64_v1` → `pathman`),
|
||||
yq (`yq_linux_amd64` → `yq`)
|
||||
|
||||
**install.sh**:
|
||||
```sh
|
||||
pkg_cmd_name="yq"
|
||||
WEBI_SINGLE=true
|
||||
|
||||
pkg_dst_cmd="$HOME/.local/bin/yq"
|
||||
pkg_dst="$pkg_dst_cmd"
|
||||
pkg_src_cmd="$HOME/.local/opt/yq-v$WEBI_VERSION/bin/yq"
|
||||
pkg_src_dir="$HOME/.local/opt/yq-v$WEBI_VERSION"
|
||||
pkg_src="$pkg_src_cmd"
|
||||
|
||||
pkg_install() {
|
||||
mkdir -p "$pkg_src_bin"
|
||||
# Binary is named yq_linux_amd64 (or yq_darwin_amd64 etc)
|
||||
mv ./yq_* "$pkg_src_cmd"
|
||||
chmod a+x "$pkg_src_cmd"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Pattern G — Full SDK / toolchain
|
||||
|
||||
Archive contains a complete runtime or SDK (hundreds to thousands of files).
|
||||
The entire tree goes into opt; multiple binaries are linked from `bin/`.
|
||||
|
||||
Representative packages: go, node, zig, flutter, julia, cmake, tinygo
|
||||
|
||||
**install.sh** (node as example):
|
||||
```sh
|
||||
pkg_cmd_name="node"
|
||||
# NOTE: pkg_src points to the directory, not a binary
|
||||
|
||||
pkg_dst_cmd="$HOME/.local/bin/node"
|
||||
pkg_dst="$HOME/.local/opt/node" # versioned-dir symlink target
|
||||
|
||||
pkg_src_cmd="$HOME/.local/opt/node-v$WEBI_VERSION/bin/node"
|
||||
pkg_src_dir="$HOME/.local/opt/node-v$WEBI_VERSION"
|
||||
pkg_src="$pkg_src_dir" # pkg_src = the directory
|
||||
|
||||
pkg_install() {
|
||||
mkdir -p "$(dirname "$pkg_src")"
|
||||
mv ./node-*/ "$pkg_src"
|
||||
}
|
||||
|
||||
pkg_link() {
|
||||
rm -f "$pkg_dst"
|
||||
ln -s "$pkg_src" "$pkg_dst"
|
||||
}
|
||||
|
||||
pkg_get_current_version() {
|
||||
node --version 2>/dev/null | head -n 1 | sed 's:^v::'
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Pattern H — .NET runtime bundle
|
||||
|
||||
Flat directory with one binary and hundreds of `.dll` files. The entire
|
||||
directory must be preserved. Like Pattern G (SDK) in structure — the
|
||||
versioned directory is the package root, with the binary directly inside
|
||||
(no `bin/` subdirectory). A `pkg_link()` creates the unversioned symlink.
|
||||
|
||||
Representative packages: pwsh (PowerShell Core)
|
||||
|
||||
**install.sh**:
|
||||
```sh
|
||||
pkg_cmd_name="pwsh"
|
||||
|
||||
# note: binary is at pkg_src_dir root, no bin/ subdirectory
|
||||
pkg_src_cmd="$HOME/.local/opt/pwsh-v$WEBI_VERSION/pwsh"
|
||||
pkg_src_dir="$HOME/.local/opt/pwsh-v$WEBI_VERSION"
|
||||
pkg_src="$pkg_src_dir"
|
||||
|
||||
pkg_dst_cmd="$HOME/.local/opt/pwsh/pwsh"
|
||||
pkg_dst="$HOME/.local/opt/pwsh"
|
||||
|
||||
pkg_install() {
|
||||
# Archive extracts flat — move all contents into the versioned dir
|
||||
mkdir -p "$pkg_src_dir"
|
||||
mv ./* "$pkg_src_dir"
|
||||
chmod a+x "$pkg_src_cmd"
|
||||
}
|
||||
|
||||
pkg_link() {
|
||||
rm -rf "$pkg_dst"
|
||||
ln -s "$pkg_src" "$pkg_dst"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Pattern I — Multi-binary distribution
|
||||
|
||||
Archive contains multiple related binaries. Install the primary one and
|
||||
link only that.
|
||||
|
||||
Representative packages: dashcore (dashd + dash-cli + dash-qt + ...),
|
||||
mutagen (mutagen + mutagen-agents.tar.gz)
|
||||
|
||||
**install.sh** (dashcore-style):
|
||||
```sh
|
||||
pkg_cmd_name="dashd"
|
||||
|
||||
pkg_dst_cmd="$HOME/.local/bin/dashd"
|
||||
pkg_dst="$pkg_dst_cmd"
|
||||
pkg_src_cmd="$HOME/.local/opt/dashcore-v$WEBI_VERSION/bin/dashd"
|
||||
pkg_src_dir="$HOME/.local/opt/dashcore-v$WEBI_VERSION"
|
||||
pkg_src="$pkg_src_cmd"
|
||||
|
||||
pkg_install() {
|
||||
mkdir -p "$(dirname "$pkg_src_dir")"
|
||||
mv ./dashcore-*/ "$pkg_src_dir"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Choosing between patterns
|
||||
|
||||
```
|
||||
Archive root contains a single binary (or binary + docs)?
|
||||
→ Pattern A (set WEBI_SINGLE=true)
|
||||
|
||||
Archive has a named subdirectory wrapping the binary?
|
||||
├─ Binary only inside subdir? → Pattern B
|
||||
├─ Binary + completions/man pages? → Pattern C
|
||||
└─ Binary + shared libraries (.so)? → Pattern D
|
||||
|
||||
Archive already has bin/ and share/ layout?
|
||||
→ Pattern E
|
||||
|
||||
Binary name doesn't match the command name?
|
||||
→ Pattern F (rename during install)
|
||||
|
||||
Archive is a full SDK (compiler, runtime, stdlib)?
|
||||
→ Pattern G (pkg_src = pkg_src_dir)
|
||||
|
||||
Flat directory with many DLLs (.NET)?
|
||||
→ Pattern H
|
||||
|
||||
Multiple binaries for a single distributed system?
|
||||
→ Pattern I
|
||||
```
|
||||
Reference in New Issue
Block a user