syncr tool #2
					 55 changed files with 1838 additions and 796 deletions
				
			
		
							
								
								
									
										2
									
								
								.gitignore
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
										
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,2 @@ | |||
| .data/ | ||||
| target/ | ||||
							
								
								
									
										12
									
								
								README.md
									
										
									
									
									
								
							
							
						
						
									
										12
									
								
								README.md
									
										
									
									
									
								
							|  | @ -1,12 +0,0 @@ | |||
| # Raine's Dotfiles | ||||
| 
 | ||||
| ## Crates | ||||
| Everything is a crate.   | ||||
| If something is distro specific you should follow the following naming scheme: | ||||
| `crate.<DISTRO>.sh` | ||||
| 
 | ||||
| ## Using it | ||||
| Just use the script tbh | ||||
| ```sh | ||||
| curl -L https://via.ixvd.net/sh | sh | ||||
| ``` | ||||
|  | @ -1,12 +0,0 @@ | |||
| super_apply() { | ||||
|     pacman -Syyu --noconfirm | ||||
|     pacman -S --needed --noconfirm 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,5 +0,0 @@ | |||
| super_apply() { | ||||
|     apt update -y | ||||
|     apt install -y netselect-apt sudo | ||||
|     netselect-apt | ||||
| } | ||||
|  | @ -1,11 +0,0 @@ | |||
| #!/bin/sh | ||||
| 
 | ||||
| describe="Install stuff on the system!" | ||||
| scripts="@distro @self" | ||||
| 
 | ||||
| super_apply() { | ||||
|     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 | ||||
| } | ||||
|  | @ -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,18 +0,0 @@ | |||
| #!/bin/sh | ||||
| 
 | ||||
| describe="setup ssh" | ||||
| 
 | ||||
| apply() { | ||||
|     [ -e "$HOME/.ssh/authorized_keys" ] || ln files/authorized_keys $HOME/.ssh/authorized_keys | ||||
|     [ -e "$HOME/.ssh/config" ] || ln files/config $HOME/.ssh/config | ||||
|     if ! [ -f "$HOME/.ssh/id_rsa" ]; then | ||||
|         echo "Creating new ssh key for this device..." | ||||
|         ssh-keygen -f $HOME/.ssh/id_rsa -p "" -q | ||||
|         echo "Adding key to authorized_keys..." | ||||
|         cat $HOME/.ssh/id_rsa.pub > $HOME/.ssh/authorized_keys | ||||
|     fi | ||||
| } | ||||
| 
 | ||||
| undo() { | ||||
|     echo "Undoing ssh keys is not supported, please do this manually." | ||||
| } | ||||
|  | @ -1 +0,0 @@ | |||
| ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCeNQfnbyyF3sht43vH5BcXDPca8nWu6bKPVGvAlWBOq4Av8ME2IQgwVe9nJ05r73ZY02/Vdqc01a8wyK5Hmw0XlPL0Cn6wc9QoiscOvq5lMUK87S2tr3EVLGkgl8o7nmVuWgLewyojiORjM02P1PZEiFhKPXVEQFxU0dFz9QtpAdm0u78Xn2HTukHpXSv44R3XDDMFZ3Ek/XRuS6J9dZVxGkgCLQhK8kpfbxuiYxaRC7MHgGlYuxjLuZ6P4i+V+SSSShfCGdm6U9bgeIAwftN6a8Pc9+OsBeZGSUrGjZjRlD35q0a7fbpoS8pKTfbwgf/ijYeu3JmAQUlY+H959mIpg4H9XOgRrKVJSYwx5/BGuhmWgVy6HIYpXCQfEbLE7QDmwC2C430KzAH6jCcrRNyurIUCuO4iq9dwoQTzboMccOK79S2Z+1B5fYgS3BZgaiTUBSME2G2FriM6utgleiBnvFu/p7oH2I8ZHL/aVcSWAw0gbzsr7ADywAuiDNZk18c= strix@ryuk | ||||
|  | @ -1,22 +0,0 @@ | |||
| ## 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 | ||||
|  | @ -1,11 +0,0 @@ | |||
| #!/bin/sh | ||||
| 
 | ||||
| pkgs="i3 i3lock i3status libpulse brightnessctl xss-lock dex maim dmenu gnome-keyring feh picom" | ||||
| 
 | ||||
| super_apply() { | ||||
|     pacman -S --needed --noconfirm $pkgs | ||||
| } | ||||
| 
 | ||||
| super_undo() { | ||||
|     pacman -R --noconfirm $pkgs | ||||
| } | ||||
|  | @ -1,11 +0,0 @@ | |||
| #!/bin/sh | ||||
| 
 | ||||
| pkgs="i3 i3lock i3status libpulse-mainloop-glib brightnessctl xss-lock dex maim dmenu gnome-keyring feh picom" | ||||
| 
 | ||||
| super_apply() { | ||||
|     apt install -y $pkgs | ||||
| } | ||||
| 
 | ||||
| super_undo() { | ||||
|     apt remove -y $pkgs | ||||
| } | ||||
|  | @ -1,26 +0,0 @@ | |||
| #!/bin/sh | ||||
| 
 | ||||
| describe="Installs i3" | ||||
| scripts="@distro @self" | ||||
| 
 | ||||
| apply() { | ||||
|     [ -d "$HOME/.config/i3" ] || mkdir -p $HOME/.config/i3 | ||||
|     [ -d "$HOME/.config/i3status" ] || mkdir -p $HOME/.config/i3status | ||||
|     [ -e "$HOME/.config/i3/config" ] || ln files/config $HOME/.config/i3/config | ||||
|     [ -e "$HOME/.config/i3status/config" ] || ln files/status_config $HOME/.config/i3status/config | ||||
|     [ -e "$HOME/.config/picom.conf" ] || ln files/picom.conf $HOME/.config/picom.conf | ||||
| } | ||||
| 
 | ||||
| undo() { | ||||
|     rm $HOME/.config/i3/config | ||||
|     rm $HOME/.config/i3status/config | ||||
| } | ||||
| 
 | ||||
| super_apply() { | ||||
|     [ -d "/etc/X11/xorg.conf.d" ] || mkdir -p /etc/X11/xorg.conf.d/ | ||||
|     cp files/40-proper-touchpad.conf /etc/X11/xorg.conf.d/40-proper-touchpad.conf | ||||
| } | ||||
| 
 | ||||
| super_undo() { | ||||
|     rm /etc/X11/xorg.conf.d/40-proper-touchpad.conf | ||||
| } | ||||
|  | @ -1,12 +0,0 @@ | |||
| #!/bin/sh | ||||
| 
 | ||||
| pkgs="i3 i3lock i3status pulseaudio-devel brightnessctl xss-lock dex maim dmenu gnome-keyring feh picom" | ||||
| 
 | ||||
| super_apply() { | ||||
|     xbps-install -y $pkgs | ||||
| } | ||||
| 
 | ||||
| super_undo() { | ||||
|     xbps-remove -y $pkgs | ||||
| } | ||||
| 
 | ||||
|  | @ -1,8 +0,0 @@ | |||
| Section "InputClass" | ||||
|         Identifier "libinput touchpad catchall" | ||||
|         MatchIsTouchpad "on" | ||||
|         MatchDevicePath "/dev/input/event*" | ||||
|         Driver "libinput" | ||||
|         Option "NaturalScrolling" "True" | ||||
|         Option "Tapping" "on" | ||||
| EndSection | ||||
|  | @ -1,206 +0,0 @@ | |||
| ####################### | ||||
| ## Raine's 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" | ||||
| } | ||||
|  | @ -1,9 +0,0 @@ | |||
| inactive-opacity = 0.75; | ||||
| 
 | ||||
