Skip to content

feat(cli): add checks list/get commands with typed formatters#1233

Merged
MichaelHogers merged 22 commits intomainfrom
michael/tim-9-10-11-checks-cli
Feb 25, 2026
Merged

feat(cli): add checks list/get commands with typed formatters#1233
MichaelHogers merged 22 commits intomainfrom
michael/tim-9-10-11-checks-cli

Conversation

@MichaelHogers
Copy link
Contributor

@MichaelHogers MichaelHogers commented Feb 23, 2026

Summary

Add checkly checks list and checkly checks get CLI commands for viewing checks, results, and error groups from the terminal.

  • REST clients for checks, check-results, check-statuses, check-groups, error-groups
  • Formatters with typed field definitions (DetailField<T>, ColumnDef<T>) — fields defined once, rendered for both terminal and markdown
  • Unit tests for formatters and render primitives, e2e tests for both commands

Also adds a new e2e testing account with no checks, representing a new user, to cover any potential edge cases we may miss in our own e2e prod account.

config.emptyApiKey = process.env.CHECKLY_EMPTY_API_KEY
config.emptyAccountId = process.env.CHECKLY_EMPTY_ACCOUNT_ID

Test plan

  • vitest --run src/formatters/__tests__/ src/commands/checks/__tests__/ — 115 tests pass
  • E2E: checks-list.spec.ts, checks-get.spec.ts (require prod env)

Add `checkly checks list` and `checkly checks get` commands with
REST clients, formatters, and e2e tests.

Formatters use typed field definitions (DetailField, ColumnDef) with
generic renderDetailFields/renderTable primitives, defining fields
once and rendering them for both terminal and markdown output formats.
- Extract appendErrors/appendAssets helpers in check-result-detail
- Collapse duplicated formatBody try/catch branches
- De-duplicate Tags column definition in checks table
- Simplify formatCheckType to pass-through
- Use stripAnsi/formatDate in get.ts instead of inline regex/formatting
@MichaelHogers MichaelHogers force-pushed the michael/tim-9-10-11-checks-cli branch from 78aeeb3 to 482d32e Compare February 23, 2026 11:02
…tive checks

- Replace this.exit(1) with process.exitCode = 1 to avoid oclif's global
  handler printing the error a second time
- Exclude inactive checks from --status filter results
- Extract filterByStatus() and add unit tests
Add .catch() fallbacks for check-statuses and check-groups API calls
so accounts with no checks don't crash with a 404.
Test checks list and get commands against an account with no checks
to verify graceful handling of empty responses and 404 fallbacks.
…nfig

- Add markdown output branch to showErrorGroupDetail (was rendering
  terminal/chalk output regardless of --output flag)
- Switch empty account e2e tests to use node-config pattern with
  describe.skipIf for graceful skipping when creds aren't set
- Add emptyApiKey/emptyAccountId to e2e config default.js
- Document optional empty account test setup in e2e README
@MichaelHogers
Copy link
Contributor Author

checking something with totals not adding up as expected, monitors causing some funniness in the counting

Show per-type counts (e.g. BROWSER: 140  API: 77  ...) on a dim line
below the status summary. Filters by activeCheckIds so the subcounts
sum to the displayed total.
@MichaelHogers
Copy link
Contributor Author

@sorccu could you give this an initial review please? 🙇

@MichaelHogers
Copy link
Contributor Author

@sorccu what do you typically do with CLI releases? do we best first dogfood these type of changes?

Copy link
Contributor

@thebiglabasky thebiglabasky left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's fine, though also believe there could be a case for already centralizing some elements that'll be common across most commands like pagination, command output parameter, or formatting.
The second point is regarding the contract, though I think we could go with this first and refactor later to move the definitions you have there into the contract package.
This made me wonder if by "contract" we wouldn't actually mean a TS SDK that we'd import both from the CLI and webapp and would act as the actual contract against the openapi spec from the BE API.

@MichaelHogers
Copy link
Contributor Author

