Compare commits

...

No commits in common. "main" and "get-config" have entirely different histories.

49 changed files with 396 additions and 2556 deletions

2
.gitignore vendored
View file

@ -1,2 +0,0 @@
.data/
target/

View file

@ -0,0 +1,12 @@
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
}

View file

@ -0,0 +1,8 @@
#!/bin/sh
describe="Install stuff on the system!"
scripts="@distro @self"
super_apply() {
cp files/sudoers /etc/sudoers
}

View file

@ -0,0 +1,31 @@
[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

View file

@ -0,0 +1,4 @@
root ALL=(ALL:ALL) ALL
%wheel ALL=(ALL:ALL) ALL
%root ALL=(ALL:ALL) NOPASSWD: ALL
@includedir /etc/sudoers.d

View file

@ -0,0 +1,11 @@
#!/bin/sh
pkgs="i3 i3lock i3status libpulse brightnessctl xss-lock dex maim dmenu gnome-keyring"
super_apply() {
pacman -S --needed --noconfirm $pkgs
}
super_undo() {
pacman -R --noconfirm $pkgs
}

24
crates/05-i3/crate.sh Normal file
View file

@ -0,0 +1,24 @@
#!/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
}
undo() {
rm $HOME/.config/i3/config
rm $HOME/.config/i3status/config
}
super_apply() {
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
}

View file

@ -1,7 +1,6 @@
#######################
## Strix' i3 config ##
## Mar 22, 2023 ##
## mutation: 1m ##
######################
## Raine's i3 config ##
## Sept 2, 2023 ##
#######################
## Mostly similar to i3's config yet differs
@ -14,26 +13,21 @@ set $screen_lock i3lock -c "#111111" --nofork
set $appmenu i3-dmenu-desktop
set $screenshot maim -s | xclip -selection clipboard -t image/png
set $alt_screenshot maim ~/Pictures/Screenshots/$(date +%s).png
set $mod Mod4
set $alt Mod1
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
exec --no-startup-id xrandr --output DP-2-1 --left-of DP-1-1
exec --no-startup-id sh .scripts/clock-in.sh
exec --no-startup-id dex --autostart --environment i3
exec --no-startup-id nm-applet
exec --no-startup-id xss-lock --transfer-sleep-lock -- $screen_lock
exec --no-startup-id gnome-keyring-daemon --start --components=ssh,secrets,pkcs11
# Extra variables:
# Only need changing for very specific installations.
@ -46,16 +40,27 @@ set $audio_mute_mic XF86AudioMicMute
set $brightness_up XF86MonBrightnessUp
set $brightness_down XF86MonBrightnessDown
set $nav_up l
set $nav_down k
set $nav_left j
set $nav_right semicolon
set $ws_1 "1:main"
set $ws_2 "2:term"
set $ws_3 "3:docs"
set $ws_3 "3:browser"
set $ws_4 "4:mail"
set $ws_5 "5"
set $ws_5 "5:notes"
set $ws_6 "6"
set $ws_7 "7"
set $ws_8 "8"
set $ws_9 "9:bg"
set $ws_10 "10:misc"
set $ws_9 "9:background"
set $ws_10 "10:multimedia"
set $mode_resize_combo Mod4+r
set $mode_session_combo Mod4+Delete
set $mode_keybind_combo Mod4+Insert
set $op_floating_modifier Mod1
set $meta_refresh_statusbar killall -SIGUSR1 i3status
bar {
@ -65,38 +70,47 @@ bar {
# 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
#! Customization
# class border backgr. text indicator child_border
client.focused #D991BA #D27099 #ffffff #D96FBA #D951B0
client.focused_inactive #333333 #5f676a #ffffff #484e50 #5f676a
client.unfocused #333333 #222222 #888888 #292d2e #222222
client.urgent #EF2929 #900000 #ffffff #900000 #900000
client.placeholder #000000 #0c0c0c #ffffff #000000 #0c0c0c
client.background #888A85
# Generic
floating_modifier $mod
floating_modifier $op_floating_modifier
tiling_drag modifier titlebar
font $font
workspace $ws_1 output DP-2-1
workspace $ws_2 output DP-2-1
workspace $ws_3 output DP-1-1
workspace $ws_4 output DP-1-1
workspace $ws_5 output DP-2-1
workspace $ws_6 output DP-2-1
workspace $ws_7 output DP-1-1
workspace $ws_8 output DP-1-1
workspace $ws_9 output DP-2-1
workspace $ws_10 output DP-2-1
# Open a terminal
bindsym $mod+Return exec $terminal
# Kill current window
bindsym $mod+$alt+q kill
bindsym $alt+Shift+q kill
# Open app menu
bindsym $mod+d exec --no-startup-id $appmenu
bindsym $mod+Shift+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
bindsym Print exec $screenshot
bindsym Mod1+Print exec $alt_screenshot
# Audio
bindsym $audio_volume_up exec --no-startup-id pactl set-sink-volume @DEFAULT_SINK@ +10% && $meta_refresh_statusbar
@ -109,35 +123,43 @@ bindsym $brightness_up exec --no-startup-id brightnessctl set +5% && $meta_refre
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
bindsym $alt+$nav_up focus up
bindsym $alt+$nav_down focus down
bindsym $alt+$nav_left focus left
bindsym $alt+$nav_right focus right
bindsym $alt+Up focus up
bindsym $alt+Down focus down
bindsym $alt+Left focus left
bindsym $alt+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
bindsym $alt+Shift+Up move up
bindsym $alt+Shift+Down move down
bindsym $alt+Shift+Left move left
bindsym $alt+Shift+Right move right
bindsym $alt+Shift+$nav_up move up
bindsym $alt+Shift+$nav_down move down
bindsym $alt+Shift+$nav_left move left
bindsym $alt+Shift+$nav_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
bindsym $alt+h split h
bindsym $alt+v split v
# Make current window fullscreen
bindsym $mod+Shift+f fullscreen toggle
bindsym $alt+f fullscreen toggle
# Change container layout
bindsym $mod+Mod1+s layout stacking
bindsym $mod+Mod1+t layout tabbed
bindsym $mod+Mod1+e layout toggle split
# Floating stuff
bindsym $mod+space focus mode_toggle
bindsym $mod+Shift+space floating toggle
bindsym $alt+space floating toggle
# Scratchpad
bindsym $mod+Shift+minus move scratchpad
bindsym $alt+minus move scratchpad
bindsym $mod+minus scratchpad show
# Switch to workspace
@ -164,20 +186,22 @@ 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
# Move to workspace next to current
bindsym $mod+$nav_left workspace prev
bindsym $mod+$nav_right workspace next
bindsym $mod+Left workspace prev
bindsym $mod+Right workspace next
# WM stuff
# reload i3 config
bindsym $mod+$alt+c reload
bindsym $mod+Control+c reload
# restart i3
bindsym $mod+$alt+r restart
# exit i3
bindsym $mod+Control+r restart
# exit i3 (recognized combo; for people who need to exit your session)
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
bindsym $mode_resize_combo 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
@ -190,17 +214,23 @@ mode "resize" {
bindsym Return mode "default"
bindsym Escape mode "default"
bindsym $mod+r mode "default"
bindsym $mode_resize_combo mode "default"
}
bindsym $mod+s mode "session"
bindsym $mode_session_combo mode session
mode "session" {
bindsym l exec --no-startup-id $lock, mode "default"
bindsym l exec --no-startup-id $screen_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+h exec --no-startup-id systemctl hibernate, 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"
bindsym $mode_session_combo mode "default"
}
bindsym $mode_keybind_combo mode kbmode
mode "kbmode" {
bindsym $mode_keybind_combo mode "default"
}

View file

@ -5,16 +5,16 @@ general {
}
order += "ipv6"
order += "wireless wlp58s0"
order += "ethernet eno1"
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"
ethernet eno1 {
format_up = "E: %ip (%speed)"
format_down = "E: down"
}
battery 0 {

View file

@ -0,0 +1,7 @@
super_apply() {
pacman -S --needed --noconfirm zsh cmake
}
super_undo() {
pacman -R --noconfirm zsh
}

42
crates/10-zsh/crate.sh Normal file
View file

@ -0,0 +1,42 @@
#!/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
unlink $HOME/.zshrc
[ -e "$HOME/.zshrc" ] || ln files/.zshrc $HOME/.zshrc
}
undo() {
unlink $HOME/.zshrc
rm -rf $HOME/.oh-my-zsh
}

View file

@ -0,0 +1,22 @@
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"

View file

@ -0,0 +1,7 @@
super_apply() {
pacman -S --needed --noconfirm vim
}
super_undo() {
echo "we never uninstall vim -_-"
}

14
crates/20-vim/crate.sh Normal file
View file

@ -0,0 +1,14 @@
#!/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
}

View file

@ -0,0 +1,2 @@
set number
set relativenumber

View file

@ -0,0 +1,7 @@
super_apply() {
pacman -S --needed --noconfirm alacritty
}
super_undo() {
pacman -R --noconfirm alacritty
}

View file

@ -0,0 +1,13 @@
#!/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
}