| blur: { | ||||
|   method = "box"; | ||||
|   size = 10; | ||||
|   background = false; | ||||
|   background-frame = false; | ||||
|   background-fixed = false; | ||||
| } | ||||
|  | @ -1,57 +0,0 @@ | |||
| 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" | ||||
| } | ||||
|  | @ -1,7 +0,0 @@ | |||
| super_apply() { | ||||
|     pacman -S --needed --noconfirm zsh | ||||
| } | ||||
| 
 | ||||
| super_undo() { | ||||
|     pacman -R --noconfirm zsh | ||||
| } | ||||
|  | @ -1,7 +0,0 @@ | |||
| super_apply() { | ||||
|     apt install -y zsh | ||||
| } | ||||
| 
 | ||||
| super_undo() { | ||||
|     apt remove -y zsh | ||||
| } | ||||
|  | @ -1,7 +0,0 @@ | |||
| super_apply() { | ||||
|     apt install -y zsh | ||||
| } | ||||
| 
 | ||||
| super_undo() { | ||||
|     apt remove -y zsh | ||||
| } | ||||
|  | @ -1,31 +0,0 @@ | |||
| #!/bin/sh | ||||
| 
 | ||||
| describe="Install zsh and oh-my-zsh!" | ||||
| scripts="@distro @self" | ||||
| 
 | ||||
