ci: upload the client build as an artifact (groundwork for PR previews) #7

Closed
opened 2026-10-12 09:32:30 +09:00 by bot-genji1024 · 2 comments
Collaborator

Problem

We want a preview of the client for every pull request. Forgejo (v16, and the docs for v17) has no workflow_run event (forgejo/forgejo#7722), so the usual GitHub pattern — build without privileges, deploy from a second workflow defined on the default branch — is not available. Deploying from a job on the koto-demo runner would let any pull request's workflow run on the host, because pull requests can choose that label.

The plan is to let the host fetch the previews instead: CI builds the client in its throwaway container as it already does, uploads client/dist as an artifact, and a timer on the host (outside this repository's workflows) downloads the artifacts of successful runs through the Actions artifacts API (/repos/{owner}/{repo}/actions/runs/{run_id}/artifacts, /actions/artifacts/{id}/zip) and serves them as static files.

Two things are unverified on this instance:

  1. Whether upload-artifact@v4 works with our runner (upstream v4 refuses to run outside github.com; Forgejo publishes a fork at code.forgejo.org/forgejo/upload-artifact@v4).
  2. Whether the artifact list and the zip download work without authentication on this public repository.

Plan

  • In .forgejo/workflows/ci.yml, after the build, upload client/dist as an artifact named client-dist with https://code.forgejo.org/forgejo/upload-artifact@v4.
  • The pull request for this issue is itself the test: check that its CI run produces the artifact, then list and download it through the API with and without a token, and report the result here.

No change to what runs on the host; the preview server itself is a later step.

## Problem We want a preview of the client for every pull request. Forgejo (v16, and the docs for v17) has no `workflow_run` event (forgejo/forgejo#7722), so the usual GitHub pattern — build without privileges, deploy from a second workflow defined on the default branch — is not available. Deploying from a job on the `koto-demo` runner would let any pull request's workflow run on the host, because pull requests can choose that label. The plan is to let the host fetch the previews instead: CI builds the client in its throwaway container as it already does, uploads `client/dist` as an artifact, and a timer on the host (outside this repository's workflows) downloads the artifacts of successful runs through the Actions artifacts API (`/repos/{owner}/{repo}/actions/runs/{run_id}/artifacts`, `/actions/artifacts/{id}/zip`) and serves them as static files. Two things are unverified on this instance: 1. Whether `upload-artifact@v4` works with our runner (upstream v4 refuses to run outside github.com; Forgejo publishes a fork at `code.forgejo.org/forgejo/upload-artifact@v4`). 2. Whether the artifact list and the zip download work without authentication on this public repository. ## Plan - In `.forgejo/workflows/ci.yml`, after the build, upload `client/dist` as an artifact named `client-dist` with `https://code.forgejo.org/forgejo/upload-artifact@v4`. - The pull request for this issue is itself the test: check that its CI run produces the artifact, then list and download it through the API with and without a token, and report the result here. No change to what runs on the host; the preview server itself is a later step.
Author
Collaborator

First result from #8 (run 185): blocked by the reverse proxy in front of Forgejo, not by Forgejo or the action.

  • https://code.forgejo.org/forgejo/upload-artifact@v4 runs on our runner: it validated the name and path (12 files from client/dist) and started the upload.
  • The upload then failed with 413 Request Entity Too Large from nginx/1.30.4, i.e. the origin's nginx in front of Forgejo (Cloudflare would answer with its own page and allows 100 MB). nginx defaults client_max_body_size to 1 MB; the build includes a 7.8 MB wasm file, and the artifact client uploads in chunks of several MB.
  • So nothing was stored, and the anonymous download could not be tested yet. Without a token, GET /actions/runs/185/artifacts answers 200 [] and GET /actions/artifacts/1/zip answers 404 (not 401), which suggests anonymous reads are allowed on this public repository, but that needs a real artifact to confirm.

Next step: raise client_max_body_size on that nginx (at least for Forgejo's Actions artifact upload paths, e.g. 64m), then re-run the CI of #8.

First result from #8 (run 185): **blocked by the reverse proxy in front of Forgejo, not by Forgejo or the action.** - `https://code.forgejo.org/forgejo/upload-artifact@v4` runs on our runner: it validated the name and path (12 files from `client/dist`) and started the upload. - The upload then failed with `413 Request Entity Too Large` from `nginx/1.30.4`, i.e. the origin's nginx in front of Forgejo (Cloudflare would answer with its own page and allows 100 MB). nginx defaults `client_max_body_size` to 1 MB; the build includes a 7.8 MB wasm file, and the artifact client uploads in chunks of several MB. - So nothing was stored, and the anonymous download could not be tested yet. Without a token, `GET /actions/runs/185/artifacts` answers 200 `[]` and `GET /actions/artifacts/1/zip` answers 404 (not 401), which suggests anonymous reads are allowed on this public repository, but that needs a real artifact to confirm. Next step: raise `client_max_body_size` on that nginx (at least for Forgejo's Actions artifact upload paths, e.g. `64m`), then re-run the CI of #8.
Author
Collaborator

With the proxy's body size raised, the re-run of run 185 (#8) passed. Both open points are confirmed:

  1. Upload works. client-dist (artifact 5, 2,610,426 bytes) holds the 12 files of client/dist: index.html, config.json, sw.js, manifest.webmanifest, the icons and the hashed assets, including the 7.8 MB wasm.
  2. Anonymous reads work on this public repository. Without a token, GET /actions/artifacts and GET /actions/runs/{id}/artifacts list it, and GET /actions/artifacts/5/zip returns 200 with the zip (application/octet-stream).

Notes for the host-side fetcher:

  • A run links to its pull request through prettyref (#8), commit_sha and event_payload, and status is success only when every step passed.
  • A re-run keeps the same run id and replaces the artifact in place (updated_at changes, the id does not), so the fetcher has to compare updated_at or the commit, not just the id.
  • Artifacts expire after 90 days (expires_at).
  • If the repository ever becomes private, the fetcher needs a read-only token.
With the proxy's body size raised, the re-run of run 185 (#8) passed. **Both open points are confirmed:** 1. **Upload works.** `client-dist` (artifact 5, 2,610,426 bytes) holds the 12 files of `client/dist`: `index.html`, `config.json`, `sw.js`, `manifest.webmanifest`, the icons and the hashed assets, including the 7.8 MB wasm. 2. **Anonymous reads work** on this public repository. Without a token, `GET /actions/artifacts` and `GET /actions/runs/{id}/artifacts` list it, and `GET /actions/artifacts/5/zip` returns `200` with the zip (`application/octet-stream`). Notes for the host-side fetcher: - A run links to its pull request through `prettyref` (`#8`), `commit_sha` and `event_payload`, and `status` is `success` only when every step passed. - A re-run keeps the same run id and replaces the artifact in place (`updated_at` changes, the id does not), so the fetcher has to compare `updated_at` or the commit, not just the id. - Artifacts expire after 90 days (`expires_at`). - If the repository ever becomes private, the fetcher needs a read-only token.
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
genji1024/koto#7
No description provided.