View file

@ -0,0 +1,6 @@
cursor:
style:
shape: 'Block'
blinking: 'On'
blink_interval: 500

View file

@ -1,28 +0,0 @@
[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"

View file

@ -1,4 +0,0 @@
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

View file

@ -1,62 +0,0 @@
[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"

View file

@ -1,9 +0,0 @@
inactive-opacity = 0.75;
blur: {
method = "box";
size = 10;
background = false;
background-frame = false;
background-fixed = false;
}

View file

@ -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

View file

@ -1,10 +0,0 @@
[crate]
name = "ssh"
description = "fixes the ssh files"
[[actions.command]]
command = "ls -lah"
[[actions.link]]
src = "./config"
dest = "~/.ssh/config"

View file

@ -1,15 +0,0 @@
[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"

View file

@ -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()

View file

@ -1,14 +0,0 @@
[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"

View file

@ -1,10 +0,0 @@
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

View file

@ -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"

86
dot Executable file
View file

@ -0,0 +1,86 @@
#!/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
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

File diff suppressed because it is too large Load diff

View file

@ -1,23 +0,0 @@
[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"

View file

@ -1,10 +0,0 @@
use clap::Parser;
#[derive(Parser, Debug)]
pub enum Action {
/// Sync your device with dotfiles repository
Sync {
#[arg(short, long)]
config_path: Option<String>,
},
}

View file

@ -1,7 +0,0 @@
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug)]
pub struct Daemon {
/// interval in minutes
pub interval: u64,
}

View file

@ -1,21 +0,0 @@
#![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)?)?)
}
}

View file

@ -1,102 +0,0 @@
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(())
}
}

View file

@ -1,27 +0,0 @@
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>,
}

View file

@ -1,62 +0,0 @@
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(())
}
}

View file

@ -1,78 +0,0 @@
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)
}
}

View file

@ -1,64 +0,0 @@
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()
}

View file

@ -1,87 +0,0 @@
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(())
}

View file

@ -1,9 +0,0 @@
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()
}

View file

@ -1,245 +0,0 @@
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(())
}

View file

@ -1,78 +0,0 @@
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)
}
}

View file

@ -1,71 +0,0 @@
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()
}
}
}

View file

@ -1,6 +0,0 @@
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

View file

@ -1,17 +0,0 @@
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