Files
VERSE 87b91c45d7 feat: update changelog (#131)
closes #130 

- CHANGELOG.md file for people who can view changelog from github
---
`iris changelog` command:
`iris changelog [version]` (e.g. iris changelog v0.5.1)
`iris changelog -n 3` (see changelog of 3 latest releaseas)

=> `iris changelog` looks just like CHANGELOG.md, grouped headers and
all. It only stops looking that way if we personally overwrite that
release's description with something else
=> With 1-hour local cache (invalidated on updater-channel change),
`--refresh` flag to bypass, graceful fallback to stale cache on GitHub
rate-limiting

---
- New auto update config
```toml
# 0 = off (default, notify only), 1 = auto-install, 2 = always confirm first
auto-update = 1
```

- New just debug-changelog and just debug-autoupdate recipes (fully
offline, mocked HTTP servers, no real network/config touched)
2026-08-07 17:16:18 +07:00

8.0 KiB

Versioning and updates

Iris has a built-in update notification system designed to be non-intrusive and zero-latency

The update system is designed to keep Iris up to date while staying out of the way. It performs an asynchronous network check when the shell starts and notifies you exactly once per version after you run a command. This prevents redundant notifications and ensures that you only see an update message when a new release is actually available. Optionally, it can also install updates for you in the background. The system also includes dedicated debugging tools to verify the notification and auto-update logic without requiring real releases

How it works

  1. Background check: every time you open a new terminal, Iris launches a background goroutine to check for updates

    • checks the network at most once every check-interval (24h by default) to avoid GitHub API rate limiting
    • has a 5 second timeout so it never hangs on a slow or missing network connection
    • if there is no network, it fails silently with zero impact on startup
  2. State persistence: state is stored in $XDG_DATA_HOME/iris/state.toml (or ~/.local/share/iris/state.toml), including:

    • updater.last-check-time / updater.seen-version - when the last check ran, and the latest version already notified about
    • updater.auto-update-target / updater.auto-update-attempt - the auto-update escalation ladder's state (see Auto-update below)
    • updater.declined-version - a version the user explicitly declined at an auto-update confirm prompt, never re-prompted
  3. Smart notification:

    • the notice only appears after you run your first command (triggered by the IRIS_CMD_STOP IPC signal from the shell hook)
    • appears only once per session, never again even if you keep the terminal open
    • if you have already been notified about a specific version, it will not show again until a newer GitHub release tag is detected
    • when iris update is run successfully, the seen-version flag is cleared
    • includes up to two changelog bullets pulled from the release's grouped notes (see Implementation details for where those come from)

Auto-update

updater.auto-update (see Configuration) controls whether the background check can install updates itself, not just notify:

  • 0 (default): notify only, exactly the behavior above.
  • 1: auto-install, with a per-version escalation ladder so a flaky network or a failing installer can't turn into a silent retry loop:
    1. first detection of a newer version: installs it in the background, no prompt. Output is captured (not streamed) since the wrapper's terminal is in raw mode - a clean one-line notice is printed once the install finishes, if you're still in the session.
    2. if that version is still current on the next check (the install didn't stick), it stops auto-installing and shows a confirm prompt instead: [y/N], pressed like any other single keystroke.
    3. if it's still not current after that, Iris gives up on that version - prints one notice pointing at iris update, and won't try again until a newer version is released.
  • 2: always confirm first, every time - never installs silently, never gives up, just keeps asking until you say yes or the version changes.

A confirm prompt declined with n/Esc/Ctrl+C records that version so it's never re-prompted (until a newer one comes along).

Build-time versioning

Iris uses Go ldflags to inject the version string at build time:

go build -ldflags="-X github.com/versenilvis/iris/root.Version=v1.2.0" -o iris main.go

If not provided, the version defaults to dev. The dev version will never trigger an update notification

Configuration

The [updater] section of ~/.config/iris/config.toml:

[updater]
check-on-startup = true
channel = "stable" # "stable" or "nightly"
check-interval = "24h"
auto-update = 0 # 0 = off, 1 = auto-install, 2 = always confirm first

Commands

  • iris version - print the current version of the running binary
  • iris update - manually check for and apply the latest release
  • iris changelog - show grouped release notes fetched from GitHub (-n <count>, --refresh)

Debugging and testing

Four separate things to test: the update command, the in-session notification banner, the changelog command, and the background auto-updater

Test 1: update command

Tests version fetching, comparison and the output message. No full Iris session needed

just build-release v0.0.1
just debug-update v1.99.0

Expected output

--- testing iris update command ---
checking for updates (current: v0.0.1)...
[IRIS] updating v0.0.1 -> v1.99.0
running: curl -sS https://raw.githubusercontent.com/versenilvis/iris/main/scripts/install.sh | sh

[IRIS] restart your terminal to use the new version

Test 2: in-session notification banner

Tests the yellow notice that appears after you run your first command inside a live Iris session. Requires iris.zsh to be active in the inner shell so IRIS_CMD_STOP fires through the IPC pipe

just build-release v0.0.1
just debug-notify v1.99.0

Inside the new session, run any command. Expected output after the command

[IRIS] new version v0.0.1 -> v1.99.0 available, run iris update to upgrade
  - <first changelog bullet>

Test 3: changelog command

Tests fetching, grouping, the response cache, and rate-limit fallback. No full Iris session needed

just build-release v0.0.1
just debug-changelog v1.99.0

Test 4: background auto-updater

Tests the escalation ladder (silent install, then escalating to a confirm-worthy state, then giving up rather than looping) fully offline, by inspecting state.toml after each simulated check cycle. The confirm-prompt UI itself is interactive and isn't exercised here - set auto-update in a real config and watch a live session with shell hooks active to see it rendered

just debug-autoupdate

Environment variables

Variable Purpose
IRIS_UPDATE_URL override the release-check endpoint with a custom URL (used by debug-update, debug-autoupdate)
IRIS_MOCK_LATEST_VERSION skip network entirely, resolve to this version immediately (used by debug-notify)
IRIS_CHANGELOG_URL override the changelog-fetch endpoint with a custom URL (used by debug-changelog)
IRIS_INSTALL_URL override the install-script fetch URL (used by debug-autoupdate)

State reset

To force a fresh check on next launch, delete the state file:

rm ~/.local/share/iris/state.toml

Implementation details

File Purpose
root/version.go holds the Version variable, defaults to dev
root/update.go version fetch/compare, state persistence, the iris update/iris version commands
root/changelog_cmd.go the iris changelog command: fetch, cache, grouping-aware rendering
root/autoupdate.go performUpdate, the auto-update escalation ladder, and the confirm-prompt keystroke handler
root/wrapper.go wires the background check into the IPC loop, prints notices on IRIS_CMD_STOP
.goreleaser.yaml changelog: block groups commits by type for the GitHub release body
cliff.toml generates the committed CHANGELOG.md from the same commit history on each stable release