easy-dotfiles
is configured via JSON files.
Why JSON? Because is extremely popular and easy to understand. Many text editors offer json support by default or you can easily add a plugin for validating json files. Also, some newer linux distros come out of the box with the jq package
preinstalled.
In order to keep things nice and tidy, the configuration is split into multiple files. You can check the config folder where you can see separate folders for each main configuration section. The configuration json files are kept private, on your private git repository and they are specific to your installations. The sample folder was created to help you bootstrap your initial configuration.
NOTE: The easy-dotfiles
scripts are expecting this configuration of folders and files to exist. If you don't want to use a specific config section you must leave the corresponding JSON file empty. No deletion of config folders or config files is accepted, just leave the json files empty and everything should run without errors.
All of the easy-dotfiles
config JSON files are starting with an json array []
. This root array contains entries specific to each configuration section.
For apps config we need the following information:
-
name
- Used by the install script to nicely print the list of apps configured for the selected distro and the app that is currently installing. -
url
- It's not used in the scripts, you can skip adding this property if it's too much overhead. I added it to have a future reference of which app that config entry is actually referring to. -
install
- This is a property object that will contain the app install command for each supported distro. The supported distros is configured in the defaults script file. If you don't want an app to be installed on a specific distro, you can skip the entry for that distro. -
files
- Property object that can have two child array properties:-
include
- Specifies the app specific dotfiles (configuration files) to be included by the import and export scripts. Supports*
character for globbing but only for the last level in the supplied path. The path can be a file or a folder. -
exclude
- Helpful if you want to exclude some files from the configuration. Maybe some cache or other binaries that are too large and you don't want to be pushed to the private git repository. Supports*
character for globbing but only for the last level in the supplied path. The path can be a file or a folder.
-
-
dconf
- Gnome uses the dconf database in order to configure it's settings. Gnome Extensions and some Gnome Applications are also usingdconf
to store their settings.dconf
is a simple folder like structure that can contain child configuration properties or sub-paths. Adconf
setting is a key-value pair. This config part is not mandatory, you can use it for Gnome apps that are storing their settings into thedconf
database. Theeasy-dotfiles
appsdconf
property object can have two child properties:-
schema_path
- This specifies thedconf
path from which all modified properties will be exported / imported. More on this later. -
file
- The file to which thedconf
properties are exported / imported. The file name is chosen by you. I usually give them the.conf
extension becausevscode
does some code highlighting for the.conf files. You can choose whatever name you like, just make sure there isn't any name clash with otherdconf
config parts from the same config section (apps, keybindings, tweaks, etc.). These files are also stored into your private git repository.
-
The provided sample apps config JSON file tries to cover most of the configuration variation you might need.
Let's talk first about the vscode
app config part as an example:
{
"name": "Visual Studio Code",
"url": "https://code.visualstudio.com/",
"install": {
"fedora": "sudo dnf install code -y",
"ubuntu": "sudo apt-get install code -y"
},
"files": {
"include": ["~/.config/Code", "~/.config/Code/User"],
"exclude": [
"~/.config/Code/*Storage*",
"~/.config/Code/*Cache*",
"~/.config/Code/*Crash*",
"~/.config/Code/*Service*",
"~/.config/Code/logs"
]
}
}
You can see here the install
property which has two child properties expressing the install commands for each supported distros. The trick here is to make the install commands run unattended by using the -y
flag. For flatpak installs use also the --noninteractive
flag:
sudo flatpak install flathub com.mattjakeman.ExtensionManager -y --noninteractive
This way you'll only enter your sudo password at the beginning of the install script and you won't be bothered with confirmation prompts afterwards. You can go out for a coffee break until the install script finishes it's run.
The files
property describes here what to include
and what to exclude
from the vscode
dotfiles (config files). You can see that the *
character is used only for the last level of the path.
If you want to include
or exclude
files like this, it won't work:
"files": {
"include": ["~/.config/Code/Ca*/extra/wanted"],
"exclude": ["~/.config/Code/*rage*/extra/unwanted"]
}
NOTE: You can use globbing only for the last level of the path:
"files": {
"include": ["~/.config/Code/extra/want*"],
"exclude": ["~/.config/Code/extra/unw*"]
}
P.S: For Visual Studio Code
I recommend that you use their builtin feature for syncing settings and plugins. The above and sample config for vscode
in only as a showcase, I'm not using that config for my private repo. I use something similar for Kodi
(not included in the sample config), which can help transferring nicely your library and you app settings to another system or VM:
{
"name": "Kodi",
"url": "https://kodi.tv/",
"install": {
"fedora": "sudo dnf install kodi -y",
"ubuntu": "sudo apt-get install kodi -y"
},
"files": {
"include": ["~/.kodi"],
"exclude": ["~/.kodi/temp", "~/.kodi/userdata/Thumbnails", "~/.kodi/addons/packages"]
}
}
Kodi
has a lot of binaries in it's config folders so you might want to be careful when including large files or folders because they will be pushed to your private git repository. Usually, git providers set some limits on how much data you can store there.
Another interesting thing here is the use of the tilde ~
character at the beginning of the path. easy-dotfiles
scripts are smart enough to handle this automatically for you. This way you can keep your apps config file rather generic and not tied to a specific user path on the system, /home/ionut
for example.
Of course you can specify the absolute path if you desire, but you'll use the ability of using the same apps config file on another system with a different user name. NOTE: Even if you use tilde ~
, your apps might save absolute paths in their dotfiles, so this solution is not bulletproof but it might help for some apps.
NOTE: include
and exclude
config properties must express absolute paths, the only exception is when using tilde ~
for the the files inside the home
folder.
Also, you're not limited only to your home
files. There might be some apps that have config files outside your home
folder. Let's have timeshift
as an example:
{
"name": "Timeshift",
"url": "https://github.com/teejee2008/timeshift",
"install": {
"fedora": "sudo dnf install timeshift -y",
"ubuntu": "sudo apt-get install timeshift -y"
},
"files": {
"include": ["/etc/timeshift/timeshift.json"]
}
}
You can see that for timeshift
we include a file that is outside our home folder, a file which we don't own: /etc/timeshift/timeshift.json
easy-dotfiles
handles outside home files by creating a .permissions
file which holds all the original owner and file permissions. When exporting the outside home files we make a copy of the file inside the private local repo, which is owned by the current user. When we import the config files to a new system, the original permissions of the outside home files are restored.
You can configure any outside home file or folders, even if your user doesn't have read permissions. The script will prompt you for the sudo password and the transfer operation will be performed.
Just skip the install
part all together or only for a specific distro that comes with that particular app preinstalled. Gnome Weather
comes preinstalled on Fedora
but not on Ubuntu
:
{
"name": "Gnome Weather",
"url": "https://wiki.gnome.org/Apps/Weather",
"install": {
"ubuntu": "sudo apt-get install gnome-weather -y"
},
"dconf": {
"schema_path": "/org/gnome/Weather/",
"file": "gnome-weather.conf"
}
}
By using the install
part you can tell easy-dotfiles
to install apps only for a specific distro, for example a visual UI app for the package manager:
{
"name": "dnfdragora",
"url": "https://github.com/manatools/dnfdragora",
"install": {
"fedora": "sudo dnf install dnfdragora -y"
}
},
{
"name": "Synaptic Package Manager",
"url": "https://www.nongnu.org/synaptic/",
"install": {
"ubuntu": "sudo apt-get install synaptic -y"
}
}
If you have the same install command for different distros (flatpaks or custom install scripts) you can use only one line for the install command config. Just put the corresponding distro names in the same field, separated by spaces:
{
"name": "Extension Manager",
"url": "https://flathub.org/apps/details/com.mattjakeman.ExtensionManager",
"install": {
"fedora ubuntu": "sudo flatpak install flathub com.mattjakeman.ExtensionManager -y --noninteractive"
}
},
{
"name": "IntelliJ IDEA Ultimate",
"url": "https://www.jetbrains.com/idea/",
"install": {
"fedora ubuntu": "../../private/scripts/apps/jidea-install.sh"
},
"files": {
"include": ["~/.config/JetBrains"]
}
}
You can use custom scripts or commands for installing apps. For example, let's look at the IntelliJ IDEA
's app config:
{
"name": "IntelliJ IDEA Ultimate",
"url": "https://www.jetbrains.com/idea/",
"install": {
"fedora ubuntu": "../../private/scripts/apps/jidea-install.sh"
},
"files": {
"include": ["~/.config/JetBrains"]
}
}
The easy-dotfiles
install script is changing the current working directory to a pre-configured work dir (~/easy-dotfiles/tmp
) so each downloaded package or intermediary files can be cleaned up automatically. Because of this, if you want a custom app install script to be run by the main install script, you need to provide here a relative path:
"install": {
"fedora ubuntu": "../../private/scripts/apps/your_custom_app_install.sh"
}
I recommend you put your custom app install scripts into the private repository (~/easy-dotfiles/private
). I chose to put them inside the ~/easy-dotfiles/private/scripts/apps/
folder just to keep things more organized.
You can check out my custom jidea install script which installs a custom version at a specific location with a custom .desktop
file.
If you don't need such elaborate custom app install scripts, but maybe just a quickly download of an .rpm
or .deb
file you can configure something similar to this:
{
"name": "TeamViewer",
"url": "https://www.teamviewer.com/",
"install": {
"fedora": "sudo dnf install https://download.teamviewer.com/download/linux/teamviewer.x86_64.rpm -y",
"ubuntu": "wget -nv https://download.teamviewer.com/download/linux/teamviewer_amd64.deb && sudo apt-get install --fix-broken ./teamviewer_amd64.deb -y"
},
"files": {
"include": ["~/.config/teamviewer/client.conf"]
}
}
You can see in the above example that for fedora
we can use dnf install
directly with the .rpm
URL. So no need for pre-downloading the package.
For ubuntu
we use wget -q
to get the .deb
file and then run sudo apt-get install --fix-broken
with the previously downloaded package.
Just make sure you keep the install
command unattended so it won't ask for confirmations of passwords. No need to clean up the files afterwards, the install script will take care of that for you. Also, you can put whatever command you see fit there, as long as the config file remains a valid JSON file.
NOTE: When doing custom install scripts or commands, please make sure that you don't have a command that's outputting some kind of download progress. For example, always use wget -nv
(wget --no-verbose
) or wget -q
(wget --quiet
), otherwise you'll pollute the easy-dotfiles
logs with lots of unnecessary data.
Before running the individual app install commands, easy-dotfiles
runs a specific distro setup script, depending on what distro you selected when prompted by the install script.
The distro setup scripts are also kept on your private repository and you can find them here: ~/easy-dotfiles/scripts/[DISTRO]/setup.sh
. The sample configuration comes with two distro specific setup scripts, one for fedora and one for ubuntu.
These scripts are very useful when you need to tinker your distro a little bit, maybe improve the package manager's speed (see fedora setup.sh: update_dnf_config
) or replace the Firefox snap with a deb version (see ubuntu setup.sh: replace_snap_firefox
).
The distro setup scripts are also a very good place to add the software repos that are missing for some of your favorite apps. For more info on them you can check out this section.
So, long story short, you need to go to the specific app official website and check if they provide a way to add their software repo to your distro. Get those commands, test them first and make them unattended, then just add the code to the specific distro setup script.
Done! Now you can properly run sudo dnf install code -y
for fedora
or sudo apt-get install code -y
for ubuntu
.
Use both files
and dconf
parts (we use here Meld
as an example even if I didn't configured files for it in the sample config):
{
"name": "Meld Merge",
"url": "https://meldmerge.org/",
"install": {
"fedora": "sudo dnf install meld -y",
"ubuntu": "sudo apt-get install meld -y"
},
"files": {
"include": ["~/.config/something"]
},
"dconf": {
"schema_path": "/org/gnome/meld/",
"file": "meld.conf"
}
}
NOTE: For dconf
file
property we need to use only the file name with no path. Why? Because this file is auto generated by easy-dotfiles
and it's used only as a vessel for exporting and importing dconf settings.
Go to this folder and check the contents of the meld.conf
file. Also, you can see how the apps data is stored inside easy-dotfiles
.
There is a tilde ~
folder that contains all the home config files for the configured apps. If you would configure app dotfiles by your full home path /home/ionut/some_config/...
the apps data folder would have a home/ionut
folder inside it. Timeshift
config file is inside the etc
folder. There you can also see the .permissions
file.
The .conf
files are located at the top level because we didn't specify a path for them. They are easily located by configuring them like this. You don't have to care much about .conf
files, just make sure their names are unique inside the same config section, like inside apps config or extensions config. Otherwise, they'll override themselves.
Hopefully you got so far and you're not utterly confused.
Extensions config JSON is more simple than the apps config. You can see it as a subset, because Gnome Extensions only use the dconf
database for their settings. The only extra addition is the extension name and URL that needs to be taken from the official Gnome Extensions website.
Let me make a shameless plug and have a look at the config for my awesome Bedtime Mode Gnome extension:
{
"name": "Bedtime Mode",
"url": "https://extensions.gnome.org/extension/4012/gnome-bedtime/",
"dconf": {
"schema_path": "/org/gnome/shell/extensions/bedtime-mode/",
"file": "bedtime-mode.conf"
}
}
-
name
- Is not mandatory, but nice to have. The install script will nicely print the extension that's currently processing. -
url
- Vital for a proper automatic installation of extensions. The install script will use this URL for getting the extension ID and compute the proper extension download URL. -
dconf
- This config part behaves exactly as the one for apps. You only need to specify the extension'sschema_path
and thefile
to which it's settings will be saved. The properschema_path
for an extension can be easily obtained by using the nice dconf editor app. You need to go to the/org/gnome/shell/extensions/
path and copy the path to the corresponding extension.
NOTE: Please make sure the dconf
file
names are unique inside this config section. Also, if an extension doesn't store any settings, you can just skip the dconf
part:
{
"name": "Auto select headset",
"url": "https://extensions.gnome.org/extension/3928/auto-select-headset/"
}
After finishing this guide one might argue that there's no need for a separate keybindings
config section. Well, I also agree on that but I'll just leave this as a separate section in order not to clutter too much the Tweaks section.
For keybindings config JSON you can leave it as it is provided in the sample folder. It should cover all the places where user defined keybindings might occur in the Gnome Shell.
You can observe that the json structure is very similar to previous mentioned dconf
parts. We just skipped the redundant dconf
parent property and used only schema_path
and file
.
On my setup, only two of those schema_path
paths have settings, so not all the sample keybindings data files have content. By looking into the .conf
files you can see my interesting collection of shortcuts that I configured for the sample data.
You might have some files, that are not tied to a specific app or extension, and you want to manage also those by using easy-dotfiles
.
The miscellaneous config section does exactly that. Here you can configure other needed files or folders that didn't fit into the apps config section.
You might have some backgrounds that you use as wallpapers and want to transfer also those to another setup. Maybe you want to preserve the configured auto start applications, your bash aliases or some other custom scripts or files. This is the place for those outcasts.
The JSON structure is a subset of the apps config, for example:
{
"name": "Startup Apps",
"files": {
"include": ["~/.config/autostart"]
}
}
-
name
- Just a nice way to name a group of similar files. -
files
- Behaves exactly as the apps config part. You specify theinclude
property child for files or folders andexclude
if you want to filter out some stuff. Supports*
character for globbing but only for the last level in the supplied path. The path can be a user home file or an outside home file. For the outside home files their permissions are stored in a.permissions
file and restored when importing.
NOTE: Be careful when configuring outside home files that are system files.
For example the /var/lib/AccountsService/users/ionut
file which is not readable by my user has some info related to my login icon and some other stuff. I can configure this file to be managed by easy-dotfiles
but when I restore it on another system, the terminal won't open anymore. So you might get unexpected results if you tinker with system files. App config files or other personal files should be usually safe to configure.
The tweaks configuration section can be used to manage your tinkering with the Gnome Shell configuration. This json contains only dconf
specific config and can look like this:
{
"schema_path": "/org/gnome/GWeather4/",
"keys": ["temperature-unit"],
"file": "g-weather.conf"
},
{
"schema_path": "/org/gtk/",
"keys": ["/settings/file-chooser/", "/gtk4/settings/file-chooser/"],
"file": "file-chooser.conf"
},
{
"schema_path": "/org/gnome/settings-daemon/plugins/power/",
"file": "power.conf"
}
The above is just a snippet from the included sample tweaks config. The structure is the same as for Keybindings and the dconf
part of the Applications config:
-
schema_path
- Thedconf
schema path of the settings you want to be managed byeasy-dotfiles
. All the settings that are stored under this path, direct children or sub-paths containing other child settings or paths are exported to the.conf
file if you don't specify the nextkeys
field. -
keys
- If you want to filter for specific keys or sub-paths. Usually, you don't need to use this field, especially fordconf
parts of apps or extensions config. It comes handy when you want manage only specific settings or a more complex scenario that involves differentschema_path
folders. More on this later on, inside the dedicated section. -
file
- The name of the file to which thedconf
settings specified byschema_path
are saved. Only file names with no path, they will be saved on the section's top level folder, see the tweaks data folder contents. Please make sure that the tweaks config file has uniquefile
names configured.
By using the tweaks configuration you can export / import all your tweaks to the Gnome Shell and Gnome Applications. Wait, what? Didn't we cover apps already?
Yes, we already covered apps, but this configuration section is mainly for core Gnome apps that are usually bundled with the Gnome shell. Most of them will be already preinstalled and are using dconf
for storing their settings.
Apps like Text Editor, Files (Nautilus), System Monitor or Calendar are storing their settings into the dconf
database and the tweaks section is a very good place to let easy-dotfiles
know of them.
Beside core apps settings, Gnome stores some of its internal settings also into the dconf
database. Things like favorite apps for the dash, light / dark theme wallpapers, power configuration, etc. The tweaks section is also the place for all of these settings. You can check the .conf
files from the sample tweaks data folder where you can see what settings were exported by using the tweaks config file.
There might be some cases when you don't want to export all the settings from a specific schema_path
. Maybe a distro will configure Gnome Shell its own way and you don't want to really import those settings on another distro that might configured it slightly different. Here comes the keys
property to the rescue.
Let's have a look at this snippet from the tweaks config file:
{
"schema_path": "/org/gnome/shell/",
"keys": ["favorite-apps", "/weather/"],
"file": "gnome.shell.conf"
}
The keys
property is an array that can specify which keys should be included from the corresponding schema_path
.
Launch the dconf editor app and go to the /org/gnome/shell/
path. You can see that there are some sub-paths (sub-folders) and some direct properties as children. The direct properties are key
- value
pairs and they can be explicitly filtered by using the keys
config json property.
You have /org/gnome/shell/
as schema_path
but you only want to manage the value for the favorite-apps
setting. Then you would add a json config like this:
{
"schema_path": "/org/gnome/shell/",
"keys": ["favorite-apps"],
"file": "gnome.shell.conf"
}
If you want to filter by multiple keys you would do something like this:
{
"schema_path": "/org/gnome/shell/",
"keys": ["favorite-apps", "command-history"],
"file": "gnome.shell.conf"
}
But what if you want to filter the keys for the current shema_path
but want to include also the direct child settings of a specific sub-path?
Then you can do this:
{
"schema_path": "/org/gnome/shell/",
"keys": ["favorite-apps", "/weather/"],
"file": "gnome.shell.conf"
}
The above will include the favorite-apps
setting from /org/gnome/shell/
path and all the direct child settings of the /org/gnome/shell/weather/
sub-path. What?
Go to the dconf editor
app and check the /org/gnome/shell/
path. Now go into the weather
sub-path and check it's child settings. There are two of them: automatic-location
and locations
. By using the "keys": ["favorite-apps", "/weather/"]
config you will manage the favorite-apps
setting and all the child settings (automatic-location
and locations
) of the /weather/
sub-path.
A sub-path is defined as a string surrounded by slashes /
: /weather/
, /calendar/
, /peripherals/keyboard/
, /peripherals/mouse/
.
So a keys
json config property can contain actual keys and (or) sub-paths and keys, separated by space.
Let's say that for the above example you only want to manage the favorite-apps
and automatic-location
setting from the /weather/
sub-path. You have two options:
- Basic
schema_path
config:
{
"schema_path": "/org/gnome/shell/",
"keys": ["favorite-apps"],
"file": "gnome.shell.fav-apps.conf"
},
{
"schema_path": "/org/gnome/shell/weather/",
"keys": ["automatic-location"],
"file": "gnome.shell.auto-location.conf"
}
- Advanced
schema_path
config:
{
"schema_path": "/org/gnome/shell/",
"keys": ["favorite-apps", "/weather/ automatic-location"],
"file": "gnome.shell.conf"
}
By using the "advanced config" approach you're not forced to create two separate .conf
files in order to manage those settings. You can use a single .conf
file and by incorporating the /weather/
sub-path in the keys
property you can filter out for the automatic-location
setting.
NOTE: Sub-paths must be enclosed by the forward slash /
character and the child keys should follow delimited by space:
{
"schema_path": "/org/gnome/desktop/",
"keys": ["/sound/ allow-volume-above-100-percent event-sounds"],
"file": "gnome.desktop.sound.conf"
}
The above will tell easy-dotfiles
that starting from the /org/gnome/desktop/
schema_path
it should manage only the allow-volume-above-100-percent
and event-sounds
settings of the /sound/
sub-path.
NOTE: Sub-path filtering will include only the direct child settings of that path. Let's say you want to do an advanced schema_path
config for the gnome desktop accessibility and peripherals settings:
{
"schema_path": "/org/gnome/desktop/",
"keys": ["/a11y/", "/peripherals/"],
"file": "gnome.desktop.conf"
}
The above configuration won't match all the settings in /a11y/
and /peripherals/
because if you go to the dconf editor
app and check the /org/gnome/desktop/peripherals/
path, there aren't any direct child settings, only other sub-paths.
If you check the /org/gnome/desktop/a11y/
path in the dconf editor
app, there are only two direct child settings of this path, but also another sub-paths, applications
, keyboard
, magnifier
and mouse
. Only the direct child settings will be managed if you use the above config json.
If you want to manage all the subsequent child settings, you need to use asterisk *
in the sub-path name: /a11y*/
, /peripherals*/
, /other/sub/path*/
.
NOTE: Only one sub-path permitted per keys
array entry and asterisk *
works only on the last part of the sub-path:
{
"schema_path": "/org/gnome/desktop/",
"keys": ["/a11y*/", "/peripherals*/"],
"file": "gnome.desktop.conf"
}
The above will include all the child settings from all the sub-paths starting from a11y
and peripherals
. Even if you use the asterisk *
character you can filter for child settings by doing this:
{
"schema_path": "/org/gnome/desktop/",
"keys": ["/a11y*/", "/peripherals*/ natural-scroll"],
"file": "gnome.desktop.conf"
}
The above will include all the child settings from the a11y
sub-paths and only natural-scroll
settings from the peripherals
sub-paths.
If you don't mind having multiple .conf
files, you can always create two separate entries for the above config and don't use the keys
property:
{
"schema_path": "/org/gnome/desktop/a11y/",
"file": "gnome.desktop.a11y.conf"
},
{
"schema_path": "/org/gnome/desktop/peripherals/",
"file": "gnome.desktop.peripherals.conf"
}
So you have multiple options on how to manage your dconf
settings.
Advanced schema_path
config can look a tad complicated but it's an interesting way of reducing the number of needed .conf
files.
{
"schema_path": "/org/gnome/desktop/",
"keys": [
"/a11y*/",
"/background/",
"/calendar/",
"/datetime/",
"/input-sources/",
"/interface/ text-scaling-factor clock-show-weekday",
"/peripherals*/",
"/privacy/",
"/sound/ allow-volume-above-100-percent event-sounds",
"/wm/preferences/"
],
"file": "gnome.desktop.conf"
}
If you reached so far, I think some congratulations are in order.
I hope you got familiar on how to configure the different JSON sections. You always have the sample config to bootstrap your easy-dotfiles
configuration or to use it as a reference. It should be set it once and forget it kind of thing.
There are some other small configuration parts that you might want to have a look at by reading this section.