diff --git a/scripts/install.sh b/scripts/install.sh index fe71b1b..cdf931a 100644 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -115,11 +115,18 @@ header() { clear printf "${BOLD}${BLUE}" cat << 'EOF' - ╔══════════════════════════════════════════════════════════╗ - ║ Some ASCII Art ║ - ╠══════════════════════════════════════════════════════════╣ - ║ Installer · Arch Linux ║ - ╚══════════════════════════════════════════════════════════╝ + ╔══════════════════════════════════════════════════════════════════╗ + ║ _ _ _____ _ _ ║ + ║ /\ | | | | | __ \ | | | | ║ + ║ / \ | |_| | __ _ ___ | | | | ___ ___| | _| |_ ___ _ __ ║ + ║ / /\ \| __| |/ _` / __| | | | |/ _ \/ __| |/ / __/ _ \| '_ \ ║ + ║ / ____ \ |_| | (_| \__ \ | |__| | __/\__ \ <| || (_) | |_) | ║ + ║ /_/ \_\__|_|\__,_|___/ |_____/ \___||___/_|\_\\__\___/| .__/ ║ + ║ | | ║ + ║ |_| ║ + ╠══════════════════════════════════════════════════════════════════╣ + ║ Installer · Arch Linux ║ + ╚══════════════════════════════════════════════════════════════════╝ EOF printf "${RESET}\n" } @@ -537,25 +544,63 @@ install_packages() { } # ───────────────────────────────────────────────────────────────────────────── -# STEP 9 — Stow dotfiles +# STEP 9 — Clone into ~/.atlas-dotfiles and stow # ───────────────────────────────────────────────────────────────────────────── stow_dotfiles() { - phase "Stowing dotfiles" + phase "Copying and stowing dotfiles" - step "Linking ${CONFIG_DIR} → ${HOME}" + # ~/.atlas-dotfiles is the permanent home of the repo. They can delete the original + # clone after installation without breaking anything. + local remote_url + remote_url=$(git -C "$REPO_DIR" remote get-url origin 2>/dev/null || true) - # The config/ dir IS the stow package (its contents mirror $HOME) - # We treat config/ as a single stow package named "config" - # stow will create symlinks in $HOME for everything inside config/ + if [[ -z $remote_url ]]; then + die "Could not determine remote URL from $REPO_DIR (is origin set?)" + fi - # Backup any existing files that would conflict + step "Setting up ~/.atlas-dotfiles as the permanent repo" + info "Remote: $remote_url" + + if [[ -d $DOTFILES_DIR/.git ]]; then + # Already a git repo — verify it points to the same remote + local existing_remote + existing_remote=$(git -C "$DOTFILES_DIR" remote get-url origin 2>/dev/null || true) + if [[ $existing_remote == $remote_url ]]; then + ok "~/.atlas-dotfiles already cloned from correct remote" + run_quiet "Pulling latest" git -C "$DOTFILES_DIR" pull --ff-only origin main + else + warn "~/.atlas-dotfiles exists but points to a different remote:" + warn " existing: $existing_remote" + warn " expected: $remote_url" + if confirm "Replace it with the correct repo?" y; then + rm -rf "$DOTFILES_DIR" + run_quiet "Cloning dotfiles" git clone "$remote_url" "$DOTFILES_DIR" + else + die "Aborted — ~/.atlas-dotfiles remote mismatch" + fi + fi + elif [[ -e $DOTFILES_DIR ]]; then + warn "~/.atlas-dotfiles exists but is not a git repo — moving to ~/.atlas-dotfiles.bak" + mv "$DOTFILES_DIR" "${DOTFILES_DIR}.bak" + run_quiet "Cloning dotfiles" git clone "$remote_url" "$DOTFILES_DIR" + else + run_quiet "Cloning dotfiles" git clone "$remote_url" "$DOTFILES_DIR" + fi + + #TODO Checkout ? + + ok "~/.atlas-dotfiles → $remote_url" + + # Backup any real files that would conflict with stow links + local stow_config_dir="$DOTFILES_DIR/config" local conflicts=() while IFS= read -r link; do - local target="$HOME/${link#$CONFIG_DIR/}" + local rel="${link#$stow_config_dir/}" + local target="$HOME/$rel" if [[ -e $target && ! -L $target ]]; then conflicts+=("$target") fi - done < <(find "$CONFIG_DIR" -type f) + done < <(find "$stow_config_dir" -type f 2>/dev/null) if (( ${#conflicts[@]} )); then warn "${#conflicts[@]} existing file(s) will be backed up (.bak):" @@ -565,29 +610,9 @@ stow_dotfiles() { done fi - # Create DOTFILES_DIR as a symlink to CONFIG_DIR, or use CONFIG_DIR directly - # We symlink ~/.atlas-dotfiles → repo's config dir so stow state lives in the repo - if [[ -L $DOTFILES_DIR ]]; then - local existing_target; existing_target=$(readlink -f "$DOTFILES_DIR") - if [[ $existing_target != "$CONFIG_DIR" ]]; then - warn "~/.atlas-dotfiles already points elsewhere: $existing_target" - warn "Removing and re-linking to $CONFIG_DIR" - rm "$DOTFILES_DIR" - ln -s "$CONFIG_DIR" "$DOTFILES_DIR" - fi - elif [[ -d $DOTFILES_DIR ]]; then - warn "~/.atlas-dotfiles exists as a real directory. Renaming to ~/.atlas-dotfiles.bak" - mv "$DOTFILES_DIR" "${DOTFILES_DIR}.bak" - ln -s "$CONFIG_DIR" "$DOTFILES_DIR" - else - ln -s "$CONFIG_DIR" "$DOTFILES_DIR" - fi - - ok "~/.atlas-dotfiles → $CONFIG_DIR" - - # Now run stow from inside the repo's parent, treating config as the package + # Stow from ~/.atlas-dotfiles treating config/ as the stow package run_quiet "Stowing config" \ - stow --dir="$REPO_DIR" --target="$HOME" --restow config + stow --dir="$DOTFILES_DIR" --target="$HOME" --restow config phase_done } @@ -709,28 +734,37 @@ enable_services() { install_updater() { phase "Dotfiles updater" - local updater_src="$SCRIPTS_DIR/update.sh" - local updater_link="$HOME/.local/bin/atlas-update" + local updater_in_dotfiles="$DOTFILES_DIR/scripts/update.sh" + local updater_link="$HOME/.local/bin/dotfiles-update" - if [[ ! -f $updater_src ]]; then - warn "Updater not found at $updater_src — skipping" + if [[ ! -f $updater_in_dotfiles ]]; then + warn "update.sh not found in ~/.atlas-dotfiles/scripts/ — skipping" + info "Expected: $updater_in_dotfiles" phase_done return fi + chmod +x "$updater_in_dotfiles" mkdir -p "$HOME/.local/bin" - chmod +x "$updater_src" - # Create symlink (stow might also handle this if update.sh is in config/) - # Here we link directly so it's always in PATH regardless of stow layout - ln -sf "$updater_src" "$updater_link" - ok "atlas-update → $updater_src" - info "Run ${BOLD}atlas-update${RESET} any time to sync with upstream" + # Symlink into PATH — stow may also handle this if scripts/ is a stow pkg, + # but we do it explicitly here so it works regardless of stow layout. + ln -sf "$updater_in_dotfiles" "$updater_link" + ok "dotfiles-update → $updater_in_dotfiles" + info "Run ${BOLD}dotfiles-update${RESET} any time to sync with upstream" - # Save repo path into state so updater knows where it lives - mkdir -p "$STATE_DIR" - state_set "repo_dir" "$REPO_DIR" - state_set "last_commit" "$(git -C "$REPO_DIR" rev-parse HEAD)" + # Initialise state inside the permanent repo + local state_dir="$DOTFILES_DIR/.state" + mkdir -p "$state_dir" + # Point state file at the permanent repo, not the installer's location + grep -q "^repo_dir=" "$state_dir/update.state" 2>/dev/null || echo "repo_dir=$DOTFILES_DIR" >> "$state_dir/update.state" + # Stamp the current HEAD so the first `dotfiles-update` knows the baseline + local head; head=$(git -C "$DOTFILES_DIR" rev-parse HEAD) + if grep -q "^last_commit=" "$state_dir/update.state" 2>/dev/null; then + sed -i "s|^last_commit=.*|last_commit=${head}|" "$state_dir/update.state" + else + echo "last_commit=$head" >> "$state_dir/update.state" + fi phase_done } @@ -744,7 +778,7 @@ finish() { cat << 'EOF' ╔══════════════════════════════════════════════════════════╗ ║ ║ - ║ ✔ Installation complete! ║ + ║ ✔ Installation complete! ║ ║ ║ ╚══════════════════════════════════════════════════════════╝ EOF diff --git a/scripts/update.sh b/scripts/update.sh index 4bfc37b..36c28bc 100644 --- a/scripts/update.sh +++ b/scripts/update.sh @@ -190,9 +190,13 @@ git_fetch_and_check() { # ── Conflict detection ──────────────────────────────────────────────────────── collect_changed_files() { - # Files changed between old commit and new commit in the repo + # Files changed inside the config/ stow package between old and new commit. + # Strip the leading "config/" prefix so paths are relative to $HOME, + # matching what stow creates as symlink targets. git -C "$DOTFILES_DIR" diff --name-only "${OLD_HASH}" "${NEW_HASH}" \ - | grep -v '^packages/' | grep -v '^\.' || true + | grep '^config/' \ + | sed 's|^config/||' \ + || true } handle_conflicts() { @@ -292,33 +296,17 @@ apply_git_update() { # ── Stow ───────────────────────────────────────────────────────────────────── apply_stow() { section "Stowing dotfiles" - cd "$DOTFILES_DIR" - # Discover stow packages (top-level dirs, excluding hidden & packages/) - local stow_pkgs=() - while IFS= read -r d; do - local name - name=$(basename "$d") - [[ $name == packages ]] && continue - stow_pkgs+=("$name") - done < <(find "$DOTFILES_DIR" -mindepth 1 -maxdepth 1 -type d ! -name '.*' | sort) - - if (( ${#stow_pkgs[@]} == 0 )); then - warn "No stow packages found in $DOTFILES_DIR" - return + local err_file; err_file=$(mktemp) + if stow --dir="$STOW_DIR" --target="$STOW_TARGET" --restow "$STOW_PKG" 2>"$err_file"; then + ok "stow: $STOW_PKG" + else + warn "stow: $STOW_PKG (errors below)" + while IFS= read -r line; do + echo " ${DIM}${line}${RESET}" + done < "$err_file" fi - - for pkg in "${stow_pkgs[@]}"; do - if stow --restow --target="$STOW_TARGET" "$pkg" 2>/tmp/stow_err; then - ok "stow: $pkg" - else - warn "stow: $pkg (see error below)" - while IFS= read -r line; do - echo " ${DIM}${line}${RESET}" - done < /tmp/stow_err - fi - done - rm -f /tmp/stow_err + rm -f "$err_file" } # ── Package management ──────────────────────────────────────────────────────── @@ -493,7 +481,8 @@ process_aur_group() { # ── Entry point ─────────────────────────────────────────────────────────────── main() { echo - echo "${BOLD} Hyprland Dotfiles Updater${RESET} ${DIM}(Arch Linux)${RESET}" + echo "${BOLD} Atlas Desktop Updater${RESET}" + echo "${DIM} Arch Linux${RESET}" echo " ${DIM}Target: ${DOTFILES_DIR}${RESET}" [[ -d $DOTFILES_DIR/.git ]] || die "$DOTFILES_DIR is not a git repository."