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)
This commit is contained in:
VERSE
2026-08-07 17:16:18 +07:00
committed by GitHub
parent 4a4ab3c102
commit 87b91c45d7
25 changed files with 2275 additions and 90 deletions
+70 -18
View File
@@ -2,24 +2,39 @@
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. The system also includes dedicated debugging tools to verify the notification logic without requiring real releases
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 6 hours to avoid GitHub API rate limiting
- 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 `~/.iris/update_state.json` with two fields:
- `last_check` - unix timestamp of the last network check
- `seen_version` - the latest version the user was already notified about
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](#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
- 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](#implementation-details) for where those come from)
## Auto-update
`updater.auto-update` (see [Configuration](#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
@@ -31,14 +46,27 @@ go build -ldflags="-X github.com/versenilvis/iris/root.Version=v1.2.0" -o iris m
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`:
```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
There are two separate things to test: the update command itself, and the in-session notification banner
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
@@ -71,27 +99,51 @@ 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
```bash
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
```bash
just debug-autoupdate
```
### Environment variables
| Variable | Purpose |
| -------------------------- | ----------------------------------------------------------------------------------- |
| `IRIS_UPDATE_URL` | override the GitHub API endpoint with a custom URL (used by `debug-update`) |
| `IRIS_MOCK_LATEST_VERSION` | skip network entirely, resolve to this version immediately (used by `debug-notify`) |
| 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 network check on next launch, delete the state file:
To force a fresh check on next launch, delete the state file:
```bash
rm ~/.iris/update_state.json
rm ~/.local/share/iris/state.toml
```
## Implementation details
| File | Purpose |
| ----------------- | ---------------------------------------------------------------------------------------- |
| `root/version.go` | holds the `Version` constant, defaults to `dev` |
| `root/update.go` | all update logic: state persistence, network fetch, version compare, commands |
| `root/wrapper.go` | wires the background check into the IPC loop, prints the notice on first `IRIS_CMD_STOP` |
| 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 |