Skip to content

Nightly R2 appcasts are served as application/x-www-form-urlencoded instead of XML #12280

Description

@austinywang

Bug description

Every nightly appcast served from files.cmux.com currently advertises this response header:

content-type: application/x-www-form-urlencoded

These are Sparkle XML/RSS documents and should be served as application/xml (or another appropriate XML media type). The GitHub release assets are recorded as application/xml; the bad metadata is on the R2 copies used by installed apps.

This is not the cause of the updater stall in #12279—Sparkle successfully parsed the feed there—but it is a release-pipeline defect that can cause strict clients, gateways, content sniffing, or future Sparkle behavior to mishandle the feed.

Reproduction

for feed in appcast.xml appcast-arm64.xml appcast-x86_64.xml appcast-universal.xml; do
  curl -sSI "https://files.cmux.com/nightly/$feed" \
    | grep -iE '^(HTTP/|content-type:|content-length:|last-modified:)'
done

Observed 2026-09-10 after successful nightly run https://github.com/manaflow-ai/cmux/actions/runs/34536136724:

appcast.xml
HTTP/2 200
content-type: application/x-www-form-urlencoded
content-length: 3634
last-modified: Thu, 10 Sep 2026 22:25:53 GMT

appcast-arm64.xml
HTTP/2 200
content-type: application/x-www-form-urlencoded
content-length: 3609
last-modified: Thu, 10 Sep 2026 22:25:52 GMT

appcast-x86_64.xml
HTTP/2 200
content-type: application/x-www-form-urlencoded
content-length: 3614
last-modified: Thu, 10 Sep 2026 22:25:53 GMT

appcast-universal.xml
HTTP/2 200
content-type: application/x-www-form-urlencoded
content-length: 3634
last-modified: Thu, 10 Sep 2026 22:25:53 GMT

All four documents are well-formed enough for Sparkle to load; the defect is their stored HTTP metadata.

Root cause

.github/workflows/nightly.yml uploads each feed through:

python3 scripts/ci/upload-r2-object.py --file ... --key nightly/<appcast> ...

In scripts/ci/upload-r2-object.py, _build_signed_request adds cache-control, x-amz-content-sha256, and x-amz-date, but no content-type. For a PUT with a byte body, urllib.request.Request supplies Content-Type: application/x-www-form-urlencoded; R2 stores that metadata and Cloudflare returns it to clients.

The recent per-architecture nightly work expanded the upload loop to all four feeds, so the same bad default is now applied consistently to the universal, arm64, and x86_64 tracks.

Expected behavior

  • XML appcasts are uploaded with an explicitly signed/stored Content-Type: application/xml (or application/rss+xml).
  • The uploader either accepts an explicit --content-type or safely infers it from the file extension; callers for non-XML immutable objects can specify their own type.
  • Dry-run/request tests assert the Content-Type header and its inclusion in SigV4 signed headers.
  • The four current nightly R2 keys are republished so a HEAD request reports the correct media type.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions