braveprefs 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- braveprefs-0.1.0/.gitignore +6 -0
- braveprefs-0.1.0/CHANGELOG.md +12 -0
- braveprefs-0.1.0/LICENSE +9 -0
- braveprefs-0.1.0/PKG-INFO +119 -0
- braveprefs-0.1.0/README.md +100 -0
- braveprefs-0.1.0/pyproject.toml +28 -0
- braveprefs-0.1.0/src/braveprefs/__init__.py +5 -0
- braveprefs-0.1.0/src/braveprefs/cli.py +143 -0
- braveprefs-0.1.0/src/braveprefs/core.py +300 -0
- braveprefs-0.1.0/src/braveprefs/keys.py +124 -0
- braveprefs-0.1.0/tests/helpers.py +50 -0
- braveprefs-0.1.0/tests/test_allowlist_keys.py +85 -0
- braveprefs-0.1.0/tests/test_export.py +169 -0
- braveprefs-0.1.0/tests/test_import.py +242 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.0
|
|
4
|
+
|
|
5
|
+
First release.
|
|
6
|
+
|
|
7
|
+
- `export` reads an allowlist of named settings from a browser profile and writes one JSON file.
|
|
8
|
+
- `diff` compares two exported files.
|
|
9
|
+
- `import` writes the restorable settings back, dry run by default, with a timestamped copy of the
|
|
10
|
+
original file and a check for a singleton lock.
|
|
11
|
+
- Settings the browser protects with an authenticator are exported read only and never written.
|
|
12
|
+
- No dependencies outside the Python standard library.
|
braveprefs-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Younes Z.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this Software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
6
|
+
|
|
7
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: braveprefs
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Export, compare and restore browser profile settings as one small readable JSON file.
|
|
5
|
+
Project-URL: Homepage, https://github.com/Rezarys/braveprefs
|
|
6
|
+
Project-URL: Source, https://github.com/Rezarys/braveprefs
|
|
7
|
+
Author: Younes Z.
|
|
8
|
+
License: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Keywords: backup,browser,chromium,cli,configuration,dotfiles,migration,preferences,profile,restore,settings
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Intended Audience :: End Users/Desktop
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Topic :: Utilities
|
|
17
|
+
Requires-Python: >=3.8
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
|
|
20
|
+
# braveprefs
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
pip install braveprefs
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Put your browser settings in one short JSON file, compare that file between two machines, and write it back on a fresh install. No sync account, no QR code, no network.
|
|
27
|
+
|
|
28
|
+
This project is not affiliated with Brave Software or with any browser publisher. It reads and writes files on your own disk and nothing else.
|
|
29
|
+
|
|
30
|
+
## Why
|
|
31
|
+
|
|
32
|
+
Today the way to move your settings to a new machine is to copy the whole profile directory, several gigabytes of caches and databases, to recover a few kilobytes of choices. The rest of that directory does not restore cleanly anyway. `braveprefs` takes the few kilobytes.
|
|
33
|
+
|
|
34
|
+
The output is a file you can read, keep in a git repository, and diff.
|
|
35
|
+
|
|
36
|
+
## Use
|
|
37
|
+
|
|
38
|
+
Find your profile directory first (see below), then:
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
braveprefs export "<profile directory>" -o settings.json
|
|
42
|
+
braveprefs diff settings.json other-machine.json
|
|
43
|
+
braveprefs import "<profile directory>" settings.json
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
`import` is a dry run by default: it prints what it would change and writes nothing. Add `--apply` to write, with the browser closed:
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
braveprefs import "<profile directory>" settings.json --apply
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Before writing, it copies the original `Preferences` file next to itself with a timestamp, so you can always put it back. If a `SingletonLock` file is present, the browser may still be running and the command stops; close the browser, or pass `--force` if you know the lock is stale.
|
|
53
|
+
|
|
54
|
+
`diff` exits with status 1 when the two files differ and 0 when they match, so it fits in a script.
|
|
55
|
+
|
|
56
|
+
## What never leaves your profile
|
|
57
|
+
|
|
58
|
+
The list of settings this tool touches is a list of named keys, written out one by one in `src/braveprefs/keys.py`. It is not a name pattern. A key that is not named there is never read and never written. Concretely, none of this is ever in the exported file:
|
|
59
|
+
|
|
60
|
+
- passwords, cookies, tokens, session state
|
|
61
|
+
- browsing history, downloads history, visited sites
|
|
62
|
+
- wallet data, payment methods, or any encrypted store
|
|
63
|
+
- your extensions and their data, except the id of your current theme, which is technically an extension in this browser
|
|
64
|
+
- account identifiers and anything the browser uses to sign you in
|
|
65
|
+
|
|
66
|
+
The exported file is plain text on purpose. Open it and read it.
|
|
67
|
+
|
|
68
|
+
Two settings in the list are folder paths, the download folder and the save as folder, and a folder path can contain your account name. When either is present the exported file says so in its `notes` field. Read the file before you share it. A folder path that exists on one machine may not exist on another, especially across operating systems; import writes it anyway, so check it still makes sense before you apply it.
|
|
69
|
+
|
|
70
|
+
## Settings the browser protects, and what this tool does about them
|
|
71
|
+
|
|
72
|
+
Some settings are not stored like the others. Upstream Chromium splits the profile in two: `Preferences`, and a second file `Secure Preferences` for a fixed set of tracked settings, with a validation authenticator recorded for each of them under `protection.macs`. Two things follow. Writing one of those settings into the plain file has no effect, because the browser reads it from the other file. Writing it into the protected file without a matching authenticator is undone at the next start, on the platforms where upstream turns enforcement on, which are Windows and macOS by default. Either way an outside tool cannot put those values back and make them stay, and it would look like it had worked.
|
|
73
|
+
|
|
74
|
+
So this tool does not write them. It exports them under a separate `read_only` key in the file, and `export` prints them by name, so you can still read them, compare two machines, and set them yourself in the settings screen. The settings in that group are the home button and home page, what opens on startup, pinned tabs, and the default search engine.
|
|
75
|
+
|
|
76
|
+
On top of that fixed list, `export` and `import` also look at your actual profile: any covered setting that sits in `Secure Preferences`, or that has an authenticator recorded for it, is treated as read only for that profile even if the fixed list does not mention it. A file edited by hand cannot get around this either, because the list of names is checked again on the way in.
|
|
77
|
+
|
|
78
|
+
This is the honest shape of the problem rather than a workaround. A tool that says what it cannot do stays useful. A tool whose writes quietly disappear does not.
|
|
79
|
+
|
|
80
|
+
## Which settings are covered
|
|
81
|
+
|
|
82
|
+
Roughly sixty named keys, grouped as:
|
|
83
|
+
|
|
84
|
+
- appearance and layout: bookmarks bar, side panel, toolbar buttons, dark mode, tab behaviour
|
|
85
|
+
- new tab page: clock, stats row, what the page shows
|
|
86
|
+
- fonts and text: font families, default and minimum sizes, caret browsing
|
|
87
|
+
- languages and spelling: accepted languages, dictionaries, translation preferences
|
|
88
|
+
- downloads and printing: download folder, ask where to save, default printer
|
|
89
|
+
- privacy and autofill: do not track, suggestions, preloading, address and payment autofill
|
|
90
|
+
- shields: your own filter rules, filter list subscriptions, embed and language settings
|
|
91
|
+
- startup and search: read only, see the section above
|
|
92
|
+
|
|
93
|
+
The file `src/braveprefs/keys.py` is the list, with a one line label for each key. Every key in it has its own test.
|
|
94
|
+
|
|
95
|
+
## Finding your profile directory
|
|
96
|
+
|
|
97
|
+
Open the browser's version page (`brave://version` in that browser, `chrome://version` in others) and copy the value next to **Profile Path**. That is the directory to pass to `braveprefs`. It is the directory that contains a file named `Preferences`, not the directory above it.
|
|
98
|
+
|
|
99
|
+
On Windows it sits under `C:\Users\<you>\AppData\Local\BraveSoftware\Brave-Browser`. On other systems the version page is the reliable answer.
|
|
100
|
+
|
|
101
|
+
## What has not been tested
|
|
102
|
+
|
|
103
|
+
No real browser profile was read while building this. The key names and the protection mechanism come from the upstream Chromium source, and the test suite runs on example files written for the tests. If you run it on a real profile, whether it works or not, please open an issue and say what happened. That is the one thing I cannot check from here.
|
|
104
|
+
|
|
105
|
+
## Where this came from
|
|
106
|
+
|
|
107
|
+
The request thread: https://github.com/brave/brave-browser/issues/46192
|
|
108
|
+
|
|
109
|
+
There are other tools in this area that apply a hardened set of settings chosen by their author. This one does the opposite: it moves *your* settings, whatever they are, and decides nothing for you.
|
|
110
|
+
|
|
111
|
+
## Notes
|
|
112
|
+
|
|
113
|
+
Built with AI assistance, reviewed and tested by me.
|
|
114
|
+
|
|
115
|
+
Requires Python 3.8 or later. No dependencies outside the standard library.
|
|
116
|
+
|
|
117
|
+
Run the tests with `python3 -m unittest discover -s tests -t tests` from a checkout, with `src` on `PYTHONPATH`.
|
|
118
|
+
|
|
119
|
+
MIT licensed. Younes Z.
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# braveprefs
|
|
2
|
+
|
|
3
|
+
```
|
|
4
|
+
pip install braveprefs
|
|
5
|
+
```
|
|
6
|
+
|
|
7
|
+
Put your browser settings in one short JSON file, compare that file between two machines, and write it back on a fresh install. No sync account, no QR code, no network.
|
|
8
|
+
|
|
9
|
+
This project is not affiliated with Brave Software or with any browser publisher. It reads and writes files on your own disk and nothing else.
|
|
10
|
+
|
|
11
|
+
## Why
|
|
12
|
+
|
|
13
|
+
Today the way to move your settings to a new machine is to copy the whole profile directory, several gigabytes of caches and databases, to recover a few kilobytes of choices. The rest of that directory does not restore cleanly anyway. `braveprefs` takes the few kilobytes.
|
|
14
|
+
|
|
15
|
+
The output is a file you can read, keep in a git repository, and diff.
|
|
16
|
+
|
|
17
|
+
## Use
|
|
18
|
+
|
|
19
|
+
Find your profile directory first (see below), then:
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
braveprefs export "<profile directory>" -o settings.json
|
|
23
|
+
braveprefs diff settings.json other-machine.json
|
|
24
|
+
braveprefs import "<profile directory>" settings.json
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
`import` is a dry run by default: it prints what it would change and writes nothing. Add `--apply` to write, with the browser closed:
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
braveprefs import "<profile directory>" settings.json --apply
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Before writing, it copies the original `Preferences` file next to itself with a timestamp, so you can always put it back. If a `SingletonLock` file is present, the browser may still be running and the command stops; close the browser, or pass `--force` if you know the lock is stale.
|
|
34
|
+
|
|
35
|
+
`diff` exits with status 1 when the two files differ and 0 when they match, so it fits in a script.
|
|
36
|
+
|
|
37
|
+
## What never leaves your profile
|
|
38
|
+
|
|
39
|
+
The list of settings this tool touches is a list of named keys, written out one by one in `src/braveprefs/keys.py`. It is not a name pattern. A key that is not named there is never read and never written. Concretely, none of this is ever in the exported file:
|
|
40
|
+
|
|
41
|
+
- passwords, cookies, tokens, session state
|
|
42
|
+
- browsing history, downloads history, visited sites
|
|
43
|
+
- wallet data, payment methods, or any encrypted store
|
|
44
|
+
- your extensions and their data, except the id of your current theme, which is technically an extension in this browser
|
|
45
|
+
- account identifiers and anything the browser uses to sign you in
|
|
46
|
+
|
|
47
|
+
The exported file is plain text on purpose. Open it and read it.
|
|
48
|
+
|
|
49
|
+
Two settings in the list are folder paths, the download folder and the save as folder, and a folder path can contain your account name. When either is present the exported file says so in its `notes` field. Read the file before you share it. A folder path that exists on one machine may not exist on another, especially across operating systems; import writes it anyway, so check it still makes sense before you apply it.
|
|
50
|
+
|
|
51
|
+
## Settings the browser protects, and what this tool does about them
|
|
52
|
+
|
|
53
|
+
Some settings are not stored like the others. Upstream Chromium splits the profile in two: `Preferences`, and a second file `Secure Preferences` for a fixed set of tracked settings, with a validation authenticator recorded for each of them under `protection.macs`. Two things follow. Writing one of those settings into the plain file has no effect, because the browser reads it from the other file. Writing it into the protected file without a matching authenticator is undone at the next start, on the platforms where upstream turns enforcement on, which are Windows and macOS by default. Either way an outside tool cannot put those values back and make them stay, and it would look like it had worked.
|
|
54
|
+
|
|
55
|
+
So this tool does not write them. It exports them under a separate `read_only` key in the file, and `export` prints them by name, so you can still read them, compare two machines, and set them yourself in the settings screen. The settings in that group are the home button and home page, what opens on startup, pinned tabs, and the default search engine.
|
|
56
|
+
|
|
57
|
+
On top of that fixed list, `export` and `import` also look at your actual profile: any covered setting that sits in `Secure Preferences`, or that has an authenticator recorded for it, is treated as read only for that profile even if the fixed list does not mention it. A file edited by hand cannot get around this either, because the list of names is checked again on the way in.
|
|
58
|
+
|
|
59
|
+
This is the honest shape of the problem rather than a workaround. A tool that says what it cannot do stays useful. A tool whose writes quietly disappear does not.
|
|
60
|
+
|
|
61
|
+
## Which settings are covered
|
|
62
|
+
|
|
63
|
+
Roughly sixty named keys, grouped as:
|
|
64
|
+
|
|
65
|
+
- appearance and layout: bookmarks bar, side panel, toolbar buttons, dark mode, tab behaviour
|
|
66
|
+
- new tab page: clock, stats row, what the page shows
|
|
67
|
+
- fonts and text: font families, default and minimum sizes, caret browsing
|
|
68
|
+
- languages and spelling: accepted languages, dictionaries, translation preferences
|
|
69
|
+
- downloads and printing: download folder, ask where to save, default printer
|
|
70
|
+
- privacy and autofill: do not track, suggestions, preloading, address and payment autofill
|
|
71
|
+
- shields: your own filter rules, filter list subscriptions, embed and language settings
|
|
72
|
+
- startup and search: read only, see the section above
|
|
73
|
+
|
|
74
|
+
The file `src/braveprefs/keys.py` is the list, with a one line label for each key. Every key in it has its own test.
|
|
75
|
+
|
|
76
|
+
## Finding your profile directory
|
|
77
|
+
|
|
78
|
+
Open the browser's version page (`brave://version` in that browser, `chrome://version` in others) and copy the value next to **Profile Path**. That is the directory to pass to `braveprefs`. It is the directory that contains a file named `Preferences`, not the directory above it.
|
|
79
|
+
|
|
80
|
+
On Windows it sits under `C:\Users\<you>\AppData\Local\BraveSoftware\Brave-Browser`. On other systems the version page is the reliable answer.
|
|
81
|
+
|
|
82
|
+
## What has not been tested
|
|
83
|
+
|
|
84
|
+
No real browser profile was read while building this. The key names and the protection mechanism come from the upstream Chromium source, and the test suite runs on example files written for the tests. If you run it on a real profile, whether it works or not, please open an issue and say what happened. That is the one thing I cannot check from here.
|
|
85
|
+
|
|
86
|
+
## Where this came from
|
|
87
|
+
|
|
88
|
+
The request thread: https://github.com/brave/brave-browser/issues/46192
|
|
89
|
+
|
|
90
|
+
There are other tools in this area that apply a hardened set of settings chosen by their author. This one does the opposite: it moves *your* settings, whatever they are, and decides nothing for you.
|
|
91
|
+
|
|
92
|
+
## Notes
|
|
93
|
+
|
|
94
|
+
Built with AI assistance, reviewed and tested by me.
|
|
95
|
+
|
|
96
|
+
Requires Python 3.8 or later. No dependencies outside the standard library.
|
|
97
|
+
|
|
98
|
+
Run the tests with `python3 -m unittest discover -s tests -t tests` from a checkout, with `src` on `PYTHONPATH`.
|
|
99
|
+
|
|
100
|
+
MIT licensed. Younes Z.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "braveprefs"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Export, compare and restore browser profile settings as one small readable JSON file."
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.8"
|
|
7
|
+
license = { text = "MIT" }
|
|
8
|
+
authors = [{ name = "Younes Z." }]
|
|
9
|
+
keywords = ["browser", "chromium", "preferences", "settings", "backup", "restore", "dotfiles", "configuration", "profile", "migration", "cli"]
|
|
10
|
+
classifiers = ["Development Status :: 4 - Beta", "Environment :: Console", "Intended Audience :: End Users/Desktop", "License :: OSI Approved :: MIT License", "Programming Language :: Python :: 3", "Topic :: Utilities"]
|
|
11
|
+
dependencies = []
|
|
12
|
+
|
|
13
|
+
[project.scripts]
|
|
14
|
+
braveprefs = "braveprefs.cli:main"
|
|
15
|
+
|
|
16
|
+
[project.urls]
|
|
17
|
+
Homepage = "https://github.com/Rezarys/braveprefs"
|
|
18
|
+
Source = "https://github.com/Rezarys/braveprefs"
|
|
19
|
+
|
|
20
|
+
[build-system]
|
|
21
|
+
requires = ["hatchling"]
|
|
22
|
+
build-backend = "hatchling.build"
|
|
23
|
+
|
|
24
|
+
[tool.hatch.build.targets.wheel]
|
|
25
|
+
packages = ["src/braveprefs"]
|
|
26
|
+
|
|
27
|
+
[tool.hatch.build.targets.sdist]
|
|
28
|
+
include = ["src", "tests", "README.md", "LICENSE", "CHANGELOG.md", "pyproject.toml"]
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
"""Command line for braveprefs: export, diff, import."""
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import json
|
|
5
|
+
import sys
|
|
6
|
+
|
|
7
|
+
from . import core
|
|
8
|
+
from .core import PrefsError
|
|
9
|
+
|
|
10
|
+
USAGE = "braveprefs {export|diff|import} ..."
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def _render(value):
|
|
14
|
+
if isinstance(value, (dict, list)):
|
|
15
|
+
return json.dumps(value, sort_keys=True, ensure_ascii=False)
|
|
16
|
+
return json.dumps(value, ensure_ascii=False)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _cmd_export(args, out):
|
|
20
|
+
bundle = core.export_settings(args.profile)
|
|
21
|
+
if args.output in (None, "-"):
|
|
22
|
+
json.dump(bundle, out, indent=2, sort_keys=True, ensure_ascii=False)
|
|
23
|
+
out.write("\n")
|
|
24
|
+
else:
|
|
25
|
+
core.write_bundle(bundle, args.output)
|
|
26
|
+
out.write("wrote %s\n" % args.output)
|
|
27
|
+
kept = len(bundle["settings"])
|
|
28
|
+
locked = len(bundle["read_only"])
|
|
29
|
+
out.write("%d setting(s) you can restore, %d read only\n" % (kept, locked))
|
|
30
|
+
if locked:
|
|
31
|
+
out.write(
|
|
32
|
+
"read only means the browser signs those values and undoes a change made from "
|
|
33
|
+
"outside it:\n"
|
|
34
|
+
)
|
|
35
|
+
for path in sorted(bundle["read_only"], key=core.group_order):
|
|
36
|
+
out.write(" %s (%s)\n" % (path, bundle["labels"].get(path, "")))
|
|
37
|
+
return 0
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _cmd_diff(args, out):
|
|
41
|
+
left = core.read_bundle(args.first)
|
|
42
|
+
right = core.read_bundle(args.second)
|
|
43
|
+
rows = core.diff_bundles(left, right)
|
|
44
|
+
if not rows:
|
|
45
|
+
out.write("no difference in the settings both files carry\n")
|
|
46
|
+
return 0
|
|
47
|
+
out.write("%d difference(s)\n" % len(rows))
|
|
48
|
+
for path, label, status, value_a, value_b in sorted(rows, key=lambda r: core.group_order(r[0])):
|
|
49
|
+
out.write("%s (%s)\n %s\n" % (path, label, status))
|
|
50
|
+
if value_a is not None or status == "different":
|
|
51
|
+
out.write(" first: %s\n" % _render(value_a))
|
|
52
|
+
if value_b is not None or status == "different":
|
|
53
|
+
out.write(" second: %s\n" % _render(value_b))
|
|
54
|
+
return 1
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _cmd_import(args, out):
|
|
58
|
+
bundle = core.read_bundle(args.bundle)
|
|
59
|
+
if args.apply:
|
|
60
|
+
rows, backup = core.apply_import(args.profile, bundle, force=args.force)
|
|
61
|
+
else:
|
|
62
|
+
rows, backup = core.plan_import(args.profile, bundle), None
|
|
63
|
+
|
|
64
|
+
written = 0
|
|
65
|
+
for path, action, current, new in sorted(rows, key=lambda r: core.group_order(r[0])):
|
|
66
|
+
if action == core.UNCHANGED:
|
|
67
|
+
continue
|
|
68
|
+
out.write("%s\n %s\n" % (path, action))
|
|
69
|
+
if action == core.SET:
|
|
70
|
+
written += 1
|
|
71
|
+
out.write(" from: %s\n" % _render(current))
|
|
72
|
+
out.write(" to: %s\n" % _render(new))
|
|
73
|
+
unchanged = sum(1 for row in rows if row[1] == core.UNCHANGED)
|
|
74
|
+
out.write("%d already match\n" % unchanged)
|
|
75
|
+
if args.apply:
|
|
76
|
+
out.write("wrote %d setting(s)\n" % written)
|
|
77
|
+
if backup:
|
|
78
|
+
out.write("copied the original file to %s\n" % backup)
|
|
79
|
+
else:
|
|
80
|
+
out.write("nothing to write, so no copy was made\n")
|
|
81
|
+
else:
|
|
82
|
+
out.write(
|
|
83
|
+
"this was a dry run and nothing was written. Close the browser and add --apply "
|
|
84
|
+
"to write %d setting(s).\n" % written
|
|
85
|
+
)
|
|
86
|
+
return 0
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def build_parser():
|
|
90
|
+
parser = argparse.ArgumentParser(
|
|
91
|
+
prog="braveprefs",
|
|
92
|
+
description="Export, compare and restore a named list of browser profile settings.",
|
|
93
|
+
)
|
|
94
|
+
subparsers = parser.add_subparsers(dest="command")
|
|
95
|
+
|
|
96
|
+
exporter = subparsers.add_parser("export", help="read the settings of a profile")
|
|
97
|
+
exporter.add_argument("profile", help="path of the browser profile directory")
|
|
98
|
+
exporter.add_argument("-o", "--output", help="file to write, or - for standard output")
|
|
99
|
+
exporter.set_defaults(handler=_cmd_export)
|
|
100
|
+
|
|
101
|
+
differ = subparsers.add_parser("diff", help="compare two exported files")
|
|
102
|
+
differ.add_argument("first")
|
|
103
|
+
differ.add_argument("second")
|
|
104
|
+
differ.set_defaults(handler=_cmd_diff)
|
|
105
|
+
|
|
106
|
+
importer = subparsers.add_parser("import", help="write settings back into a profile")
|
|
107
|
+
importer.add_argument("profile", help="path of the browser profile directory")
|
|
108
|
+
importer.add_argument("bundle", help="file produced by braveprefs export")
|
|
109
|
+
importer.add_argument(
|
|
110
|
+
"--apply", action="store_true",
|
|
111
|
+
help="actually write; without it the command only says what would change",
|
|
112
|
+
)
|
|
113
|
+
importer.add_argument(
|
|
114
|
+
"--dry-run", action="store_true",
|
|
115
|
+
help="say what would change and write nothing. This is the default.",
|
|
116
|
+
)
|
|
117
|
+
importer.add_argument(
|
|
118
|
+
"--force", action="store_true",
|
|
119
|
+
help="write even though a singleton lock file is present",
|
|
120
|
+
)
|
|
121
|
+
importer.set_defaults(handler=_cmd_import)
|
|
122
|
+
return parser
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def main(argv=None, out=None, err=None):
|
|
126
|
+
out = out or sys.stdout
|
|
127
|
+
err = err or sys.stderr
|
|
128
|
+
parser = build_parser()
|
|
129
|
+
args = parser.parse_args(list(sys.argv[1:] if argv is None else argv))
|
|
130
|
+
if getattr(args, "handler", None) is None:
|
|
131
|
+
parser.print_help(out)
|
|
132
|
+
return 2
|
|
133
|
+
if getattr(args, "dry_run", False):
|
|
134
|
+
args.apply = False
|
|
135
|
+
try:
|
|
136
|
+
return args.handler(args, out)
|
|
137
|
+
except PrefsError as exc:
|
|
138
|
+
err.write("braveprefs: %s\n" % exc)
|
|
139
|
+
return 1
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
if __name__ == "__main__":
|
|
143
|
+
sys.exit(main())
|