feat: add user secrets file parser and shared validator (PLAT-240)#26723
Conversation
Add codersdk.ParseSecretsFile to parse .env/.json/.yaml secret files into CreateUserSecretRequests, plus codersdk.ValidateCreateUserSecretRequest as the shared per-entry validator. Both are transport-agnostic so the import endpoint and a future CLI can reuse them unchanged.
This stack of pull requests is managed by Graphite. Learn more about stacking. |
Add test entries for \r escape, unknown escape preservation, and export=foo key edge case in TestParseSecretsFileEnv.
|
There is the failing doc check, but a later PR in this stack is for the UI, I'll add docs there |
BobbyHo
left a comment
There was a problem hiding this comment.
Syntax-wise, the changes look good to me. I just left one minor comment. I don’t have the full context on this feature yet, so I’ll leave the final approval to reviewers who are more familiar with it.
|
Thanks for the review @BobbyHo |
| // ParseSecretsFile parses a secrets file into CreateUserSecretRequests. | ||
| // It checks structure and duplicate keys; per-entry validation is left to | ||
| // ValidateCreateUserSecretRequest. | ||
| func ParseSecretsFile(format SecretsFileFormat, content string) ([]CreateUserSecretRequest, error) { |
There was a problem hiding this comment.
Document the supported dotenv subset here: no $VAR expansion, no inline-comment stripping, no multi-line quoted values. Good strict choices for secrets — but worth stating why this is hand-rolled instead of joho/godotenv / hashicorp/go-envparse (dependency in codersdk, expansion footgun), so nobody "fixes" this into godotenv semantics later.
Coder Agents on behalf of @Emyrk.
There was a problem hiding this comment.
I'd prefer us using a lib to parse env files, but I'll defer that to you if you think we can maintain this easily.
There was a problem hiding this comment.
Yeah I've been somewhat punting on docs to the later PRs once the functionality is surfaced. I'll make sure this gets addressed in the second or third PR in this stack 👍
There was a problem hiding this comment.
Re importing was custom rolling, I believe the current implementation is correct: every Go dotenv lib either expands $VAR or strips inline #, both of which silently corrupt secret values, and they return maps so we lose order, line numbers, and dup detection. Wrapping one to undo that costs about as much code as the parser, plus a new dep in codersdk. Documented the subset and reasoning in the code so nobody "fixes" it into godotenv semantics later. Happy to revisit if you disagree 👍
| // ParseSecretsFile parses a secrets file into CreateUserSecretRequests. | ||
| // It checks structure and duplicate keys; per-entry validation is left to | ||
| // ValidateCreateUserSecretRequest. | ||
| func ParseSecretsFile(format SecretsFileFormat, content string) ([]CreateUserSecretRequest, error) { |
There was a problem hiding this comment.
I'd prefer us using a lib to parse env files, but I'll defer that to you if you think we can maintain this easily.

Part of the PLAT-240 bulk secret import stack: this PR adds the
codersdkparser and shared create-secret validator used by the follow-up batch endpoint and UI PRs.ParseSecretsFileparses.env,.json, and.yamlfiles intoCreateUserSecretRequestentries in source order, with size, count, duplicate-key, structure, and malformed-input checks.ValidateCreateUserSecretRequestnow backs the single-create handler too, so create validation has one SDK-level implementation.Part of https://linear.app/codercom/issue/PLAT-240