Release assets through the API up to the asset limit 5533e91a2aa4
The API's asset upload had the web framework's default 2 MiB body limit,
so oh release upload refused ordinary builds with 413; it now has the
configured asset limit (512 MiB), as the web form does.
closes #66
Author Andy McCall @andymccall <[email protected] >
When 1 month ago · 2026-09-30 18:57 UTC
Signature ✔ signed by Andy McCall
Hash 5533e91a2aa4e0b015aa3903b01dd0678175b99abe81002f0cc517e1159e3fa6
Depends on 3f4b0ea6eabf 90326c9601f9 b6b7e4cf6b24 d23464c0ac60
Depended on by 93f445fa50d8 166f6e9bf4f1 17a3a84b00d1 ced6d3afc5cf
In bookmarks api-unknown-fields cancel-landed-runs clear-waiting close-proposal-140 diffusion-pin-bookmark draft-ready image-check job-waits main oh-comment-body-file proposal-list-fast run-one-job wiki-says-so
In tags v0.2.0 v0.3.0 v0.3.1 v0.4.0 v0.4.1 v0.4.2 v0.5.0 v0.5.1 v0.5.2 v0.6.0 v0.6.1 v0.7.0 v0.8.0 v0.9.0 v0.9.1 v0.9.11 v0.9.15 v0.9.16 v0.9.19 v0.9.2 v0.9.20 v0.9.21 v0.9.22 v0.9.23 v0.9.24 v0.9.3 v0.9.4 v0.9.5 v0.9.6 v0.9.7 v0.9.8 v0.9.9
Edits 6 atoms, 0 tree operations, 3 files
Browse the tree at this change
crates/hub/src/api/mod.rs crates/hub/src/web/mod.rs crates/hub/tests/releases.rs
modified
crates/hub/src/api/mod.rs
+7 −2
…
21 unchanged lines
22
22
ApiError(StatusCode::NOT_FOUND, "no such API route".into())
23
23
}
24
24
25
-
pub fn router() -> Router<Shared> {
25
+
/// The API, with release assets up to `asset_limit` bytes (the web form's
26
+
/// limit too; axum's default of 2 MiB refused ordinary builds).
27
+
pub fn router(asset_limit: usize) -> Router<Shared> {
26
28
Router::new()
27
29
.fallback(no_route)
28
30
.route("/flow/claim", post(crate::flow::claim))
…
52 unchanged lines
81
83
.route("/repos/{owner}/{name}/checks/required", get(required_checks).put(set_required_checks))
82
84
.route("/repos/{owner}/{name}/checks/{state}", get(checks_for_state))
83
85
.route("/repos/{owner}/{name}/releases", get(releases).post(create_release))
84
-
.route("/repos/{owner}/{name}/releases/{tag}/assets", post(upload_asset))
86
+
.route(
87
+
"/repos/{owner}/{name}/releases/{tag}/assets",
88
+
post(upload_asset).layer(axum::extract::DefaultBodyLimit::max(asset_limit + (1 << 20))),
89
+
)
85
90
}
86
91
87
92
async fn releases(
…
1582 unchanged lines
modified
crates/hub/src/web/mod.rs
+1 −1
…
94 unchanged lines
95
95
.route("/admin/runners/{id}", get(runners::show))
96
96
.route("/admin/runners/{id}/state", post(runners::set_state))
97
97
.route("/admin/runners/{id}/remove", post(runners::remove))
98
-
.nest("/api/v1", crate::api::router())
98
+
.nest("/api/v1", crate::api::router(app.config.limits.asset_limit.0 as usize ))
99
99
.route("/{owner}", get(home::owner))
100
100
.route("/{owner}/{name}", get(repo::overview))
101
101
.route("/{owner}/{name}/tree/{r}", get(repo::tree_root))
…
496 unchanged lines
modified
crates/hub/tests/releases.rs
+14 −0
…
190 unchanged lines
191
191
assert_eq!(v["assets"][0]["hash"], hex::encode(blake3::hash(b"\x7fELF binary").as_bytes()));
192
192
let got = send(app, "GET", &url, None, None, None, vec![]).await;
193
193
assert_eq!(got.body, b"\x7fELF binary");
194
+
// A real build is megabytes: the API takes assets up to the configured
195
+
// limit, not the web framework's 2 MiB default.
196
+
let big = vec![7u8; 5 << 20];
197
+
let r = send(
198
+
app,
199
+
"POST",
200
+
"/api/v1/repos/andy/demo/releases/v0.2/assets?name=demo-big.tar.gz",
201
+
None,
202
+
Some("site"),
203
+
Some("application/gzip"),
204
+
big.clone(),
205
+
)
206
+
.await;
207
+
assert_eq!(r.status, StatusCode::CREATED, "{}", r.text());
194
208
let list = send(app, "GET", "/andy/demo/releases", None, None, None, vec![]).await.text();
195
209
assert!(list.contains("Zero two") && list.contains("demo-linux.bin") && list.contains("latest"), "{list}");
196
210
…
131 unchanged lines