thanks @thebiglabasky going through the comments now (on the formatting side i didn't feel super strongly about the code, because it is bound to change and nothing depends directly on that for a while)

@MichaelHogers
Copy link
Contributor Author

MichaelHogers commented Feb 24, 2026

I think it's fine, though also believe there could be a case for already centralizing some elements that'll be common across most commands like pagination, command output parameter, or formatting. The second point is regarding the contract, though I think we could go with this first and refactor later to move the definitions you have there into the contract package. This made me wonder if by "contract" we wouldn't actually mean a TS SDK that we'd import both from the CLI and webapp and would act as the actual contract against the openapi spec from the BE API.

@thebiglabasky the way i've done this before is:

  • backend exports an openapi.json (to me this can be seen as "the contract") - with the versioned header approach these should be permanently stored and named by date* (could sit in the repo)
  • any client/consumer can turn that openapi.json into a sdk (CLI and webapp likely use different libraries to turn it into their own SDK, e.g. webapp wants a vue friendly one) + i used to have a CI check to ensure webapp uses latest backend openapi.json

*: we need to also build some tooling to check that older contracts still work against latest backend, this could be by validating the contracts directly or simply e2e suits between the range of versions of clients and latest backend, probably former is easiest

@thebiglabasky
Copy link
Contributor

  • any client/consumer can turn that openapi.json into a sdk (CLI and webapp likely use different libraries to turn it into their own SDK, e.g. webapp wants a vue friendly one) + i used to have a CI check to ensure webapp uses latest backend openapi.json

*: we need to also build some tooling to check that older contracts still work against latest backend, this could be by validating the contracts directly or simply e2e suits between the range of versions of clients and latest backend, probably former is easiest

I was thinking this but then wondered why we'd actually bother to separate all these things since it's all TypeScript in the end, so having all the openapi, types and even handy wrapping call methods in a single package suddenly looked appealing to me: so you don't even bother with the API endpoint and structure, it's all typed through the SDK. The openapi part becomes the elements used by the backend and for docs.
I might be naive though, but I'm not sure about the benefits of splitting the Vue consumption from the CLI. Maybe skeletons / streaming or stuff I'm overlooking?

@thebiglabasky
Copy link
Contributor

Note for self: came back from the SDK idea, and now sold on sticking to only zod so we can generate the openapi spec, and get runtime validation all in a single place

Replace client-side filtering that fetched all checks (5+ API calls for
large accounts) with server-side query params on GET /v1/checks. The
command now makes just 2 API calls regardless of filters applied.

Remove --status flag (not supported server-side), client-side filter
helpers (filterByStatus, buildActiveCheckIds), and their tests.
Extract shared outputFlag factory to flags.ts. Also improve e2e test
assertions per PR review feedback.
oclif auto-discovers all files in the commands directory as commands,
causing flags.ts to be registered as checks:flags. Move to src/flags/.
Move flags.ts back to commands/checks/ where it belongs and configure
oclif to skip files named flags.* via globPatterns negation.
Revert oclif globPatterns config and move flags.ts to src/helpers/
where it won't interfere with oclif command discovery.
@MichaelHogers
Copy link
Contributor Author

@thebiglabasky finished now with feedback and some improvements

@sorccu could use some pointers on how to do the release now and in the future, sent slack msg as well

thebiglabasky
thebiglabasky previously approved these changes Feb 25, 2026
Copy link
Contributor

@thebiglabasky thebiglabasky left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

The --status flag was removed in 372cbe2 when we moved to server-side
filtering, but this test was left behind causing CI failures.
@MichaelHogers
Copy link
Contributor Author

I noticed tests are failing — they were still using the --status flag which was removed in 372cbe2 when we moved to server-side filtering. I've cleaned up the failing test in 80ecd78.

otherwise, in a separate agent session I'm looking into adding status filtering to the public API so we can re-enable --status in the CLI as a proper server-side filter, this should be a low complexity change.

TLDR: we can merge this, but i am looking at adding a status filter to our public api endpoint in the meantime, it is quite useful to have

@sorccu i still need some instructions on how we publish our CLI! 🙏

@MichaelHogers MichaelHogers merged commit 048b121 into main Feb 25, 2026
7 of 8 checks passed
@MichaelHogers MichaelHogers deleted the michael/tim-9-10-11-checks-cli branch February 25, 2026 15:19
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.

2 participants