mirror of
https://github.com/SqrtMinusOne/dotfiles.git
synced 2025-12-10 19:23:03 +03:00
Compare commits
3 commits
db033371c3
...
2715390f16
| Author | SHA1 | Date | |
|---|---|---|---|
| 2715390f16 | |||
| 2e64d77ba8 | |||
| 6c489b4a32 |
12 changed files with 204 additions and 60 deletions
|
|
@ -3,6 +3,7 @@ enabled_backends = ["arch"]
|
||||||
hostname_groups_enabled = true
|
hostname_groups_enabled = true
|
||||||
|
|
||||||
[hostname_groups]
|
[hostname_groups]
|
||||||
|
violet = ["system","office","nvidia","music","mail","latex","emacs","dev","desktop-rofi","desktop-polybar","desktop-misc","desktop","console","browsers"]
|
||||||
weiss = ["system","office","music","mail","latex","emacs","dev","desktop-rofi","desktop-polybar","desktop-misc","desktop","console","browsers"]
|
weiss = ["system","office","music","mail","latex","emacs","dev","desktop-rofi","desktop-polybar","desktop-misc","desktop","console","browsers"]
|
||||||
archlinux = ["system","office","music","mail","latex","emacs","dev","desktop-rofi","desktop-polybar","desktop-misc","desktop","console","browsers"]
|
archlinux = ["system","office","music","mail","latex","emacs","dev","desktop-rofi","desktop-polybar","desktop-misc","desktop","console","browsers"]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
arch = [
|
arch = [
|
||||||
|
"veracrypt",
|
||||||
"android-file-transfer",
|
"android-file-transfer",
|
||||||
"remmina",
|
"remmina",
|
||||||
"noto-fonts-emoji",
|
"noto-fonts-emoji",
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ arch = [
|
||||||
"xf86-video-ati",
|
"xf86-video-ati",
|
||||||
"xf86-video-amdgpu",
|
"xf86-video-amdgpu",
|
||||||
"vulkan-radeon",
|
"vulkan-radeon",
|
||||||
"nvidia-utils",
|
|
||||||
"vulkan-intel",
|
"vulkan-intel",
|
||||||
"intel-media-driver",
|
"intel-media-driver",
|
||||||
"libva-intel-driver",
|
"libva-intel-driver",
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
[Unit]
|
[Unit]
|
||||||
Description=Run notmuch new every 5 minutes
|
Description=Run notmuch sync script every 5 minutes
|
||||||
|
|
||||||
[Timer]
|
[Timer]
|
||||||
OnBootSec=1min
|
OnBootSec=1min
|
||||||
|
|
|
||||||
|
|
@ -381,14 +381,14 @@ DIR is either 'left or 'right."
|
||||||
_t_: Terminal (Alacritty)
|
_t_: Terminal (Alacritty)
|
||||||
_b_: Browser (Firefox)
|
_b_: Browser (Firefox)
|
||||||
_s_: Rocket.Chat
|
_s_: Rocket.Chat
|
||||||
_e_: Element
|
_d_: DBeaver
|
||||||
_d_: Discord
|
_c_: Chromium
|
||||||
"
|
"
|
||||||
("t" (lambda () (interactive) (my/run-in-background "alacritty")))
|
("t" (lambda () (interactive) (my/run-in-background "alacritty")))
|
||||||
("b" (lambda () (interactive) (my/run-in-background "firefox")))
|
("b" (lambda () (interactive) (my/run-in-background "firefox")))
|
||||||
("s" (lambda () (interactive) (my/run-in-background "flatpak run chat.rocket.RocketChat")))
|
("s" (lambda () (interactive) (my/run-in-background "rocketchat-desktop")))
|
||||||
("e" (lambda () (interactive) (my/run-in-background "flatpak run im.riot.Riot")))
|
("d" (lambda () (interactive) (my/run-in-background "dbeaver")))
|
||||||
("d" (lambda () (interactive) (my/run-in-background "flatpak run com.discordapp.Discord"))))
|
("c" (lambda () (interactive) (my/run-in-background "chromium"))))
|
||||||
|
|
||||||
(defun my/exwm-lock ()
|
(defun my/exwm-lock ()
|
||||||
(interactive)
|
(interactive)
|
||||||
|
|
@ -594,10 +594,10 @@ _d_: Discord
|
||||||
|
|
||||||
(my/exwm-set-wallpaper)
|
(my/exwm-set-wallpaper)
|
||||||
(my/exwm-run-shepherd)
|
(my/exwm-run-shepherd)
|
||||||
(my/exwm-run-systemd)
|
|
||||||
(my/run-in-background "gpgconf --reload gpg-agent")
|
(my/run-in-background "gpgconf --reload gpg-agent")
|
||||||
(my/exwm-run-polybar)
|
(my/exwm-run-polybar)
|
||||||
(setenv "DBUS_SESSION_BUS_ADDRESS" "unix:path=/run/user/1000/bus")
|
(setenv "DBUS_SESSION_BUS_ADDRESS" "unix:path=/run/user/1000/bus")
|
||||||
|
(my/exwm-run-systemd)
|
||||||
(when (my/is-arch)
|
(when (my/is-arch)
|
||||||
(my/run-in-background "set_layout")))
|
(my/run-in-background "set_layout")))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -82,6 +82,8 @@
|
||||||
;; I have everything I need in polybar
|
;; I have everything I need in polybar
|
||||||
(emms-mode-line-mode -1)
|
(emms-mode-line-mode -1)
|
||||||
(emms-playing-time-display-mode -1)
|
(emms-playing-time-display-mode -1)
|
||||||
|
(delq 'emms-mark-mode evil-emacs-state-modes)
|
||||||
|
(delq 'emms-browser-mode evil-emacs-state-modes)
|
||||||
(defun emms-info-mpd-process (track info)
|
(defun emms-info-mpd-process (track info)
|
||||||
(dolist (data info)
|
(dolist (data info)
|
||||||
(let ((name (car data))
|
(let ((name (car data))
|
||||||
|
|
|
||||||
|
|
@ -375,8 +375,11 @@ REMOTE = '<rclone-remote>'
|
||||||
FOLDERS = json.loads('<rclone-folders-json>')
|
FOLDERS = json.loads('<rclone-folders-json>')
|
||||||
OPTIONS = json.loads('<rclone-options>')
|
OPTIONS = json.loads('<rclone-options>')
|
||||||
|
|
||||||
|
for folder, i in zip(FOLDERS, range(len(FOLDERS))):
|
||||||
|
folder['id'] = i
|
||||||
|
|
||||||
def rclone_make_command(local_path, remote_path, remote):
|
|
||||||
|
def rclone_make_command(local_path, remote_path, remote, extra_args=[]):
|
||||||
return [
|
return [
|
||||||
'rclone',
|
'rclone',
|
||||||
'bisync',
|
'bisync',
|
||||||
|
|
@ -390,7 +393,8 @@ def rclone_make_command(local_path, remote_path, remote):
|
||||||
'NEVER',
|
'NEVER',
|
||||||
'--use-json-log',
|
'--use-json-log',
|
||||||
'--stats',
|
'--stats',
|
||||||
'9999m'
|
'9999m',
|
||||||
|
*extra_args
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -418,15 +422,26 @@ def process_output(output):
|
||||||
except Exception:
|
except Exception:
|
||||||
print(line)
|
print(line)
|
||||||
|
|
||||||
def rclone_run(folder):
|
def process_command_for_print(command):
|
||||||
|
res = []
|
||||||
|
for c in command:
|
||||||
|
if ' ' in c:
|
||||||
|
res.append(f'\\'{c}\\'')
|
||||||
|
else:
|
||||||
|
res.append(c)
|
||||||
|
return ' '.join(res)
|
||||||
|
|
||||||
|
|
||||||
|
def rclone_run(folder, extra_args=[]):
|
||||||
command = rclone_make_command(
|
command = rclone_make_command(
|
||||||
folder['local-path'], folder['remote-path'], folder['remote']
|
folder['local-path'], folder['remote-path'], folder['remote'], extra_args
|
||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
|
print(str(folder['id']) + '. ' + process_command_for_print(command))
|
||||||
result = subprocess.run(command, check=True, capture_output=True, text=True)
|
result = subprocess.run(command, check=True, capture_output=True, text=True)
|
||||||
except subprocess.CalledProcessError as e:
|
except subprocess.CalledProcessError as e:
|
||||||
print(f'=== Error syncing {folder['local-path']} ===')
|
print(f'=== Error syncing {folder['local-path']} ===')
|
||||||
print(f'Command: {' '.join(command)}')
|
print(f'Command: {process_command_for_print(command)}')
|
||||||
print(f'--- STDOUT ---')
|
print(f'--- STDOUT ---')
|
||||||
process_output(e.stdout)
|
process_output(e.stdout)
|
||||||
print(f'--- STDERR ---')
|
print(f'--- STDERR ---')
|
||||||
|
|
@ -434,7 +449,6 @@ def rclone_run(folder):
|
||||||
return {'success': False, 'stats': {}}
|
return {'success': False, 'stats': {}}
|
||||||
return {'success': True, 'stats': parse_rclone_stats(result.stderr)}
|
return {'success': True, 'stats': parse_rclone_stats(result.stderr)}
|
||||||
|
|
||||||
|
|
||||||
def notify(summary, body, level='normal', expire_time=5000):
|
def notify(summary, body, level='normal', expire_time=5000):
|
||||||
subprocess.run(['notify-send', '-u', level, '-t', str(expire_time), summary, body])
|
subprocess.run(['notify-send', '-u', level, '-t', str(expire_time), summary, body])
|
||||||
|
|
||||||
|
|
@ -447,17 +461,16 @@ def sizeof_fmt(num, suffix='B'):
|
||||||
return f'{num:.1f}Yi{suffix}'
|
return f'{num:.1f}Yi{suffix}'
|
||||||
|
|
||||||
|
|
||||||
def rclone_run_all(folders):
|
def rclone_run_all(folders, extra_args=[]):
|
||||||
error_folders = []
|
error_folders = []
|
||||||
total_bytes = 0
|
total_bytes = 0
|
||||||
total_transfers = 0
|
total_transfers = 0
|
||||||
total_deleted = 0
|
total_deleted = 0
|
||||||
total_renamed = 0
|
total_renamed = 0
|
||||||
for folder in folders:
|
for folder in folders:
|
||||||
print(f'Running rclone for {folder}')
|
res = rclone_run(folder, extra_args)
|
||||||
res = rclone_run(folder)
|
|
||||||
if not res['success']:
|
if not res['success']:
|
||||||
error_folders.append(folder['local-path'])
|
error_folders.append(folder)
|
||||||
else:
|
else:
|
||||||
total_bytes += res.get('stats', {}).get('bytes', 0)
|
total_bytes += res.get('stats', {}).get('bytes', 0)
|
||||||
total_transfers += res.get('stats', {}).get('transfers', 0)
|
total_transfers += res.get('stats', {}).get('transfers', 0)
|
||||||
|
|
@ -469,21 +482,44 @@ def rclone_run_all(folders):
|
||||||
if total_transfers > 0:
|
if total_transfers > 0:
|
||||||
msg += f'''Transferred {total_transfers} files ({sizeof_fmt(total_bytes)})\n'''
|
msg += f'''Transferred {total_transfers} files ({sizeof_fmt(total_bytes)})\n'''
|
||||||
if total_deleted > 0:
|
if total_deleted > 0:
|
||||||
msg += f'''Deleted {total_transfers} files\n'''
|
msg += f'''Deleted {total_deleted} files\n'''
|
||||||
if total_renamed > 0:
|
if total_renamed > 0:
|
||||||
msg += f'''Renamed {total_renamed} files\n'''
|
msg += f'''Renamed {total_renamed} files\n'''
|
||||||
|
|
||||||
if len(error_folders) > 0:
|
if len(error_folders) > 0:
|
||||||
msg += '''\nSync errors for the following folders:'''
|
msg += '''\nSync errors for the following folders:'''
|
||||||
for folder in error_folders:
|
for folder in error_folders:
|
||||||
msg += '''\n- ''' + folder
|
msg += '''\n- ''' + str(folder['id']) + '. ' + folder['local-path']
|
||||||
level = 'critical'
|
level = 'critical'
|
||||||
|
|
||||||
if len(msg) > 0:
|
if len(msg) > 0:
|
||||||
notify(f'rclone sync {REMOTE}', msg, level=level)
|
notify(f'rclone sync {REMOTE}', msg, level=level)
|
||||||
|
|
||||||
|
def parse_arguments():
|
||||||
|
if len(sys.argv) < 2:
|
||||||
|
return None, []
|
||||||
|
|
||||||
|
id_arg = sys.argv[1]
|
||||||
|
folder_ids = [int(x.strip()) for x in id_arg.split(',')]
|
||||||
|
|
||||||
|
extra_args = sys.argv[2:]
|
||||||
|
|
||||||
|
return folder_ids, extra_args
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
rclone_run_all(FOLDERS)
|
folder_ids, extra_args = parse_arguments()
|
||||||
|
|
||||||
|
if folder_ids is None:
|
||||||
|
selected_folders = FOLDERS
|
||||||
|
else:
|
||||||
|
selected_folders = [f for f in FOLDERS if f['id'] in folder_ids]
|
||||||
|
found_ids = {f['id'] for f in selected_folders}
|
||||||
|
missing_ids = set(folder_ids) - found_ids
|
||||||
|
if missing_ids:
|
||||||
|
print(f'Warning: folder IDs not found: {sorted(missing_ids)}')
|
||||||
|
|
||||||
|
rclone_run_all(selected_folders, extra_args)
|
||||||
"))
|
"))
|
||||||
(setq script
|
(setq script
|
||||||
(thread-last script
|
(thread-last script
|
||||||
|
|
@ -562,6 +598,23 @@ The return value is a list of commands as defined by
|
||||||
commands))
|
commands))
|
||||||
(nreverse commands)))
|
(nreverse commands)))
|
||||||
|
|
||||||
|
(defun my/index-rclone-reset ()
|
||||||
|
"Delete all rclone test files."
|
||||||
|
(interactive)
|
||||||
|
(let* ((tree (my/index--tree-retrive))
|
||||||
|
(folders (my/index--rclone-get-folders tree))
|
||||||
|
files-to-delete)
|
||||||
|
(dolist (folder folders)
|
||||||
|
(let ((test-file-path
|
||||||
|
(concat
|
||||||
|
(alist-get :local-path folder)
|
||||||
|
(format ".rclone-test-%s" (alist-get :remote folder)))))
|
||||||
|
(when (file-exists-p test-file-path)
|
||||||
|
(push test-file-path files-to-delete))))
|
||||||
|
(when (y-or-n-p (format "Delete %d files" (seq-length files-to-delete)))
|
||||||
|
(dolist (file files-to-delete)
|
||||||
|
(delete-file file)))))
|
||||||
|
|
||||||
(defun my/index--git-commands (tree)
|
(defun my/index--git-commands (tree)
|
||||||
"Get commands to clone the yet uncloned git repos in TREE.
|
"Get commands to clone the yet uncloned git repos in TREE.
|
||||||
|
|
||||||
|
|
|
||||||
49
Arch.org
49
Arch.org
|
|
@ -70,6 +70,12 @@ Host *
|
||||||
AddKeysToAgent yes
|
AddKeysToAgent yes
|
||||||
#+end_src
|
#+end_src
|
||||||
|
|
||||||
|
And run:
|
||||||
|
#+begin_src bash
|
||||||
|
systemctl enable --user --now ssh-agent
|
||||||
|
#+end_src
|
||||||
|
It looks like it is necessary to run at least once.
|
||||||
|
|
||||||
Then, clone the dotfiles repo with =yadm=:
|
Then, clone the dotfiles repo with =yadm=:
|
||||||
#+begin_src bash
|
#+begin_src bash
|
||||||
yadm clone git@github.com:SqrtMinusOne/dotfiles.git
|
yadm clone git@github.com:SqrtMinusOne/dotfiles.git
|
||||||
|
|
@ -134,6 +140,8 @@ sudo systemctl disable --now systemd-networkd
|
||||||
#+end_src
|
#+end_src
|
||||||
And reboot. After the reboot, =NetworkManager= should run using =wpa_supplicant= as backend for Wi-Fi.
|
And reboot. After the reboot, =NetworkManager= should run using =wpa_supplicant= as backend for Wi-Fi.
|
||||||
|
|
||||||
|
Don't forget to sync mail and enable the sync timer.
|
||||||
|
|
||||||
* Metapac configuration
|
* Metapac configuration
|
||||||
[[https://github.com/ripytide/metapac][metapac]] is a declarative wrapper around different package managers, including [[https://wiki.archlinux.org/title/Pacman][pacman]] and [[https://github.com/Morganamilo/paru][paru]]. This means the required packages can be listed in configuration files and checked into version control.
|
[[https://github.com/ripytide/metapac][metapac]] is a declarative wrapper around different package managers, including [[https://wiki.archlinux.org/title/Pacman][pacman]] and [[https://github.com/Morganamilo/paru][paru]]. This means the required packages can be listed in configuration files and checked into version control.
|
||||||
|
|
||||||
|
|
@ -143,20 +151,21 @@ In =metapac=, packages are listed in "groups", each group being a TOML file stat
|
||||||
|
|
||||||
Below is the table enabling different groups on different hostnames:
|
Below is the table enabling different groups on different hostnames:
|
||||||
#+NAME: metapac-groups
|
#+NAME: metapac-groups
|
||||||
| Profile | archlinux | weiss |
|
| Profile | archlinux | weiss | violet |
|
||||||
| browsers | + | + |
|
| browsers | + | + | + |
|
||||||
| console | + | + |
|
| console | + | + | + |
|
||||||
| desktop | + | + |
|
| desktop | + | + | + |
|
||||||
| desktop-misc | + | + |
|
| desktop-misc | + | + | + |
|
||||||
| desktop-polybar | + | + |
|
| desktop-polybar | + | + | + |
|
||||||
| desktop-rofi | + | + |
|
| desktop-rofi | + | + | + |
|
||||||
| dev | + | + |
|
| dev | + | + | + |
|
||||||
| emacs | + | + |
|
| emacs | + | + | + |
|
||||||
| latex | + | + |
|
| latex | + | + | + |
|
||||||
| mail | + | + |
|
| mail | + | + | + |
|
||||||
| music | + | + |
|
| music | + | + | + |
|
||||||
| office | + | + |
|
| nvidia | | | + |
|
||||||
| system | + | + |
|
| office | + | + | + |
|
||||||
|
| system | + | + | + |
|
||||||
|
|
||||||
And the code to format it as TOML:
|
And the code to format it as TOML:
|
||||||
#+NAME: metapac-groups-format
|
#+NAME: metapac-groups-format
|
||||||
|
|
@ -231,16 +240,26 @@ Various drivers, I'm not sure which I actually need, so...
|
||||||
| libva-intel-driver |
|
| libva-intel-driver |
|
||||||
| intel-media-driver |
|
| intel-media-driver |
|
||||||
| vulkan-intel |
|
| vulkan-intel |
|
||||||
| nvidia-utils |
|
|
||||||
| vulkan-radeon |
|
| vulkan-radeon |
|
||||||
| xf86-video-amdgpu |
|
| xf86-video-amdgpu |
|
||||||
| xf86-video-ati |
|
| xf86-video-ati |
|
||||||
|
|
||||||
|
NVIDIA drivers for violet
|
||||||
|
| Category | Arch dependency |
|
||||||
|
|----------+-----------------|
|
||||||
|
| nvidia | cuda |
|
||||||
|
| nvidia | nvidia-utils |
|
||||||
|
| nvidia | nvidia |
|
||||||
|
|
||||||
#+NAME: packages
|
#+NAME: packages
|
||||||
#+begin_src emacs-lisp :tangle no :var category=""
|
#+begin_src emacs-lisp :tangle no :var category=""
|
||||||
(my/format-arch-dependencies category)
|
(my/format-arch-dependencies category)
|
||||||
#+end_src
|
#+end_src
|
||||||
|
|
||||||
|
#+begin_src scheme :tangle .config/metapac/groups/nvidia.toml :noweb yes
|
||||||
|
<<packages("nvidia")>>
|
||||||
|
#+end_src
|
||||||
|
|
||||||
#+begin_src scheme :tangle .config/metapac/groups/system.toml :noweb yes
|
#+begin_src scheme :tangle .config/metapac/groups/system.toml :noweb yes
|
||||||
<<packages()>>
|
<<packages()>>
|
||||||
#+end_src
|
#+end_src
|
||||||
|
|
|
||||||
14
Desktop.org
14
Desktop.org
|
|
@ -965,14 +965,14 @@ A +transient+ hydra for shortcuts for the most frequent apps.
|
||||||
_t_: Terminal (Alacritty)
|
_t_: Terminal (Alacritty)
|
||||||
_b_: Browser (Firefox)
|
_b_: Browser (Firefox)
|
||||||
_s_: Rocket.Chat
|
_s_: Rocket.Chat
|
||||||
_e_: Element
|
_d_: DBeaver
|
||||||
_d_: Discord
|
_c_: Chromium
|
||||||
"
|
"
|
||||||
("t" (lambda () (interactive) (my/run-in-background "alacritty")))
|
("t" (lambda () (interactive) (my/run-in-background "alacritty")))
|
||||||
("b" (lambda () (interactive) (my/run-in-background "firefox")))
|
("b" (lambda () (interactive) (my/run-in-background "firefox")))
|
||||||
("s" (lambda () (interactive) (my/run-in-background "flatpak run chat.rocket.RocketChat")))
|
("s" (lambda () (interactive) (my/run-in-background "rocketchat-desktop")))
|
||||||
("e" (lambda () (interactive) (my/run-in-background "flatpak run im.riot.Riot")))
|
("d" (lambda () (interactive) (my/run-in-background "dbeaver")))
|
||||||
("d" (lambda () (interactive) (my/run-in-background "flatpak run com.discordapp.Discord"))))
|
("c" (lambda () (interactive) (my/run-in-background "chromium"))))
|
||||||
#+end_src
|
#+end_src
|
||||||
*** Locking up
|
*** Locking up
|
||||||
Run i3lock.
|
Run i3lock.
|
||||||
|
|
@ -1219,10 +1219,10 @@ And the EXWM config itself.
|
||||||
|
|
||||||
(my/exwm-set-wallpaper)
|
(my/exwm-set-wallpaper)
|
||||||
(my/exwm-run-shepherd)
|
(my/exwm-run-shepherd)
|
||||||
(my/exwm-run-systemd)
|
|
||||||
(my/run-in-background "gpgconf --reload gpg-agent")
|
(my/run-in-background "gpgconf --reload gpg-agent")
|
||||||
(my/exwm-run-polybar)
|
(my/exwm-run-polybar)
|
||||||
(setenv "DBUS_SESSION_BUS_ADDRESS" "unix:path=/run/user/1000/bus")
|
(setenv "DBUS_SESSION_BUS_ADDRESS" "unix:path=/run/user/1000/bus")
|
||||||
|
(my/exwm-run-systemd)
|
||||||
(when (my/is-arch)
|
(when (my/is-arch)
|
||||||
(my/run-in-background "set_layout")))
|
(my/run-in-background "set_layout")))
|
||||||
|
|
||||||
|
|
@ -4239,6 +4239,7 @@ This section generates manifests for various desktop software that I'm using.
|
||||||
| office | okular |
|
| office | okular |
|
||||||
| office | obs-studio |
|
| office | obs-studio |
|
||||||
| office | rocketchat-desktop |
|
| office | rocketchat-desktop |
|
||||||
|
|
||||||
** LaTeX
|
** LaTeX
|
||||||
| Category | Arch dependency | Disabled |
|
| Category | Arch dependency | Disabled |
|
||||||
|----------+--------------------------+----------|
|
|----------+--------------------------+----------|
|
||||||
|
|
@ -4540,6 +4541,7 @@ Other desktop programs I use are listed below.
|
||||||
| desktop-misc | noto-fonts-emoji | |
|
| desktop-misc | noto-fonts-emoji | |
|
||||||
| desktop-misc | remmina | |
|
| desktop-misc | remmina | |
|
||||||
| desktop-misc | android-file-transfer | |
|
| desktop-misc | android-file-transfer | |
|
||||||
|
| desktop-misc | veracrypt | |
|
||||||
|
|
||||||
#+NAME: packages
|
#+NAME: packages
|
||||||
#+begin_src emacs-lisp :tangle no
|
#+begin_src emacs-lisp :tangle no
|
||||||
|
|
|
||||||
84
Emacs.org
84
Emacs.org
|
|
@ -10433,6 +10433,8 @@ References:
|
||||||
;; I have everything I need in polybar
|
;; I have everything I need in polybar
|
||||||
(emms-mode-line-mode -1)
|
(emms-mode-line-mode -1)
|
||||||
(emms-playing-time-display-mode -1)
|
(emms-playing-time-display-mode -1)
|
||||||
|
(delq 'emms-mark-mode evil-emacs-state-modes)
|
||||||
|
(delq 'emms-browser-mode evil-emacs-state-modes)
|
||||||
<<emms-fixes>>)
|
<<emms-fixes>>)
|
||||||
#+end_src
|
#+end_src
|
||||||
**** MPD
|
**** MPD
|
||||||
|
|
@ -12870,8 +12872,11 @@ REMOTE = '<rclone-remote>'
|
||||||
FOLDERS = json.loads('<rclone-folders-json>')
|
FOLDERS = json.loads('<rclone-folders-json>')
|
||||||
OPTIONS = json.loads('<rclone-options>')
|
OPTIONS = json.loads('<rclone-options>')
|
||||||
|
|
||||||
|
for folder, i in zip(FOLDERS, range(len(FOLDERS))):
|
||||||
|
folder['id'] = i
|
||||||
|
|
||||||
def rclone_make_command(local_path, remote_path, remote):
|
|
||||||
|
def rclone_make_command(local_path, remote_path, remote, extra_args=[]):
|
||||||
return [
|
return [
|
||||||
'rclone',
|
'rclone',
|
||||||
'bisync',
|
'bisync',
|
||||||
|
|
@ -12885,7 +12890,8 @@ def rclone_make_command(local_path, remote_path, remote):
|
||||||
'NEVER',
|
'NEVER',
|
||||||
'--use-json-log',
|
'--use-json-log',
|
||||||
'--stats',
|
'--stats',
|
||||||
'9999m'
|
'9999m',
|
||||||
|
,*extra_args
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -12913,15 +12919,26 @@ def process_output(output):
|
||||||
except Exception:
|
except Exception:
|
||||||
print(line)
|
print(line)
|
||||||
|
|
||||||
def rclone_run(folder):
|
def process_command_for_print(command):
|
||||||
|
res = []
|
||||||
|
for c in command:
|
||||||
|
if ' ' in c:
|
||||||
|
res.append(f'\\'{c}\\'')
|
||||||
|
else:
|
||||||
|
res.append(c)
|
||||||
|
return ' '.join(res)
|
||||||
|
|
||||||
|
|
||||||
|
def rclone_run(folder, extra_args=[]):
|
||||||
command = rclone_make_command(
|
command = rclone_make_command(
|
||||||
folder['local-path'], folder['remote-path'], folder['remote']
|
folder['local-path'], folder['remote-path'], folder['remote'], extra_args
|
||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
|
print(str(folder['id']) + '. ' + process_command_for_print(command))
|
||||||
result = subprocess.run(command, check=True, capture_output=True, text=True)
|
result = subprocess.run(command, check=True, capture_output=True, text=True)
|
||||||
except subprocess.CalledProcessError as e:
|
except subprocess.CalledProcessError as e:
|
||||||
print(f'=== Error syncing {folder['local-path']} ===')
|
print(f'=== Error syncing {folder['local-path']} ===')
|
||||||
print(f'Command: {' '.join(command)}')
|
print(f'Command: {process_command_for_print(command)}')
|
||||||
print(f'--- STDOUT ---')
|
print(f'--- STDOUT ---')
|
||||||
process_output(e.stdout)
|
process_output(e.stdout)
|
||||||
print(f'--- STDERR ---')
|
print(f'--- STDERR ---')
|
||||||
|
|
@ -12929,7 +12946,6 @@ def rclone_run(folder):
|
||||||
return {'success': False, 'stats': {}}
|
return {'success': False, 'stats': {}}
|
||||||
return {'success': True, 'stats': parse_rclone_stats(result.stderr)}
|
return {'success': True, 'stats': parse_rclone_stats(result.stderr)}
|
||||||
|
|
||||||
|
|
||||||
def notify(summary, body, level='normal', expire_time=5000):
|
def notify(summary, body, level='normal', expire_time=5000):
|
||||||
subprocess.run(['notify-send', '-u', level, '-t', str(expire_time), summary, body])
|
subprocess.run(['notify-send', '-u', level, '-t', str(expire_time), summary, body])
|
||||||
|
|
||||||
|
|
@ -12942,17 +12958,16 @@ def sizeof_fmt(num, suffix='B'):
|
||||||
return f'{num:.1f}Yi{suffix}'
|
return f'{num:.1f}Yi{suffix}'
|
||||||
|
|
||||||
|
|
||||||
def rclone_run_all(folders):
|
def rclone_run_all(folders, extra_args=[]):
|
||||||
error_folders = []
|
error_folders = []
|
||||||
total_bytes = 0
|
total_bytes = 0
|
||||||
total_transfers = 0
|
total_transfers = 0
|
||||||
total_deleted = 0
|
total_deleted = 0
|
||||||
total_renamed = 0
|
total_renamed = 0
|
||||||
for folder in folders:
|
for folder in folders:
|
||||||
print(f'Running rclone for {folder}')
|
res = rclone_run(folder, extra_args)
|
||||||
res = rclone_run(folder)
|
|
||||||
if not res['success']:
|
if not res['success']:
|
||||||
error_folders.append(folder['local-path'])
|
error_folders.append(folder)
|
||||||
else:
|
else:
|
||||||
total_bytes += res.get('stats', {}).get('bytes', 0)
|
total_bytes += res.get('stats', {}).get('bytes', 0)
|
||||||
total_transfers += res.get('stats', {}).get('transfers', 0)
|
total_transfers += res.get('stats', {}).get('transfers', 0)
|
||||||
|
|
@ -12964,21 +12979,44 @@ def rclone_run_all(folders):
|
||||||
if total_transfers > 0:
|
if total_transfers > 0:
|
||||||
msg += f'''Transferred {total_transfers} files ({sizeof_fmt(total_bytes)})\n'''
|
msg += f'''Transferred {total_transfers} files ({sizeof_fmt(total_bytes)})\n'''
|
||||||
if total_deleted > 0:
|
if total_deleted > 0:
|
||||||
msg += f'''Deleted {total_transfers} files\n'''
|
msg += f'''Deleted {total_deleted} files\n'''
|
||||||
if total_renamed > 0:
|
if total_renamed > 0:
|
||||||
msg += f'''Renamed {total_renamed} files\n'''
|
msg += f'''Renamed {total_renamed} files\n'''
|
||||||
|
|
||||||
if len(error_folders) > 0:
|
if len(error_folders) > 0:
|
||||||
msg += '''\nSync errors for the following folders:'''
|
msg += '''\nSync errors for the following folders:'''
|
||||||
for folder in error_folders:
|
for folder in error_folders:
|
||||||
msg += '''\n- ''' + folder
|
msg += '''\n- ''' + str(folder['id']) + '. ' + folder['local-path']
|
||||||
level = 'critical'
|
level = 'critical'
|
||||||
|
|
||||||
if len(msg) > 0:
|
if len(msg) > 0:
|
||||||
notify(f'rclone sync {REMOTE}', msg, level=level)
|
notify(f'rclone sync {REMOTE}', msg, level=level)
|
||||||
|
|
||||||
|
def parse_arguments():
|
||||||
|
if len(sys.argv) < 2:
|
||||||
|
return None, []
|
||||||
|
|
||||||
|
id_arg = sys.argv[1]
|
||||||
|
folder_ids = [int(x.strip()) for x in id_arg.split(',')]
|
||||||
|
|
||||||
|
extra_args = sys.argv[2:]
|
||||||
|
|
||||||
|
return folder_ids, extra_args
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
rclone_run_all(FOLDERS)
|
folder_ids, extra_args = parse_arguments()
|
||||||
|
|
||||||
|
if folder_ids is None:
|
||||||
|
selected_folders = FOLDERS
|
||||||
|
else:
|
||||||
|
selected_folders = [f for f in FOLDERS if f['id'] in folder_ids]
|
||||||
|
found_ids = {f['id'] for f in selected_folders}
|
||||||
|
missing_ids = set(folder_ids) - found_ids
|
||||||
|
if missing_ids:
|
||||||
|
print(f'Warning: folder IDs not found: {sorted(missing_ids)}')
|
||||||
|
|
||||||
|
rclone_run_all(selected_folders, extra_args)
|
||||||
#+end_src
|
#+end_src
|
||||||
|
|
||||||
A function that templates the script above:
|
A function that templates the script above:
|
||||||
|
|
@ -13073,6 +13111,26 @@ The return value is a list of commands as defined by
|
||||||
(nreverse commands)))
|
(nreverse commands)))
|
||||||
#+end_src
|
#+end_src
|
||||||
|
|
||||||
|
Also, a command to remove all =.rclone-test-*= files. This is necessary, e.g. after a change in the filter file.
|
||||||
|
#+begin_src emacs-lisp
|
||||||
|
(defun my/index-rclone-reset ()
|
||||||
|
"Delete all rclone test files."
|
||||||
|
(interactive)
|
||||||
|
(let* ((tree (my/index--tree-retrive))
|
||||||
|
(folders (my/index--rclone-get-folders tree))
|
||||||
|
files-to-delete)
|
||||||
|
(dolist (folder folders)
|
||||||
|
(let ((test-file-path
|
||||||
|
(concat
|
||||||
|
(alist-get :local-path folder)
|
||||||
|
(format ".rclone-test-%s" (alist-get :remote folder)))))
|
||||||
|
(when (file-exists-p test-file-path)
|
||||||
|
(push test-file-path files-to-delete))))
|
||||||
|
(when (y-or-n-p (format "Delete %d files" (seq-length files-to-delete)))
|
||||||
|
(dolist (file files-to-delete)
|
||||||
|
(delete-file file)))))
|
||||||
|
#+end_src
|
||||||
|
|
||||||
**** Git repos
|
**** Git repos
|
||||||
To sync git, we just need to clone the required git repos. Removing the repos is handled by the folder sync commands.
|
To sync git, we just need to clone the required git repos. Removing the repos is handled by the folder sync commands.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ I use the [[https://leanpub.com/lit-config/read][literate configuration]] strate
|
||||||
|
|
||||||
The files themselves are managed and deployed via [[https://yadm.io/][yadm]], although I use Org Mode for things like config templating.
|
The files themselves are managed and deployed via [[https://yadm.io/][yadm]], although I use Org Mode for things like config templating.
|
||||||
|
|
||||||
My current GNU/Linux distribution is [[https://guix.gnu.org/][GNU Guix]]. I like Guix because, among other things, it allows [[https://guix.gnu.org/cookbook/en/html_node/Advanced-package-management.html#Advanced-package-management][to declare the required software]] in configuration files, so I can have the same set of programs across multiple machines (look for tables with "Guix dependency" in the header).
|
My current GNU/Linux distribution is [[https://guix.gnu.org/][Arch Linux]], managed declaratively with [[https://github.com/ripytide/metapac][metapac]] (look for tables with "Arch dependency" in the header). I used to use Guix for the same purpose for 4.5 years; I keep the config now for archival purposes.
|
||||||
|
|
||||||
The central program to all of that is, of course, [[https://www.gnu.org/software/emacs/][GNU Emacs]]. At the time of this writing, it takes ~50% of my screen time and has the largest share of configuration here.
|
The central program to all of that is, of course, [[https://www.gnu.org/software/emacs/][GNU Emacs]]. At the time of this writing, it takes ~50% of my screen time and has the largest share of configuration here.
|
||||||
|
|
||||||
|
|
@ -22,15 +22,14 @@ Table of contents and software:
|
||||||
- [[file:Console.org][Console.org]]
|
- [[file:Console.org][Console.org]]
|
||||||
- /Active/: [[file:Console.org::*=.profile=][.profile]], [[file:Console.org::*Bash][Bash]], [[file:Console.org::*Fish][Fish]], [[file:Console.org::*Starship prompt][Starship]], [[file:Console.org::*Tmux][Tmux]], [[file:Console.org::*Alacritty][Alacritty]]
|
- /Active/: [[file:Console.org::*=.profile=][.profile]], [[file:Console.org::*Bash][Bash]], [[file:Console.org::*Fish][Fish]], [[file:Console.org::*Starship prompt][Starship]], [[file:Console.org::*Tmux][Tmux]], [[file:Console.org::*Alacritty][Alacritty]]
|
||||||
- /In Limbo/: [[file:Console.org::*Nushell][Nushell]]
|
- /In Limbo/: [[file:Console.org::*Nushell][Nushell]]
|
||||||
- [[file:Guix.org][Guix.org]]
|
- [[file:Arch.org][Arch.org]]
|
||||||
- [[file:Mail.org][Mail.org]]
|
- [[file:Mail.org][Mail.org]]
|
||||||
- /Active/: [[file:Mail.org::*Lieer][Lieer]], [[file:Mail.org::*DavMail][DavMail]], [[file:Mail.org::*OfflineIMAP][OfflineIMAP]], [[file:Mail.org::*Notmuch][Notmuch]]
|
- /Active/: [[file:Mail.org::*Lieer][Lieer]], [[file:Mail.org::*DavMail][DavMail]], [[file:Mail.org::*OfflineIMAP][OfflineIMAP]], [[file:Mail.org::*Notmuch][Notmuch]]
|
||||||
|
- [[file:Guix.org][Guix.org]] (archive)
|
||||||
|
|
||||||
(/Apparently, links on the second level work only in Emacs 🙁/)
|
(/Apparently, links on the second level work only in Emacs 🙁/)
|
||||||
|
|
||||||
A few other repositories I may consider a part of my config:
|
A few other repositories I may consider a part of my config:
|
||||||
- [[https://github.com/SqrtMinusOne/channel-q][channel-q]] is my Guix channel
|
|
||||||
- [[https://github.com/SqrtMinusOne/sqrt-data][sqrt-data]] is a home for my statistics gathering effort
|
|
||||||
- [[https://sqrtminusone.xyz/emacs-packages/][My Emacs Packages]], some of which originated in my Emacs config
|
- [[https://sqrtminusone.xyz/emacs-packages/][My Emacs Packages]], some of which originated in my Emacs config
|
||||||
|
|
||||||
See also [[https://sqrtminusone.xyz/posts/][my blog posts]].
|
See also [[https://sqrtminusone.xyz/posts/][my blog posts]].
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,21 @@
|
||||||
{
|
{
|
||||||
"title": "OS/Distro",
|
"title": "OS/Distro",
|
||||||
"elements": [
|
"elements": [
|
||||||
|
{
|
||||||
|
"name": "Arch Linux",
|
||||||
|
"states": [
|
||||||
|
{
|
||||||
|
"startDate": "2025-11-23"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"color": "y"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "GNU Guix",
|
"name": "GNU Guix",
|
||||||
"states": [
|
"states": [
|
||||||
{
|
{
|
||||||
"startDate": "2021-06-11"
|
"startDate": "2021-06-11",
|
||||||
|
"endDate": "2025-11-23"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"color": "y"
|
"color": "y"
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue