-
Notifications
You must be signed in to change notification settings - Fork 5
/
eos-transient-setup
executable file
·174 lines (139 loc) · 5.91 KB
/
eos-transient-setup
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
#!/usr/bin/env python3
# eos-transient-setup – configures the system for transient sessions
# Copyright (C) 2016-2018 Endless Mobile, Ltd.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
import argparse
import glob
import json
import logging
import os
import subprocess
import sys
import tempfile
import gi
gi.require_version('OSTree', '1.0')
# pylint: disable=wrong-import-position
from gi.repository import GLib, Gio, OSTree # noqa: E402
log = logging.getLogger(sys.argv[0])
EOS_INSTALLER = 'com.endlessm.Installer.desktop'
EOS_INSTALLER_PATH = os.path.join('/usr/share/applications', EOS_INSTALLER)
LOCAL_APPS_DIR = '/usr/local/share/applications'
LOCAL_DESKTOP_PATH = os.path.join(LOCAL_APPS_DIR, EOS_INSTALLER)
LIVE_SETTINGS_DB = '/var/lib/eos-image-defaults/settings.live'
USER_PROFILE_PATH = '/usr/local/share/dconf/profile/user'
USER_PROFILE = '''user-db:user
file-db:/var/lib/eos-image-defaults/settings.live
file-db:/var/lib/eos-image-defaults/settings
file-db:/usr/share/eos-default-settings/settings
'''
SHELL_SCHEMA = 'org.gnome.shell'
FAVORITE_APPS_KEY = 'favorite-apps'
GS_SCHEMA = 'org.gnome.software'
ALLOW_UPDATES = 'allow-updates'
class AdjustGSettings:
def __init__(self):
self.keyfile = GLib.KeyFile()
def update(self, schema, key, variant):
"""Stages 'variant' as the new value for 'key' in 'schema'."""
value = variant.print_(False)
log.info('Updating %s: %s to %s', schema, key, value)
self.keyfile.set_string(schema.replace('.', '/'), key, value)
def prepare(self):
"""Stage all settings to be overridden."""
self.update_favorite_apps()
self.disallow_app_center_updates()
def write_dconf_compile(self):
"""Write dconf database with overridden settings.
We also adjust the 'user' profile to use it.
"""
with tempfile.TemporaryDirectory(suffix='.d') as tempdir:
keyfile_path = os.path.join(tempdir, '00-live')
log.info('writing keyfile to %s', keyfile_path)
self.keyfile.save_to_file(keyfile_path)
os.makedirs(os.path.dirname(LIVE_SETTINGS_DB), exist_ok=True)
args = ['dconf', 'compile', LIVE_SETTINGS_DB, tempdir]
log.info('$ %s', ' '.join(args))
subprocess.check_call(args)
log.info('Installing new DConf profile to %s', USER_PROFILE_PATH)
os.makedirs(os.path.dirname(USER_PROFILE_PATH), exist_ok=True)
with open(USER_PROFILE_PATH, 'w') as user_profile_file:
user_profile_file.write(USER_PROFILE)
def write_stdout(self):
"""Write keyfile with overridden settings to stdout, for debugging."""
data, _ = self.keyfile.to_data()
print(data)
def update_favorite_apps(self):
"""Adjust default favourite apps, which are shown on the taskbar."""
settings = Gio.Settings(schema=SHELL_SCHEMA)
favorite_apps = settings.get_strv(FAVORITE_APPS_KEY)
# Prepend installer icon
if EOS_INSTALLER not in favorite_apps:
favorite_apps.insert(0, EOS_INSTALLER)
self.update(SHELL_SCHEMA, FAVORITE_APPS_KEY,
GLib.Variant('as', favorite_apps))
def disallow_app_center_updates(self):
"""Forbid installing app updates."""
self.update(GS_SCHEMA, ALLOW_UPDATES, GLib.Variant('b', False))
def install_installer_desktop_file():
"""Make eos-installer visible in user sessions.
eos-installer is shipped in all images, but its desktop file contains
NoDisplay=true. Make a copy with this setting removed so it can be added to
the desktop and taskbar and found via search.
"""
log.info('Copying %s to %s with NoDisplay removed',
EOS_INSTALLER_PATH, LOCAL_DESKTOP_PATH)
os.makedirs(LOCAL_APPS_DIR, exist_ok=True)
eos_installer_desktop = GLib.KeyFile()
eos_installer_desktop.load_from_file(
EOS_INSTALLER_PATH,
GLib.KeyFileFlags.KEEP_COMMENTS | GLib.KeyFileFlags.KEEP_TRANSLATIONS,
)
eos_installer_desktop.remove_key('Desktop Entry', 'NoDisplay')
eos_installer_desktop.save_to_file(LOCAL_DESKTOP_PATH)
def reduce_ostree_min_free_space():
'''Don't require any free space on disk when installing apps. On live
systems, free space is at most half of physical RAM, and running out is not
a big deal.'''
repo = OSTree.Repo.new_default()
repo.open()
config = repo.copy_config()
# -size alone takes precedence but set both for clarity.
config.set_string('core', 'min-free-space-size', '0MB')
config.set_integer('core', 'min-free-space-percent', 0)
repo.write_config(config)
def main():
"""Configures system settings for live sessions."""
parser = argparse.ArgumentParser(description=main.__doc__)
parser.add_argument(
"--dry-run",
action="store_true",
help="Just print the DConf keyfile to stdout",
)
args = parser.parse_args()
logging.basicConfig(
level=logging.INFO,
format='%(name)s:%(lineno)-3s %(funcName)20s %(levelname)7s: '
'%(message)s')
setup = AdjustGSettings()
setup.prepare()
if args.dry_run:
setup.write_stdout()
else:
setup.write_dconf_compile()
reduce_ostree_min_free_space()
install_installer_desktop_file()
if __name__ == '__main__':
main()