Releases: systeminit/swamp
swamp 20260226.005850.0-sha.972b02bf
What's Changed
- fix: reject path traversal in definition names (#480)
Summary
Fixes: #477
Defense in depth: swamp model create currently accepts absolute paths and path traversal sequences (e.g., /etc/passwd, ../../../foo) as model names without error. While definition names are not currently used to construct file paths (UUIDs are used instead), this is a validation gap that could become a security issue if naming conventions change in the future.
This PR adds Zod validation to DefinitionSchema that rejects .., /, \, and null bytes in definition names, mirroring the existing pattern already present in DataMetadataSchema.
Changes
src/domain/definitions/definition.ts— Added.refine()validation to thenamefield inDefinitionSchemathat rejects path traversal characterssrc/domain/definitions/definition_test.ts— Added 4 test cases covering forward slashes, backslashes,..path traversal, and null bytes
Test plan
-
deno check— type checking passes -
deno lint— linting passes -
deno fmt— formatting passes -
deno run test src/domain/definitions/definition_test.ts— all 40 tests pass (4 new) -
deno run test— full test suite passes (2078 tests)
🤖 Generated with Claude Code
Installation
macOS (Apple Silicon):
curl -L https://github.com/systeminit/swamp/releases/download/v20260226.005850.0-sha.972b02bf/swamp-darwin-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/macOS (Intel):
curl -L https://github.com/systeminit/swamp/releases/download/v20260226.005850.0-sha.972b02bf/swamp-darwin-x86_64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/Linux (x86_64):
curl -L https://github.com/systeminit/swamp/releases/download/v20260226.005850.0-sha.972b02bf/swamp-linux-x86_64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/Linux (aarch64):
curl -L https://github.com/systeminit/swamp/releases/download/v20260226.005850.0-sha.972b02bf/swamp-linux-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/swamp 20260225.204918.0-sha.8f6b52ca
What's Changed
- feat: associate telemetry with authenticated users (#476)
Summary
- Send authenticated user's API key as
Authorization: Bearerheader on telemetry flush requests, enabling server-side user resolution in Mixpanel distinct_idremains the anonymous UUID — no identity data added to the event payload- Auth credentials loaded from
~/.config/swamp/auth.jsonat init; silently skipped if absent or unreadable
Closes #475
Test plan
-
deno checkpasses -
deno lintpasses -
deno fmtpasses - All 18 telemetry tests pass (4 new)
- Manual: run a
swampcommand withauth.jsonpresent, confirm flush request includesAuthorizationheader - Manual: run without
auth.json, confirm noAuthorizationheader
🤖 Generated with Claude Code
Installation
macOS (Apple Silicon):
curl -L https://github.com/systeminit/swamp/releases/download/v20260225.204918.0-sha.8f6b52ca/swamp-darwin-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/macOS (Intel):
curl -L https://github.com/systeminit/swamp/releases/download/v20260225.204918.0-sha.8f6b52ca/swamp-darwin-x86_64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/Linux (x86_64):
curl -L https://github.com/systeminit/swamp/releases/download/v20260225.204918.0-sha.8f6b52ca/swamp-linux-x86_64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/Linux (aarch64):
curl -L https://github.com/systeminit/swamp/releases/download/v20260225.204918.0-sha.8f6b52ca/swamp-linux-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/swamp 20260225.161946.0-sha.d11bb4f9
What's Changed
- fix: make .gitignore management opt-in via --include-gitignore flag (#474)
Summary
Follows up on #461, which added automatic .gitignore managed section
during swamp repo init and swamp repo upgrade.
Problem: Automatically modifying .gitignore is surprising behavior.
Established repos have their own gitignore conventions, and many users
track their .swamp/ or .claude/ directories intentionally. Writing to
.gitignore without being asked violates the principle of least surprise
and can create unwanted noise in diffs/PRs.
Solution: Make gitignore management opt-in via a --include-gitignore
CLI flag, with the preference persisted in .swamp.yaml so subsequent
upgrades honor the choice without requiring the flag again.
Behavior
| Command | Behavior |
|---|---|
swamp init |
Does NOT manage .gitignore (default off) |
swamp init --include-gitignore |
Manages .gitignore, persists gitignoreManaged: true in marker |
swamp upgrade |
Manages .gitignore ONLY if marker has gitignoreManaged: true |
swamp upgrade --include-gitignore |
Opts in, manages .gitignore, persists preference |
swamp upgrade --no-include-gitignore |
Opts out, persists gitignoreManaged: false, skips gitignore |
Changes
repo_marker_repository.ts— AddedgitignoreManaged?: booleantoRepoMarkerDatarepo_service.ts— Added"skipped"toGitignoreActiontype;init()skips gitignore by default, manages whenincludeGitignore: true;upgrade()respects persisted marker preference with CLI overriderepo_init.ts— Added--include-gitignoreoption to init, upgrade, and repo commandsrepo_service_test.ts— Updated 15 existing tests, added 5 new tests covering opt-in/opt-out/persistence behavior
Why this matters
The managed section machinery from #461 is preserved — when a user opts in, they get the same sentinel-marker-based section management with legacy migration, tool-specific entries, and safe upgrades. The only change is that users must explicitly ask for it, which respects repos that:
- Already have comprehensive
.gitignorefiles - Intentionally track
.claude/or.agents/directories - Use CI workflows that would be disrupted by unexpected
.gitignorechanges
Test plan
-
deno checkpasses -
deno lintpasses -
deno fmtpasses - All 2054 tests pass (50 repo service tests, including 5 new ones)
-
deno run compilesucceeds - Manual:
swamp repo initdoes NOT create/modify .gitignore - Manual:
swamp repo init --include-gitignorecreates managed section - Manual:
swamp repo upgradeafter opt-in preserves gitignore management - Manual:
swamp repo upgrade --no-include-gitignoreopts out and persists
🤖 Generated with Claude Code
Installation
macOS (Apple Silicon):
curl -L https://github.com/systeminit/swamp/releases/download/v20260225.161946.0-sha.d11bb4f9/swamp-darwin-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/macOS (Intel):
curl -L https://github.com/systeminit/swamp/releases/download/v20260225.161946.0-sha.d11bb4f9/swamp-darwin-x86_64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/Linux (x86_64):
curl -L https://github.com/systeminit/swamp/releases/download/v20260225.161946.0-sha.d11bb4f9/swamp-linux-x86_64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/Linux (aarch64):
curl -L https://github.com/systeminit/swamp/releases/download/v20260225.161946.0-sha.d11bb4f9/swamp-linux-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/swamp 20260225.155850.0-sha.9faeb31c
What's Changed
- feat: show SHA-256 integrity check status in update output (#473)
Summary
- Adds an info line "SHA-256 integrity check passed" to the
swamp updateoutput after a successful update
After this change, the output looks like:
15:48:34.397 info update swamp updated successfully!
15:48:34.400 info update "20260206.200442.0-sha." → "20260225.153610.0-sha.c4405aab"
15:48:34.401 info update SHA-256 integrity check passed
This gives users confidence that the checksum verification from #472 actually ran. If verification had failed, the update would have aborted with an error before reaching this point.
Test plan
-
deno run test src/presentation/output/update_output_test.ts— all 8 tests pass
🤖 Generated with Claude Code
Installation
macOS (Apple Silicon):
curl -L https://github.com/systeminit/swamp/releases/download/v20260225.155850.0-sha.9faeb31c/swamp-darwin-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/macOS (Intel):
curl -L https://github.com/systeminit/swamp/releases/download/v20260225.155850.0-sha.9faeb31c/swamp-darwin-x86_64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/Linux (x86_64):
curl -L https://github.com/systeminit/swamp/releases/download/v20260225.155850.0-sha.9faeb31c/swamp-linux-x86_64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/Linux (aarch64):
curl -L https://github.com/systeminit/swamp/releases/download/v20260225.155850.0-sha.9faeb31c/swamp-linux-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/swamp 20260225.153610.0-sha.c4405aab
What's Changed
- fix: verify SHA-256 checksum before installing self-update binaries (#472)
Summary
Fixes #469 — adds SHA-256 integrity verification to swamp update, addressing CWE-494 (Download of Code Without Integrity Check).
Previously, swamp update downloaded a tarball from the CDN and installed it with zero integrity verification. An attacker who controls the download path (CDN/S3 compromise, DNS hijack) could replace the binary with arbitrary code that then has access to vault secrets, API keys, and full shell access.
What Changed
New: src/domain/update/integrity.ts
Domain-layer functions for integrity verification (pure, no I/O):
validateRedirectUrl(url)— ensures redirect URLs point tohttps://artifacts.systeminit.com; rejects non-HTTPS, untrusted hosts, and malformed URLschecksumUrlFromTarballUrl(url)— derives the checksum URL by appending.sha256to the tarball URLparseChecksumFile(content)— parses standardsha256sumformat (<hex> <filename>) and extracts the hex digestverifyChecksum(expected, actual)— hard-fail comparison that throwsUserErroron mismatch
Modified: src/domain/update/update_service.ts
- Extended
UpdateCheckerinterface withfetchChecksum(tarballUrl)method andexpectedChecksumparameter ondownloadAndInstall() UpdateService.check()now validates the redirect URL before comparing versionsUpdateService.update()now: validates redirect URL → fetches checksum → downloads tarball with verification → installs
Modified: src/infrastructure/update/http_update_checker.ts
- New
fetchChecksum()method: fetches{tarballUrl}.sha256from CDN, parses it downloadAndInstall()now: downloads tarball → reads it back → computes SHA-256 using existingcomputeChecksumutility → verifies against expected checksum → only then extracts and installs
New: src/domain/update/integrity_test.ts
13 unit tests covering all integrity functions.
Modified: src/domain/update/update_service_test.ts
Updated mock checker for new interface; added tests for redirect validation and checksum orchestration flow.
Why This Is Correct
-
Verification happens before extraction — the tarball is fully downloaded and written to disk, then read back and hashed. Only after the checksum matches does
tar -xzfrun. A tampered tarball is never extracted. -
Redirect URL validation — prevents redirect-based attacks where an attacker could point the stable URL redirect to a malicious host. Only
https://artifacts.systeminit.comis trusted. -
Hard fail, no fallback — if the checksum doesn't match or the
.sha256file can't be fetched, the update aborts with a clear error. There is no "continue anyway" path that an attacker could exploit. -
Reuses existing
computeChecksumfromsrc/domain/models/checksum.ts— no new crypto code, same battle-tested SHA-256 implementation used elsewhere in the codebase. -
Clean DDD separation — pure validation/parsing logic lives in the domain layer (
integrity.ts), I/O lives in the infrastructure adapter (http_update_checker.ts), and orchestration lives in the domain service (update_service.ts).
Dependency
This PR requires systeminit/swamp-uat#38 to be deployed first. That PR modifies the upload script to:
- Verify raw binary checksums against GitHub Release before creating tarballs
- Compute SHA-256 of each tarball and upload a
.sha256file alongside it to S3
Without the swamp-uat change, the .sha256 files won't exist on the CDN and swamp update will fail with "Failed to fetch checksum: HTTP 404".
User Impact
- No breaking change for current users — existing swamp binaries don't call
fetchChecksumor verify checksums, so they continue working as before. - After updating to this version — all subsequent
swamp updatecalls will verify integrity. If a checksum mismatch is detected, users will see a clear error: "Checksum verification failed. The downloaded file may have been tampered with." - No new flags or configuration — verification is always on, which is the correct default for a security feature.
Test plan
-
deno checkpasses -
deno lintpasses -
deno fmtpasses - All 49 update-related tests pass (13 new integrity tests + 18 service tests + 18 existing)
- After deploying swamp-uat#38: run
swamp updateand verify checksum verification succeeds end-to-end
🤖 Generated with Claude Code
Installation
macOS (Apple Silicon):
curl -L https://github.com/systeminit/swamp/releases/download/v20260225.153610.0-sha.c4405aab/swamp-darwin-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/macOS (Intel):
curl -L https://github.com/systeminit/swamp/releases/download/v20260225.153610.0-sha.c4405aab/swamp-darwin-x86_64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/Linux (x86_64):
curl -L https://github.com/systeminit/swamp/releases/download/v20260225.153610.0-sha.c4405aab/swamp-linux-x86_64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/Linux (aarch64):
curl -L https://github.com/systeminit/swamp/releases/download/v20260225.153610.0-sha.c4405aab/swamp-linux-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/swamp 20260225.151449.0-sha.a40584f1
What's Changed
- fix: manage a swamp section in existing .gitignore files (#471)
Summary
Fixes #461
Previously, swamp repo init and swamp repo upgrade would skip .gitignore entirely if one already existed. This meant repos with an existing .gitignore — the common case for any established project — never got swamp's required entries (telemetry dir, secrets keyfile, cached bundles, tool-specific dirs) added to their .gitignore. Worse, swamp repo upgrade could never deliver new gitignore entries to users who had already run init.
This PR introduces a managed section pattern using sentinel comment markers:
# BEGIN swamp managed section - DO NOT EDIT
...swamp entries...
# END swamp managed section
Why this matters
- New users initializing in an existing repo get swamp entries added to their
.gitignoreautomatically, instead of silently getting nothing - Existing users running
upgradereceive new gitignore entries as swamp evolves (e.g., new cache directories, new tool integrations) — the same wayupgradealready merges Claudesettings.local.jsonpermissions - User content is never touched — everything outside the markers is preserved byte-for-byte
Behavior
| Scenario | Action |
|---|---|
No .gitignore exists |
Create file with managed section |
.gitignore exists, no swamp section |
Append managed section |
.gitignore exists, has managed section |
Replace section if content differs |
.gitignore has legacy format (# Swamp managed defaults header, no markers) |
Migrate: replace legacy block with managed section |
Breaking change
The JSON output field gitignoreCreated: boolean is replaced by gitignoreAction: "created" | "updated" | "unchanged" in both swamp repo init --json and swamp repo upgrade --json. This provides more granular information about what happened to the .gitignore.
Files changed
src/domain/repo/repo_service.ts— Core logic:ensureGitignoreSectionreplacescreateGitignoreIfNotExists, handles all four scenarios above plus legacy migrationsrc/domain/repo/repo_service_test.ts— Updated 6 existing tests, added 7 new tests (append, tool switch, unchanged detection, legacy migration, trailing newline handling)src/presentation/output/repo_output.ts— Output interface field renamesrc/cli/commands/repo_init.ts— CLI data mapping update
Test plan
-
deno checkpasses -
deno lintpasses -
deno fmtpasses - All 2047 tests pass (43 repo service tests, including 7 new ones)
-
deno run compilesucceeds - Manual:
swamp repo initin a dir with existing.gitignore→ section appended - Manual:
swamp repo upgradeon repo with managed section → unchanged - Manual:
swamp repo upgradewith tool switch → section updated in-place
🤖 Generated with Claude Code
Co-authored-by: Blake Irvin blakeirvin@me.com
Installation
macOS (Apple Silicon):
curl -L https://github.com/systeminit/swamp/releases/download/v20260225.151449.0-sha.a40584f1/swamp-darwin-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/macOS (Intel):
curl -L https://github.com/systeminit/swamp/releases/download/v20260225.151449.0-sha.a40584f1/swamp-darwin-x86_64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/Linux (x86_64):
curl -L https://github.com/systeminit/swamp/releases/download/v20260225.151449.0-sha.a40584f1/swamp-linux-x86_64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/Linux (aarch64):
curl -L https://github.com/systeminit/swamp/releases/download/v20260225.151449.0-sha.a40584f1/swamp-linux-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/swamp 20260225.142717.0-sha.2ad52605
What's Changed
- fix: coerce --input string values to match Zod schema types (#467)
Fixes #462
Problem
When a user runs:
swamp model method run my-model myMethod --input deleteOrphans=true
the value "true" stays as a string through the entire pipeline until method.arguments.safeParse() rejects it with a Zod validation error. The existing coerceInputTypes() only coerces against JSON Schema (InputsSchema) for definition-level inputs — there was no coercion for method arguments or global arguments against their Zod schemas.
User impact
Before this fix, any --input flag passing a boolean or number value to a method argument would fail with a cryptic Zod validation error like Invalid input: expected boolean, received string. Users had no workaround — the CLI always passes --input values as strings. After this fix, --input deleteOrphans=true and --input maxCount=5 work as expected.
Solution
Add Zod schema-aware type coercion in method_execution_service.ts that converts string values to their expected types before safeParse() is called:
"true"/"false"→boolean- Numeric strings (e.g.,
"42","3.14","-3") →number - Non-coercible strings and non-string values pass through unchanged
The coercion uses the same Zod _def introspection pattern already established in sensitive_field_extractor.ts and input_override_validation_service.ts.
Plan vs implementation
The implementation matches the plan with one significant improvement:
| Area | Plan | Implementation | Reason |
|---|---|---|---|
Method args in execute() |
Coerce before safeParse |
Same | Method receives argsResult.data (parsed/typed), so coercion before parse is sufficient |
Global args in executeWorkflow() |
Coerce before safeParse |
Coerce before safeParse + write parsed values back into definition |
The plan had a gap: execute() sets context.globalArgs = definition.globalArguments (the raw values). Without writing back, methods would still receive "true" instead of true via context.globalArgs |
| Tests | 1 integration test | 2 integration tests, with the global args test asserting received types | The extra test for global args verifies the write-back fix works end-to-end |
| License headers | deno run license-headers |
Headers written directly into new files | Same result, one fewer step |
Files changed
| Action | File | Description |
|---|---|---|
| Create | src/domain/models/zod_type_coercion.ts |
coerceMethodArgs() — unwraps Zod wrappers, coerces string values to match leaf types |
| Create | src/domain/models/zod_type_coercion_test.ts |
18 unit tests: boolean/number coercion, NaN handling, wrappers, edge cases |
| Modify | src/domain/models/method_execution_service.ts |
Coerce method args and global args before safeParse; write coerced global args back to definition |
| Modify | src/domain/models/method_execution_service_test.ts |
2 integration tests verifying both code paths |
Verification
deno check— passdeno lint— passdeno fmt— passdeno run test— 2041 passed, 0 faileddeno run compile— pass
🤖 Generated with Claude Code
Installation
macOS (Apple Silicon):
curl -L https://github.com/systeminit/swamp/releases/download/v20260225.142717.0-sha.2ad52605/swamp-darwin-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/macOS (Intel):
curl -L https://github.com/systeminit/swamp/releases/download/v20260225.142717.0-sha.2ad52605/swamp-darwin-x86_64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/Linux (x86_64):
curl -L https://github.com/systeminit/swamp/releases/download/v20260225.142717.0-sha.2ad52605/swamp-linux-x86_64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/Linux (aarch64):
curl -L https://github.com/systeminit/swamp/releases/download/v20260225.142717.0-sha.2ad52605/swamp-linux-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/swamp 20260225.141350.0-sha.7ce741e4
What's Changed
- fix: strip build metadata from deno version in embedded runtime download (#466)
Summary
Fixes the release workflow failure: https://github.com/systeminit/swamp/actions/runs/22400151598/job/64844202816
Problem
Deno 2.7.0 started including build metadata in its version string (semver +build suffix):
deno 2.7.0+fb4db33
The download_deno.ts script parses this version and uses it to construct the GitHub release download URL:
https://github.com/denoland/deno/releases/download/v2.7.0+fb4db33/deno-x86_64-unknown-linux-gnu.zip
^^^^^^^^^^
But Deno's GitHub releases use tags without build metadata:
https://github.com/denoland/deno/releases/download/v2.7.0/deno-x86_64-unknown-linux-gnu.zip
^^^^^^
This causes a 404 Not Found during deno run compile, breaking all release builds.
Verified:
v2.7.0→ 302 (asset exists)v2.7.0+fb4db33→ 404 (does not exist)
Fix
Strip the build metadata suffix (everything after +) from the parsed version string in getDenoVersion() before constructing the download URL. This follows semver spec where build metadata is informational and should not affect version resolution.
// "2.7.0+fb4db33" → "2.7.0"
return match[1].replace(/\+.*$/, "");Test plan
-
deno check— passes -
deno lint— passes -
deno fmt— passes -
deno run test— all 2021 tests pass -
deno run compile— binary compiles successfully (downloads deno with clean version) - Manually verified v2.7.0 URL returns 302, v2.7.0+fb4db33 returns 404
🤖 Generated with Claude Code
Installation
macOS (Apple Silicon):
curl -L https://github.com/systeminit/swamp/releases/download/v20260225.141350.0-sha.7ce741e4/swamp-darwin-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/macOS (Intel):
curl -L https://github.com/systeminit/swamp/releases/download/v20260225.141350.0-sha.7ce741e4/swamp-darwin-x86_64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/Linux (x86_64):
curl -L https://github.com/systeminit/swamp/releases/download/v20260225.141350.0-sha.7ce741e4/swamp-linux-x86_64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/Linux (aarch64):
curl -L https://github.com/systeminit/swamp/releases/download/v20260225.141350.0-sha.7ce741e4/swamp-linux-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/swamp 20260225.011820.0-sha.4c63442a
What's Changed
- fix: store auth.json in XDG config dir alongside identity.json (#459)
Summary
- Moves auth credentials storage from
~/.swamp/auth.jsonto~/.config/swamp/auth.json(or$XDG_CONFIG_HOME/swamp/auth.json) - All user-level config (identity + auth) now lives in the same XDG-compliant directory
- Updates tests to use
XDG_CONFIG_HOMEinstead ofHOMEfor consistent path resolution
Test Plan
- All 2021 existing tests pass
- Auth repository tests verify save/load/delete at the new XDG config path
- File permission test confirms
0o600mode at the new location
🤖 Generated with Claude Code
Installation
macOS (Apple Silicon):
curl -L https://github.com/systeminit/swamp/releases/download/v20260225.011820.0-sha.4c63442a/swamp-darwin-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/macOS (Intel):
curl -L https://github.com/systeminit/swamp/releases/download/v20260225.011820.0-sha.4c63442a/swamp-darwin-x86_64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/Linux (x86_64):
curl -L https://github.com/systeminit/swamp/releases/download/v20260225.011820.0-sha.4c63442a/swamp-linux-x86_64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/Linux (aarch64):
curl -L https://github.com/systeminit/swamp/releases/download/v20260225.011820.0-sha.4c63442a/swamp-linux-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/swamp 20260225.000548.0-sha.4f9112ef
What's Changed
- feat: add changelog to GitHub release notes (#458)
Summary
- Add PR title and body as a "What's Changed" section to GitHub release notes
- Previously releases only had static installation instructions with no indication of what changed
Context
Each release is triggered by a single merged PR, but the release notes contained only download/install instructions. Users browsing releases had no way to see what actually changed without clicking through to the commit or PR.
This adds a "Generate release body" step that:
- Extracts the PR title and body from the
github.event.pull_requestcontext - Writes them into a "What's Changed" section at the top of the release notes
- Uses
env:variables andprintenvto safely handle PR content containing shell-unsafe characters (backticks,$(), quotes, etc.) - Falls back to the HEAD commit message for manual
workflow_dispatchruns - Keeps installation instructions below a
---separator
Test plan
- Merge this PR and verify the resulting GitHub release includes the PR title and body in the release notes
- Verify installation instructions still render correctly below the separator
🤖 Generated with Claude Code
Installation
macOS (Apple Silicon):
curl -L https://github.com/systeminit/swamp/releases/download/v20260225.000548.0-sha.4f9112ef/swamp-darwin-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/macOS (Intel):
curl -L https://github.com/systeminit/swamp/releases/download/v20260225.000548.0-sha.4f9112ef/swamp-darwin-x86_64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/Linux (x86_64):
curl -L https://github.com/systeminit/swamp/releases/download/v20260225.000548.0-sha.4f9112ef/swamp-linux-x86_64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/Linux (aarch64):
curl -L https://github.com/systeminit/swamp/releases/download/v20260225.000548.0-sha.4f9112ef/swamp-linux-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/