Compare commits
No commits in common. "next" and "main" have entirely different histories.
47 changed files with 2757 additions and 257 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
.data/
|
||||||
|
target/
|
|
@ -1,11 +0,0 @@
|
||||||
super_apply() {
|
|
||||||
pacman -S --needed sudo reflector
|
|
||||||
if ! grep -q "Reflector" /etc/pacman.d/mirrorlist; then
|
|
||||||
cp /etc/pacman.d/mirrorlist /etc/pacman.d/mirrorlist.bak
|
|
||||||
reflector -c NL -f 10 --threads 4 --save /etc/pacman.d/mirrorlist
|
|
||||||
else
|
|
||||||
echo "err: reflector already executed -- skipping..."
|
|
||||||
fi
|
|
||||||
|
|
||||||
cp files/pacman.conf /etc/pacman.conf
|
|
||||||
}
|
|
|
@ -1,7 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
|
|
||||||
describe="Install stuff on the system!"
|
|
||||||
|
|
||||||
super_apply() {
|
|
||||||
cp files/sudoers /etc/sudoers
|
|
||||||
}
|
|
|
@ -1,31 +0,0 @@
|
||||||
[options]
|
|
||||||
HoldPkg = pacman glibc yay
|
|
||||||
Architecture = auto
|
|
||||||
|
|
||||||
Color
|
|
||||||
CheckSpace
|
|
||||||
ParallelDownloads = 5
|
|
||||||
|
|
||||||
SigLevel = Required DatabaseOptional
|
|
||||||
LocalFileSigLevel = Optional
|
|
||||||
|
|
||||||
#[testing]
|
|
||||||
#Include = /etc/pacman.d/mirrorlist
|
|
||||||
|
|
||||||
[core]
|
|
||||||
Include = /etc/pacman.d/mirrorlist
|
|
||||||
|
|
||||||
[extra]
|
|
||||||
Include = /etc/pacman.d/mirrorlist
|
|
||||||
|
|
||||||
#[community-testing]
|
|
||||||
#Include = /etc/pacman.d/mirrorlist
|
|
||||||
|
|
||||||
[community]
|
|
||||||
Include = /etc/pacman.d/mirrorlist
|
|
||||||
|
|
||||||
#[multilib-testing]
|
|
||||||
#Include = /etc/pacman.d/mirrorlist
|
|
||||||
|
|
||||||
[multilib]
|
|
||||||
Include = /etc/pacman.d/mirrorlist
|
|
|
@ -1,4 +0,0 @@
|
||||||
root ALL=(ALL:ALL) ALL
|
|
||||||
%wheel ALL=(ALL:ALL) ALL
|
|
||||||
%root ALL=(ALL:ALL) NOPASSWD: ALL
|
|
||||||
@includedir /etc/sudoers.d
|
|
|
@ -1,7 +0,0 @@
|
||||||
super_apply() {
|
|
||||||
pacman -S --needed zsh cmake
|
|
||||||
}
|
|
||||||
|
|
||||||
super_undo() {
|
|
||||||
pacman -R zsh
|
|
||||||
}
|
|
|
@ -1,41 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
|
|
||||||
describe="Install zsh and oh-my-zsh!"
|
|
||||||
scripts="@distro @self"
|
|
||||||
|
|
||||||
super_apply() {
|
|
||||||
[ -d /tmp/fastfetch ] || git clone https://github.com/LinusDierheimer/fastfetch /tmp/fastfetch
|
|
||||||
|
|
||||||
if ! command -v fastfetch &> /dev/null; then
|
|
||||||
cd /tmp/fastfetch
|
|
||||||
mkdir -p build
|
|
||||||
cd build
|
|
||||||
cmake ..
|
|
||||||
cmake --build . --target fastfetch
|
|
||||||
mv fastfetch /usr/local/bin/fastfetch
|
|
||||||
fi
|
|
||||||
|
|
||||||
usermod $USER --shell /bin/zsh
|
|
||||||
}
|
|
||||||
|
|
||||||
super_undo() {
|
|
||||||
usermod $USER --shell /bin/bash
|
|
||||||
}
|
|
||||||
|
|
||||||
apply() {
|
|
||||||
if [ ! -d "$HOME/.oh-my-zsh" ]; then
|
|
||||||
sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" "" --unattended
|
|
||||||
|
|
||||||
PL_DIR=${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-autosuggestions
|
|
||||||
[ -d "$PL_DIR" ] || git clone https://github.com/zsh-users/zsh-autosuggestions $PL_DIR
|
|
||||||
PL_DIR=${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-syntax-highlighting
|
|
||||||
[ -d "$PL_DIR" ] || git clone https://github.com/zsh-users/zsh-syntax-highlighting.git $PL_DIR
|
|
||||||
fi
|
|
||||||
|
|
||||||
[ -e "$HOME/.zshrc" ] || ln files/.zshrc $HOME/.zshrc
|
|
||||||
}
|
|
||||||
|
|
||||||
undo() {
|
|
||||||
unlink $HOME/.zshrc
|
|
||||||
rm -rf $HOME/.oh-my-zsh
|
|
||||||
}
|
|
|
@ -1,23 +0,0 @@
|
||||||
export GPG_TTY=$(tty)
|
|
||||||
|
|
||||||
# paths
|
|
||||||
export PATH="$HOME/.yarn/bin:$HOME/.config/yarn/global/node_modules/.bin:$PATH"
|
|
||||||
|
|
||||||
# ZSH
|
|
||||||
export ZSH="$HOME/.oh-my-zsh"
|
|
||||||
|
|
||||||
ZSH_THEME="afowler"
|
|
||||||
plugins=(git docker docker-compose node zsh-autosuggestions zsh-syntax-highlighting)
|
|
||||||
source $ZSH/oh-my-zsh.sh
|
|
||||||
|
|
||||||
# ALIASES
|
|
||||||
alias open="xdg-open"
|
|
||||||
alias nobeep="sudo modprobe -r pcspkr"
|
|
||||||
alias s="title $HOST && cmatrix -rs && clear"
|
|
||||||
alias q="exit"
|
|
||||||
|
|
||||||
# COSMETICS
|
|
||||||
fastfetch
|
|
||||||
|
|
||||||
export PATH="${PATH}:/home/${USER}/.local/bin"
|
|
||||||
export SSH_AUTH_SOCK="/run/user/1000/keyring/ssh"
|
|
|
@ -1,7 +0,0 @@
|
||||||
super_apply() {
|
|
||||||
pacman -S --needed vim
|
|
||||||
}
|
|
||||||
|
|
||||||
super_undo() {
|
|
||||||
echo "we never uninstall vim -_-"
|
|
||||||
}
|
|
|
@ -1,14 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
|
|
||||||
describe="Install vim and it's stuff"
|
|
||||||
scripts="@distro @self"
|
|
||||||
|
|
||||||
apply() {
|
|
||||||
[ -e "$HOME/.vimrc" ] || ln files/.vimrc $HOME/.vimrc
|
|
||||||
[ -e "$HOME/.ideavimrc" ] || ln files/.ideavimrc $HOME/.ideavimrc
|
|
||||||
}
|
|
||||||
|
|
||||||
undo() {
|
|
||||||
unlink $HOME/.vimrc
|
|
||||||
unlink $HOME/.ideavimrc
|
|
||||||
}
|
|
|
@ -1,2 +0,0 @@
|
||||||
set number
|
|
||||||
set relativenumber
|
|
|
@ -1,7 +0,0 @@
|
||||||
super_apply() {
|
|
||||||
pacman -S --needed alacritty
|
|
||||||
}
|
|
||||||
|
|
||||||
super_undo() {
|
|
||||||
pacman -R alacritty
|
|
||||||
}
|
|
|
@ -1,12 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
|
|
||||||
describe="Installs alacritty and configs"
|
|
||||||
scripts="@distro @self"
|
|
||||||
|
|
||||||
apply() {
|
|
||||||
[ -e "$HOME/.config/alacritty/alacritty.yml" ] || ln files/alacritty.yml $HOME/.config/alacritty/alacritty.yml
|
|
||||||
}
|
|
||||||
|
|
||||||
undo() {
|
|
||||||
unlink $HOME/.config/alacritty/alacritty.yml
|
|
||||||
}
|
|
|
@ -1,6 +0,0 @@
|
||||||
cursor:
|
|
||||||
style:
|
|
||||||
shape: 'Block'
|
|
||||||
blinking: 'On'
|
|
||||||
blink_interval: 500
|
|
||||||
|
|
28
crates/common/crate.toml
Normal file
28
crates/common/crate.toml
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
[crate]
|
||||||
|
name = "common"
|
||||||
|
description = "A versatile crate for managing dotfiles and system configurations."
|
||||||
|
|
||||||
|
[[packages]]
|
||||||
|
name = "git"
|
||||||
|
description = "Version control system"
|
||||||
|
|
||||||
|
[[packages]]
|
||||||
|
name = "nvim"
|
||||||
|
description = "Text editor for creating and editing files"
|
||||||
|
distro_name_mapping = { pacman = "neovim" }
|
||||||
|
|
||||||
|
[[packages]]
|
||||||
|
name = "zsh"
|
||||||
|
description = "Shell designed for interactive use"
|
||||||
|
|
||||||
|
[[actions.command]]
|
||||||
|
user = "root"
|
||||||
|
command = "sh ./scripts/pam_wheel.sh"
|
||||||
|
description = "Pam wheel setup"
|
||||||
|
|
||||||
|
[[actions.command]]
|
||||||
|
command = "git config --global user.name Strix && git config --global user.email strix@saluco.nl"
|
||||||
|
description = "git setup"
|
||||||
|
|
||||||
|
[metadata]
|
||||||
|
repository = "https://git.saluco.nl/strix/dotfiles"
|
4
crates/common/scripts/pam_wheel.sh
Normal file
4
crates/common/scripts/pam_wheel.sh
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
if [ -f /usr/lib/security/pam_wheel.so ] && ! grep -q "# pam_wheel.so added" /etc/pam.d/su; then
|
||||||
|
echo "auth sufficient pam_wheel.so trust use_uid" > /etc/pam.d/su
|
||||||
|
echo "# pam_wheel.so added" > /etc/pam.d/su
|
||||||
|
fi
|
62
crates/i3/crate.toml
Normal file
62
crates/i3/crate.toml
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
[crate]
|
||||||
|
name = "i3"
|
||||||
|
description = "install and setup i3"
|
||||||
|
|
||||||
|
[[packages]]
|
||||||
|
name = "i3"
|
||||||
|
|
||||||
|
[[packages]]
|
||||||
|
name = "i3lock"
|
||||||
|
|
||||||
|
[[packages]]
|
||||||
|
name = "i3status"
|
||||||
|
|
||||||
|
[[packages]]
|
||||||
|
name = "libpulse"
|
||||||
|
require = ["distro:arch"]
|
||||||
|
|
||||||
|
[[packages]]
|
||||||
|
name = "brightnessctl"
|
||||||
|
|
||||||
|
[[packages]]
|
||||||
|
name = "xss-lock"
|
||||||
|
|
||||||
|
[[packages]]
|
||||||
|
name = "dex"
|
||||||
|
|
||||||
|
[[packages]]
|
||||||
|
name = "maim"
|
||||||
|
|
||||||
|
[[packages]]
|
||||||
|
name = "dmenu"
|
||||||
|
|
||||||
|
[[packages]]
|
||||||
|
name = "gnome-keyring"
|
||||||
|
|
||||||
|
[[packages]]
|
||||||
|
name = "feh"
|
||||||
|
|
||||||
|
[[packages]]
|
||||||
|
name = "picom"
|
||||||
|
|
||||||
|
[[actions.link]]
|
||||||
|
src = "files/i3config"
|
||||||
|
dest = "~/.config/i3/config"
|
||||||
|
|
||||||
|
[[actions.link]]
|
||||||
|
src = "files/i3status_config"
|
||||||
|
dest = "~/.config/i3status/config"
|
||||||
|
|
||||||
|
[[actions.link]]
|
||||||
|
src = "files/picom.conf"
|
||||||
|
dest = "~/.config/picom.conf"
|
||||||
|
|
||||||
|
[[actions.command]]
|
||||||
|
user = "root"
|
||||||
|
command = "[ -d '/etc/X11/xorg.conf.d' ] || mkdir -p /etc/X11/xorg.conf.d/"
|
||||||
|
description = "ensure /etc/X11/xorg.conf.d"
|
||||||
|
|
||||||
|
[[actions.command]]
|
||||||
|
user = "root"
|
||||||
|
command = "cp files/touchpad.conf /etc/X11/xorg.conf.d/40-touchpad.conf"
|
||||||
|
description = "copy touchpad config to /etc/X11/xorg.conf.d"
|
206
crates/i3/files/i3config
Normal file
206
crates/i3/files/i3config
Normal file
|
@ -0,0 +1,206 @@
|
||||||
|
#######################
|
||||||
|
## Strix' i3 config ##
|
||||||
|
## Mar 22, 2023 ##
|
||||||
|
## mutation: 1m ##
|
||||||
|
#######################
|
||||||
|
|
||||||
|
## Mostly similar to i3's config yet differs
|
||||||
|
|
||||||
|
# Useful variables:
|
||||||
|
# This section probably gets changed a lot
|
||||||
|
# ---
|
||||||
|
set $terminal alacritty
|
||||||
|
set $screen_lock i3lock -c "#111111" --nofork
|
||||||
|
set $appmenu i3-dmenu-desktop
|
||||||
|
|
||||||
|
set $screenshot maim -s | xclip -selection clipboard -t image/png
|
||||||
|
|
||||||
|
set $font pango:monospace 8
|
||||||
|
|
||||||
|
# Startup Applications:
|
||||||
|
# Things that will startup when starting i3.
|
||||||
|
# ---
|
||||||
|
exec_always --no-startup-id dex --autostart --environment i3
|
||||||
|
exec_always --no-startup-id nm-applet
|
||||||
|
exec_always --no-startup-id xss-lock --transfer-sleep-lock -- $screen_lock
|
||||||
|
exec_always --no-startup-id gnome-keyring-daemon --start --components=ssh,secrets,pkcs11
|
||||||
|
exec_always --no-startup-id feh --bg-fill ~/Pictures/wallpaper.jpg
|
||||||
|
exec_always --no-startup-id picom
|
||||||
|
|
||||||
|
# Gaps & Borders:
|
||||||
|
# ---
|
||||||
|
gaps inner 10
|
||||||
|
smart_gaps on
|
||||||
|
|
||||||
|
default_border pixel 1
|
||||||
|
hide_edge_borders smart_no_gaps
|
||||||
|
|
||||||
|
# Extra variables:
|
||||||
|
# Only need changing for very specific installations.
|
||||||
|
# ---
|
||||||
|
set $audio_volume_up XF86AudioRaiseVolume
|
||||||
|
set $audio_volume_down XF86AudioLowerVolume
|
||||||
|
set $audio_mute XF86AudioMute
|
||||||
|
set $audio_mute_mic XF86AudioMicMute
|
||||||
|
|
||||||
|
set $brightness_up XF86MonBrightnessUp
|
||||||
|
set $brightness_down XF86MonBrightnessDown
|
||||||
|
|
||||||
|
set $ws_1 "1:main"
|
||||||
|
set $ws_2 "2:term"
|
||||||
|
set $ws_3 "3:docs"
|
||||||
|
set $ws_4 "4:mail"
|
||||||
|
set $ws_5 "5"
|
||||||
|
set $ws_6 "6"
|
||||||
|
set $ws_7 "7"
|
||||||
|
set $ws_8 "8"
|
||||||
|
set $ws_9 "9:bg"
|
||||||
|
set $ws_10 "10:misc"
|
||||||
|
|
||||||
|
set $meta_refresh_statusbar killall -SIGUSR1 i3status
|
||||||
|
bar {
|
||||||
|
strip_workspace_numbers yes
|
||||||
|
status_command i3status
|
||||||
|
}
|
||||||
|
|
||||||
|
# END OF CONFIG
|
||||||
|
|
||||||
|
set $mod Mod4
|
||||||
|
set $alt Mod1
|
||||||
|
|
||||||
|
# Customization
|
||||||
|
|
||||||
|
# class border backgr. text indic. child_border
|
||||||
|
client.focused #81A1C1 #81A1C1 #ffffff #D8DEE9
|
||||||
|
client.focused_inactive #2E3440 #2E3440 #88C0D0 #454948
|
||||||
|
client.unfocused #2E3440 #2E3440 #88C0D0 #454948
|
||||||
|
client.urgent #D08770 #3B4252 #ffffff #268BD2
|
||||||
|
client.placeholder #000000 #0c0c0c #ffffff #000000
|
||||||
|
client.background #3B4252
|
||||||
|
|
||||||
|
# Generic
|
||||||
|
floating_modifier $mod
|
||||||
|
tiling_drag modifier titlebar
|
||||||
|
font $font
|
||||||
|
|
||||||
|
# Open a terminal
|
||||||
|
bindsym $mod+Return exec $terminal
|
||||||
|
|
||||||
|
# Kill current window
|
||||||
|
bindsym $mod+$alt+q kill
|
||||||
|
|
||||||
|
# Open app menu
|
||||||
|
bindsym $mod+d exec --no-startup-id $appmenu
|
||||||
|
|
||||||
|
# Lock screen
|
||||||
|
bindsym $mod+l exec --no-startup-id $screen_lock
|
||||||
|
|
||||||
|
# Screenshots
|
||||||
|
bindsym Print exec --no-startup-id $screenshot
|
||||||
|
|
||||||
|
# Audio
|
||||||
|
bindsym $audio_volume_up exec --no-startup-id pactl set-sink-volume @DEFAULT_SINK@ +10% && $meta_refresh_statusbar
|
||||||
|
bindsym $audio_volume_down exec --no-startup-id pactl set-sink-volume @DEFAULT_SINK@ -10% && $meta_refresh_statusbar
|
||||||
|
bindsym $audio_mute exec --no-startup-id pactl set-sink-mute @DEFAULT_SINK@ toggle && $meta_refresh_statusbar
|
||||||
|
bindsym $audio_mute_mic exec --no-startup-id pactl set-source-mute @DEFAULT_SOURCE@ toggle && $meta_refresh_statusbar
|
||||||
|
|
||||||
|
# Brightness
|
||||||
|
bindsym $brightness_up exec --no-startup-id brightnessctl set +5% && $meta_refresh_statusbar
|
||||||
|
bindsym $brightness_down exec --no-startup-id brightnessctl set 5%- && $meta_refresh_statusbar
|
||||||
|
|
||||||
|
# Change focused window
|
||||||
|
bindsym $mod+Up focus up
|
||||||
|
bindsym $mod+Down focus down
|
||||||
|
bindsym $mod+Left focus left
|
||||||
|
bindsym $mod+Right focus right
|
||||||
|
|
||||||
|
# Move focused window
|
||||||
|
bindsym $mod+Shift+Up move up
|
||||||
|
bindsym $mod+Shift+Down move down
|
||||||
|
bindsym $mod+Shift+Left move left
|
||||||
|
bindsym $mod+Shift+Right move right
|
||||||
|
|
||||||
|
# Splits
|
||||||
|
bindsym $mod+Shift+h split h
|
||||||
|
bindsym $mod+Shift+v split v
|
||||||
|
|
||||||
|
# Change container layout
|
||||||
|
bindsym $mod+Shift+s layout stacking
|
||||||
|
bindsym $mod+Shift+t layout tabbed
|
||||||
|
bindsym $mod+Shift+d layout toggle split
|
||||||
|
|
||||||
|
# Make current window fullscreen
|
||||||
|
bindsym $mod+Shift+f fullscreen toggle
|
||||||
|
|
||||||
|
# Floating stuff
|
||||||
|
bindsym $mod+space focus mode_toggle
|
||||||
|
bindsym $mod+Shift+space floating toggle
|
||||||
|
|
||||||
|
# Scratchpad
|
||||||
|
bindsym $mod+Shift+minus move scratchpad
|
||||||
|
bindsym $mod+minus scratchpad show
|
||||||
|
|
||||||
|
# Switch to workspace
|
||||||
|
bindsym $mod+1 workspace $ws_1
|
||||||
|
bindsym $mod+2 workspace $ws_2
|
||||||
|
bindsym $mod+3 workspace $ws_3
|
||||||
|
bindsym $mod+4 workspace $ws_4
|
||||||
|
bindsym $mod+5 workspace $ws_5
|
||||||
|
bindsym $mod+6 workspace $ws_6
|
||||||
|
bindsym $mod+7 workspace $ws_7
|
||||||
|
bindsym $mod+8 workspace $ws_8
|
||||||
|
bindsym $mod+9 workspace $ws_9
|
||||||
|
bindsym $mod+0 workspace $ws_10
|
||||||
|
|
||||||
|
# Move focused container to workspace
|
||||||
|
bindsym $mod+Shift+1 move container to workspace $ws_1
|
||||||
|
bindsym $mod+Shift+2 move container to workspace $ws_2
|
||||||
|
bindsym $mod+Shift+3 move container to workspace $ws_3
|
||||||
|
bindsym $mod+Shift+4 move container to workspace $ws_4
|
||||||
|
bindsym $mod+Shift+5 move container to workspace $ws_5
|
||||||
|
bindsym $mod+Shift+6 move container to workspace $ws_6
|
||||||
|
bindsym $mod+Shift+7 move container to workspace $ws_7
|
||||||
|
bindsym $mod+Shift+8 move container to workspace $ws_8
|
||||||
|
bindsym $mod+Shift+9 move container to workspace $ws_9
|
||||||
|
bindsym $mod+Shift+0 move container to workspace $ws_10
|
||||||
|
|
||||||
|
# Move through workspaces
|
||||||
|
bindsym $mod+$alt+Left workspace prev
|
||||||
|
bindsym $mod+$alt+Right workspace next
|
||||||
|
|
||||||
|
# WM stuff
|
||||||
|
# reload i3 config
|
||||||
|
bindsym $mod+$alt+c reload
|
||||||
|
# restart i3
|
||||||
|
bindsym $mod+$alt+r restart
|
||||||
|
# exit i3
|
||||||
|
bindsym $mod+Shift+e exec "i3-nagbar -t warning -m 'Exit i3?' -B 'Yes, exit i3' 'i3-msg exit'"
|
||||||
|
|
||||||
|
# Modes
|
||||||
|
bindsym $mod+r mode resize
|
||||||
|
mode "resize" {
|
||||||
|
bindsym $nav_left resize shrink width 10 px or 10 ppt
|
||||||
|
bindsym $nav_down resize grow height 10 px or 10 ppt
|
||||||
|
bindsym $nav_up resize shrink height 10 px or 10 ppt
|
||||||
|
bindsym $nav_right resize grow width 10 px or 10 ppt
|
||||||
|
bindsym Left resize shrink width 10 px or 10 ppt
|
||||||
|
bindsym Down resize grow height 10 px or 10 ppt
|
||||||
|
bindsym Up resize shrink height 10 px or 10 ppt
|
||||||
|
bindsym Right resize grow width 10 px or 10 ppt
|
||||||
|
|
||||||
|
bindsym Return mode "default"
|
||||||
|
bindsym Escape mode "default"
|
||||||
|
bindsym $mod+r mode "default"
|
||||||
|
}
|
||||||
|
|
||||||
|
bindsym $mod+s mode "session"
|
||||||
|
mode "session" {
|
||||||
|
bindsym l exec --no-startup-id $lock, mode "default"
|
||||||
|
bindsym e exec --no-startup-id i3-msg exit, mode "default"
|
||||||
|
bindsym Shift+r exec --no-startup-id systemctl reboot, mode "default"
|
||||||
|
bindsym Shift+s exec --no-startup-id systemctl poweroff -i, mode "default"
|
||||||
|
|
||||||
|
bindsym Return mode "default"
|
||||||
|
bindsym Escape mode "default"
|
||||||
|
bindsym $mod+s mode "default"
|
||||||
|
}
|
57
crates/i3/files/i3status_config
Normal file
57
crates/i3/files/i3status_config
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
general {
|
||||||
|
output_format = "i3bar"
|
||||||
|
colors = true
|
||||||
|
interval = 5
|
||||||
|
}
|
||||||
|
|
||||||
|
order += "ipv6"
|
||||||
|
order += "wireless wlp58s0"
|
||||||
|
order += "battery 0"
|
||||||
|
order += "disk /"
|
||||||
|
order += "memory"
|
||||||
|
order += "load"
|
||||||
|
order += "tztime local"
|
||||||
|
|
||||||
|
wireless wlp58s0 {
|
||||||
|
format_up = "W: (%quality at %essid, %bitrate) %ip"
|
||||||
|
format_down = "W: down"
|
||||||
|
}
|
||||||
|
|
||||||
|
battery 0 {
|
||||||
|
format = "%status %percentage %remaining %emptytime"
|
||||||
|
format_down = "No battery"
|
||||||
|
status_chr = "⚡ CHR"
|
||||||
|
status_bat = "🔋 BAT"
|
||||||
|
status_unk = "? UNK"
|
||||||
|
status_full = "☻ FULL"
|
||||||
|
path = "/sys/class/power_supply/BAT%d/uevent"
|
||||||
|
low_threshold = 10
|
||||||
|
}
|
||||||
|
|
||||||
|
tztime local {
|
||||||
|
format = "%Y-%m-%d %H:%M:%S"
|
||||||
|
hide_if_equals_localtime = false
|
||||||
|
}
|
||||||
|
|
||||||
|
tztime berlin {
|
||||||
|
format = "%Y-%m-%d %H:%M:%S %Z"
|
||||||
|
timezone = "Europe/Berlin"
|
||||||
|
}
|
||||||
|
|
||||||
|
load {
|
||||||
|
format = "%5min"
|
||||||
|
}
|
||||||
|
|
||||||
|
memory {
|
||||||
|
format = "%used"
|
||||||
|
threshold_degraded = "10%"
|
||||||
|
format_degraded = "MEMORY: %free"
|
||||||
|
}
|
||||||
|
|
||||||
|
disk "/" {
|
||||||
|
format = "%free"
|
||||||
|
}
|
||||||
|
|
||||||
|
read_file uptime {
|
||||||
|
path = "/proc/uptime"
|
||||||
|
}
|
9
crates/i3/files/picom.conf
Normal file
9
crates/i3/files/picom.conf
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
inactive-opacity = 0.75;
|
||||||
|
|
||||||
|
blur: {
|
||||||
|
method = "box";
|
||||||
|
size = 10;
|
||||||
|
background = false;
|
||||||
|
background-frame = false;
|
||||||
|
background-fixed = false;
|
||||||
|
}
|
8
crates/i3/files/touchpad.conf
Normal file
8
crates/i3/files/touchpad.conf
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
Section "InputClass"
|
||||||
|
Identifier "libinput touchpad catchall"
|
||||||
|
MatchIsTouchpad "on"
|
||||||
|
MatchDevicePath "/dev/input/event*"
|
||||||
|
Driver "libinput"
|
||||||
|
Option "NaturalScrolling" "True"
|
||||||
|
Option "Tapping" "on"
|
||||||
|
EndSection
|
22
crates/ssh/config
Normal file
22
crates/ssh/config
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
## neb servers
|
||||||
|
|
||||||
|
# hydrogen red helix
|
||||||
|
Host H
|
||||||
|
Hostname hydrogen.red.helix.saluco.nl
|
||||||
|
|
||||||
|
# argon red helix
|
||||||
|
Host A
|
||||||
|
Hostname argon.red.helix.saluco.nl
|
||||||
|
|
||||||
|
# iron red sphere
|
||||||
|
Host I
|
||||||
|
Hostname iron.red.sphere.saluco.nl
|
||||||
|
|
||||||
|
## utility servers
|
||||||
|
Host git
|
||||||
|
Hostname git.saluco.nl
|
||||||
|
User git
|
||||||
|
|
||||||
|
Host github
|
||||||
|
Hostname github.com
|
||||||
|
User git
|
10
crates/ssh/crate.toml
Normal file
10
crates/ssh/crate.toml
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
[crate]
|
||||||
|
name = "ssh"
|
||||||
|
description = "fixes the ssh files"
|
||||||
|
|
||||||
|
[[actions.command]]
|
||||||
|
command = "ls -lah"
|
||||||
|
|
||||||
|
[[actions.link]]
|
||||||
|
src = "./config"
|
||||||
|
dest = "~/.ssh/config"
|
15
crates/vim/crate.toml
Normal file
15
crates/vim/crate.toml
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
[crate]
|
||||||
|
name = "vim"
|
||||||
|
description = "install & configure vim"
|
||||||
|
|
||||||
|
|
||||||
|
[[packages]]
|
||||||
|
name = "neovim"
|
||||||
|
|
||||||
|
[[actions.link]]
|
||||||
|
src = "./ideavimrc"
|
||||||
|
dest = "~/.ideavimrc"
|
||||||
|
|
||||||
|
[[actions.link]]
|
||||||
|
src = "./init.vim"
|
||||||
|
dest = "~/.config/nvim/init.vim"
|
27
crates/vim/init.vim
Normal file
27
crates/vim/init.vim
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
" vim preferences
|
||||||
|
set number
|
||||||
|
set relativenumber
|
||||||
|
syntax on
|
||||||
|
|
||||||
|
" No arrow keys
|
||||||
|
noremap <Up> <Nop>
|
||||||
|
noremap <Down> <Nop>
|
||||||
|
noremap <Left> <Nop>
|
||||||
|
noremap <Right> <Nop>
|
||||||
|
|
||||||
|
" ensure vim-plug
|
||||||
|
let data_dir = has('nvim') ? stdpath('data') . '/site' : '~/.vim'
|
||||||
|
if empty(glob(data_dir . '/autoload/plug.vim'))
|
||||||
|
silent execute '!curl -fLo '.data_dir.'/autoload/plug.vim --create-dirs https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim'
|
||||||
|
autocmd VimEnter * PlugInstall --sync | source $MYVIMRC
|
||||||
|
endif
|
||||||
|
|
||||||
|
call plug#begin()
|
||||||
|
|
||||||
|
Plug 'ThePrimeagen/vim-be-good'
|
||||||
|
Plug 'machakann/vim-highlightedyank'
|
||||||
|
Plug 'tpope/vim-commentary'
|
||||||
|
Plug 'wellle/targets.vim'
|
||||||
|
Plug 'vim-scripts/loremipsum'
|
||||||
|
|
||||||
|
call plug#end()
|
14
crates/zsh/crate.toml
Normal file
14
crates/zsh/crate.toml
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
[crate]
|
||||||
|
name = "zsh"
|
||||||
|
description = "install & configure zsh"
|
||||||
|
|
||||||
|
[[packages]]
|
||||||
|
name = "zsh"
|
||||||
|
|
||||||
|
[[actions.command]]
|
||||||
|
command = "sh ./setup-omzsh.sh"
|
||||||
|
description = "oh-my-zsh setup"
|
||||||
|
|
||||||
|
[[actions.link]]
|
||||||
|
src = "./zshrc"
|
||||||
|
dest = "~/.zshrc"
|
10
crates/zsh/setup-omzsh.sh
Normal file
10
crates/zsh/setup-omzsh.sh
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
if [ ! -d "$HOME/.oh-my-zsh" ]; then
|
||||||
|
sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" "" --unattended
|
||||||
|
|
||||||
|
PL_DIR=${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-autosuggestions
|
||||||
|
[ -d "$PL_DIR" ] || git clone https://github.com/zsh-users/zsh-autosuggestions $PL_DIR
|
||||||
|
PL_DIR=${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-syntax-highlighting
|
||||||
|
[ -d "$PL_DIR" ] || git clone https://github.com/zsh-users/zsh-syntax-highlighting.git $PL_DIR
|
||||||
|
fi
|
||||||
|
|
||||||
|
[ -f "$HOME/.zshrc" ] && unlink $HOME/.zshrc
|
44
crates/zsh/zshrc
Normal file
44
crates/zsh/zshrc
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
#!/bin/zsh
|
||||||
|
|
||||||
|
# Raine's .zshrc
|
||||||
|
|
||||||
|
if [ "$PROFILEINC" = "1" ]; then
|
||||||
|
. ~/.profile
|
||||||
|
fi
|
||||||
|
|
||||||
|
export EDITOR=vim
|
||||||
|
export GPG_TTY=$(tty)
|
||||||
|
export PATH="$PATH:$HOME/.yarn/bin:$HOME/.config/yarn/global/node_modules/.bin:$HOME/.local/bin"
|
||||||
|
|
||||||
|
# oh-my-zsh init
|
||||||
|
if [ -f "$HOME/.oh-my-zsh/oh-my-zsh.sh" ]; then
|
||||||
|
export ZSH="$HOME/.oh-my-zsh"
|
||||||
|
ZSH_THEME="afowler"
|
||||||
|
plugins=(git docker docker-compose node zsh-autosuggestions zsh-syntax-highlighting zsh-cargo-completion)
|
||||||
|
source $ZSH/oh-my-zsh.sh
|
||||||
|
else
|
||||||
|
echo "oh-my-zsh not detected :("
|
||||||
|
PS1="$(whoami)@${HOSTNAME:-$(hostname)} $ "
|
||||||
|
fi
|
||||||
|
|
||||||
|
update_dotfiles() {
|
||||||
|
cd $HOME/.dotfiles
|
||||||
|
git pull
|
||||||
|
cd - &> /dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
container() {
|
||||||
|
docker run \
|
||||||
|
-ti \
|
||||||
|
--rm \
|
||||||
|
--name tmp-$(id -u)-$(openssl rand -hex 8) \
|
||||||
|
--network ${CONTAINER_NETWORK:-internal} \
|
||||||
|
alpine \
|
||||||
|
ash
|
||||||
|
}
|
||||||
|
|
||||||
|
[ -f "$HOME/.config/i3/shortcuts-i3.sh" ] && . ~/.config/i3/shortcuts-i3.sh
|
||||||
|
|
||||||
|
alias q="exit"
|
||||||
|
alias vim="nvim"
|
||||||
|
alias vi="nvim"
|
85
dot
85
dot
|
@ -1,85 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
|
|
||||||
ask=0
|
|
||||||
|
|
||||||
is_function() {
|
|
||||||
type "$1" 2> /dev/null | sed "s/$1//" | grep -qwi function
|
|
||||||
}
|
|
||||||
|
|
||||||
curr_distro() {
|
|
||||||
cat /etc/os-release | grep -G "^ID=" | sed 's/ID=//'
|
|
||||||
}
|
|
||||||
|
|
||||||
include() {
|
|
||||||
[ -f "$1" ] || return 1
|
|
||||||
. $1
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func() {
|
|
||||||
if is_function $(echo "super_$2"); then
|
|
||||||
sudo sh -c ". $1 && super_$2"
|
|
||||||
fi
|
|
||||||
is_function $2 && $2
|
|
||||||
}
|
|
||||||
|
|
||||||
get_command() {
|
|
||||||
case $1 in
|
|
||||||
a*)
|
|
||||||
printf "apply"
|
|
||||||
;;
|
|
||||||
u*)
|
|
||||||
printf "undo"
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
echo "err: not supported" >&2
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
}
|
|
||||||
|
|
||||||
# only run this *in crate dir*
|
|
||||||
run_crate() {
|
|
||||||
include ./crate.sh || exit 1
|
|
||||||
if [ -n "$describe" ]; then
|
|
||||||
echo "desc($(basename $PWD)): $describe"
|
|
||||||
fi
|
|
||||||
cmd=$(get_command $1)
|
|
||||||
scripts=${scripts:-"@self @distro"}
|
|
||||||
for s in $scripts; do
|
|
||||||
echo "exec($(basename $PWD)): $s/$cmd"
|
|
||||||
case $s in
|
|
||||||
@self)
|
|
||||||
func ./crate.sh $cmd
|
|
||||||
;;
|
|
||||||
@distro)
|
|
||||||
unset -f super_$cmd
|
|
||||||
unset -f $cmd
|
|
||||||
include ./crate.$(curr_distro).sh
|
|
||||||
func ./crate.$(curr_distro).sh $cmd
|
|
||||||
unset -f super_$cmd
|
|
||||||
unset -f $cmd
|
|
||||||
include ./crate.sh
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
sh $s
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
if [ $# -eq 2 ]; then
|
|
||||||
[ -d crates/*$1 ] || exit 1
|
|
||||||
cd crates/*$1
|
|
||||||
run_crate $2
|
|
||||||
cd ../..
|
|
||||||
else
|
|
||||||
[ $# -eq 0 ] && exit 2
|
|
||||||
for crate in $(find ./crates -mindepth 1 -maxdepth 1 -type d | sort); do
|
|
||||||
cd $crate
|
|
||||||
run_crate $1
|
|
||||||
cd ../..
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
|
|
||||||
|
|
1332
sync-runner/Cargo.lock
generated
Normal file
1332
sync-runner/Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
23
sync-runner/Cargo.toml
Normal file
23
sync-runner/Cargo.toml
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
[package]
|
||||||
|
name = "sync-runner"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
clap = { version = "4.5.29", features = ["derive"] }
|
||||||
|
colored = "3.0.0"
|
||||||
|
dbus = "0.9.7"
|
||||||
|
execute = "0.2.13"
|
||||||
|
fern = "0.7.1"
|
||||||
|
git2 = "0.20.0"
|
||||||
|
glob = "0.3.2"
|
||||||
|
hex = "0.4.3"
|
||||||
|
lazy_static = "1.5.0"
|
||||||
|
log = "0.4.26"
|
||||||
|
regex = "1.11.1"
|
||||||
|
resolve-path = "0.1.0"
|
||||||
|
serde = { version = "1.0.218", features = ["serde_derive"] }
|
||||||
|
sha2 = "0.10.8"
|
||||||
|
shellexpand = "3.1.0"
|
||||||
|
toml = "0.8.20"
|
||||||
|
whoami = "1.5.2"
|
10
sync-runner/src/action.rs
Normal file
10
sync-runner/src/action.rs
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
use clap::Parser;
|
||||||
|
|
||||||
|
#[derive(Parser, Debug)]
|
||||||
|
pub enum Action {
|
||||||
|
/// Sync your device with dotfiles repository
|
||||||
|
Sync {
|
||||||
|
#[arg(short, long)]
|
||||||
|
config_path: Option<String>,
|
||||||
|
},
|
||||||
|
}
|
7
sync-runner/src/cfg/daemon.rs
Normal file
7
sync-runner/src/cfg/daemon.rs
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
pub struct Daemon {
|
||||||
|
/// interval in minutes
|
||||||
|
pub interval: u64,
|
||||||
|
}
|
21
sync-runner/src/cfg/mod.rs
Normal file
21
sync-runner/src/cfg/mod.rs
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
|
||||||
|
#![allow(unused)]
|
||||||
|
|
||||||
|
use std::{collections::HashMap, fs::read_to_string};
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
mod daemon;
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
pub struct Config {
|
||||||
|
pub title: String,
|
||||||
|
pub daemon: daemon::Daemon,
|
||||||
|
pub source: HashMap<String, crate::source::Source>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Config {
|
||||||
|
pub fn parse(path: &str) -> Result<Config, Box<dyn std::error::Error>> {
|
||||||
|
Ok(toml::from_str::<Config>(&read_to_string(path)?)?)
|
||||||
|
}
|
||||||
|
}
|
102
sync-runner/src/crates/action.rs
Normal file
102
sync-runner/src/crates/action.rs
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
use std::{
|
||||||
|
collections::HashMap,
|
||||||
|
fs::{self, remove_file},
|
||||||
|
process::{Command, Stdio},
|
||||||
|
time::Duration,
|
||||||
|
};
|
||||||
|
|
||||||
|
use dbus::{
|
||||||
|
arg::Variant,
|
||||||
|
blocking::{BlockingSender, Connection},
|
||||||
|
Message,
|
||||||
|
};
|
||||||
|
use log::{debug, error, info, trace};
|
||||||
|
use resolve_path::PathResolveExt;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::tags::Tag;
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
pub struct Actions {
|
||||||
|
/// command actions
|
||||||
|
#[serde(rename = "command")]
|
||||||
|
pub commands: Option<Vec<CommandAction>>,
|
||||||
|
/// link actions
|
||||||
|
#[serde(rename = "link")]
|
||||||
|
pub links: Option<Vec<LinkAction>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
pub struct CommandAction {
|
||||||
|
#[serde(default = "whoami::username")]
|
||||||
|
user: String,
|
||||||
|
command: String,
|
||||||
|
pub description: Option<String>,
|
||||||
|
pub require: Option<Vec<Tag>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
pub struct LinkAction {
|
||||||
|
pub src: String,
|
||||||
|
pub dest: String,
|
||||||
|
overwrite: Option<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CommandAction {
|
||||||
|
pub fn new<S: Into<String>>(command: S, user: S) -> Self {
|
||||||
|
Self {
|
||||||
|
command: command.into(),
|
||||||
|
user: user.into(),
|
||||||
|
description: None,
|
||||||
|
require: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run(&self) -> Result<i32, Box<dyn std::error::Error>> {
|
||||||
|
trace!("running \"{}\" as {}...", &self.command, &self.user);
|
||||||
|
if self.user != whoami::username() {
|
||||||
|
Ok(Command::new("sudo")
|
||||||
|
.arg("-u")
|
||||||
|
.arg(&self.user)
|
||||||
|
.arg("--")
|
||||||
|
.arg("sh")
|
||||||
|
.arg("-c")
|
||||||
|
.arg(&self.command)
|
||||||
|
.status()?
|
||||||
|
.code()
|
||||||
|
.unwrap_or(1))
|
||||||
|
} else {
|
||||||
|
Ok(Command::new("sh")
|
||||||
|
.arg("-c")
|
||||||
|
.arg(&self.command)
|
||||||
|
.status()?
|
||||||
|
.code()
|
||||||
|
.unwrap_or(1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LinkAction {
|
||||||
|
pub fn link(&self) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
trace!(
|
||||||
|
"linking from {:?} to {:?}...",
|
||||||
|
&self.src.resolve(),
|
||||||
|
&self.dest.resolve()
|
||||||
|
);
|
||||||
|
if let Ok(existing) = fs::read_link(&self.dest.resolve()) {
|
||||||
|
if existing == self.src.resolve() {
|
||||||
|
debug!("link OK");
|
||||||
|
return Ok(());
|
||||||
|
} else {
|
||||||
|
if self.overwrite.unwrap_or(false) == true {
|
||||||
|
debug!("removing {}...", self.dest);
|
||||||
|
remove_file(self.dest.resolve())?;
|
||||||
|
} else {
|
||||||
|
return Err("Destination is linked to a different path".into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::os::unix::fs::symlink(&self.src.resolve(), &self.dest.resolve())?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
27
sync-runner/src/crates/manifest.rs
Normal file
27
sync-runner/src/crates/manifest.rs
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
use std::fmt::Display;
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use super::{action::Actions, package::Package};
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
pub struct CrateManifest {
|
||||||
|
#[serde(rename = "crate")]
|
||||||
|
pub crate_info: CrateInfo,
|
||||||
|
pub packages: Option<Vec<Package>>,
|
||||||
|
pub actions: Option<Actions>,
|
||||||
|
pub metadata: Option<Metadata>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
pub struct CrateInfo {
|
||||||
|
pub name: String,
|
||||||
|
pub description: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
pub struct Metadata {
|
||||||
|
pub homepage: Option<String>,
|
||||||
|
pub repository: Option<String>,
|
||||||
|
pub issues: Option<String>,
|
||||||
|
}
|
62
sync-runner/src/crates/mod.rs
Normal file
62
sync-runner/src/crates/mod.rs
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
use log::{error, info, warn};
|
||||||
|
use manifest::CrateManifest;
|
||||||
|
use package::PackageManager;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
pub mod action;
|
||||||
|
pub mod manifest;
|
||||||
|
pub mod package;
|
||||||
|
|
||||||
|
pub struct Crate {
|
||||||
|
pub manifest: CrateManifest,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Crate {
|
||||||
|
pub fn from_toml_str(string: &str) -> Result<Self, toml::de::Error> {
|
||||||
|
Ok(Crate {
|
||||||
|
manifest: toml::from_str::<manifest::CrateManifest>(string)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn install_packages(&self) -> bool {
|
||||||
|
if let Some(packages) = &self.manifest.packages {
|
||||||
|
info!("Installing packages...");
|
||||||
|
let pkgs: Vec<String> = packages
|
||||||
|
.iter()
|
||||||
|
.map(|p| p.get_correct_package_name())
|
||||||
|
.collect();
|
||||||
|
info!(target: "item", "pkgs: {}", pkgs.join(", "));
|
||||||
|
if let Some(pm) = PackageManager::get_available() {
|
||||||
|
pm.install(pkgs).is_ok()
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run_actions(&self) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
if let Some(actions) = &self.manifest.actions {
|
||||||
|
if let Some(commands) = &actions.commands {
|
||||||
|
for command in commands {
|
||||||
|
info!(
|
||||||
|
"Running {}...",
|
||||||
|
&command.description.clone().unwrap_or("action".to_string())
|
||||||
|
);
|
||||||
|
command.run()?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(links) = &actions.links {
|
||||||
|
for link in links {
|
||||||
|
info!("Link {} -> {}...", link.src, link.dest);
|
||||||
|
if let Err(e) = link.link() {
|
||||||
|
error!("could not link: {e}");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
78
sync-runner/src/crates/package.rs
Normal file
78
sync-runner/src/crates/package.rs
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
use std::{collections::HashMap, io};
|
||||||
|
|
||||||
|
use log::{error, info};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use super::action::CommandAction;
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub enum PackageManager {
|
||||||
|
#[serde(rename = "pacman")]
|
||||||
|
Pacman,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
pub struct Package {
|
||||||
|
pub name: String,
|
||||||
|
pub distro_name_mapping: Option<HashMap<PackageManager, String>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PackageManager {
|
||||||
|
pub fn install(&self, packages: Vec<String>) -> Result<bool, Box<dyn std::error::Error>> {
|
||||||
|
match &self {
|
||||||
|
PackageManager::Pacman => {
|
||||||
|
CommandAction::new(
|
||||||
|
format!("pacman -S --noconfirm {}", packages.join(" ")),
|
||||||
|
"root".to_string(),
|
||||||
|
)
|
||||||
|
.run()?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_available() -> Option<Self> {
|
||||||
|
Self::from_str(match whoami::distro().as_str() {
|
||||||
|
"Manjaro Linux" => "pacman",
|
||||||
|
_ => "unknown"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_str(distro: &str) -> Option<Self> {
|
||||||
|
match distro {
|
||||||
|
"pacman" => Some(PackageManager::Pacman),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Package {
|
||||||
|
pub fn get_correct_package_name(&self) -> String {
|
||||||
|
if let Some(pm) = PackageManager::get_available() {
|
||||||
|
if let Some(mappings) = &self.distro_name_mapping {
|
||||||
|
if let Some(name) = mappings.get(&pm) {
|
||||||
|
name.to_string()
|
||||||
|
} else {
|
||||||
|
self.name.clone()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.name.clone()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.name.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn install(&self) -> Result<bool, Box<dyn std::error::Error>> {
|
||||||
|
if let Some(pm) = PackageManager::get_available() {
|
||||||
|
pm.install(vec![self.get_correct_package_name()])?;
|
||||||
|
} else {
|
||||||
|
error!("no package manager found...");
|
||||||
|
return Err(Box::new(io::Error::new(
|
||||||
|
io::ErrorKind::NotFound,
|
||||||
|
"package manager not found",
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
Ok(false)
|
||||||
|
}
|
||||||
|
}
|
64
sync-runner/src/logging.rs
Normal file
64
sync-runner/src/logging.rs
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
use std::env;
|
||||||
|
|
||||||
|
use colored::Colorize;
|
||||||
|
use fern::Dispatch;
|
||||||
|
use log::{Record, SetLoggerError};
|
||||||
|
|
||||||
|
fn format_regular<S: Into<String>>(log: S, record: &Record) -> String {
|
||||||
|
let log = log.into();
|
||||||
|
let line_prefix = |line: String, extend: bool| {
|
||||||
|
let prefix = if extend {
|
||||||
|
match record.level() {
|
||||||
|
log::Level::Trace => " ]".bright_blue(),
|
||||||
|
log::Level::Debug => " ?".green(),
|
||||||
|
log::Level::Info => " >".blue(),
|
||||||
|
log::Level::Warn => " #".yellow(),
|
||||||
|
log::Level::Error => " !".red(),
|
||||||
|
}.to_string()
|
||||||
|
} else {
|
||||||
|
match record.level() {
|
||||||
|
log::Level::Trace => "[TRACE]".bright_blue().italic(),
|
||||||
|
log::Level::Debug => "??".green(),
|
||||||
|
log::Level::Info => "=>".blue(),
|
||||||
|
log::Level::Warn => "##".yellow(),
|
||||||
|
log::Level::Error => "!!".red().bold()
|
||||||
|
}.to_string()
|
||||||
|
};
|
||||||
|
return format!("{} {}", prefix, line);
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut lines = log.lines().peekable();
|
||||||
|
let mut output = match lines.peek() {
|
||||||
|
Some(_line) => line_prefix(lines.next().unwrap().to_string(), false),
|
||||||
|
None => return "".to_string(),
|
||||||
|
};
|
||||||
|
|
||||||
|
for line in lines {
|
||||||
|
output.push_str(&*format!("\n{}", line_prefix(line.to_string(), true)));
|
||||||
|
}
|
||||||
|
|
||||||
|
output
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setup_logger() -> Result<(), SetLoggerError> {
|
||||||
|
Dispatch::new()
|
||||||
|
.format(|out, message, record| {
|
||||||
|
match record.metadata().target() {
|
||||||
|
// command output logging
|
||||||
|
"command:stdout" => out.finish(format_args!("{} {}", ">>".cyan(), message.to_string())),
|
||||||
|
"command:stderr" => out.finish(format_args!("{} {}", ">>".red(), message.to_string())),
|
||||||
|
// this target means, it's an item and not a log.
|
||||||
|
"item" => out.finish(format_args!("{} {}", "*".blue(), message.to_string())),
|
||||||
|
// default logging
|
||||||
|
_ => out.finish(format_args!("{}", format_regular(message.to_string(), record))),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.level(
|
||||||
|
env::var("SYNCR_LOG_LEVEL")
|
||||||
|
.unwrap_or_else(|_| "info".to_string())
|
||||||
|
.parse()
|
||||||
|
.unwrap_or(log::LevelFilter::Info),
|
||||||
|
)
|
||||||
|
.chain(std::io::stdout())
|
||||||
|
.apply()
|
||||||
|
}
|
87
sync-runner/src/main.rs
Normal file
87
sync-runner/src/main.rs
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
use std::{
|
||||||
|
env::set_current_dir,
|
||||||
|
fs::{exists, read_to_string, File},
|
||||||
|
path::{absolute, Path},
|
||||||
|
process::{exit, Stdio},
|
||||||
|
};
|
||||||
|
|
||||||
|
use action::Action;
|
||||||
|
use cfg::Config;
|
||||||
|
use clap::Parser;
|
||||||
|
use colored::Colorize;
|
||||||
|
use execute::{command_args, Execute};
|
||||||
|
use log::{debug, error, info, trace, warn};
|
||||||
|
use prelude::abspath;
|
||||||
|
|
||||||
|
mod action;
|
||||||
|
mod cfg;
|
||||||
|
mod crates;
|
||||||
|
mod logging;
|
||||||
|
mod prelude;
|
||||||
|
mod source;
|
||||||
|
mod tags;
|
||||||
|
|
||||||
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
logging::setup_logger()?;
|
||||||
|
|
||||||
|
info!(target: "item", "user: {}", whoami::username());
|
||||||
|
info!(target: "item", "distro: {}", whoami::distro());
|
||||||
|
|
||||||
|
let action = Action::parse();
|
||||||
|
match action {
|
||||||
|
Action::Sync { config_path } => {
|
||||||
|
trace!("fetching config dir... {config_path:?}");
|
||||||
|
if let Some(config_path) = abspath(&config_path.unwrap_or("~/.syncr".into())) {
|
||||||
|
trace!("setting config dir as cwd... {config_path}");
|
||||||
|
set_current_dir(config_path)?;
|
||||||
|
}
|
||||||
|
let config = Config::parse(&abspath("./syncr.toml").unwrap())?;
|
||||||
|
info!("syncing \"{}\"...", config.title.bold());
|
||||||
|
|
||||||
|
info!("updating sources...");
|
||||||
|
let mut available_sources = vec![];
|
||||||
|
for (name, source) in &config.source {
|
||||||
|
debug!("checking {name}...");
|
||||||
|
if !source.available() {
|
||||||
|
warn!("source \"{name}\" unavailable.");
|
||||||
|
} else {
|
||||||
|
info!("source \"{name}\" available!");
|
||||||
|
available_sources.push(source);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if available_sources.len() == 0 {
|
||||||
|
error!("{}", "sync impossible; no sources.".bold());
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
let oldpwd = absolute(".")?;
|
||||||
|
for source in available_sources {
|
||||||
|
// cd to source dir
|
||||||
|
source.go_to_dir()?;
|
||||||
|
for (mut path, c) in source.get_crates()? {
|
||||||
|
path.pop();
|
||||||
|
set_current_dir(absolute(path)?)?;
|
||||||
|
info!("Syncing crate: {}...", c.manifest.crate_info.name);
|
||||||
|
|
||||||
|
c.install_packages();
|
||||||
|
|
||||||
|
if let Err(e) = c.run_actions() {
|
||||||
|
error!("action failed: {e}");
|
||||||
|
}
|
||||||
|
|
||||||
|
set_current_dir(&oldpwd)?; // i hate this but im lazy okay
|
||||||
|
source.go_to_dir()?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set_current_dir(oldpwd)?;
|
||||||
|
|
||||||
|
info!("Completed sync.");
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
println!("{action:#?}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
9
sync-runner/src/prelude.rs
Normal file
9
sync-runner/src/prelude.rs
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
|
use std::env;
|
||||||
|
|
||||||
|
pub fn abspath(p: &str) -> Option<String> {
|
||||||
|
let exp_path = shellexpand::full(p).ok()?;
|
||||||
|
let can_path = std::fs::canonicalize(exp_path.as_ref()).ok()?;
|
||||||
|
can_path.into_os_string().into_string().ok()
|
||||||
|
}
|
245
sync-runner/src/source/git.rs
Normal file
245
sync-runner/src/source/git.rs
Normal file
|
@ -0,0 +1,245 @@
|
||||||
|
use std::{
|
||||||
|
env::current_dir,
|
||||||
|
fs::{self, create_dir_all, exists},
|
||||||
|
io::Write,
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
};
|
||||||
|
|
||||||
|
use git2::{build::RepoBuilder, Repository};
|
||||||
|
use log::{debug, info, trace, warn};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use sha2::{Digest, Sha256, Sha512};
|
||||||
|
|
||||||
|
use crate::prelude::abspath;
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
pub struct Git {
|
||||||
|
pub url: String,
|
||||||
|
#[serde(default = "default_branch")]
|
||||||
|
pub branch: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_branch() -> String {
|
||||||
|
"main".to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Git {
|
||||||
|
fn url_hash(&self) -> String {
|
||||||
|
let mut hasher = Sha256::new();
|
||||||
|
hasher.update(&self.url);
|
||||||
|
let hash = hasher.finalize();
|
||||||
|
hex::encode(hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn branch_hash(&self) -> String {
|
||||||
|
let mut hasher = Sha256::new();
|
||||||
|
hasher.update(&self.branch);
|
||||||
|
let hash = hasher.finalize();
|
||||||
|
hex::encode(hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn repository_path_str(&self) -> String {
|
||||||
|
format!(".data/git/{}.{}", self.url_hash(), self.branch_hash())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn repository_path(&self) -> Result<PathBuf, std::io::Error> {
|
||||||
|
fs::canonicalize(self.repository_path_str())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn exists_on_fs(&self) -> bool {
|
||||||
|
self.repository_path().is_ok()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clone_repository(&self) -> Result<Repository, git2::Error> {
|
||||||
|
RepoBuilder::new()
|
||||||
|
.branch(&self.branch)
|
||||||
|
.clone(&self.url, Path::new(&self.repository_path_str()))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn repository(&self) -> Result<Repository, git2::Error> {
|
||||||
|
if !self.exists_on_fs() {
|
||||||
|
create_dir_all(self.repository_path_str()).unwrap();
|
||||||
|
}
|
||||||
|
match Repository::open(self.repository_path().unwrap()) {
|
||||||
|
Ok(r) => Ok(r),
|
||||||
|
Err(_) => self.clone_repository(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn up_to_date(&self) -> Result<bool, Box<dyn std::error::Error>> {
|
||||||
|
debug!("checking repo up to date...");
|
||||||
|
let repo = self.repository()?;
|
||||||
|
let mut remote = repo.find_remote("origin")?;
|
||||||
|
|
||||||
|
// Fetch latest references from remote
|
||||||
|
remote.fetch(&[self.branch.clone()], None, None)?;
|
||||||
|
|
||||||
|
let fetch_head = repo.refname_to_id(&format!("refs/remotes/origin/{}", self.branch))?;
|
||||||
|
let local_head = repo.refname_to_id(&format!("refs/heads/{}", self.branch))?;
|
||||||
|
|
||||||
|
Ok(fetch_head == local_head)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update(&self) -> Result<bool, Box<dyn std::error::Error>> {
|
||||||
|
if self.up_to_date()? {
|
||||||
|
return Ok(true);
|
||||||
|
}
|
||||||
|
debug!("updating repository...");
|
||||||
|
let repository = self.repository()?;
|
||||||
|
let mut remote = repository.find_remote("origin")?;
|
||||||
|
let mut cb = git2::RemoteCallbacks::new();
|
||||||
|
|
||||||
|
cb.transfer_progress(|stats| {
|
||||||
|
if stats.received_objects() == stats.total_objects() {
|
||||||
|
print!(
|
||||||
|
"resolving deltas {}/{}\r",
|
||||||
|
stats.indexed_deltas(),
|
||||||
|
stats.total_deltas()
|
||||||
|
);
|
||||||
|
} else if stats.total_objects() > 0 {
|
||||||
|
print!(
|
||||||
|
"received {}/{} objects ({}) in {} bytes\r",
|
||||||
|
stats.received_objects(),
|
||||||
|
stats.total_objects(),
|
||||||
|
stats.indexed_objects(),
|
||||||
|
stats.received_bytes()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
std::io::stdout().flush().unwrap();
|
||||||
|
true
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut fo = git2::FetchOptions::new();
|
||||||
|
fo.remote_callbacks(cb);
|
||||||
|
// Always fetch all tags.
|
||||||
|
// Perform a download and also update tips
|
||||||
|
fo.download_tags(git2::AutotagOption::All);
|
||||||
|
info!("fetching {}...", remote.name().unwrap());
|
||||||
|
remote.fetch(&[self.branch.clone()], Some(&mut fo), None)?;
|
||||||
|
|
||||||
|
let fetch_head = repository.find_reference("FETCH_HEAD")?;
|
||||||
|
|
||||||
|
do_merge(
|
||||||
|
&repository,
|
||||||
|
&self.branch,
|
||||||
|
repository.reference_to_annotated_commit(&fetch_head)?,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ensure(&self) -> Result<&Self, Box<dyn std::error::Error>> {
|
||||||
|
if self.exists_on_fs() {
|
||||||
|
self.update();
|
||||||
|
} else {
|
||||||
|
self.clone_repository()?;
|
||||||
|
}
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn do_merge<'a>(
|
||||||
|
repo: &'a Repository,
|
||||||
|
remote_branch: &str,
|
||||||
|
fetch_commit: git2::AnnotatedCommit<'a>,
|
||||||
|
) -> Result<(), git2::Error> {
|
||||||
|
// 1. do a merge analysis
|
||||||
|
let analysis = repo.merge_analysis(&[&fetch_commit])?;
|
||||||
|
|
||||||
|
// 2. Do the appopriate merge
|
||||||
|
if analysis.0.is_fast_forward() {
|
||||||
|
info!("doing a fast forward...");
|
||||||
|
// do a fast forward
|
||||||
|
let refname = format!("refs/heads/{}", remote_branch);
|
||||||
|
match repo.find_reference(&refname) {
|
||||||
|
Ok(mut r) => {
|
||||||
|
fast_forward(repo, &mut r, &fetch_commit)?;
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
// The branch doesn't exist so just set the reference to the
|
||||||
|
// commit directly. Usually this is because you are pulling
|
||||||
|
// into an empty repository.
|
||||||
|
repo.reference(
|
||||||
|
&refname,
|
||||||
|
fetch_commit.id(),
|
||||||
|
true,
|
||||||
|
&format!("Setting {} to {}", remote_branch, fetch_commit.id()),
|
||||||
|
)?;
|
||||||
|
repo.set_head(&refname)?;
|
||||||
|
repo.checkout_head(Some(
|
||||||
|
git2::build::CheckoutBuilder::default()
|
||||||
|
.allow_conflicts(true)
|
||||||
|
.conflict_style_merge(true)
|
||||||
|
.force(),
|
||||||
|
))?;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} else if analysis.0.is_normal() {
|
||||||
|
// do a normal merge
|
||||||
|
let head_commit = repo.reference_to_annotated_commit(&repo.head()?)?;
|
||||||
|
normal_merge(&repo, &head_commit, &fetch_commit)?;
|
||||||
|
} else {
|
||||||
|
info!("nothing to do...");
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fast_forward(
|
||||||
|
repo: &Repository,
|
||||||
|
lb: &mut git2::Reference,
|
||||||
|
rc: &git2::AnnotatedCommit,
|
||||||
|
) -> Result<(), git2::Error> {
|
||||||
|
let name = match lb.name() {
|
||||||
|
Some(s) => s.to_string(),
|
||||||
|
None => String::from_utf8_lossy(lb.name_bytes()).to_string(),
|
||||||
|
};
|
||||||
|
let msg = format!("fast-forward: setting {} to id: {}", name, rc.id());
|
||||||
|
info!("{}", msg);
|
||||||
|
lb.set_target(rc.id(), &msg)?;
|
||||||
|
repo.set_head(&name)?;
|
||||||
|
repo.checkout_head(Some(
|
||||||
|
git2::build::CheckoutBuilder::default()
|
||||||
|
// For some reason the force is required to make the working directory actually get updated
|
||||||
|
// I suspect we should be adding some logic to handle dirty working directory states
|
||||||
|
// but this is just an example so maybe not.
|
||||||
|
.force(),
|
||||||
|
))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn normal_merge(
|
||||||
|
repo: &Repository,
|
||||||
|
local: &git2::AnnotatedCommit,
|
||||||
|
remote: &git2::AnnotatedCommit,
|
||||||
|
) -> Result<(), git2::Error> {
|
||||||
|
let local_tree = repo.find_commit(local.id())?.tree()?;
|
||||||
|
let remote_tree = repo.find_commit(remote.id())?.tree()?;
|
||||||
|
let ancestor = repo
|
||||||
|
.find_commit(repo.merge_base(local.id(), remote.id())?)?
|
||||||
|
.tree()?;
|
||||||
|
let mut idx = repo.merge_trees(&ancestor, &local_tree, &remote_tree, None)?;
|
||||||
|
|
||||||
|
if idx.has_conflicts() {
|
||||||
|
warn!("merge conficts detected...");
|
||||||
|
repo.checkout_index(Some(&mut idx), None)?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
let result_tree = repo.find_tree(idx.write_tree_to(repo)?)?;
|
||||||
|
// now create the merge commit
|
||||||
|
let msg = format!("Merge: {} into {}", remote.id(), local.id());
|
||||||
|
let sig = repo.signature()?;
|
||||||
|
let local_commit = repo.find_commit(local.id())?;
|
||||||
|
let remote_commit = repo.find_commit(remote.id())?;
|
||||||
|
// Do our merge commit and set current branch head to that commit.
|
||||||
|
let _merge_commit = repo.commit(
|
||||||
|
Some("HEAD"),
|
||||||
|
&sig,
|
||||||
|
&sig,
|
||||||
|
&msg,
|
||||||
|
&result_tree,
|
||||||
|
&[&local_commit, &remote_commit],
|
||||||
|
)?;
|
||||||
|
// Set working tree to match head.
|
||||||
|
repo.checkout_head(None)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
78
sync-runner/src/source/mod.rs
Normal file
78
sync-runner/src/source/mod.rs
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
use std::{
|
||||||
|
env::{current_dir, set_current_dir},
|
||||||
|
fs::{create_dir_all, exists, read_to_string},
|
||||||
|
path::PathBuf,
|
||||||
|
};
|
||||||
|
|
||||||
|
use log::{debug, info, trace};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::{crates::Crate, prelude::abspath};
|
||||||
|
|
||||||
|
pub mod git;
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
#[serde(default)]
|
||||||
|
pub struct Source {
|
||||||
|
interval: u64,
|
||||||
|
git: Option<git::Git>,
|
||||||
|
dir: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Source {
|
||||||
|
fn default() -> Self {
|
||||||
|
Source {
|
||||||
|
interval: 60,
|
||||||
|
git: None,
|
||||||
|
dir: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Source {
|
||||||
|
pub fn available(&self) -> bool {
|
||||||
|
if let Some(git) = &self.git {
|
||||||
|
trace!("checking git...");
|
||||||
|
return git.ensure().is_ok();
|
||||||
|
}
|
||||||
|
if let Some(dir) = &self.dir {
|
||||||
|
return exists(dir).is_ok();
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn go_to_dir(&self) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
if let Some(git) = &self.git {
|
||||||
|
if PathBuf::from(git.repository_path_str()) == current_dir()? {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
let dir = git.ensure()?.repository_path()?;
|
||||||
|
trace!(
|
||||||
|
"setting git dir as cwd... ({}@{}, {})",
|
||||||
|
git.url,
|
||||||
|
git.branch,
|
||||||
|
dir.display()
|
||||||
|
);
|
||||||
|
set_current_dir(dir)?;
|
||||||
|
}
|
||||||
|
if let Some(path) = &self.dir {
|
||||||
|
set_current_dir(path)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_crates(&self) -> Result<Vec<(PathBuf, Crate)>, Box<dyn std::error::Error>> {
|
||||||
|
let mut crates = vec![];
|
||||||
|
// get crates (read dir, crates/*/crate.toml)
|
||||||
|
for crate_file in glob::glob("crates/*/crate.toml").expect("err") {
|
||||||
|
match crate_file {
|
||||||
|
Ok(cd) => {
|
||||||
|
debug!("found {}", cd.display());
|
||||||
|
crates.push((cd.clone(), Crate::from_toml_str(&read_to_string(cd)?)?))
|
||||||
|
}
|
||||||
|
_ => continue,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(crates)
|
||||||
|
}
|
||||||
|
}
|
71
sync-runner/src/tags.rs
Normal file
71
sync-runner/src/tags.rs
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Tag {
|
||||||
|
pub category: String,
|
||||||
|
pub value: String
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> Deserialize<'de> for Tag {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
// First, deserialize the string
|
||||||
|
let s: String = Deserialize::deserialize(deserializer)?;
|
||||||
|
|
||||||
|
// Split the string into category and value by ":"
|
||||||
|
let parts: Vec<&str> = s.splitn(2, ':').collect();
|
||||||
|
if parts.len() != 2 {
|
||||||
|
return Err(serde::de::Error::invalid_value(
|
||||||
|
serde::de::Unexpected::Str(&s),
|
||||||
|
&"a string in the format 'category:value'",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return a Tag with the split parts
|
||||||
|
Ok(Tag {
|
||||||
|
category: parts[0].to_string(),
|
||||||
|
value: parts[1].to_string(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serialize for Tag {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer,
|
||||||
|
{
|
||||||
|
serializer.serialize_str(&format!("{}:{}", self.category, self.value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<String> for Tag {
|
||||||
|
fn into(self) -> String {
|
||||||
|
self.category + " : " + &self.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<String> for Tag {
|
||||||
|
type Error = std::io::Error;
|
||||||
|
fn try_from(value: String) -> Result<Self, Self::Error> {
|
||||||
|
let parts: Vec<&str> = value.split(':').collect();
|
||||||
|
if parts.len() != 2 {
|
||||||
|
Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "could not match string to tag"))
|
||||||
|
} else {
|
||||||
|
Ok(Self {
|
||||||
|
category: parts[0].into(),
|
||||||
|
value: parts[1].into()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Tag {
|
||||||
|
pub fn distro() -> Tag {
|
||||||
|
Tag {
|
||||||
|
category: "distro".into(),
|
||||||
|
value: whoami::distro()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
6
sync.sh
Normal file
6
sync.sh
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
if ! [ -f /tmp/sync-runner ]; then
|
||||||
|
curl -O /tmp/sync-runner https://git.saluco.nl/repos/strix/releases/download/latest/sync-runner
|
||||||
|
chmod +x /tmp/sync-runner
|
||||||
|
fi
|
||||||
|
|
||||||
|
/tmp/sync-runner
|
17
syncr.toml
Normal file
17
syncr.toml
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
title = "strix's syncr config"
|
||||||
|
|
||||||
|
[daemon]
|
||||||
|
# interval
|
||||||
|
# discription: how often to check for new updates, this is the default for syncs
|
||||||
|
# you can define a custom interval for specific sources
|
||||||
|
# unit = minutes
|
||||||
|
interval = 60
|
||||||
|
|
||||||
|
[source.personal]
|
||||||
|
dir = "."
|
||||||
|
|
||||||
|
# [source.personal.git] # default is the uid
|
||||||
|
# url = "https://git.saluco.nl/strix/dotfiles.git"
|
||||||
|
# branch = "syncr"
|
||||||
|
# crate_dir = "./crates" # default
|
||||||
|
# cfg_toml = "./syncr.toml" # default
|
Loading…
Add table
Add a link
Reference in a new issue