OXYGENHUB

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
· 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

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