fix(changelog): refuse to release a version older than the newest

`release 0.4.9` on a changelog topped by 0.5.0 wrote the section above it
with a backwards compare link and deleted the entries; `check` caught the
file only afterwards (Pullfrog on #82). The version is now compared before
anything is written.

Also: the #78 entry names the calendar's open offers and the session API's
read-path allowlist, added there since.

Claude-Session: https://claude.ai/code/session_01RqPPVeVDrnLoP7NUZmLRxY
This commit is contained in:
Rasyidan Akbar F. 2026-11-04 20:47:20 +07:00
commit eb4e2d4162
3 changed files with 27 additions and 3 deletions

View file

@ -1,5 +1,9 @@
### New Features
- `idx stocks actions` and `get_corporate_actions` list one stock's corporate actions from your Stockbit session, newest first: cash and stock dividends, bonus shares, rights issues, warrants, splits and reverse splits, shareholders' meetings, public exposes and tender offers. `--kind` picks kinds, and `--kind ipo` adds the stock's IPO. (#78)
- `idx stocks calendar` and `get_corporate_calendar` list the market's actions dated in a range, oldest first: by default today and the next 30 days, at most 366 days. (#78)
- `idx stocks calendar` and `get_corporate_calendar` list the market's actions dated in a range, oldest first: by default today and the next 30 days, at most 366 days. Rights trading, tender offers and IPO offers still open in the range are listed too, so an offer that began earlier shows while you can act on it. (#78)
- An action's `date` is its cum date where there is an entitlement (dividends, bonuses, rights, splits), else the event's first day. Where Stockbit gives only an ex date, the table marks it `(ex)`. Dividends paid in another currency carry `currency`. (#78)
### Improvements
- idx reads Stockbit's session API only on the endpoints it uses. The same API places orders and withdraws cash; any other path fails inside idx before a request leaves your machine. (#78)

View file

@ -188,6 +188,13 @@ EOF
if changelog release 0.5.0 2026-11-06 >/dev/null 2>&1; then
fail "release accepted a version already released"
fi
if err="$(changelog release 0.4.9 2026-11-06 2>&1)"; then
fail "release accepted a version older than the newest"
elif ! grep -q "older than 0.5.0" <<<"$err"; then
fail "release of an older version said: ${err}"
fi
[[ -f "$work/changes/unreleased/again.md" ]] || fail "a refused release deleted the entries"
diff -q "$expected" "$work/CHANGELOG.md" >/dev/null || fail "a refused release changed CHANGELOG.md"
rm "$work/changes/unreleased/again.md"
if changelog release 0.5.1 >/dev/null 2>&1; then
fail "release accepted no entries"

View file

@ -132,6 +132,13 @@ cmd_check() {
printf 'changelog: ok (%s unreleased entries)\n' "$(entries | wc -l | tr -d ' ')"
}
# A version as a string that sorts like the version: 0.10.0 after 0.9.9.
version_key() {
local major minor patch
IFS=. read -r major minor patch <<<"$1"
printf '%06d%06d%06d' "$((10#$major))" "$((10#$minor))" "$((10#$patch))"
}
# The newest version in CHANGELOG.md, if any.
latest_version() {
awk '/^## \[[0-9]/ { print substr($2, 2, length($2) - 2); exit }' "$changelog"
@ -182,6 +189,13 @@ cmd_release() {
if grep -q "^## \[${version//./\\.}\] " "$changelog"; then
die "${version} is already in CHANGELOG.md"
fi
# The new section goes on top, so it must be the newest version; checked
# before anything is written or an entry deleted.
local previous
previous="$(latest_version)"
if [[ -n "$previous" ]] && [[ "$(version_key "$version")" < "$(version_key "$previous")" ]]; then
die "${version} is older than ${previous}, the newest version in CHANGELOG.md"
fi
local files=()
local file
@ -190,8 +204,7 @@ cmd_release() {
done < <(entries)
((${#files[@]} > 0)) || die "no entries in changes/unreleased/ to release"
local previous section body
previous="$(latest_version)"
local section body
local out
out="$(mktemp "${TMPDIR:-/tmp}/changelog.XXXXXX")"
{