Skip to content

fix(api): dynamically reload apps if modified on disk - #5506

Merged
ReenigneArcher merged 15 commits into
LizardByte:masterfrom
eduardomozart:fix/applist-reload
Sep 29, 2026
Merged

ReenigneArcher merged 15 commits into
LizardByte:masterfrom
eduardomozart:fix/applist-reload

Conversation

@eduardomozart

@eduardomozart eduardomozart commented Aug 14, 2026 •

Copy link
Copy Markdown
Contributor

Description

When apps.json is modified directly on the filesystem (e.g. by external scripts or manually in a text editor), the Moonlight client still receives the old app list because the API relies on an in-memory cache of proc::proc. This pull request fixes the issue by securely tracking the file's last_write_time in proc::refresh() and calling refresh dynamically on the /applist API endpoint, seamlessly reloading the list only if modifications have occurred.

Test Covers
UpdateAppsAndEnv_UpdatesAppsList Apps list is actually replaced
UpdateAppsAndEnv_PreservesRunningState _app_id / placebo survive the update (placebo desktop app)
UpdateAppsAndEnv_PreservesSessionEnvironment Active session environment variables (e.g., SUNSHINE_APP_NAME) survive the update and are successfully passed to undo_cmd scripts
Refresh_ParsesFileOnFirstCall First refresh() always parses regardless of file_time_type{} semantics
Refresh_SkipsUnchangedFile Second call with same timestamp is a no-op
Refresh_ReparseAfterFileModified Re-parse occurs after file modification (mtime increased)
Refresh_ReparseAfterFileMtimeDecreased Re-parse occurs if a backup is restored (mtime is strictly earlier than cached)
Refresh_PreservesRunningAppDuringReparse End-to-end: launch app → edit file → refresh → verify running() preserved
FileTimeType_DefaultValueComparison Documents the UCRT64 file_time_type{} comparison behavior

Screenshot

Issues Fixed or Closed

Roadmap Issues

Type of Change

  • feat: New feature (non-breaking change which adds functionality)
  • fix: Bug fix (non-breaking change which fixes an issue)
  • docs: Documentation only changes
  • style: Changes that do not affect the meaning of the code (white-space, formatting, missing semicolons, etc.)
  • refactor: Code change that neither fixes a bug nor adds a feature
  • perf: Code change that improves performance
  • test: Adding missing tests or correcting existing tests
  • build: Changes that affect the build system or external dependencies
  • ci: Changes to CI configuration files and scripts
  • chore: Other changes that don't modify src or test files
  • revert: Reverts a previous commit
  • BREAKING CHANGE: Introduces a breaking change (can be combined with any type above)

Checklist

  • Code follows the style guidelines of this project
  • Code has been self-reviewed
  • Code has been commented, particularly in hard-to-understand areas
  • Code docstring/documentation-blocks for new or existing methods/components have been added or updated
  • Unit tests have been added or updated for any new or modified functionality

AI Usage

See our AI usage policy.

  • None: No AI tools were used in creating this PR
  • Light: AI provided minor assistance (formatting, simple suggestions)
  • Moderate: AI helped with code generation or debugging specific parts
  • Heavy: AI generated most or all of the code changes

This adds a timestamp check in proc::refresh() and calls it from the /applist endpoint to ensure changes made to apps.json outside the Web UI are immediately reflected in Moonlight.
This adds a timestamp check in proc::refresh() and calls it from the /applist endpoint to ensure changes made to apps.json outside the Web UI are immediately reflected in Moonlight.
@eduardomozart
eduardomozart marked this pull request as ready for review August 18, 2026 05:32

@ReenigneArcher ReenigneArcher left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I found two functional regressions in this version. The timestamp guard skips the initial apps.json parse on UCRT64, and reloading the list can discard a running app's process state. I confirmed the timestamp ordering with MSYS2 UCRT64 GCC 16.2; I did not run a full Sunshine build or streaming session.

Comment thread src/process.cpp Outdated
Comment thread src/nvhttp.cpp
…n refresh

The timestamp guard in proc::refresh() compared against a value-initialized
file_time_type{}, which on MSYS2 UCRT64 GCC 16.2 compares greater-or-equal
to file_time_type::clock::now(). This caused the very first refresh() call
at startup to skip parsing apps.json, leaving the application list empty.

Replace the bare file_time_type with std::optional<file_time_type> so that
nullopt unambiguously represents 'never parsed', regardless of platform
clock epoch.

Additionally, refresh() previously move-assigned the entire newly parsed
proc_t over the global proc::proc, which overwrites _app_id, _process,
_process_group, and cleanup iterators for an in-flight streaming session.
Refreshing the Moonlight app list during a session could therefore lose
process tracking, preventing /cancel from terminating the running app.

Add proc_t::update_apps_and_env() to replace only _apps and _env from the
newly parsed result, preserving all active process state.

Add regression tests covering both fixes.
@ReenigneArcher

This comment was marked as resolved.

Comment thread tests/unit/test_process.cpp Outdated
@ReenigneArcher ReenigneArcher changed the title fix(api): Dynamically reload apps.json for Moonlight API if modified on disk fix(api): dynamically reload apps if modified on disk Sep 29, 2026

@ReenigneArcher ReenigneArcher left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The original startup and active-session findings are addressed, and the new environment check covers the cleanup variable that was previously lost. One timestamp edge case remains in the new reload guard.

Comment thread src/process.cpp Outdated
@LizardByte LizardByte deleted a comment from sonarqubecloud Bot Sep 29, 2026
Comment thread tests/unit/test_process.cpp Outdated

@eduardomozart eduardomozart left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Tests are now passing as expected, no Sonar issues.

