feat(classic): accept --from-file on create and update - #343
Merged
Conversation
Classic create and update read their XML body from --from-file, falling back to stdin when the flag is absent. Both routes go through one new helper, readClassicBody, which tolerates an absent body so the file-field flags (--mobileconfig-file, --script-file, --custom-payload-file) can still supply one on their own. Stdin alone left these commands unreachable from the callers that most want them. AutoPkg's JamfCLIRunner supplies a body either as --from-file or as a `data` dict it serialises to JSON; a JSON body on a /JSSResource path is sent with Content-Type: application/xml and refused. So a classic resource with no name-resolution collection — classic-computer-invitations and its kin, which get no `apply` — had no file-based route at all. The body can carry credentials (an SMTP or LDAP account password), which is why this is a file path and not a flag value. needsBytes now covers every create/update, since both send bytes.NewReader over the body they read; needsOS narrows to the delete and apply paths, which are all that still reach os directly. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
neilmartin83
enabled auto-merge
August 26, 2026 18:49
grahampugh
approved these changes
Aug 27, 2026
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.
Problem
Classic
createandupdateread their XML body from stdin only. That makes them unusable from AutoPkg'sJamfCLIRunnerprocessor, which supplies a body one of two ways:from_file/body_file→ a flag (--from-file), which classic create/update did not accept → cobra exits 2, unknown flag.data→ a dictjson.dumps'd to stdin. That path reaches classic create, but the body is JSON whileinternal/client/client.gostampsContent-Type: application/xmlon every/JSSResourcewrite, so the server refuses it.Resources with no name-resolution collection get no
apply(which has always taken--from-file), so for those —classic-computer-invitations,classic-smtp-server, and friends — there was no file-based route at all.Change
createandupdatenow take--from-file <path>, falling back to stdin when the flag is absent. One helper does the reading:It deliberately tolerates an absent body and returns
nil, nil, leaving each caller to decide whether that is fatal — the file-field flags (--mobileconfig-file,--script-file,--custom-payload-file) can supply a body on their own, so the config-profile and script creates must still accept an empty one. Where a body genuinely is required, the error now names both routes instead of only stdin.The body can carry credentials — an SMTP or LDAP account password — which is why this is a file path and not a flag value, per the credential policy at the top of
CLAUDE.md.Template-side bookkeeping that came with it:
needsByteswidened to every create/update, since both now sendbytes.NewReaderover the body they read.needsOSnarrowed to the delete and apply paths — the only ones still touchingosdirectly, now that body reading lives in the registry. Without this,classic-smtp-serverandclassic-gsx-connection(get+update singletons, no delete) failed to compile with"os" imported and not used.Usage
Stdin still works exactly as before;
--from-filewins when both are present, matchingapply.Tests
internal/commands/pro/generated/classic_from_file_test.go—readClassicBodyunit coverage (file read, file-wins-over-stdin, unreadable file names the flag, absent body is not an error) plus four end-to-end runs through the realRunEagainst a recording client:classic-computer-invitations create --from-filePOSTs the file verbatim (the resource with noapply, i.e. the case that was unreachable).createwith neither body nor flag errors and sends nothing, with--from-filenamed in the message.classic-smtp-server update 1 --from-filePUTs the file verbatim.classic-macos-config-profiles create --from-file— the other create shape, where a file field is injected into the supplied XML.Every one of these parks data on stdin without closing the writer, so a regression that wrongly reads stdin blocks and times out rather than passing quietly.
generator/classic/generator_test.gogainsTestGenerate_CreateAndUpdateTakeFromFile, asserting the template emits exactly two body--from-fileregistrations (create and update —delete's--from-fileis a list of IDs, not a body) and that the stdin-only error text is gone.make test,make lint,make verify-generatedall clean.🤖 Generated with Claude Code