Skip to content

fix(desktop): keep one local bundle format from destroying the rest - #5410

Merged
lidge-jun merged 1 commit into
devfrom
codex/260921-build-local-per-format
Sep 21, 2026
Merged

lidge-jun merged 1 commit into
devfrom
codex/260921-build-local-per-format

Conversation

@lidge-jun

@lidge-jun lidge-jun commented Sep 21, 2026 •

Copy link
Copy Markdown
Owner

Summary

On Linux, bun run build:local asked for appimage,deb in ONE tauri build invocation. When AppImage bundling fails — as it does on a stock GNOME workstation where linuxdeploy's own dependencies are missing — the invocation dies and the deb is never attempted: the contributor gets zero artifacts and an error that names a tool they never invoked (failed to run linuxdeploy, with linuxdeploy's diagnostics swallowed by the bundler). Observed on a real GNOME desktop; recorded in devlog/_plan/260921_app_runtime_ownership/120_install_verification.md.

Each format now builds in its own invocation, every requested format is attempted, and the run ends with a per-format summary next to the artifacts that did build; the exit code is non-zero when any requested format failed. A failing format is retried once with --verbose: at the bundler's default log level the error is a bare failed to run linuxdeploy, and the verbose pass is the branch where the tool's own stderr reaches the terminal, so the failure says why instead of naming a binary.

The release workflow builds its artifacts on its own runner image and is untouched.

Verification

Local checks: NOT RUN (lane rule: no bun test, typecheck, builds, installs, or runtime execution — static review plus hosted CI only). The regression tests are written but intentionally unrun locally; what they pin:

  • a failing format does not destroy the formats that build — with AppImage scripted to fail and deb to succeed, both formats are attempted, the summary reports appimage: FAILED AND deb: ok, the deb artifact path is printed, and the exit code is 1. Without the fix, the shared invocation never reaches the deb.
  • a failing format is retried verbosely — exactly one --verbose invocation, only for the failing format, so the bundler's debug-level stderr (linuxdeploy's own diagnostics) reaches the terminal.
  • stale bundle output is not reported: the artifact list is snapshotted before the build and only files created or updated during the run are printed, so a previous run's AppImage is not claimed as output of a failed attempt. Boundary cases are pinned too: spawn failures, null statuses, a successful verbose retry not flipping the recorded failure, exact invocation shape, extra argv forwarding, and the macOS format list.
  • summarizeAttempts decides the exit code from per-format outcomes, and the fully-successful path keeps the existing "updater artifacts skipped" note.

Mechanism evidence (static, read from the tauri-cli-v2.11.1 tag): crates/tauri-cli/src/bundle.rs maps verbosity 0 to bundler log level Error; crates/tauri-bundler/src/bundle/linux/appimage/linuxdeploy.rs runs the plain-output branch (bare failed to run linuxdeploy) only at Level::Error and output_ok() — which pipes the tool's stderr through debug logging — at anything higher; --verbose is a global clap arg (count), valid pre-subcommand.

A gpt-5.6-sol audit of this diff passed with two minor findings (stale artifact reporting, boundary test coverage); both are folded.

Hosted CI at exact head 9c9d592: all requested jobs green. 38 check-runs: every requested job succeeded — gates (typecheck + scans), test 1/4-4/4 (including the new "local bundle builds" tests), desktop shell (fmt/clippy/test), macos 1/2 + 2/2, macos widget + bundle, docker smoke, storage policy, api usage, keyring ubuntu/windows/macos, linux-systemd, windows-schtasks, both macos-launchd, enforce-target, hygiene, label, react-doctor, resolve-pr. Skipped by event scope, not counted as green: windows shards (workflow_dispatch-gated by recorded decision), macos control, npm-global, structure gate, docs site build. No failures, no cancellations.

Checklist

  • Scope stays focused and avoids unrelated cleanup.
  • Docs or release notes were updated when needed. (desktop/README.md's build:local section now describes the per-format behavior.)
  • Security-sensitive changes were reviewed for secrets, auth, and unsafe defaults. (No secrets; build tooling only; the verbose retry prints bundler diagnostics to the caller's own terminal.)

On Linux, bun run build:local asked for appimage,deb in ONE tauri
invocation. When AppImage bundling failed (linuxdeploy missing a host
dependency), the invocation died and the deb was never attempted — a
contributor following the README got zero artifacts and an error that
named a tool they never invoked (observed on a real GNOME desktop,
devlog plan 260921 / 120_install_verification.md).

Each format now builds in its own invocation, every format is
attempted, and the summary reports each outcome beside the artifacts
that did build; the exit code is non-zero when any requested format
failed. A failing format is retried once with --verbose: at the
bundler's default log level the error is a bare "failed to run
linuxdeploy" with the tool's stderr discarded, and the verbose pass is
the branch where those diagnostics reach the terminal.

The release workflow builds its artifacts on its own runner image and
is untouched.
@lidge-jun
lidge-jun requested a review from Ingwannu as a code owner September 21, 2026 04:59
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 21, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-09-21T05:02:53.351077Z 9c9d592 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@github-actions

Copy link
Copy Markdown
Contributor

✅ Deterministic PR hygiene checks passed.

@github-actions github-actions Bot added the bug Something isn't working label Sep 21, 2026
@coderabbitai

coderabbitai Bot commented Sep 21, 2026 •

Copy link
Copy Markdown
Contributor

Review Change StackReview Change Stack

Understand this PR’s impact

Explore downstream dependencies and potential security impact with Blast Radius.

View blast radius →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository: lidge-jun/opencodex/.coderabbit.yaml

Review profile: ASSERTIVE

Plan: Advanced

Run ID: 7db0f408-0c6e-4428-8734-5df5f66a1c1e

📥 Commits

Reviewing files that changed from the base of the PR and between bd4822b and 9c9d592.

📒 Files selected for processing (3)
  • desktop/README.md
  • desktop/scripts/build-local.ts
  • tests/ci-workflows/release-desktop-scripts.test.ts

Included review availability: Your plan provides up to 10 included reviews per hour; 6 remain after this review.


📝 Walkthrough

Walkthrough

The desktop local build now processes each bundle format separately, retries failed formats with --verbose, reports artifacts created or modified during the run, and returns failure when any format fails. Tests cover orchestration, platform formats, artifact filtering, and exit status handling.

Changes

Local bundle build orchestration

Layer / File(s) Summary
Per-format execution and result contracts
desktop/scripts/build-local.ts
The script adds dependency and result interfaces. It validates platforms inside runBuildLocal, invokes Tauri once per format, retries failed formats with --verbose, and preserves the original failure status.
Artifact reporting and CLI wiring
desktop/scripts/build-local.ts
Artifact paths and modification times are compared with the pre-build snapshot. The runner reports per-format results and connects the orchestration result to the process entry point.
Behavior validation and usage documentation
tests/ci-workflows/release-desktop-scripts.test.ts, desktop/README.md
Tests cover retries, continued format builds, artifact filtering, startup failures, argument forwarding, platform-specific formats, and summary exit codes. The README documents the revised command behavior.

Priority: ⬇️ Low

Estimated code review effort: 3 (Moderate) | ~25 minutes

Change: Bug fix

Sequence Diagram(s)

sequenceDiagram
  participant runBuildLocal
  participant TauriCLI
  participant ArtifactFilesystem
  participant summarizeAttempts
  runBuildLocal->>ArtifactFilesystem: snapshot existing artifacts
  runBuildLocal->>TauriCLI: build one bundle format
  TauriCLI-->>runBuildLocal: exit status
  runBuildLocal->>TauriCLI: retry failed format with --verbose
  TauriCLI-->>runBuildLocal: retry exit status
  runBuildLocal->>ArtifactFilesystem: find new or modified artifacts
  runBuildLocal->>summarizeAttempts: summarize format outcomes
  summarizeAttempts-->>runBuildLocal: lines and aggregate exit code
Loading

Merge Risk: ⚪ Minimal · up to 9c9d5

The local build command’s failure status and artifact reporting match the documented behavior, with no actionable merge-blocking issue identified.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 3 functions across 2 files. (1 skipped: 1 … Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: isolating local bundle format failures so one failed format does not prevent the remaining formats from building.
Full details: Docstring Coverage

Explanation

Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 3 functions across 2 files. (1 skipped: 1 unsupported.)

  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9c9d592e3b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

// Name what THIS run produced even when something failed: an error line at the end is
// the least visible place for artifacts that already built.
const produced = deps.listArtifacts().filter(
entry => !baseline.has(entry.path) || baseline.get(entry.path) !== entry.mtimeMs,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Stop using only mtime to identify produced bundles

On filesystems with coarse timestamp granularity, rebuilding an existing bundle within the same timestamp tick can leave mtimeMs unchanged, so this filter discards a successfully produced artifact and the new summary omits its path. This repository already documents container filesystems where write invalidation based on mtime is unreliable; report artifacts associated with successful format attempts, or use stronger creation/replacement evidence, rather than requiring the timestamp to change.

AGENTS.md reference: AGENTS.md:L313-L316

Useful? React with 👍 / 👎.

@lidge-jun

Copy link
Copy Markdown
Owner Author

리뷰 · 우선순위 48 / 80

리눅스에서 bun run build:local은 AppImage와 deb를 빌드 한 번에 같이 넘겼습니다. AppImage가 실패하면 그 한 번이 통째로 멈추고, 만들 수 있던 deb는 시작도 하지 않습니다. 그놈 데스크톱처럼 linuxdeploy에 필요한 파일이 없을 때 이런 일이 납니다. 기여자는 설치 파일을 하나도 못 받고, 화면에 나오는 이름은 자기가 직접 실행한 적 없는 linuxdeploy입니다.

이제는 형식을 하나씩 따로 빌드합니다. 하나가 실패해도 나머지는 시도하고, 마지막에 형식마다 성공인지 실패인지 적습니다. 실패한 형식은 --verbose로 한 번 더 돌려서, 숨겨져 있던 실패 이유를 터미널에 보여 줍니다. 하나라도 실패하면 종료 코드는 0이 아닙니다. 예전에 만들어 둔 파일을 이번 결과로 세지 않으려고, 빌드 전에 파일 경로와 수정 시각을 적어 둡니다. 릴리스용 빌드는 그대로입니다. 이 동작은 테스트로 고정해 두었고, 그 테스트는 로컬에서 돌리지 않았다고 PR 본문에 적혀 있습니다.

desktop/scripts/build-local.ts:122 - 이번 실행이 만든 파일인지는 수정 시각이 달라졌는지만 봅니다. 시각을 초 단위로만 적는 디스크에서는, 방금 다시 쓴 파일의 시각이 예전과 같아서 목록에서 빠집니다. 이 저장소는 컨테이너에서 같은 일이 난다고 이미 적어 두었습니다(AGENTS.md의 mtime 항목). macOS의 .app는 폴더라서, 안의 파일만 바뀌면 폴더 시각은 그대로일 수 있습니다. 성공한 파일이 요약에 안 나오면, 이 PR이 보여 주려던 결과가 다시 사라집니다.

desktop/scripts/build-local.ts:114 - 실패 뒤에 --verbose로 tauri build를 통째로 다시 돌리지만, 그 두 번째 결과는 기록하지 않습니다. 두 번째가 파일을 만들어도 요약은 FAILED로 남습니다. 그 파일의 수정 시각이 바뀌면 경로는 따로 찍히므로, 화면에는 파일 경로와 FAILED가 같이 나옵니다. 그 파일을 써도 되는지 알 수 없습니다. 테스트는 재시도가 성공해도 실패로 남긴다고 고정하는데, 그 가짜 빌드는 verbose 성공 때 파일을 만들지 않아서 이 경우는 검사하지 않습니다.

desktop/scripts/build-local.ts:103 - 형식마다 tauri build를 처음부터 다시 시작합니다. 리눅스에서 AppImage가 실패하는 흔한 경우는 실패 한 번, 진단용 재실행 한 번, 그다음 deb까지 세 번입니다. 둘 다 성공해도 예전 한 번이 두 번이 됩니다. macOS의 app/dmg, Windows의 msi/nsis도 같이 갈라집니다.

메인테이너의 판단이 필요한 지점

실패한 형식마다 빌드를 한 번 더 돌릴 가치가 있는지, 로컬 빌드는 첫 실행부터 이유를 보여 줄지. 두 번째 실행이 성공하면 성공으로 바꿀지, 첫 실패를 유지할지. 나누는 대상을 리눅스 AppImage와 deb만으로 둘지, macOS와 Windows도 같이 둘지. dmg는 보통 app가 있어야 만듭니다. 둘을 따로 띄우는 시간이 아깝지 않은지는 tauri가 각 호출 안에서 app를 다시 만드는지에 달려 있고, 그 부분은 이 저장소 코드만으로는 확인하지 않았습니다.

너의 추천

형식별로 나누는 수정은 남기는 쪽이 맞습니다. 한 형식의 실패가 나머지를 막던 것이 이 패치의 이유입니다. 합치기 전에 두 곳은 고치는 편이 좋습니다. 나온 파일은 수정 시각만으로 고르지 말고, 이번에 성공한 형식의 결과만 적으면 됩니다. 실패한 형식은 빌드를 통째로 다시 하지 말고, 첫 실행에서 이유가 보이게 하거나, 다시 돌려서 성공하면 그 성공을 남기면 됩니다. 릴리스 워크플로는 지금처럼 건드리지 않으면 됩니다.

이 댓글은 grok-bot이 작성했습니다

@lidge-jun
lidge-jun merged commit 28d88ab into dev Sep 21, 2026
43 checks passed
@lidge-jun
lidge-jun deleted the codex/260921-build-local-per-format branch September 21, 2026 06:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant