--attach stack 5/8: Add the flag and upload behind it - #14181
Conversation
--attach stack 5/8: Add the flag and upload behind it
There was a problem hiding this comment.
Pull request overview
Adds internal --attach plumbing for validated image/video uploads and Markdown rewriting. No command exposes the flag yet.
Changes:
- Parses, validates, and deduplicates attachments.
- Uploads assets with partial-failure handling.
- Adds comprehensive attachment tests and helpers.
Show a summary per file
| File | Description |
|---|---|
internal/attachments/flags.go |
Implements flag parsing and validation. |
internal/attachments/flags_test.go |
Tests flag behavior and conflicts. |
internal/attachments/attach.go |
Coordinates uploads and Markdown updates. |
internal/attachments/attach_test.go |
Tests upload and rewriting flows. |
internal/attachments/test.go |
Provides shared attachment test helpers. |
Review details
💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
- Files reviewed: 5/5 changed files
- Comments generated: 5
- Review effort level: Balanced
962970d to
95efd19
Compare
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
95efd19 to
a3b137e
Compare
babakks
left a comment
There was a problem hiding this comment.
Thanks for this one, @BagToad! 🙏
The orchestration in UploadAndAttach is nicely reasoned, especially stopping at the first upload failure and returning the partial markdown plus an uploaded count so we never orphan an asset that already reached the server. The parseArg splitter handling # in filenames by probing the longest existing path is clever, and the coverage across both test files is great.
My main piece of feedback is about a design concern rather than a bug, so I'm approving to keep the stack moving with the understanding that it lands in a follow-up:
- Flag-wise intimacy between
attachmentsand command packages. I'd like us to fix the coupling where this package reaches into command-owned flags.checkFlagConflictsinspects--web,--delete-last, and--dry-run, which the package has no business knowing about, andFromFlagValueslooks the flag up by name. I left a sketch of a design that encapsulates the flag in anAttachFlagtype returned from anAddAttachFlag(cmd, name, shorthand)constructor, withFromFlagValuesbecoming a method on it. The commands would then own their own mut-ex checks (even at the cost of a little duplication).
Smaller stuff in the inline comments: an os.Lstat swap (with a stubbable indirection) to sidestep symlink resolution, a refs to args rename to avoid clashing with attachmentRef, reordering the empty-addition check in appendParagraph, and a help-text nitpick.
Nice work! 🚀
Part of the pull request stack tracked in #14186.
Description
This is the fifth layer of the stack. The first commit adds the flag and reads its values. The second ties uploading to rewriting and decides when the resulting markdown is safe to write. No command offers the flag yet.
The flag is repeatable and takes a file path, optionally followed by
#and alt text. Without alt text the file name is used. Every named file is validated before anything is uploaded.Naming the same file twice is refused, and that includes a file and a symlink to it, and a file and a hard link to it, since both would upload the same bytes twice. An empty value is refused too, which is what a script produces when a variable is unset.
Conflicting flags are checked while the flag is being read, rather than in each command, so a command that offers the flag cannot forget to check them.
The flag is registered as a string array rather than a string slice, because a string slice splits on commas and a comma is legal in a file name:
How did you test this change?
The whole stack was built and the failure path was exercised by hand against a private test repository. Three files were attached with the middle one made unreadable. Exactly one upload request left the machine for three attached files, the command exited non zero and named the file that failed, and the comment was still posted carrying the one file that did upload.
When the only attached file failed, nothing was posted at all and the command said so.
Duplicate detection was exercised with a plain duplicate, with a symlink to the same file, and with a hard link to it.
Key points
An upload cannot be undone and there is no endpoint to delete an uploaded asset. That single fact drives the design of the second commit.
The function reports how many assets reached the server, and a caller writes the body only when that count is above zero. Markdown that does not reference an uploaded asset would orphan that asset for good. A count of zero means nothing was uploaded, so nothing is lost by writing nothing.
Uploading stops at the first failure and nothing after it is attempted. A reference to a file that did not upload keeps its local path, so a partial failure leaves a body containing links that do not resolve. That is the accepted cost of writing a body that already carries a real uploaded asset, and it is a thing a user will see.
Notes for reviewers
The count returned by the upload function is the whole safety mechanism. Read that first, then read the callers in the layers above to see how it is used.
Authorship and follow-up
Who wrote this:
Who answers review comments: