Skip to content

feat: persist Chrome country overrides on macOS - #29

Open
hhh2210 wants to merge 1 commit into
lcandy2:mainfrom
hhh2210:feat/macos-country-persistence
Open

feat: persist Chrome country overrides on macOS#29
hhh2210 wants to merge 1 commit into
lcandy2:mainfrom
hhh2210:feat/macos-country-persistence

Conversation

@hhh2210

@hhh2210 hhh2210 commented Jul 17, 2026

Copy link
Copy Markdown

Draft RFC: I am looking for feedback on the macOS scheduling approach before marking this ready for merge.

Chrome can replace variations_country after an update or a new variations seed. Running the script again fixes it, but that currently requires manual intervention.

This change adds an optional macOS LaunchAgent. It checks the configuration after installation, at login, and every Sunday at 04:00. The job exits without touching Chrome when the country and eligibility values are already correct.

The patch also fixes two problems in the existing restart path:

  • macOS now reopens the .app bundle through LaunchServices instead of starting the executable directly.
  • Chrome and its child processes are given time to exit before Local State is changed. Remaining processes are killed after the timeout.

Local State is written through a temporary file and replaced atomically. Chrome is reopened in a finally block if patching fails.

Commands

uv run main.py --install-persistence
uv run main.py --remove-persistence

The country defaults to us. --country CC accepts another two-letter country code.

Removing the LaunchAgent stops future checks. It does not restore the previous Chrome values.

Validation

uv run python -m unittest discover -s tests -q
Ran 14 tests: OK

uvx ruff check .
All checks passed!

uv run python -m py_compile main.py launchd.py chrome_processes.py tests/test_persistence.py
git diff --check

The LaunchAgent was also loaded and run on macOS 27.0 (26A5378n) with Chrome 150.0.7871.129. It exited with status 0, and the no-change path left the running Chrome process untouched.

Related: #23 and #25.

Backup and restore remain outside this patch and are tracked in #8. The broader Glic preference changes from #28 are also left unchanged.

@hhh2210
hhh2210 marked this pull request as ready for review July 17, 2026 06:05
Copilot AI review requested due to automatic review settings July 17, 2026 06:05

Copilot AI 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.

Pull request overview

This PR adds an optional macOS LaunchAgent (“persistence mode”) to periodically verify and re-apply Chrome’s country/eligibility overrides, and refactors Chrome shutdown/restart to be safer and more macOS-native.

Changes:

  • Add launchd.py and CLI flags to install/uninstall a macOS LaunchAgent that runs at login and weekly.
  • Refactor Chrome process management into chrome_processes.py, including reopening via LaunchServices on macOS.
  • Add unit tests for persistence/atomic writes and update READMEs with persistence-mode docs.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
main.py Adds CLI parsing, persistence install/remove commands, atomic Local State writes, and no-op short-circuiting when no patch is needed.
launchd.py Implements LaunchAgent plist generation plus install/uninstall via launchctl.
chrome_processes.py New module to detect, terminate, wait, and restart Chrome appropriately per platform (macOS uses open).
tests/test_persistence.py New tests covering Local State patching/atomic write behavior, launchd install flow, and Chrome shutdown/restart helpers.
README.md Documents macOS persistence mode and updated macOS restart/process-detection behavior.
README.zh.md Same as above, in Chinese documentation.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread launchd.py
Comment on lines +28 to +32
'StartCalendarInterval': {
'Weekday': 0,
'Hour': 4,
'Minute': 0,
},
Comment thread main.py
Comment on lines +206 to +210
if not pending:
print('All Chrome profiles already match country %s' % args.country)
if not args.yes:
input('Enter to continue...')
return
Comment thread tests/test_persistence.py
Comment on lines +92 to +100
with mock.patch.dict(os.environ, {'HOME': directory.name}):
launchd.install_launch_agent('us', '/repo/main.py')
agent_path = launchd.launch_agent_path()
with open(agent_path, 'rb') as fp:
agent = plistlib.load(fp)

self.assertEqual(agent['Label'], launchd.LABEL)
self.assertEqual(agent['ProgramArguments'][-3:], ['--country', 'us', '--yes'])
domain = 'gui/%d' % os.getuid()
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.

2 participants