mirror of
https://github.com/webinstall/webi-installers.git
synced 2026-08-23 22:16:48 +00:00
docs(skills): add installer skill for writing install.sh and install.ps1
Covers the full workflow: inspect GitHub releases API to discover archive layout, choose from 9 patterns (A-I), write POSIX shell and PowerShell scripts, and identify classification/variant issues. Reference files: - PATTERNS.md: install.sh/ps1 skeletons for all 9 patterns - ARCHIVE-LAYOUTS.md: real tar -t output for representative packages - CLASSIFICATION.md: when to add variant tags, canonical vocab
This commit is contained in:
@@ -0,0 +1,349 @@
|
||||
---
|
||||
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.
|
||||
license: MIT
|
||||
compatibility: Requires git, curl, tar, jq. GitHub API access needed for
|
||||
discovery phase. Designed for Claude Code in the webi-installers repo.
|
||||
metadata:
|
||||
author: AJ ONeal
|
||||
version: "1.0"
|
||||
---
|
||||
|
||||
# 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.
|
||||
|
||||
## 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)
|
||||
|
||||
```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 releases for a repo
|
||||
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 / tar.zst
|
||||
curl -fsSL "$DOWNLOAD_URL" | tar -tz
|
||||
|
||||
# zip
|
||||
curl -fsSL "$DOWNLOAD_URL" -o /tmp/pkg.zip && unzip -l /tmp/pkg.zip
|
||||
|
||||
# bare binary (no archive)
|
||||
# Just the binary itself — no unpacking needed (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** | Binary needs rename during install | pathman, yq |
|
||||
| **G** | Full SDK/toolchain (many files) | go, node, zig, flutter, julia |
|
||||
| **H** | .NET runtime bundle (flat dir, many DLLs) | 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/template.sh`) handles: user-agent detection, version
|
||||
resolution, download, checksum verification, and PATH management. Your script
|
||||
provides the package-specific part: where to find the binary and how to move it.
|
||||
|
||||
### Required variables
|
||||
|
||||
```sh
|
||||
pkg_cmd_name="tool" # command name on PATH
|
||||
|
||||
pkg_dst_cmd="$HOME/.local/bin/tool" # final symlink destination
|
||||
pkg_dst="$pkg_dst_cmd" # same as above (or pkg_src_dir for SDKs)
|
||||
|
||||
pkg_src_cmd="$HOME/.local/opt/tool-v$WEBI_VERSION/bin/tool" # versioned binary
|
||||
pkg_src_dir="$HOME/.local/opt/tool-v$WEBI_VERSION" # versioned install dir
|
||||
pkg_src="$pkg_src_cmd" # same as above (or pkg_src_dir for SDKs)
|
||||
```
|
||||
|
||||
### Required function
|
||||
|
||||
```sh
|
||||
pkg_install() {
|
||||
mkdir -p "$pkg_src_bin" # $pkg_src_dir/bin
|
||||
mv ./tool-*/tool "$pkg_src_cmd" # pattern-specific — see below
|
||||
chmod a+x "$pkg_src_cmd"
|
||||
}
|
||||
```
|
||||
|
||||
### Recommended function
|
||||
|
||||
```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
|
||||
}
|
||||
```
|
||||
|
||||
### Pattern flag
|
||||
|
||||
Set `WEBI_SINGLE=true` when the archive is a flat directory (binary + optional
|
||||
docs at the root, **no** named subdirectory). This is Pattern A.
|
||||
|
||||
```sh
|
||||
WEBI_SINGLE=true
|
||||
```
|
||||
|
||||
### Skeleton by pattern
|
||||
|
||||
**Pattern A** — flat archive, binary at root (set `WEBI_SINGLE=true`):
|
||||
```sh
|
||||
WEBI_SINGLE=true
|
||||
pkg_install() {
|
||||
mkdir -p "$pkg_src_bin"
|
||||
mv ./tool* "$pkg_src_cmd"
|
||||
chmod a+x "$pkg_src_cmd"
|
||||
}
|
||||
```
|
||||
|
||||
**Pattern B** — binary in `tool-{ver}-{triplet}/` subdirectory:
|
||||
```sh
|
||||
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:
|
||||
```sh
|
||||
pkg_install() {
|
||||
mkdir -p "$pkg_src_bin"
|
||||
mv ./tool-*/tool "$pkg_src_cmd"
|
||||
chmod a+x "$pkg_src_cmd"
|
||||
|
||||
# completions (install what exists — not all builds include them)
|
||||
if [ -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 [ -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 [ -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
|
||||
|
||||
# man page
|
||||
if [ -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 E** — FHS layout (archive already has `bin/` and `share/`):
|
||||
```sh
|
||||
pkg_install() {
|
||||
mkdir -p "$(dirname "$pkg_src_dir")"
|
||||
mv ./tool-*/ "$pkg_src_dir"
|
||||
}
|
||||
# bin/tool is already at the right relative path; no chmod needed
|
||||
```
|
||||
|
||||
**Pattern F** — binary needs rename:
|
||||
```sh
|
||||
pkg_install() {
|
||||
mkdir -p "$pkg_src_bin"
|
||||
mv ./tool_linux_amd64 "$pkg_src_cmd" # or whatever the archive names it
|
||||
chmod a+x "$pkg_src_cmd"
|
||||
}
|
||||
```
|
||||
|
||||
**Pattern G** — full SDK:
|
||||
```sh
|
||||
pkg_src="$pkg_src_dir" # NOTE: pkg_src points to the directory, not a binary
|
||||
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_dir" "$pkg_dst"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Write `install.ps1`
|
||||
|
||||
PowerShell scripts are more explicit — they handle download, extract, and
|
||||
placement inline. 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
|
||||
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
|
||||
}
|
||||
|
||||
# "Symlink" on Windows = 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
|
||||
```
|
||||
|
||||
Adjust the `Move-Item` path glob to match the archive's actual subdirectory
|
||||
name (or remove the subdirectory if Pattern A).
|
||||
|
||||
---
|
||||
|
||||
## 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` — Go classifier uses `darwin`; legacy cache uses `macos`
|
||||
- `x86_64` vs `amd64` — both recognised; output normalised to `amd64`
|
||||
- `aarch64` vs `arm64` — both recognised; output normalised to `arm64`
|
||||
- `armv7` (missing trailing `l`) — should be `armv7l`
|
||||
|
||||
These are handled automatically during classification. Only flag them if the
|
||||
asset list contains something genuinely unusual.
|
||||
|
||||
### Variant assets needing tags
|
||||
|
||||
Flag if you see:
|
||||
- **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 Node installer
|
||||
- **Electron 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 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
|
||||
@@ -0,0 +1,292 @@
|
||||
# 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 you don't have zstd for:
|
||||
curl -fsSL "$URL" | python3 -c "
|
||||
import sys, subprocess
|
||||
subprocess.run(['tar', '--zstd', '-tz'], stdin=sys.stdin)
|
||||
" | 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)
|
||||
@@ -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)
|
||||
@@ -0,0 +1,377 @@
|
||||
# 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 the archive is flat.
|
||||
|
||||
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, goreleaser, lsd, rg/ripgrep, sd,
|
||||
watchexec, zoxide
|
||||
|
||||
**Completion directory name varies by package**:
|
||||
- `completions/` — sd, goreleaser, 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
|
||||
- `manpages/tool.1.gz` — goreleaser
|
||||
- `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 [ -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 [ -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 [ -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 [ -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 [ -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 intact.
|
||||
|
||||
Representative packages: pwsh (PowerShell Core)
|
||||
|
||||
**install.sh**:
|
||||
```sh
|
||||
pkg_cmd_name="pwsh"
|
||||
|
||||
pkg_dst_cmd="$HOME/.local/bin/pwsh"
|
||||
pkg_dst="$pkg_dst_cmd"
|
||||
pkg_src_cmd="$HOME/.local/opt/pwsh-v$WEBI_VERSION/bin/pwsh"
|
||||
pkg_src_dir="$HOME/.local/opt/pwsh-v$WEBI_VERSION"
|
||||
pkg_src="$pkg_src_cmd"
|
||||
|
||||
pkg_install() {
|
||||
mkdir -p "$pkg_src_dir"
|
||||
# Archive extracts flat — move all contents into bin/
|
||||
mkdir -p "$pkg_src_bin"
|
||||
mv ./* "$pkg_src_bin/" 2>/dev/null || true
|
||||
chmod a+x "$pkg_src_cmd"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 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