diff --git a/docs/release.md b/docs/release.md index 95e3c93..09be57c 100644 --- a/docs/release.md +++ b/docs/release.md @@ -137,16 +137,23 @@ byte for byte. `release.sh` pins everything that would otherwise vary: - `normalize_package` forces 755 on directories and the program and 644 on everything else, so the builder's umask cannot leak into the archive. -To check a published build, compare the binary inside the archive rather than -the archive hash: +- the Windows zip is packaged under `LC_ALL=C` and `TZ=UTC`, because `sort` + orders entries by locale and zip stores DOS local time with no zone. + +With those in place the archives themselves reproduce across hosts: a build on +`ubuntu-latest` (umask 022, C locale, UTC) and one on a workstation (umask 002, +`ru_RU.UTF-8`, UTC+08) produce identical checksums for all five archives. + +The strongest check is still the binary, since it does not depend on the host's +`tar` and `gzip` at all: ```bash tar -xzf sshkeeper__linux_amd64.tar.gz sha256sum sshkeeper__linux_amd64/sshkeeper ``` -The archive hash additionally depends on the `tar` and `gzip` implementations -on the build host, so it is the weaker check of the two. +Comparing whole-archive hashes works too, as long as both builds used the same +Go version. ## Publish in GitHub Release diff --git a/docs/releases/v0.3.2.md b/docs/releases/v0.3.2.md new file mode 100644 index 0000000..2da6b31 --- /dev/null +++ b/docs/releases/v0.3.2.md @@ -0,0 +1,140 @@ +Release automation and reproducible packaging. sshkeeper itself behaves exactly +as in v0.3.1 — no functional changes to the TUI or the CLI. + +This is also the first release published by GitHub Actions rather than by hand. + +## In this release + +**Archives are now reproducible.** Rebuilding a tag on a different machine used +to produce different checksums even when every packaged file was byte-identical, +because three host properties leaked into the archives: + +| Leak | Effect | +|------|--------| +| File modes followed the builder's umask | umask 002 packaged `664`/`775`, umask 022 packaged `644`/`755` | +| `sort` orders entries by locale | a `ru_RU.UTF-8` host emitted `docs/` before `LICENSE`, a C locale the reverse | +| zip stores DOS local time with no zone | the same commit embedded `19:06` at UTC+08 and `11:06` at UTC | + +All three are pinned now. A build on `ubuntu-latest` and one on a workstation +with a different umask, locale and timezone produce identical checksums for all +five archives. The binaries were always reproducible; only the packaging varied. + +**CI.** The repository previously had no automation. It now runs `gofmt`, +`go vet` and `go test` on Linux *and* macOS for every push and pull request, +plus a cross-build of all five release targets. macOS is a stated release +target that until now was only ever cross-compiled, never tested. + +**Releases are automated.** Pushing a `v*` tag runs the release checks, builds +through the same `release.sh` used locally, and publishes. Nightly builds from +`main` are published as a separate `nightly` prerelease, so the `Latest` badge +always points at a real release. + +--- + +# Everything since v0.2.0 + +## Breaking change: full help moved off F1 + +**`Ctrl+H` opens full help. `F1` no longer has any binding.** `?` still opens +contextual quick help outside text editors. This landed in v0.3.0. + +`Ctrl+H` is the BS control character (0x08). xterm and most modern emulators +send DEL (0x7F) for Backspace, so help and text editing do not collide. A +terminal configured to send BS for Backspace cannot tell them apart; switch it +to DEL (in xterm, `backarrowKey: false`). + +Nothing else requires action when upgrading. Vaults, server profiles and stored +port forwards are unchanged, and no migration runs. + +## The TUI was rebuilt around one shell (v0.3.0) + +In v0.2.0 only the server dashboard had a real layout. Other screens rendered +free-form strings or the default Bubbles list frame, so they had no shared +width budget, no borders, and footers that floated wherever the content ended. + +Every full-screen state now shares one contract: a header with breadcrumb and +truthful vault status, a separator, framed content panels, and a contextual +footer anchored to the last terminal row. + +- Actions, search, tag input, confirmations and both help screens render inside + the shell. +- The port forward manager and editor use framed, width-budgeted layouts. Column + widths derive from the panel's inner width, so no row consumes the terminal's + last column. +- Tag, command template, template picker/mode/results and tunnel managers use + framed lists with a `>` selection marker, so selection never depends on colour + alone. +- Server and template editors use a framed form panel with the title moved into + the breadcrumb. Required markers, validation and dirty-state confirmation are + unchanged. + +**Responsive layouts.** The supported floor is `60x16`. Wide (100+ columns) +shows two panels, medium (70–99) stacks them, narrow (60–69) keeps a single +compact panel. Below the floor only the minimum-size message renders. Long +ASCII, Cyrillic, CJK, combining and emoji content truncates by display cells +rather than byte count. + +**Safety.** Destructive actions confirm with Cancel selected first and name the +exact target and its consequence. Status and help context stay truthful to +actual vault and connection state. Form validation prevents silent loss of +edits. + +## `forward add` was completely broken (fixed in v0.3.1) + +`sshkeeper forward add` could never succeed in v0.2.0 or v0.3.0. The command +read `--local-port` and marked it required, but the flag was never registered, +so cobra rejected it as unknown and the value fell back to 0: + +``` +$ sshkeeper forward add web --type local --local-port 15432 \ + --remote-addr 127.0.0.1 --remote-port 5432 +unknown flag: --local-port +``` + +No combination of arguments worked. Both documented forms work now: + +``` +$ sshkeeper forward add web --name "Local PostgreSQL" --type local \ + --local-port 15432 --remote-addr db01.internal.example.com --remote-port 5432 +✓ Forward added [1] + +$ sshkeeper forward add web --name "SOCKS proxy" --type dynamic --local-port 1080 +✓ Forward added [2] +``` + +Omitting the flag now reports `required flag(s) "local-port" not set` instead of +a misleading port-range error. The TUI (`Ctrl+W`) was never affected. + +The command's tests had constructed their own throwaway cobra command and +registered the flags by hand, so the real command's registration was never +exercised and the suite passed against a broken command. Coverage now parses +argv into the actual command. An audit of every other command found no further +flag that is read but never registered. + +## Also fixed since v0.2.0 + +- Port forward fields accept digits correctly (`10bcc07`). +- Platform and repository status are stated accurately in the docs: Linux and + macOS are primary release targets, Windows is experimental. + +## Release-by-release + +| Version | Contents | +|---------|----------| +| v0.3.0 | Unified TUI shell, responsive layouts, `F1` → `Ctrl+H` | +| v0.3.1 | `forward add` fix | +| v0.3.2 | Reproducible packaging, CI, automated releases | + +## Install + +```bash +tar -xzf sshkeeper_v0.3.2_linux_amd64.tar.gz +sudo install -m 0755 sshkeeper_v0.3.2_linux_amd64/sshkeeper /usr/local/bin/sshkeeper +``` + +Verify downloads against `checksums.txt`. Linux and macOS are the primary +release targets; the Windows build is experimental and needs OpenSSH Client +available as `ssh.exe` on `PATH`. + +To verify a build yourself, check out the tag and run `./release.sh v0.3.2` — +the checksums should match this release exactly, given the same Go version.