| super_apply() { | ||||
|     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 | ||||
| 
 | ||||
|     [ -f "$HOME/.zshrc" ] && unlink $HOME/.zshrc | ||||
|     [ -e "$HOME/.zshrc" ] || ln files/.zshrc $HOME/.zshrc | ||||
| } | ||||
| 
 | ||||
| undo() { | ||||
|     unlink $HOME/.zshrc | ||||
|     rm -rf $HOME/.oh-my-zsh | ||||
| } | ||||
|  | @ -1,10 +0,0 @@ | |||
| #!/bin/sh | ||||
| 
 | ||||
| super_apply() { | ||||
|     xbps-install -y zsh | ||||
| } | ||||
| 
 | ||||
| super_undo() { | ||||
|     xbps-remove -y zsh | ||||
| } | ||||
| 
 | ||||
|  | @ -1,44 +0,0 @@ | |||
| #!/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" | ||||
|  | @ -1,7 +0,0 @@ | |||
| super_apply() { | ||||
|     pacman -S --needed --noconfirm neovim | ||||
| } | ||||
| 
 | ||||
| super_undo() { | ||||
|     echo "we never uninstall vim -_-" | ||||
| } | ||||
|  | @ -1,7 +0,0 @@ | |||
| super_apply() { | ||||
|     apt install -y neovim | ||||
| } | ||||
| 
 | ||||
| super_undo() { | ||||
|     echo "we never uninstall vim -_-" | ||||
| } | ||||
|  | @ -1,15 +0,0 @@ | |||
| #!/bin/sh | ||||
| 
 | ||||
| describe="Install vim and it's stuff" | ||||
| scripts="@distro @self" | ||||
| 
 | ||||
| apply() { | ||||
|     [ -d "$HOME/.config/nvim" ] || mkdir -p "$HOME/.config/nvim" | ||||
|     [ -e "$HOME/.config/nvim/init.vim" ] || ln files/init.vim $HOME/.config/nvim/init.vim | ||||
|     [ -e "$HOME/.ideavimrc" ] || ln files/.ideavimrc $HOME/.ideavimrc | ||||
| } | ||||
| 
 | ||||
| undo() { | ||||
|     unlink $HOME/.config/nvim/init.vim | ||||
|     unlink $HOME/.ideavimrc | ||||
| } | ||||
|  | @ -1,7 +0,0 @@ | |||
| super_apply() { | ||||
|     xbps-install -y neovim | ||||
| } | ||||
| 
 | ||||
| super_undo() { | ||||
|     echo "we never uninstall vim -_-" | ||||
| } | ||||
|  | @ -1,10 +0,0 @@ | |||
| source ~/.vimrc | ||||
| 
 | ||||
| set scrolloff=5 | ||||
| set incsearch | ||||
| 
 | ||||
| map Q gq | ||||
| 
 | ||||
| Plug 'machakann/vim-highlightedyank' | ||||
| Plug 'tpope/vim-commentary' | ||||
| Plug 'wellle/targets.vim' | ||||
|  | @ -1,27 +0,0 @@ | |||
| " 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() | ||||
|  | @ -1,7 +0,0 @@ | |||
| super_apply() { | ||||
|     pacman -S --needed --noconfirm alacritty | ||||
| } | ||||
| 
 | ||||
| super_undo() { | ||||
|     pacman -R --noconfirm alacritty | ||||
| } | ||||
|  | @ -1,7 +0,0 @@ | |||
| super_apply() { | ||||
|     apt install -y alacritty | ||||
| } | ||||
| 
 | ||||
| super_undo() { | ||||
|     apt remove -y alacritty | ||||
| } | ||||
|  | @ -1,13 +0,0 @@ | |||
| #!/bin/sh | ||||
| 
 | ||||
| describe="Installs alacritty and configs" | ||||
| scripts="@distro @self" | ||||
| 
 | ||||
| apply() { | ||||
|     [ -d "$HOME/.config/alacritty" ] || mkdir -p $HOME/.config/alacritty | ||||
|     [ -e "$HOME/.config/alacritty/alacritty.yml" ] || ln files/alacritty.yml $HOME/.config/alacritty/alacritty.yml | ||||
| } | ||||
| 
 | ||||
| undo() { | ||||
|     unlink $HOME/.config/alacritty/alacritty.yml | ||||
| } | ||||
|  | @ -1,7 +0,0 @@ | |||
| super_apply() { | ||||
|     xbps-install -y alacritty | ||||
| } | ||||
| 
 | ||||
| super_undo() { | ||||
|     xbps-remove -y alacritty | ||||
| } | ||||
|  | @ -1,6 +0,0 @@ | |||
| cursor: | ||||
|   style: | ||||
|     shape: 'Block' | ||||
|     blinking: 'On' | ||||
|   blink_interval: 500 | ||||
| 
 | ||||
|  | @ -1,9 +0,0 @@ | |||
| #!/bin/sh | ||||
| 
 | ||||
| describe="Setup git" | ||||
| scripts="@distro @self" | ||||
| 
 | ||||
| apply() { | ||||
|     git config --global user.name Strix | ||||
|     git config --global user.email strix@saluco.nl | ||||
| } | ||||
							
								
								
									
										23
									
								
								crates/common/crate.toml
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								crates/common/crate.toml
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,23 @@ | |||
| name = "common" | ||||
| description = "Common set of utilities" | ||||
| 
 | ||||
| [pkgs] | ||||
| openssh-server = {} | ||||
| openssh-client = {} | ||||
| git = {} | ||||
| netselect = { distros = ["debian", "ubuntu"] } | ||||
| reflector = { distros = ["arch"] } | ||||
| 
 | ||||
| [actions] | ||||
| setup_ssh = ["sh", "./scripts/%"] | ||||
| reflector = { args = ["sh", "./scripts/setup_pm/%"], distro = ["arch"] } | ||||
| netselect = { args = [ | ||||
|   "sh", | ||||
|   "./scripts/setup_pm/%", | ||||
| ], distro = [ | ||||
|   "debian", | ||||
|   "ubuntu", | ||||
| ] } | ||||
| 
 | ||||
| [super_actions] | ||||
| pam_wheel = ["sh", "./scripts/%"] | ||||
							
								
								
									
										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 | ||||
							
								
								
									
										89
									
								
								dot
									
										
									
									
									
								
							
							
						
						
									
										89
									
								
								dot
									
										
									
									
									
								
							|  | @ -1,89 +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 | ||||
|         [ "${DO_SUDO:-yes}" = "yes" ] || return 0 | ||||
|         ecmd=". $1 && super_$2" | ||||
|         [ "$(id -u)" = "0" ] && sh -c "$ecmd" || sudo sh -c "$ecmd" | ||||
|         unset ecmd | ||||
|     fi | ||||
|     is_function $2 && $2 | ||||
| } | ||||
| 
 | ||||
| # only run this *in crate dir* | ||||
| run_crate() { | ||||
|     enabled=1 | ||||
|     include ./crate.sh || exit 1 | ||||
|     if [ -n "$describe" ]; then | ||||
|         echo "desc($(basename $PWD)): $describe" | ||||
|     fi | ||||
|     if [ $enabled -ne 1 ]; then | ||||
|         return | ||||
|     fi | ||||
|     cmd=$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 | ||||
| } | ||||
| 
 | ||||
| echo "# details:" | ||||
| echo "# user: $USER (${UID:-$(id -u)})" | ||||
| echo "# groups: $(groups)" | ||||
| echo "# distro: $(curr_distro)" | ||||
| 
 | ||||
| cmd=$1 | ||||
| if [ -z "$cmd" ]; then | ||||
|     echo "usage: $0 <command> [...args]" | ||||
|     exit 1 | ||||
| else | ||||
|     shift | ||||
| fi | ||||
| 
 | ||||
| for c in ${@:-$(ls crates)}; do | ||||
|     [ -d crates/*$c ] || exit 1 | ||||
|     cd crates/*$c | ||||
|     case $cmd in | ||||
|     a | apply) | ||||
|         run_crate apply | ||||
|         ;; | ||||
|     u | undo) | ||||
|         run_crate undo | ||||
|         ;; | ||||
|     *) exit 1 ;; | ||||
|     esac | ||||
|     cd ../.. | ||||
| done | ||||
|  | @ -1,14 +0,0 @@ | |||
| #!/bin/sh | ||||
| 
 | ||||
| # this script is meant to be ran when .dotfiles is not present. | ||||
| #  | ||||
| # example: | ||||
| # curl https://git.saluco.nl/strix/dotfiles/raw/branch/main/remote_script.sh | sh | ||||
| 
 | ||||
| set -e | ||||
| 
 | ||||
| HOME=${HOME:-/home/${USER:-$(whomai)}} | ||||
| 
 | ||||
| [ -d "$HOME/.dotfiles" ] || git clone https://git.saluco.nl/strix/dotfiles $HOME/.dotfiles | ||||
| cd $HOME/.dotfiles | ||||
| ./dot a | ||||
|  | @ -1,13 +0,0 @@ | |||
| #!/bin/sh | ||||
| 
 | ||||
| set -e | ||||
| 
 | ||||
| echo "Downloading newest package..." | ||||
| curl -Lo /tmp/discord.deb "https://discord.com/api/download?platform=linux&format=deb" | ||||
| cd /tmp | ||||
| echo "Extracting package..." | ||||
| xdeb discord.deb | ||||
| echo "Installing package..." | ||||
| sudo xbps-install -R /tmp/binpkgs discord | ||||
| rm -rf /tmp/binpkgs | ||||
| cd - | ||||
							
								
								
									
										1149
									
								
								sync-runner/Cargo.lock
									
										
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										1149
									
								
								sync-runner/Cargo.lock
									
										
									
										generated
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										20
									
								
								sync-runner/Cargo.toml
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								sync-runner/Cargo.toml
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,20 @@ | |||
| [package] | ||||
| name = "sync-runner" | ||||
| version = "0.1.0" | ||||
| edition = "2021" | ||||
| 
 | ||||
| [dependencies] | ||||
| clap = { version = "4.5.29", features = ["derive"] } | ||||
| colored = "3.0.0" | ||||
| 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" | ||||
| serde = { version = "1.0.218", features = ["serde_derive"] } | ||||
| sha2 = "0.10.8" | ||||
| shellexpand = "3.1.0" | ||||
| toml = "0.8.20" | ||||
							
								
								
									
										11
									
								
								sync-runner/package_manager.list
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								sync-runner/package_manager.list
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,11 @@ | |||
| install(pacman): pacman -Sy %args | ||||
| uninstall(pacman): pacman -Rn %args | ||||
| 
 | ||||
| install(apt): apt install %args | ||||
| uninstall(apt): apt remove %args | ||||
| 
 | ||||
| install(dnf): dnf install %args | ||||
| uninstall(dnf): dnf remove %args | ||||
| 
 | ||||
| install(yum): yum install %args | ||||
| uninstall(yum): yum remove %args | ||||
							
								
								
									
										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, | ||||
| } | ||||
							
								
								
									
										15
									
								
								sync-runner/src/cfg/mod.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								sync-runner/src/cfg/mod.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,15 @@ | |||
| 
 | ||||
| #![allow(unused)] | ||||
| 
 | ||||
| use std::collections::HashMap; | ||||
| 
 | ||||
| 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> | ||||
| } | ||||
							
								
								
									
										34
									
								
								sync-runner/src/crates/mod.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								sync-runner/src/crates/mod.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,34 @@ | |||
| use std::collections::HashMap; | ||||
| 
 | ||||
| use serde::{Deserialize, Serialize}; | ||||
| 
 | ||||
| mod pm; | ||||
| 
 | ||||
| #[derive(Debug, Serialize, Deserialize)] | ||||
| pub struct Package { | ||||
|     name: String, | ||||
| } | ||||
| 
 | ||||
| impl Package { | ||||
|     pub fn install(&self) { | ||||
|         todo!() | ||||
|     } | ||||
| 
 | ||||
|     pub fn uninstall(&self) { | ||||
|         todo!() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug, Serialize, Deserialize)] | ||||
| pub struct CrateAction { | ||||
|     pub name: String, | ||||
|     pub command: String, | ||||
|     pub args: Vec<String>, | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug, Serialize, Deserialize)] | ||||
| pub struct Crate { | ||||
|     pub pkgs: HashMap<String, Package>, | ||||
|     pub actions: HashMap<String, CrateAction>, | ||||
|     pub super_actions: HashMap<String, CrateAction>, | ||||
| } | ||||
							
								
								
									
										90
									
								
								sync-runner/src/crates/pm.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										90
									
								
								sync-runner/src/crates/pm.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,90 @@ | |||
| use lazy_static::lazy_static; | ||||
| use regex::Regex; | ||||
| use std::{process::Command, str::FromStr}; | ||||
| 
 | ||||
| const pm_cfg: &str = include_str!("../../package_manager.list"); | ||||
| 
 | ||||
| /// regex: `(?<pm>[a-z]+)>(?<action>(?:un)?install+): (?<command>.*)`
 | ||||
| /// example: pacman>install: pacman -Sy %args
 | ||||
| 
 | ||||
| lazy_static! { | ||||
|     static ref PM_REGEX: Regex = | ||||
|         Regex::new(r"(?P<pm>[a-z]+)>(?P<action>(?:un)?install+): (?P<command>.*)").unwrap(); | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug)] | ||||
| struct PackageManager { | ||||
|     name: String, | ||||
|     command: String, | ||||
|     args: Vec<String>, | ||||
| } | ||||
| 
 | ||||
| impl PackageManager { | ||||
|     fn new(name: &str, command: &str, args: Vec<String>) -> Self { | ||||
|         Self { | ||||
|             name: name.to_string(), | ||||
|             command: command.to_string(), | ||||
|             args, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn install(&self, packages: Vec<String>) -> Result<(), Vec<String>> { | ||||
|         Command::new(&self.command) | ||||
|             .args(&self.args) | ||||
|             .args(packages) | ||||
|             .spawn() | ||||
|             .expect("err"); | ||||
|         Ok(()) | ||||
|     } | ||||
|     pub fn uninstall(&self, packages: Vec<String>) -> Result<(), Vec<String>> { | ||||
|         todo!(); | ||||
|     } | ||||
| } | ||||
| impl FromStr for PackageManager { | ||||
|     type Err = String; | ||||
|     fn from_str(s: &str) -> Result<Self, Self::Err> { | ||||
|         let caps = PM_REGEX.captures(s).ok_or("invalid package manager")?; | ||||
|         let name = caps.name("pm").ok_or("invalid package manager")?.as_str(); | ||||
|         let command = caps | ||||
|             .name("command") | ||||
|             .ok_or("invalid package manager")? | ||||
|             .as_str(); | ||||
|         let args = caps | ||||
|             .name("args") | ||||
|             .ok_or("invalid package manager")? | ||||
|             .as_str() | ||||
|             .split_whitespace() | ||||
|             .map(|s| s.to_string()) | ||||
|             .collect(); | ||||
|         Ok(Self::new(name, command, args)) | ||||
|     } | ||||
| } | ||||
| #[derive(Debug)] | ||||
| struct Package { | ||||
|     name: String, | ||||
| } | ||||
| impl FromStr for Package { | ||||
|     type Err = String; | ||||
|     fn from_str(s: &str) -> Result<Self, Self::Err> { | ||||
|         let caps = PM_REGEX.captures(s).ok_or("invalid package")?; | ||||
|         let name = caps.name("name").ok_or("invalid package")?.as_str(); | ||||
|         Ok(Self::new(name)) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Package { | ||||
|     fn new(name: &str) -> Self { | ||||
|         Self { | ||||
|             name: name.to_string(), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub fn package_managers() -> Vec<PackageManager> { | ||||
|     pm_cfg | ||||
|         .lines() | ||||
|         .map(|s| s.to_string()) | ||||
|         .map(|s| s.parse::<PackageManager>()) | ||||
|         .collect::<Result<Vec<PackageManager>, String>>() | ||||
|         .expect("invalid package manager") | ||||
| } | ||||
							
								
								
									
										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() | ||||
| } | ||||
							
								
								
									
										77
									
								
								sync-runner/src/main.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								sync-runner/src/main.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,77 @@ | |||
| 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; | ||||
| 
 | ||||
| fn main() -> Result<(), Box<dyn std::error::Error>> { | ||||
|     logging::setup_logger()?; | ||||
| 
 | ||||
|     let git_sha1 = String::from_utf8( | ||||
|         command_args!("git", "rev-parse", "HEAD") | ||||
|             .stdout(Stdio::piped()) | ||||
|             .execute_output()? | ||||
|             .stdout, | ||||
|     )?; | ||||
| 
 | ||||
|     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 = | ||||
|                 toml::from_str::<Config>(&read_to_string(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 c in source.get_crates()? { | ||||
|                     info!("{} pkgs", c.pkgs.len()) | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         _ => { | ||||
|             println!("{action:#?}"); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     Ok(()) | ||||
| } | ||||
							
								
								
									
										5
									
								
								sync-runner/src/prelude.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								sync-runner/src/prelude.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,5 @@ | |||
| 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() | ||||
| } | ||||
							
								
								
									
										241
									
								
								sync-runner/src/source/git.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										241
									
								
								sync-runner/src/source/git.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,241 @@ | |||
| use std::{ | ||||
|     env::current_dir, fs::{self, create_dir_all, exists}, io::Write, path::{Path, PathBuf} | ||||
| }; | ||||
| 
 | ||||
| use git2::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.url); | ||||
|         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> { | ||||
|         Repository::clone_recurse(&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(()) | ||||
| } | ||||
							
								
								
									
										68
									
								
								sync-runner/src/source/mod.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								sync-runner/src/source/mod.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,68 @@ | |||
| use std::{env::{current_dir, set_current_dir}, fs::{create_dir_all, 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>, | ||||
| } | ||||
| 
 | ||||
| impl Default for Source { | ||||
|     fn default() -> Self { | ||||
|         Source { | ||||
|             interval: 60, | ||||
|             git: None, | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Source { | ||||
|     pub fn available(&self) -> bool { | ||||
|         if let Some(git) = &self.git { | ||||
|             trace!("checking git..."); | ||||
|             return git.ensure().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)?; | ||||
|         } | ||||
|         Ok(()) | ||||
|     } | ||||
| 
 | ||||
|     pub fn get_crates(&self) -> Result<Vec<Crate>, Box<dyn std::error::Error>> { | ||||
|         let mut crates = vec![]; | ||||
|         if let Some(git) = &self.git { | ||||
|             trace!("getting crates from git..."); | ||||
|             debug!("{}", current_dir()?.display()); | ||||
| 
 | ||||
|             // get crates (read dir, crates/*/crate.toml)
 | ||||
|             for crate_file in glob::glob("crates/*/crate.toml").expect("err") { | ||||
|                 debug!("{crate_file:#?}"); | ||||
|                 match crate_file { | ||||
|                     Ok(cd) =>{ 
 | ||||
|                         debug!("{}", cd.display()); | ||||
|                         crates.push(toml::from_str(&read_to_string(cd)?)?) | ||||
|                     }, | ||||
|                     _ => continue
 | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         debug!("{:#?}", crates); | ||||
|         Ok(crates) | ||||
|     } | ||||
| } | ||||
							
								
								
									
										1
									
								
								sync.sh
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								sync.sh
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1 @@ | |||
| node sync-runner/index.js | ||||
							
								
								
									
										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.git] # default is the uid | ||||
| url = "https://git.saluco.nl/strix/dotfiles.git" | ||||
| crate_dir = "./crates"                           # default | ||||
| cfg_toml = "./syncr.toml"                        # default | ||||
| 
 | ||||
| [source.work.git] | ||||
| url = "https://git.saluco.nl/dotfiles.git" | ||||
| branch = "work" | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue