Skip to content

Give monitorctl and monitord a real front door - #50

Merged
evandhoffman merged 1 commit into
mainfrom
feat/issue-48-cli-help
Sep 5, 2026
Merged

Give monitorctl and monitord a real front door#50
evandhoffman merged 1 commit into
mainfrom
feat/issue-48-cli-help

Conversation

@evandhoffman

Copy link
Copy Markdown
Contributor

Summary

  • Both CLIs are now ParsableCommands (swift-argument-parser), so --help is rendered from the @Option/@Flag declarations and an unrecognised flag is refused by the same table. A flag added here appears in --help because there is no second place to add it to. Sources/monitord/Monitord.swift, Sources/monitorctl/Monitorctl.swift.
  • --help / -h / help print usage to stdout and exit 0. --version prints the version and the commit it was built from. An unknown flag, a missing value or an unparseable value is named on stderr and exits 64, and nothing starts. A value that parses but cannot work — a non-positive --interval, a --count below one — is rejected in validate().
  • Choices come from the type: LogRetention.allValueStrings and the source registry's allIDs, rather than a list written out in prose that was already one literal away from lying.
  • SourceRegistry.allIDs becomes a stored property (Sources/MonitorSources/SourceRegistry.swift:27). It called makeAll() on every access, and makeAll() builds real readers — SMCSource opens an IOKit connection. Cheap while the app asked once at launch, and not cheap at all once the list went into a --help string ArgumentParser rebuilds on every parse: the new suite took four minutes before this and takes under a second after it.
  • Tradeoff: this adds the package's first third-party dependency, Apple's swift-argument-parser, used only by the two CLI targets — nothing in the app, the UI or the sources links it. Package.resolved is now committed rather than ignored, because a version range resolves to whatever is newest on the day and a release built from a different revision than the one tested is not the release that was tested. AGENTS.md and docs/ui.md drop the no-dependencies rule.

Closes #48

Test plan

  • swift test — 257 tests in 32 suites pass. CommandLineTests is new and covers both binaries' front doors, which no other suite touches: the daemons themselves were never broken.

  • swift build -c release

  • swiftformat Sources Tests Plugins --lint --cache ignore — clean

  • The issue's table re-run against the release binaries. All five argv rows now print and exit instead of booting a daemon, and none of them writes a CSV:

    argv exit output CSV
    --help 0 usage no
    -h 0 usage no
    help 0 usage no
    --version 0 1.6.0 (v1.6.0-…) no
    --nonsense 64 error + usage no
  • CI green, including two new steps: --help/--version against the real release binaries, and an assertion that monitord --help leaves its --dir empty — a daemon booting instead of printing is only visible from outside the process.

Out of scope

The per-fan sensor.fan.N.mode column asked for in the second comment on #48 is a metric change rather than a parsing one, and is not in this PR.

`monitord --help` started the daemon. So did `-h`, `help`, `--version` and
`--nonsense`: every one fell through to the start path, wrote a CSV and
printed nothing. The usage text was compiled into the binary and complete,
with no route to a terminal. `monitorctl` rejected an unknown command but
not an unknown flag, so `--intrval 0.1` sampled at the default rate and
said nothing.

Both are now ParsableCommands, so the help is rendered from the @option
and @Flag declarations and an unrecognised flag is refused by the same
table. A flag added here appears in --help because there is no second
place to add it to.

- `--help`, `-h` and `help` print usage to stdout and exit 0.
- `--version` prints the version and the commit it was built from.
- An unknown flag, a missing value or an unparseable value is named on
  stderr and exits 64. Nothing starts.
- A value that parses but cannot work is rejected in validate(): a
  non-positive --interval, a --count below one.
- Choices come from the type — LogRetention.allValueStrings and the
  source registry's allIDs — not from a list written out in prose.

SourceRegistry.allIDs becomes a stored property. It called makeAll() on
every access, and makeAll() builds real readers: SMCSource opens an IOKit
connection. That was cheap while the app asked once at launch and not
cheap at all once the list went into a --help string ArgumentParser
rebuilds on every parse — the new suite took four minutes before this and
takes under a second after it.

This adds the package's first third-party dependency, Apple's
swift-argument-parser, used only by the two CLIs. Package.resolved is now
committed rather than ignored: a version range resolves to whatever is
newest on the day, and a release built from a different revision than the
one tested is not the release that was tested. AGENTS.md and docs/ui.md
drop the no-dependencies rule.

CommandLineTests covers both binaries' front doors, which no other suite
touches — the daemons themselves were never broken. CI additionally runs
--help and --version against the real release binaries and asserts that
`monitord --help` leaves its log directory empty, since a daemon booting
instead of printing is only visible from outside the process.
@evandhoffman
evandhoffman merged commit 61ea498 into main Sep 5, 2026
2 checks passed
@evandhoffman
evandhoffman deleted the feat/issue-48-cli-help branch September 5, 2026 12:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

monitord: --help, --version, and unknown flags all start the daemon instead of printing usage

1 participant