Skip to content

fix(app): handle overlapping update and settings requests - #2184

Open
4gray wants to merge 1 commit into
masterfrom
codex/fix-update-and-settings-races
Open

4gray wants to merge 1 commit into
masterfrom
codex/fix-update-and-settings-races

Conversation

@4gray

@4gray 4gray commented Nov 10, 2026

Copy link
Copy Markdown
Owner

Saving an update channel while a check is running now queues a check for the latest saved channel and discards superseded availability results. Electron main accepts one installation attempt until the updater reports a failure.

Settings navigation and native window close share one unsaved-changes decision, including the save it triggers. An intercepted close takes precedence over navigation.

Validation

  • Regression tests fail on the base revision for duplicate installer calls, a channel change during a check, and overlapping settings exit dialogs.
  • 127 targeted Electron unit tests and 156 targeted web unit tests pass.
  • Seven Electron E2E scenarios pass on macOS, including competing exits with Stay and Save, persisted settings after restart, both update entry points, and renderer reload recovery.
  • Application and affected spec type checks, changed-file ESLint, agent guidance validation, and release-note validation pass. ESLint retains an existing unused-parameter warning in an unchanged test.
  • Real signed-update installation remains outside local validation. Update tests use a synthetic updater.

Local review

  • Local Codex review of 9ef9d10e5 against origin/master finished with no actionable findings before the first push.
  • Greptile CLI is unavailable in this environment. The repository's local-review policy allows the available reviewer to proceed in that case.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Nov 10, 2026 •

Copy link
Copy Markdown
Codex Review Summary: ✅ Completed

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

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-11-10T22:12:08.263935Z 9ef9d10 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.

@greptile-apps

greptile-apps Bot commented Nov 10, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerView in GreptileConfidence Score: 5/5

[Medium impact] The PR appears safe to merge; no actionable issues were found.

Summary

The PR coordinates overlapping update requests and settings exits.

  • Update checks follow the latest saved channel when the setting changes.
  • The updater starts only one installation attempt at a time.
  • Settings shares one unsaved-changes decision across exits.

Diagram

sequenceDiagram
    participant Router
    participant Guard as Settings unload guard
    participant Settings
    participant Main as Electron main
    Router->>Guard: Confirm navigation
    Guard->>Settings: Ask Save, Discard, or Stay
    Main->>Guard: Native close requested
    Note over Guard,Settings: Reuse the pending decision and save
    Settings-->>Guard: Decision finishes
    Guard-->>Router: Cancel navigation
    alt Saved or discarded
        Guard->>Main: Confirm close
    else Stayed or save failed
        Guard->>Main: Cancel close
    end
Loading

Reviews (1) · Last reviewed commit: "fix(app): serialize update checks and se..." · Reviewed by Greptile

@codecov

codecov Bot commented Nov 10, 2026

Copy link
Copy Markdown

Bundle Report

Changes will increase total bundle size by 472 bytes (0.0%) ⬆️. This is within the configured threshold ✅

Detailed changes
Bundle name Size Change
iptvnator-renderer 17.23MB 472 bytes (0.0%) ⬆️

Affected Assets, Files, and Routes:

view changes for bundle: iptvnator-renderer

Assets Changed:

