feat(pro report): add --bundle-id and --path to software-installs - #348
Open
ktn-jamf wants to merge 3 commits into
Open
feat(pro report): add --bundle-id and --path to software-installs#348ktn-jamf wants to merge 3 commits into
ktn-jamf wants to merge 3 commits into
Conversation
`software-installs` reported title, version and device_count, aggregating over /v4/computers-inventory?section=APPLICATIONS. Both new fields were already on the wire: the ComputerApplication schema declares bundleId and path, and the function already read path to feed isSystemApp. Each flag extends the grouping key rather than adding a display column. One title and version can map to several bundle IDs or several install paths — a repackaged app, or the same app installed in two locations — so a column alone would print one arbitrary member of the group and silently hide the rest. The two grouping tests are the ones that fail if that is ever undone. softwareKey gains bundleID and path, left at the zero value when the flag is off, so the counting loop keys itself correctly in all four flag combinations with no branching. Sorting gained tie-breakers on bundle ID then path, because a title and version can now span rows. The row builder gates each optional key on the flag, never on whether the value is empty. A table's columns are the keys of its first row, so an emptiness gate would make the column set depend on sort order and an application with no bundleId on the wire would drop the column for every row. Both flags are off by default, so the default output keeps exactly its three columns, which a test asserts. Flags are kebab-case, matching --include-system and every other flag in this CLI; the issue text asked for --bundle_id. Verified: `make test` green in full. The four new tests cover each flag alone, both together, and the unchanged default. The command needs a live tenant, so the fixture-client tests are the runtime evidence for the aggregation; the rendered --help was checked on the built binary. Closes #325 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Both flags are boolean, and both names invite a value. `pro report
software-installs --path /Applications/Foo.app` set --path true and left the
path as a positional argument, which the command discarded without a word. It
then ran the full fleet report, so the operator got fleet-wide output for what
they had typed as a filter. Verified on the built binary before the fix: that
invocation, and a bare `software-installs junkarg`, produced output identical
to a clean run and exited 1 on the network rather than 2 on usage.
The hole is repo-wide and pre-existing. `pro report inventory-summary junkarg`
and `pro categories list junkarg` swallow a positional the same way, so this
change is scoped to the one command whose new flag names make a latent hole
reachable.
`Args: cobra.NoArgs` is the mechanism. Cobra reports the refusal as `unknown
command`, ClassifyError already maps that prefix to exitcode.Usage, and
ValidateArgs runs ahead of PersistentPreRunE, so the refusal lands before any
credential resolves or any request is sent. Confirmed on the binary: exit 2
and an exitCodeName of "usage".
Three tests come with it. The sort tie-breakers had no test that could observe
them, because nothing in this file asserted row order at all. The new order
test puts a tie at each of the four comparator levels and pins the full row
sequence. Both flags are on, because the path level is unreachable otherwise:
with --path off every key holds the same empty path, so any two keys reaching
that comparison are the same key. Every level was mutation-proved. Flipping
title, version, bundle ID or path in turn fails the test, and so does dropping
Args: cobra.NoArgs.
The last two tests cover an application the wire reports with no bundleId, and
one with no path. The row builder gates each optional key on the flag and never
on whether the value is empty, so both still get a row and still carry the
column. An emptiness gate would drop the column for every row, because a
table's columns are the keys of its first row. Both mutations were proved as
well. The path case is not a duplicate of the bundle ID case: isSystemApp("")
is false, so an application with no path is never filtered as a system app and
reaches even the default output.
The two new flags also join the site examples.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… env This commit closes two test-quality gaps in the --bundle-id and --path work. The default-output test asserted the row key set. Its fixture gave each title and version one bundle ID and one path. An unconditional assignment to key.bundleID or key.path therefore changed no row count, and both mutations kept the whole internal/commands package green. The default output then rendered one title and version as two identical rows. Each row held half of the device count, and no column explained the split. The fixture now gives Zoom 5.0 two bundle IDs and two paths across two devices, and the test asserts the merged device count. The stray-positional test is the only test in the file that runs the real root command, and it read the ambient environment. With the Args guard removed and Jamf Pro credentials exported, the test sent the paginated computers-inventory request and took 12 seconds. A developer with live tenant credentials therefore risked a fleet inventory sweep from `go test ./...`. The test now calls clearAuthEnv, which root_test.go already uses. The spec version argument is now "unknown", because checkTenantVersion returns early only for that value. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #325.
pro report software-installsreported three columns:title,version,device_count. This adds--bundle-idand--path, which report an application's bundle identifier and its install path.Both fields were already on the wire. The
ComputerApplicationschema declaresbundleIdandpath, and the function already readpathto feedisSystemApp. No spec change and no regeneration are needed.Each flag extends the grouping key
This is the correctness point of the change. One title and version can map to several bundle identifiers or several install paths — a repackaged application, or the same application installed in two locations. A display-only column would print one arbitrary member of the group and hide the rest, with nothing in the output to reveal it. So each flag joins the aggregation key:
Without the flag those three installs are one row reading
3forZoom 5.0.Column order is alphabetical. The table formatter reads its column set from
sortedKeys(rows[0]), which floats onlyidandname, so every table in this CLI orders the rest that way. Use--select title,version,bundle_id,device_countfor a different order.softwareKeygainsbundleIDandpath, left at the zero value when the flag is off, so the counting loop keys itself correctly in all four flag combinations with no branching. Sorting gained tie-breakers on bundle identifier then path, because a title and version can now span rows.The default output is unchanged
The row builder gates each optional key on the flag, never on whether the value is empty. A table's columns are the keys of its first row, so an emptiness gate would make the column set depend on sort order, and an application with no
bundleIdon the wire would drop the column for every row. Both flags are off by default, so anything parsing the current three columns is unaffected. A test asserts that the default rows carry exactly those three keys and no more.Flag names
Kebab-case, matching
--include-systemand every other flag in this CLI. The issue text asks for--bundle_id; snake_case would be the only such flag in the binary.--pathcovers the--file_pathrequest in the issue comment.Tests
Four cases beside the existing four:
TestRunReportSoftwareInstalls_BundleIDExtendsTheGroupingKeyTestRunReportSoftwareInstalls_PathExtendsTheGroupingKeyTestRunReportSoftwareInstalls_DefaultRowsCarryOnlyTheThreeOriginalKeysTestRunReportSoftwareInstalls_BundleIDAndPathTogetherThe first two are the ones that fail if the grouping is ever reduced to a display column. That was confirmed by mutation: discarding the two key assignments makes exactly the grouping tests fail, and no others.
make testpasses in full with zero failures.go vet ./internal/commands/is clean.make verify-siteandmake verify-site-outputboth pass, since the change touchesdocs/site.Second commit, from review
A deep review found one confirmed defect and two real test gaps.
Both flags are boolean and both names invite a value, so
--path /Applications/Foo.appset the flag and left the path as a positional argument, which the command discarded in silence and then reported the whole fleet. The operator got fleet-wide output for what they had typed as a filter.Args: cobra.NoArgsrefuses it: cobra reports it asunknown command, whichClassifyErroralready maps to exit 2, andValidateArgsruns ahead ofPersistentPreRunE, so the refusal lands before any credential resolves or any request is sent.The hole itself is repo-wide and pre-existing —
pro report inventory-summary junkargandpro categories list junkargswallow a positional the same way — so the fix is scoped to the one command whose new flag names make a latent hole reachable. The general case deserves its own change.The two test gaps: nothing in this file asserted row order at all, so the new tie-breakers were unobservable, and neither empty-value case was covered.
TestRunReportSoftwareInstalls_RowOrderFollowsEveryComparatorLevelputs a tie at each of the four comparator levels and pins the full sequence; flipping any level in turn fails it. Two further tests cover an application the wire reports with nobundleIdand one with nopath, both of which still get a row carrying the column, because the row builder gates on the flag rather than on the value. Mutating either gate to test the value fails them.The two flags also joined
docs/site/examples.json. No test validates that file, so it was checked by parsing it, comparing its key set against every other entry, and running both documented commands.The command needs a live Jamf Pro tenant, so the fixture-client tests are the runtime evidence for the aggregation. The rendered
--helpwas checked on the built binary.make lintwas not run. Thegolangci-linton the development machine is built with go1.26.2 whilego.modtargets 1.27.0, so it aborts at config load before it reads any file. CI installs its own version and is unaffected.🤖 Generated with Claude Code