diff options
| author | seraphim <18ycm9tx@anonaddy.me> | 2026-08-20 21:42:04 +0700 |
|---|---|---|
| committer | seraphim <18ycm9tx@anonaddy.me> | 2026-08-20 21:42:04 +0700 |
| commit | b57f994aa7c74d1b4e17e3df31ddf94702a8f6de (patch) | |
| tree | 4996797bc050b064f009e524e0b722737c01a66c /update.sh | |
| parent | e24fec3b8f2ac06ad8eb149df3fa36770df3bd2f (diff) | |
add update.sh; sync live system state from /etc/portage into repo
Diffstat (limited to 'update.sh')
| -rwxr-xr-x | update.sh | 167 |
1 files changed, 167 insertions, 0 deletions
diff --git a/update.sh b/update.sh new file mode 100755 index 0000000..8a7d108 --- /dev/null +++ b/update.sh @@ -0,0 +1,167 @@ +#!/usr/bin/env bash +set -euo pipefail + +REPO_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +QUICK=no +DO_COMMIT=no +INSTALL_HOOK=no +DRY_RUN=no + +usage() { + cat <<EOF +Sync live system state back into the dotfiles repo. + +Usage: $0 [options] + + -q, --quick non-interactive, minimal output (used by postsync hook) + -c, --commit git add -A && git commit after syncing + -h, --hook-install write /etc/portage/postsync.d hook (auto after each emerge) + -n, --dry-run show what would change without copying + --help show this help +EOF +} + +while [[ $# -gt 0 ]]; do + case "$1" in + -q|--quick) QUICK=yes ;; + -c|--commit) DO_COMMIT=yes ;; + -h|--hook-install) INSTALL_HOOK=yes ;; + -n|--dry-run) DRY_RUN=yes ;; + --help) usage; exit 0 ;; + *) echo "unknown option: $1" >&2; usage >&2; exit 2 ;; + esac + shift +done + +log() { printf '\033[1;32m[sync]\033[0m %s\n' "$*"; } +warn() { printf '\033[1;33m[warn]\033[0m %s\n' "$*"; } + +root_cp() { + if [[ "$DRY_RUN" == "yes" ]]; then + log " would copy $1 -> $2" + return 0 + fi + if [[ $EUID -eq 0 ]]; then + cp "$1" "$2" + elif cp "$1" "$2" 2>/dev/null; then + : # readable as current user — done + elif command -v sudo >/dev/null 2>&1; then + sudo cp "$1" "$2" + else + warn "need root to copy $1" + return 1 + fi +} + +rsync_portage() { + # -L: dereference symlinks so /etc/portage symlinks back into the repo + # are copied as content, never re-created as self-referencing symlinks. + # Broken symlinks are excluded (rsync -L aborts on dangling referents). + local exfile + exfile="$(mktemp)" + printf '.gitkeep\nmake.profile\n' > "$exfile" + find /etc/portage -type l ! -exec test -e {} \; -printf '%P\n' >> "$exfile" + + local -a opts=(-aL --delete "--exclude-from=$exfile") + [[ "$DRY_RUN" == "yes" ]] && opts+=(-n) + log "portage /etc/portage -> repo/portage/etc/portage" + rsync "${opts[@]}" /etc/portage/ "$REPO_DIR/portage/etc/portage/" + rm -f "$exfile" +} + +sync_portage() { + rsync_portage + + if [[ -e /etc/portage/make.profile ]]; then + log "profile: $(readlink /etc/portage/make.profile)" + if [[ "$DRY_RUN" != "yes" ]]; then + printf '%s\n' "$(readlink /etc/portage/make.profile)" > "$REPO_DIR/portage/PROFILE" + else + log " would write: portage/PROFILE" + fi + fi + + log "world /var/lib/portage/world -> repo" + if [[ -L /var/lib/portage/world ]] \ + && [[ "$(readlink -f /var/lib/portage/world)" == "$REPO_DIR/portage/var/lib/portage/world" ]]; then + log " already a symlink into the repo — no copy needed" + else + root_cp /var/lib/portage/world "$REPO_DIR/portage/var/lib/portage/world" + fi + + if [[ -s /var/lib/portage/world_sets ]]; then + log "world_sets /var/lib/portage/world_sets -> repo" + root_cp /var/lib/portage/world_sets "$REPO_DIR/portage/var/lib/portage/world_sets" + fi +} + +sync_system() { + log "fstab /etc/fstab -> repo/fstab/etc/fstab" + root_cp /etc/fstab "$REPO_DIR/fstab/etc/fstab" + + if [[ -f /etc/greetd/config.toml ]]; then + log "greetd /etc/greetd/config.toml -> repo/config.toml" + root_cp /etc/greetd/config.toml "$REPO_DIR/config.toml" + fi +} + +install_hook() { + local hook=/etc/portage/postsync.d/update-dotfiles + if [[ "$DRY_RUN" == "yes" ]]; then + log " would write: $hook" + return 0 + fi + log "writing $hook" + if [[ $EUID -eq 0 ]]; then + mkdir -p /etc/portage/postsync.d + cat > "$hook" <<EOF +#!/usr/bin/env bash +# generated by update.sh --hook-install — keeps dotfiles fresh after each emerge +exec "$REPO_DIR/update.sh" --quick +EOF + chmod +x "$hook" + elif command -v sudo >/dev/null 2>&1; then + sudo tee "$hook" >/dev/null <<EOF +#!/usr/bin/env bash +# generated by update.sh --hook-install — keeps dotfiles fresh after each emerge +exec "$REPO_DIR/update.sh" --quick +EOF + sudo chmod +x "$hook" + else + warn "need root to write $hook" + fi +} + +commit() { + cd "$REPO_DIR" + if [[ -n "$(git status --porcelain)" ]]; then + git add -A + git commit -q -m "sync system state: $(date '+%Y-%m-%d %H:%M')" + log "committed." + else + log "nothing to commit." + fi +} + +main() { + if [[ "$DRY_RUN" == "yes" ]]; then + log "dry-run mode — showing changes, nothing written" + fi + sync_portage + sync_system + [[ "$INSTALL_HOOK" == "yes" ]] && install_hook + + if [[ "$DO_COMMIT" == "yes" ]]; then + commit + elif [[ "$DRY_RUN" != "yes" && "$QUICK" != "yes" ]]; then + cd "$REPO_DIR" + if [[ -n "$(git status --porcelain)" ]]; then + warn "dotfiles changed — review and commit (or rerun with --commit)." + git status --short + else + log "repo is up to date." + fi + fi +} + +main "$@"
\ No newline at end of file |
