Tutorial 2 โ Ship a tool¶
Time: 10 minutes. You'll end up with: a single binary you can email, SCP, or drop on a server. It has no runtime dependencies. Recipients don't need perch installed.
The setup¶
Suppose you maintain a small team operations CLI: ops backup, ops restart_api, ops check_disk. Today it's a folder of bash scripts. Half the team has the folder cloned; half doesn't. There's no help text. Onboarding takes 20 minutes.
We're going to fold the whole thing into one commands.perch and ship the result as ops.
Step 1 โ Write the commands¶
commands.perch:
name "ops"
about "Internal ops CLI for the platform team"
version "1.0.0"
DB_HOST = "db-primary.internal"
BACKUP_S3 = "s3://backups-bucket"
requires
bin "pg_dump"
bin "aws"
bin "kubectl"
write "/tmp"
end
command backup
description "Snapshot the primary DB to S3"
do
stamp = now "unix"
shell "pg_dump -h ${DB_HOST} -Fc > /tmp/backup-${stamp}.dump"
aws s3 cp /tmp/backup-${stamp}.dump ${BACKUP_S3}/db-${stamp}.dump
rm "/tmp/backup-${stamp}.dump"
print "โ uploaded as db-${stamp}.dump"
end
end
command restart_api
description "Roll-restart the api deployment"
do
kubectl rollout restart deployment/api -n prod
kubectl rollout status deployment/api -n prod
print "โ api restarted"
end
end
command check_disk
description "Print disk usage on each api node"
do
shell "kubectl get nodes -l role=api -o name | xargs -I {} kubectl debug {} --image=busybox -- df -h"
end
end
catch unknown
do
print "Unknown command: ${unknown}"
print "Try: backup | restart_api | check_disk"
exit 1
end
end
You could already invoke this as perch backup, perch restart_api, etc. But we want a standalone tool the team can run without thinking about perch.
Step 2 โ Build the binary¶
That's it. The output ops is a self-contained executable:
file ./ops
# โ Mach-O 64-bit executable arm64 (or ELF / PE depending on your host)
./ops --version # โ 1.0.0
./ops --help # โ lists backup, restart_api, check_disk
./ops backup # โ runs the dump+upload pipeline
./ops blorp # โ falls into your `catch unknown`
Notice:
--versionreturns the program'sversionfield, not perch's version.--helpis auto-generated fromdescriptionlines.-f any.perchis ignored โ the embedded program always wins.
Step 3 โ Distribute¶
Same-OS, same-arch is the only constraint:
# Email it
mutt -s 'new ops binary' -a ops team@example.com
# SCP to bastion
scp ops bastion.prod:/usr/local/bin/
# Drop in artifact storage
aws s3 cp ops s3://ops-binaries/ops-v1.0.0
# Distribute via npm-like channel? Coming soon (`perch share`)
The recipient runs ./ops backup immediately. No perch install. No Go toolchain.
Step 4 โ How it works (briefly)¶
perch --build:
- Reads
commands.perch, parses it via the same capy pipelineperchuses. - Marshals the resulting
domain.Programas JSON. - Copies the running
perchbinary to your output path. - Appends the JSON + a footer:
<8 bytes length><8 bytes magic "PRCHEMB1">.
When the resulting binary starts:
- It reads its own last 16 bytes.
- If the magic matches, it seeks back, loads the embedded JSON, and runs against that program.
- If not, it behaves like normal perch.
Full format spec: docs/embedding.md.
Step 5 โ Update flow¶
When you change commands.perch:
# Edit commands.perch
$EDITOR commands.perch
# Rebuild โ strips the old embedded program automatically
perch --build -o ops
# Ship the new binary
Re---build is idempotent. No accumulating layers.
Limitations¶
- Same OS / arch only. Building on macOS arm64 produces a Mach-O arm64 binary; you can't target Linux from there. Cross-compile is on the roadmap.
- Op catalog is frozen at build time. The binary uses the ops in whatever perch you
--build'ed from. Adding new ops to perch later doesn't retroactively give old binaries those ops โ rebuild. - No code signing built-in. For distribution outside your trust boundary, sign with
codesign(macOS) orsigntool(Windows).
What you learned¶
perch --build -o NAMEproduces a portable single-file binary.- The embedded
Programis JSON; the runtime is a copy of perch. - The output binary's
--helpand--versionreflect the embedded program. - Same file drives both
perch <cmd>(dev) and./ops <cmd>(shipped).
Next¶
โ Tutorial 3: Cross-platform installer โ write one commands.perch that installs deps on macOS / Linux / Windows from the same source.