@ReenigneArcher ReenigneArcher left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The earlier timestamp finding is fixed: the guard now compares for equality, and the new test covers a decreasing mtime. Focused UCRT64 syntax checks passed for the changed process source and test file. The remaining comments are style/documentation cleanup under AGENTS.md and .clang-format. git diff --check currently reports trailing whitespace at test_process.cpp:379 and an extra blank line at EOF; the targeted clang-format dry run also fails. Verification status: the GitHub Actions workflows are awaiting maintainer approval, and the GH Pages documentation job timed out waiting for its dependent workflow, so full CI test results are not available yet.

Comment thread tests/unit/test_process.cpp Outdated
Comment thread tests/unit/test_process.cpp Outdated
@codecov

codecov Bot commented Sep 29, 2026 •

Copy link
Copy Markdown

Bundle Report

Bundle size has no change ✅

ReenigneArcher

This comment was marked as resolved.

@sonarqubecloud

Copy link
Copy Markdown

@ReenigneArcher

Copy link
Copy Markdown
Member

Thanks for your patience with all the requested changes. I'm trying out a new process to review PRs with Codex in order to try to get through this backlog faster.

@codecov

codecov Bot commented Sep 29, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 95.45455% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 39.98%. Comparing base (f309ef6) to head (0fbbe26).
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
src/nvhttp.cpp 0.00% 1 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #5506      +/-   ##
==========================================
+ Coverage   39.40%   39.98%   +0.57%     
==========================================
  Files         122      122              
  Lines       28323    28361      +38     
  Branches    12353    12356       +3     
==========================================
+ Hits        11161    11339     +178     
- Misses      15264    15911     +647     
+ Partials     1898     1111     -787     
Flag Coverage Δ
Alpine-aarch64 24.27% <66.66%> (+0.60%) ⬆️
Alpine-x86_64 24.39% <66.66%> (+0.60%) ⬆️
Archlinux 22.46% <66.66%> (+0.64%) ⬆️
FreeBSD-amd64 24.52% <66.66%> (+0.53%) ⬆️
Homebrew-macos-15 40.14% <83.33%> (+1.10%) ⬆️
Homebrew-macos-26 40.46% <83.33%> (+1.10%) ⬆️
Homebrew-ubuntu-24.04 26.09% <72.22%> (+0.75%) ⬆️
Linux-AppImage-aarch64 27.28% <72.22%> (+0.76%) ⬆️
Linux-AppImage-x86_64 27.45% <72.22%> (+0.75%) ⬆️
PowerShell 100.00% <ø> (ø)
Web-UI 68.42% <ø> (ø)
Windows-AMD64 31.90% <83.33%> (+0.82%) ⬆️
Windows-ARM64 26.82% <66.66%> (+0.60%) ⬆️
macOS-arm64 33.18% <66.66%> (+0.74%) ⬆️
macOS-x86_64 33.77% <66.66%> (+0.74%) ⬆️

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

Files with missing lines Coverage Δ
src/process.cpp 64.45% <100.00%> (+30.96%) ⬆️
src/process.h 92.30% <100.00%> (+62.30%) ⬆️
src/nvhttp.cpp 62.75% <0.00%> (-0.06%) ⬇️

... and 39 files with indirect coverage changes


Continue to review full report in Codecov by Harness.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update f309ef6...0fbbe26. Read the comment docs.

@github-actions

github-actions Bot commented Sep 29, 2026 •

Copy link
Copy Markdown
Last Updated 2026-09-29 21:36:48 UTC
Source Run CI Run #5911
Commit 0fbbe2689fce4cc5d5c193019ac48e0d0ff327a6

Screenshot Comparison

PR #5506 screenshots vs screenshots baseline.

Matrix: AppImage-aarch64

Image Baseline PR
sunshine_tray_initial.png
sunshine_tray_menu.png
sunshine_tray_pairing_request.png
sunshine_tray_paused.png
sunshine_tray_stopped.png
sunshine_tray_streaming.png
sunshine_tray_tooltip.png

Matrix: AppImage-x86_64

Image Baseline PR
sunshine_tray_initial.png
sunshine_tray_menu.png
sunshine_tray_pairing_request.png
sunshine_tray_paused.png
sunshine_tray_stopped.png
sunshine_tray_streaming.png
sunshine_tray_tooltip.png

Matrix: Windows-AMD64

Image Baseline PR
sunshine_tray_initial.png
sunshine_tray_menu.png
sunshine_tray_pairing_request.png
sunshine_tray_paused.png
sunshine_tray_stopped.png
sunshine_tray_streaming.png
sunshine_tray_tooltip.png

Matrix: Windows-ARM64

Image Baseline PR
sunshine_tray_initial.png
sunshine_tray_menu.png
sunshine_tray_pairing_request.png
sunshine_tray_paused.png
sunshine_tray_stopped.png
sunshine_tray_streaming.png
sunshine_tray_tooltip.png

Matrix: macOS-arm64

Image Baseline PR
sunshine_tray_initial.png
sunshine_tray_menu.png
sunshine_tray_pairing_request.png
sunshine_tray_paused.png
sunshine_tray_stopped.png
sunshine_tray_streaming.png
sunshine_tray_tooltip.png

Matrix: macOS-x86_64

Image Baseline PR
sunshine_tray_initial.png
sunshine_tray_menu.png
sunshine_tray_pairing_request.png
sunshine_tray_paused.png
sunshine_tray_stopped.png
sunshine_tray_streaming.png
sunshine_tray_tooltip.png

@ReenigneArcher
ReenigneArcher merged commit 820700c into LizardByte:master Sep 29, 2026
145 of 149 checks passed
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.

macOS: Desync between Web UI and Moonlight API when manually editing apps.json

2 participants