ci: swap artifact upload to the mirrored action (issue 2270)
Desktop (Tauri) / Windows installer (cross-compiled) (push) Successful in 2m42s
Desktop (Tauri) / Tauri desktop (Linux) (push) Successful in 4m32s
Desktop (Tauri) / Update manifest (push) Successful in 3s

Both desktop upload steps used actions/upload-artifact@v3, which reports
success while Gitea stores the result in a format its v4-only artifact API
will never serve back — 110 artifacts on this repo are on disk, have valid
DB rows, and are invisible to the REST API, the web download route and the
MCP tools alike. Green jobs producing nothing retrievable.

Point both at bvandeusen/upload-artifact (pull mirror of the Forgejo
project's fork, GHES refusal disabled), pinned by SHA because the mirror
auto-syncs. Not actions/upload-artifact@v4: its isGhes() throws on the
hostname before opening a connection, so no server-side change reaches it.

Also drop continue-on-error and set if-no-files-found: error on both steps.
Between them, a failed or empty upload was reported as a green run — the
same silence that let this go unnoticed for a month.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-30 15:27:57 -04:00
co-authored by Claude Opus 5
parent 13e48672c0
commit 505904b1e5
2 changed files with 44 additions and 10 deletions
+17 -10
View File
@@ -154,20 +154,25 @@ jobs:
run: bash desktop/packaging/arch/package-prebuilt.sh run: bash desktop/packaging/arch/package-prebuilt.sh
# Make the built .deb + .AppImage downloadable from the run (for hand-testing). # Make the built .deb + .AppImage downloadable from the run (for hand-testing).
# continue-on-error: the Forgejo artifact backend may not be configured yet; a # Mirrored action, never actions/upload-artifact: @v4+ throws
# failed upload must not fail the build itself. # GHESNotSupportedError on the hostname before it connects, and @v3 uploads
# Forgejo doesn't support the v4 artifact protocol (@actions/artifact v2+), # something Gitea stores but will never serve back (it returns artifacts only
# so pin v3, which uses the older protocol the instance accepts. # through the v4 API, which filters on content_encoding='application/zip').
# Pinned by SHA — the mirror auto-syncs, so a moved upstream tag would
# silently change what runs. See Scribe issues 2255 / 2270.
# No continue-on-error: a swallowed upload failure is exactly how 110
# unreachable artifacts accumulated here unnoticed. Fail loudly instead.
- name: Upload bundles - name: Upload bundles
continue-on-error: true uses: https://git.fabledsword.com/bvandeusen/upload-artifact@cb8afe72b42edc798abfb8fcb556cf660d894245
uses: actions/upload-artifact@v3
with: with:
name: thoughtsync-linux name: thoughtsync-linux
path: | path: |
desktop/src-tauri/target/release/bundle/appimage/*.AppImage desktop/src-tauri/target/release/bundle/appimage/*.AppImage
desktop/src-tauri/target/release/bundle/deb/*.deb desktop/src-tauri/target/release/bundle/deb/*.deb
desktop/src-tauri/target/release/bundle/arch/*.pkg.tar.* desktop/src-tauri/target/release/bundle/arch/*.pkg.tar.*
if-no-files-found: warn # error, not warn: a build that bundles nothing should report as a
# failure, not as a green run with an empty artifact.
if-no-files-found: error
# Tag builds only: publish a real, versioned Fabled-Git Release with the # Tag builds only: publish a real, versioned Fabled-Git Release with the
# AppImage + .deb attached — the stable fetch target the install script and # AppImage + .deb attached — the stable fetch target the install script and
@@ -262,13 +267,15 @@ jobs:
--config "$updater" --config "$updater"
working-directory: desktop/src-tauri working-directory: desktop/src-tauri
# Mirrored action, never actions/upload-artifact — see the Linux job's
# Upload bundles step for the full reasoning. Pinned by SHA because the
# mirror auto-syncs.
- name: Upload installer - name: Upload installer
continue-on-error: true uses: https://git.fabledsword.com/bvandeusen/upload-artifact@cb8afe72b42edc798abfb8fcb556cf660d894245
uses: actions/upload-artifact@v3
with: with:
name: thoughtsync-windows name: thoughtsync-windows
path: desktop/src-tauri/target/x86_64-pc-windows-msvc/release/bundle/nsis/*.exe path: desktop/src-tauri/target/x86_64-pc-windows-msvc/release/bundle/nsis/*.exe
if-no-files-found: warn if-no-files-found: error
# Publishes to the SAME release as the Linux job. Safe to run twice: the # Publishes to the SAME release as the Linux job. Safe to run twice: the
# script reuses an existing release (409) and nullglob means each job uploads # script reuses an existing release (409) and nullglob means each job uploads
+27
View File
@@ -45,6 +45,33 @@ entirely on `ci-python:3.14`.
(family rule 46). (family rule 46).
- The production runtime `Dockerfile` tracks python:3.12 so test results stay - The production runtime `Dockerfile` tracks python:3.12 so test results stay
representative of the deployed image. representative of the deployed image.
- **Artifacts — use the mirrored upload action, never `actions/upload-artifact`.**
```yaml
uses: https://git.fabledsword.com/bvandeusen/upload-artifact@cb8afe72b42edc798abfb8fcb556cf660d894245
```
Upstream's `actions/upload-artifact@v4` cannot work against this instance and
no server-side change will help: its `isGhes()` rejects any hostname that isn't
`github.com` / `*.ghe.com` / `*.localhost` and throws before it opens a
connection, so the server is never asked what it supports. `@v3` is worse — it
reports success, and Gitea then serves artifacts back only through the v4 API
(`content_encoding = application/zip`), so a v3 upload is stored but invisible
to every retrieval path. A green job producing nothing retrievable.
`bvandeusen/upload-artifact` is our pull mirror of `forgejo/upload-artifact`
(the Forgejo project's fork, one commit on upstream v5.0.0 disabling that
check). Mirrored so CI depends on a commit we hold; pinned by SHA because the
mirror auto-syncs and a moved upstream tag would otherwise change what runs.
Both desktop upload steps also set `if-no-files-found: error` and carry **no**
`continue-on-error`. They previously had both defaults inverted, which is how
110 unreachable artifacts accumulated on this repo without anyone noticing —
the upload could fail or match nothing and the run still went green. Scribe
issues 2255 / 2270 have the full teardown.
Download: `GET /api/v1/repos/{owner}/{repo}/actions/runs/{run_id}/artifacts`
for the id (global run id, not the repo-scoped run number), then
`…/actions/artifacts/{id}/zip`. Note the workstation has no `unzip` — use
`python3 -m zipfile -e`.
## Desktop (Tauri) lane — separate workflow ## Desktop (Tauri) lane — separate workflow