feat: add multipart/form-data support to glab api via --form flag
What does this MR do and why?
Adds a --form flag to glab api that sends the request body as multipart/form-data instead of JSON. This enables file uploads to GitLab REST API endpoints that require a binary file stream, such as wiki attachments.
Previously, --field file=@path would read the file and JSON-encode its contents as a string, which caused a 400 Bad Request from these endpoints.
Usage
glab api --method POST projects/:fullpath/wikis/attachments \
--form "file=@./image.png" \
--form "branch=main"
Text fields and file fields can be freely mixed. Use @- to read a file from stdin.
Implementation notes
-
buildMultipartBodyinhttp.goconstructs themultipart/form-databody using Go's standardmime/multipartpackage, following the same pattern used bygitlab.com/gitlab-org/api/client-go/v2'sUploadRequest. The client-go method could not be reused directly because it targetsretryablehttp.Requestand only supports a single fixed-name file field. -
--formis mutually exclusive with--field,--raw-field, and--input. - When
--formis used without an explicit--method, the method defaults toPOST. - The
Content-Type: multipart/form-data; boundary=...header is set automatically.
Related issues
Closes #8229 (closed)
Screenshots or screen recordings
Tested:
./bin/glab api --method POST projects/34675721/wikis/attachments --form "file=@./docs/source/img/glab-logo.png" --form "branch=main"
{
"file_name": "glab-logo.png",
"file_path": "uploads/503af223ceed78bdc396d175990214b1/glab-logo.png",
"branch": "main",
"link": {
"url": "uploads/503af223ceed78bdc396d175990214b1/glab-logo.png",
"markdown": ""
}
}
How to set up and validate locally
# Upload a file to a wiki (requires a project with a wiki enabled)
glab api --method POST projects/:fullpath/wikis/attachments \
--form "file=@./myfile.png" \
--form "branch=main"
MR acceptance checklist
- This MR does not break existing behavior
-
Tests added for
buildMultipartBody(unit) and flag parsing / mutual exclusions -
Documentation regenerated via
make gen-docs
Edited by Kai Armstrong