You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
* chore: update to zig 0.16 and zigzag v0.1.5
Migrates sshz to Zig 0.16's reworked I/O model, where filesystem,
process, and network operations require an `Io` instance. The main
entry point now takes `std.process.Init`, and `io` plus `environ_map`
are threaded through ssh_config, meta, app, and checker.
Notable changes:
- main: `std.process.spawn(io, ...)` replaces `std.process.Child.init`
- main: `std.Io.File.stdout()` replaces `std.fs.File{ .handle = ... }`
- main: `init.minimal.args.toSlice(arena)` replaces `argsAlloc/argsFree`
- ssh_config/meta: switch to `std.Io.Dir.cwd()` with buffered writers
and `init.environ_map.get("HOME")` for env access
- checker: use `std.Io.net.HostName.connect(io, port, .{})` for TCP
status checks
- views: `zz.Color.cyan`/`white` are now consts (not functions)
- CI: install Zig 0.16.0
- build.zig.zon: bump zigzag to v0.1.5, set minimum_zig_version
Note: the zigzag dependency hash needs to be regenerated with
`zig fetch --save=zigzag <url>` against a real Zig 0.16 toolchain;
the current value in build.zig.zon is the v0.1.2 hash and will be
rejected at build time.
* fix: correct readFileAlloc/rename param order for zig 0.16
- readFileAlloc takes (io, sub_path, gpa, limit) not (io, gpa, sub_path, limit)
- rename uses method call style: dir.rename(old_path, new_dir, new_path, io)
* fix: use ArrayList .empty and std.Io.Mutex for zig 0.16
- Unmanaged std.ArrayList no longer has default field values, so .{}
initialization fails; switch to .empty
- std.Thread.Mutex is gone; use std.Io.Mutex with lockUncancelable(io)
and unlock(io)
- ResultQueue now also takes an Io instance so its mutex can lock
* fix: use std.Io.Timestamp.now() instead of removed std.time.timestamp
* fix: complete migration to zig 0.16 - build and tests pass
- build.zig.zon: use real v0.1.5 hash from zig fetch
- app.zig: ArrayList(...).empty (not .{}) for new unmanaged ArrayList
- checker.zig: pass .mode = .stream for HostName.connect
- meta.zig/utils.zig/ssh_config.zig: route io through for the new
std.Io.Timestamp.now(.real) clock API
- tests/test_meta.zig: pass std.testing.io to recordConnection
Verified locally: zig 0.16.0 builds the exe, all 29 tests pass,
and zig fmt --check src/ is clean.
* chore: remove accidentally committed zig-pkg cache, add to .gitignore
* fix: address PR #3 review feedback
- main: guard args[1][0] with len > 0 to avoid OOB on empty arg
- checker: unlock mutex on toOwnedSlice OOM in joinAllThreads
- checker: parse IPv4/IPv6 literals before HostName.init so bare
IPv6 hosts no longer always report offline
- ssh_config: createDirPath before openDir in writeFile so save
succeeds when ~/.ssh is missing
- ssh_config/meta: propagate dir/file errors instead of swallowing
them in createDirPath/backupFile/rotateBackups
* fix: address pre-existing bugs flagged in PR #3 review
- ssh_config: Config now owns the raw file buffer so hosts/raw_lines
slices remain valid after readFile (CodeRabbit critical: dangling
reference on `parse` content)
- meta: JSON-escape host names, tags, and port-forward fields when
serialising so values containing `"`, `\\`, or control characters
no longer corrupt meta.json (CodeRabbit major)
- app.deleteSelectedHost: look up the config entry by name from the
visible list instead of by display index, so sorting no longer
causes the wrong host to be deleted (CodeRabbit critical)
- app.saveForm: always call setTags so clearing the tags field in
the edit form persists an empty list (CodeRabbit major)
- app.connectToSelected: stop recording the connection in the TUI
path; directConnect already records it after the TUI exits, so
this was double-counting connect_count (CodeAnt major)
* fix: maintain visible_indices so search/tag filters keep selected in sync
Move filter logic from the renderer into Model.rebuildVisibleIndices.
`selected` is now an index into `visible_indices` (the filtered display
order), so navigation, edit ('e'), forward ('f'), delete ('d'),
connect (Enter), and confirm-delete prompts all reference the same
host the user actually sees highlighted, regardless of search text or
tag filter.
- add Model.visible_indices, rebuilt on rebuildHostList,
cycleSortMode, cycleTagFilter, and every search-text mutation
- moveDown bounds-check against visible_indices.items.len
- views/host_list.zig drops matchesSearch/hasTag (now in app.zig)
and iterates model.visible_indices directly
* fix: deep-copy strings in ssh_config so addHost/removeHost don't leak
`parse` previously returned a `Config` whose `Host` fields and
`raw_lines` borrowed from the input buffer; `addHost` mixed in
allocPrint'd lines, leaving `deinit` unable to tell which strings to
free. The pre-existing leak forced one test to use `page_allocator`
and skip `deinit` entirely.
This change makes Config the sole owner of every string inside it:
- `parse` deep-copies host names, host fields, and every raw line
(with `errdefer` cleanup on partial OOM)
- `setHostField` becomes fallible and dupes its value
- `addHost` deep-copies its incoming `Host` and lines, then commits
with a two-phase swap so a failed `toOwnedSlice` doesn't strand
half-owned state on `config`
- `removeHost` frees the dropped host's strings and the dropped
`raw_lines` entries
- `Config.deinit` walks hosts/lines and frees their strings via a
shared `freeHostStrings` helper
- `readFile` reverts to `defer free(content)` — no buffer transfer
needed now that `parse` owns its copies
- `Model.saveForm` no longer pre-dupes form values, since `addHost`
now takes ownership; this also closes a leak when the form `name`
field was empty
- ssh_config tests for addHost switch to `std.testing.allocator`
with `defer config.deinit(...)`, validating the ownership story
with the leak detector
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: midasdf <midasdf@users.noreply.github.com>
0 commit comments