Asset Name Size Change Total Size Change (%)
chunk-DaS1QNh6.js (New) 2.64MB 2.64MB 100.0% 🚀
chunk-DKqH0Rm8.js (New) 858.69kB 858.69kB 100.0% 🚀
chunk-BeI2ngu0.js (New) 334.63kB 334.63kB 100.0% 🚀
chunk-BrEh_54Y.js (New) 278.61kB 278.61kB 100.0% 🚀
main-7DMKGLTG.js (New) 276.02kB 276.02kB 100.0% 🚀
chunk-CndNhKrK.js (New) 221.49kB 221.49kB 100.0% 🚀
chunk-CSvNfof7.js (New) 208.08kB 208.08kB 100.0% 🚀
chunk-DT0-6o76.js (New) 187.31kB 187.31kB 100.0% 🚀
chunk-BdRLVP6G.js (New) 169.8kB 169.8kB 100.0% 🚀
chunk-HMmY4e_k.js (New) 165.56kB 165.56kB 100.0% 🚀
chunk-wXUlLwro.js (New) 159.92kB 159.92kB 100.0% 🚀
chunk-DHKDF38V.js (New) 149.58kB 149.58kB 100.0% 🚀
chunk-BMQASq0M.js (New) 145.09kB 145.09kB 100.0% 🚀
chunk-CuRBr1lV.js (New) 90.38kB 90.38kB 100.0% 🚀
chunk-CnxD3TF3.js (New) 73.51kB 73.51kB 100.0% 🚀
chunk-K0CglVHP.js (New) 70.17kB 70.17kB 100.0% 🚀
chunk-nu_Gd77f.js (New) 64.19kB 64.19kB 100.0% 🚀
chunk-D7QODt-R.js (New) 59.82kB 59.82kB 100.0% 🚀
chunk-S_7toCAM.js (New) 59.5kB 59.5kB 100.0% 🚀
chunk-C4BZW-o9.js (New) 54.16kB 54.16kB 100.0% 🚀
chunk-DxUtFJdD.js (New) 52.44kB 52.44kB 100.0% 🚀
chunk-DWttGoK6.js (New) 49.67kB 49.67kB 100.0% 🚀
chunk-Df5Qfd9n.js (New) 46.2kB 46.2kB 100.0% 🚀
chunk-Cy6TxtCt.js (New) 43.14kB 43.14kB 100.0% 🚀
chunk-oxnHbr33.js (New) 40.19kB 40.19kB 100.0% 🚀
chunk-BDzNsj7u.js (New) 35.04kB 35.04kB 100.0% 🚀
chunk-BxvuUV_J.js (New) 34.73kB 34.73kB 100.0% 🚀
chunk-jI3sr7eM.js (New) 30.19kB 30.19kB 100.0% 🚀
chunk-04B90QSI.js (New) 30.09kB 30.09kB 100.0% 🚀
chunk-DHNmdIzO.js (New) 24.85kB 24.85kB 100.0% 🚀
chunk-BFyKSN_Y.js (New) 22.29kB 22.29kB 100.0% 🚀
chunk-CP4vqbmV.js (New) 21.5kB 21.5kB 100.0% 🚀
chunk-CvVy0FAJ.js (New) 19.8kB 19.8kB 100.0% 🚀
chunk-CqYoOpBu.js (New) 19.55kB 19.55kB 100.0% 🚀
chunk-BtBQgc4d.js (New) 18.05kB 18.05kB 100.0% 🚀
chunk-_P260cIy.js (New) 17.9kB 17.9kB 100.0% 🚀
chunk-Bh31CUex.js (New) 16.86kB 16.86kB 100.0% 🚀
chunk-CBNQ10wL.js (New) 15.62kB 15.62kB 100.0% 🚀
chunk-AuHmP6-k.js (New) 15.39kB 15.39kB 100.0% 🚀
chunk-COQa_W5B.js (New) 13.29kB 13.29kB 100.0% 🚀
chunk-C8nwfXPa.js (New) 12.53kB 12.53kB 100.0% 🚀
chunk-Cn38Sdz_.js (New) 12.11kB 12.11kB 100.0% 🚀
chunk-BLOvOljw.js (New) 11.04kB 11.04kB 100.0% 🚀
chunk-BgCNE6q7.js (New) 10.62kB 10.62kB 100.0% 🚀
chunk-B-bUIlgT.js (New) 9.52kB 9.52kB 100.0% 🚀
chunk-LjEK-ns9.js (New) 8.84kB 8.84kB 100.0% 🚀
chunk-DZuMp01W.js (New) 7.34kB 7.34kB 100.0% 🚀
chunk-CjQTumgV.js (New) 5.63kB 5.63kB 100.0% 🚀
chunk-CT0YhChF.js (New) 5.43kB 5.43kB 100.0% 🚀
chunk-NvKZrEoL.js (New) 5.13kB 5.13kB 100.0% 🚀
chunk-BEgr6zsq.js (New) 4.87kB 4.87kB 100.0% 🚀
chunk-CBqZP6gO.js (New) 4.68kB 4.68kB 100.0% 🚀
chunk-BvL5lUeQ.js (New) 4.42kB 4.42kB 100.0% 🚀
chunk-tduoysko.js (New) 4.41kB 4.41kB 100.0% 🚀
chunk-Cuxue2Ej.js (New) 3.88kB 3.88kB 100.0% 🚀
chunk-9yJVDcYf.js (New) 3.61kB 3.61kB 100.0% 🚀
chunk-C4bjRTei.js (New) 2.85kB 2.85kB 100.0% 🚀
chunk-DkTZIfiL.js (New) 2.83kB 2.83kB 100.0% 🚀
chunk-v6UsFC0X.js (New) 2.28kB 2.28kB 100.0% 🚀
chunk-Dg-lMQvb.js (New) 2.24kB 2.24kB 100.0% 🚀
chunk-nyMZ-n_5.js (New) 2.08kB 2.08kB 100.0% 🚀
chunk-u7mb8JWQ.js (New) 2.02kB 2.02kB 100.0% 🚀
chunk-DUBUfVJ8.js (New) 1.81kB 1.81kB 100.0% 🚀
chunk-vsPzprrE.js (New) 1.8kB 1.8kB 100.0% 🚀
chunk-ptKC1dYQ.js (New) 1.71kB 1.71kB 100.0% 🚀
chunk-DhyKjUtM.js (New) 1.69kB 1.69kB 100.0% 🚀
chunk-DZ80WwJ1.js (New) 1.68kB 1.68kB 100.0% 🚀
chunk-*.js (New) 1.68kB 1.68kB 100.0% 🚀
chunk-Czoo5-tP.js (New) 1.67kB 1.67kB 100.0% 🚀
chunk-BMmNgOW1.js (New) 1.67kB 1.67kB 100.0% 🚀
chunk-FWkk5zfz.js (New) 1.51kB 1.51kB 100.0% 🚀
chunk-DsfNmpTW.js (New) 1.51kB 1.51kB 100.0% 🚀
chunk-BL1wWQnt.js (New) 1.51kB 1.51kB 100.0% 🚀
chunk-8XT-0GZo.js (New) 1.5kB 1.5kB 100.0% 🚀
chunk-Bd1-W3gt.js (New) 1.44kB 1.44kB 100.0% 🚀
chunk-BV4Zv0Ky.js (New) 1.43kB 1.43kB 100.0% 🚀
chunk-e8GH-NHs.js (New) 1.33kB 1.33kB 100.0% 🚀
chunk-Bikbtcv6.js (New) 731 bytes 731 bytes 100.0% 🚀
chunk-D9MhINSc.js (New) 730 bytes 730 bytes 100.0% 🚀
chunk-xuUKPpxh.js (New) 637 bytes 637 bytes 100.0% 🚀
chunk-DpW2pzYk.js (New) 588 bytes 588 bytes 100.0% 🚀
chunk-vcbv9ARO.js (New) 477 bytes 477 bytes 100.0% 🚀
chunk-S2NrCaKs.js (New) 443 bytes 443 bytes 100.0% 🚀
chunk-BzPAp2n5.js (New) 437 bytes 437 bytes 100.0% 🚀
chunk-CUbO0rAZ.js (New) 437 bytes 437 bytes 100.0% 🚀
chunk-Fkh_WMxz.js (New) 383 bytes 383 bytes 100.0% 🚀
chunk-Do0mj-CN.js (New) 242 bytes 242 bytes 100.0% 🚀
chunk-q1zk87z1.js (New) 238 bytes 238 bytes 100.0% 🚀
chunk-DBth6i4K.js (Deleted) -2.64MB 0 bytes -100.0% 🗑️
chunk-1VxRarwT.js (Deleted) -858.69kB 0 bytes -100.0% 🗑️
chunk-Dilptbds.js (Deleted) -334.16kB 0 bytes -100.0% 🗑️
chunk-BJ_cTbJ5.js (Deleted) -278.61kB 0 bytes -100.0% 🗑️
main-52I2TAGH.js (Deleted) -276.02kB 0 bytes -100.0% 🗑️
chunk-su_JhSDI.js (Deleted) -221.49kB 0 bytes -100.0% 🗑️
chunk-CoAQkwID.js (Deleted) -208.08kB 0 bytes -100.0% 🗑️
chunk-BkA7n47B.js (Deleted) -187.31kB 0 bytes -100.0% 🗑️
chunk-BRvws5qU.js (Deleted) -169.8kB 0 bytes -100.0% 🗑️
chunk-Cw5H9OEd.js (Deleted) -165.56kB 0 bytes -100.0% 🗑️
chunk-FA-mJK4A.js (Deleted) -159.92kB 0 bytes -100.0% 🗑️
chunk-CfFl5jJb.js (Deleted) -149.58kB 0 bytes -100.0% 🗑️
chunk-DSkP2xHf.js (Deleted) -145.09kB 0 bytes -100.0% 🗑️
chunk-Dtbs9k6l.js (Deleted) -90.38kB 0 bytes -100.0% 🗑️
chunk-BuUZcZmR.js (Deleted) -73.51kB 0 bytes -100.0% 🗑️
chunk-BcSYApdn.js (Deleted) -70.17kB 0 bytes -100.0% 🗑️
chunk-DnmxlAXt.js (Deleted) -64.19kB 0 bytes -100.0% 🗑️
chunk-CoiP2qxh.js (Deleted) -59.82kB 0 bytes -100.0% 🗑️
chunk-CdPiziCt.js (Deleted) -59.5kB 0 bytes -100.0% 🗑️
chunk-DCi0lyQj.js (Deleted) -54.16kB 0 bytes -100.0% 🗑️
chunk-DEDpzbjn.js (Deleted) -52.44kB 0 bytes -100.0% 🗑️
chunk-DxiL0gj7.js (Deleted) -49.67kB 0 bytes -100.0% 🗑️
chunk-DPnNSppF.js (Deleted) -46.2kB 0 bytes -100.0% 🗑️
chunk-6ykNwQ4i.js (Deleted) -43.14kB 0 bytes -100.0% 🗑️
chunk-s9WP_qZH.js (Deleted) -40.19kB 0 bytes -100.0% 🗑️
chunk-DiGJEsDv.js (Deleted) -35.04kB 0 bytes -100.0% 🗑️
chunk-BtiejvYM.js (Deleted) -34.73kB 0 bytes -100.0% 🗑️
chunk-CITJCJQ-.js (Deleted) -30.19kB 0 bytes -100.0% 🗑️
chunk-d10d89w8.js (Deleted) -30.09kB 0 bytes -100.0% 🗑️
chunk-B7hWfIrM.js (Deleted) -24.85kB 0 bytes -100.0% 🗑️
chunk-BIvpRMch.js (Deleted) -22.29kB 0 bytes -100.0% 🗑️
chunk-CxlYZmsN.js (Deleted) -21.5kB 0 bytes -100.0% 🗑️
chunk-CCmhnmDz.js (Deleted) -19.8kB 0 bytes -100.0% 🗑️
chunk-B4KVCTPC.js (Deleted) -19.55kB 0 bytes -100.0% 🗑️
chunk-DOUmF5jo.js (Deleted) -18.05kB 0 bytes -100.0% 🗑️
chunk-BIesm2ZH.js (Deleted) -17.9kB 0 bytes -100.0% 🗑️
chunk-BRuNBb5_.js (Deleted) -16.86kB 0 bytes -100.0% 🗑️
chunk-C3eJ2f0t.js (Deleted) -15.62kB 0 bytes -100.0% 🗑️
chunk-Cc2q6XvD.js (Deleted) -15.39kB 0 bytes -100.0% 🗑️
chunk-BX3V78Ad.js (Deleted) -13.29kB 0 bytes -100.0% 🗑️
chunk-D_ItvtrZ.js (Deleted) -12.53kB 0 bytes -100.0% 🗑️
chunk-B4vSrVR3.js (Deleted) -12.11kB 0 bytes -100.0% 🗑️
chunk-CgEDpYpJ.js (Deleted) -11.04kB 0 bytes -100.0% 🗑️
chunk-eRYNNukA.js (Deleted) -10.62kB 0 bytes -100.0% 🗑️
chunk-Br0O3ZlP.js (Deleted) -9.52kB 0 bytes -100.0% 🗑️
chunk-CHi7tFRU.js (Deleted) -8.84kB 0 bytes -100.0% 🗑️
chunk-P2z6AAja.js (Deleted) -7.34kB 0 bytes -100.0% 🗑️
chunk-PPcCBD8e.js (Deleted) -5.63kB 0 bytes -100.0% 🗑️
chunk-BOgbMaZn.js (Deleted) -5.43kB 0 bytes -100.0% 🗑️
chunk-DPVGVZka.js (Deleted) -5.13kB 0 bytes -100.0% 🗑️
chunk-4XmsXm-B.js (Deleted) -4.87kB 0 bytes -100.0% 🗑️
chunk--5OSthjI.js (Deleted) -4.68kB 0 bytes -100.0% 🗑️
chunk-39YXO3Gm.js (Deleted) -4.42kB 0 bytes -100.0% 🗑️
chunk-D0-Sff5j.js (Deleted) -4.41kB 0 bytes -100.0% 🗑️
chunk-BMLr5EzD.js (Deleted) -3.88kB 0 bytes -100.0% 🗑️
chunk-Fv01IEkp.js (Deleted) -3.61kB 0 bytes -100.0% 🗑️
chunk-DO1gC0m4.js (Deleted) -2.85kB 0 bytes -100.0% 🗑️
chunk-DgN4kjaI.js (Deleted) -2.83kB 0 bytes -100.0% 🗑️
chunk-z5OVHDRs.js (Deleted) -2.28kB 0 bytes -100.0% 🗑️
chunk-Ba4GpbPL.js (Deleted) -2.24kB 0 bytes -100.0% 🗑️
chunk-D5ICoGUu.js (Deleted) -2.08kB 0 bytes -100.0% 🗑️
chunk-C8BGPzhP.js (Deleted) -2.02kB 0 bytes -100.0% 🗑️
chunk-CK-aVl4C.js (Deleted) -1.81kB 0 bytes -100.0% 🗑️
chunk-CvHQLqi7.js (Deleted) -1.8kB 0 bytes -100.0% 🗑️
chunk-D49f5Tr1.js (Deleted) -1.71kB 0 bytes -100.0% 🗑️
chunk-DmzMfjwA.js (Deleted) -1.69kB 0 bytes -100.0% 🗑️
chunk-CoKOjpMv.js (Deleted) -1.68kB 0 bytes -100.0% 🗑️
chunk-C3HfbOFS.js (Deleted) -1.68kB 0 bytes -100.0% 🗑️
chunk-tzC6aeAv.js (Deleted) -1.67kB 0 bytes -100.0% 🗑️
chunk-Bu4gJaAL.js (Deleted) -1.67kB 0 bytes -100.0% 🗑️
chunk-CMoZ0IyX.js (Deleted) -1.51kB 0 bytes -100.0% 🗑️
chunk-D42QorKJ.js (Deleted) -1.51kB 0 bytes -100.0% 🗑️
chunk-Hdb7XTdf.js (Deleted) -1.51kB 0 bytes -100.0% 🗑️
chunk-CbB13XPF.js (Deleted) -1.5kB 0 bytes -100.0% 🗑️
chunk-GpTFA5Cg.js (Deleted) -1.44kB 0 bytes -100.0% 🗑️
chunk-CisiNSCk.js (Deleted) -1.43kB 0 bytes -100.0% 🗑️
chunk-7iAyvXpu.js (Deleted) -1.33kB 0 bytes -100.0% 🗑️
chunk-28FLBzo0.js (Deleted) -731 bytes 0 bytes -100.0% 🗑️
chunk-BKFco2gK.js (Deleted) -730 bytes 0 bytes -100.0% 🗑️
chunk-BLT7BYH0.js (Deleted) -637 bytes 0 bytes -100.0% 🗑️
chunk-BhCJhO2t.js (Deleted) -588 bytes 0 bytes -100.0% 🗑️
chunk-CEwX1nOp.js (Deleted) -477 bytes 0 bytes -100.0% 🗑️
chunk-DCBEwv9X.js (Deleted) -443 bytes 0 bytes -100.0% 🗑️
chunk-B4V9mjS3.js (Deleted) -437 bytes 0 bytes -100.0% 🗑️
chunk-zDpGEtSB.js (Deleted) -437 bytes 0 bytes -100.0% 🗑️
chunk-C0sBiSQx.js (Deleted) -383 bytes 0 bytes -100.0% 🗑️
chunk-DttMQCWA.js (Deleted) -242 bytes 0 bytes -100.0% 🗑️
chunk-8MRMTeQJ.js (Deleted) -238 bytes 0 bytes -100.0% 🗑️

@codecov

codecov Bot commented Nov 10, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 81.25000% with 6 lines in your changes missing coverage. Please review.
✅ Project coverage is 81.52%. Comparing base (6df4880) to head (9ef9d10).
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
...ron-backend/src/app/services/app-update.service.ts 80.00% 1 Missing and 2 partials ⚠️
.../src/app/settings/settings-unload-guard.service.ts 76.92% 0 Missing and 3 partials ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##           master    #2184   +/-   ##
=======================================
  Coverage   81.52%   81.52%           
=======================================
  Files        1875     1875           
  Lines       98070    98089   +19     
  Branches    22876    22884    +8     
=======================================
+ Hits        79949    79967   +18     
+ Misses      11109    11106    -3     
- Partials     7012     7016    +4     
Flag Coverage Δ
unit 81.52% <81.25%> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

This branch has not been deployed

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant