~mil/sxmo-utils

1c07327fd84597f8702c3e6df2f5c9e155617e98 — Maarten van Gompel 4 months ago c7c9233
sxmo_migrate: new versioned migration

This patch ensures that user customisations to configuration files and
hooks can be more easily kept up to date when we make changes with new
sxmo releases. We need this because our hooks are modified both by us
and the user, to ensure the user has maximal flexibility and freedom in
adjusting the system to his/her needs; needing a mechanism like this
is the cost for that.

Each config file carries a 'configversion' comment that signals the version of
that particular configuration file. The version is unique for each file
and can be anything (as long it wasn't used before); I suggest a simple
incrementing integer.

Configurations that are out of date are moved out of the way when Sxmo
starts, presenting the system-default for that file instead. This
ensures that users always have a working system after an upgrade. Users
need to run sxmo_migrate.sh to 'merge' the changes.

sxmo_migrate.sh now has several modes (1st parameter):

* interactive: the default mode, shows diffs and prompts the user to
  merge changes for file that are out of date. Like before, but makes
  sure to use the file that were 'moved out of the way' earlier.
* all: same as interactive, but prompts for all files even if not
  out of date
* reset: removes all user hooks and reverts to default configurations, good
  option if a user is stuck somehow
* sync: this is a non-interactive mode automatically invoked from
  sxmo_[xw]init.sh, it takes care of making sure default
  configuration files exist and it checks the configversion, moving
  outdated configs out of the way in favour of defaults.
* state: returns the current state, i.e. lists the files
  that have been marked as needing revision.
  (does not sync by itself)

Signed-off-by: Stacy Harper <contact@stacyharper.net>
M Makefile => Makefile +0 -2
@@ 3,7 3,6 @@ PREFIX:=/usr
.PHONY: install shellcheck

VERSION:=1.8.1
CONFIGVERSION:=1.8.0

GITVERSION:=$(shell git describe --tags)



@@ 36,7 35,6 @@ install: $(PROGRAMS)
	cd configs && find default_hooks -type f -exec install -D -m 0755 "{}" "$(DESTDIR)$(PREFIX)/share/sxmo/{}" \; && cd ..

	[ -n "$(GITVERSION)" ] && echo "$(GITVERSION)" > "$(DESTDIR)$(PREFIX)/share/sxmo/version" || echo "$(VERSION)" > "$(DESTDIR)$(PREFIX)/share/sxmo/version"
	echo "$(CONFIGVERSION)" > "$(DESTDIR)$(PREFIX)/share/sxmo/configversion"

	cd resources && find . -type f -exec install -D -m 0644 "{}" "$(DESTDIR)$(PREFIX)/share/sxmo/{}" \; && cd ..


M configs/default_hooks/start => configs/default_hooks/start +2 -2
@@ 79,6 79,6 @@ else
	fi
fi

if ls "$XDG_CONFIG_HOME"/sxmo.old-* 1> /dev/null 2>&1; then
	sxmo_notify_user.sh --urgency=critical "Config has been migrated" "Your configs are out of date - using defaults."
if ! sxmo_migrate.sh state; then
	sxmo_notify_user.sh --urgency=critical "Config needs migration" "$? file(s) in your sxmo configuration are out of date and disabled - using defaults until you migrate"
fi

M configs/profile.d/sxmo_init.sh => configs/profile.d/sxmo_init.sh +0 -24
@@ 62,30 62,6 @@ _sxmo_load_environments() {
	[ -f "$deviceprofile" ] && . "$deviceprofile"
}

_sxmo_check_and_move_config() {
	# if user needs to migrate configs, move them and alert the user
	REQUIRED_VER="$(cat /usr/share/sxmo/configversion)"

	# First start
	if ! [ -d "$XDG_CONFIG_HOME/sxmo" ]; then
		mkdir -p "$XDG_CONFIG_HOME/sxmo"
		printf %s "$REQUIRED_VER" > "$XDG_CONFIG_HOME/sxmo/.configversion"
		return
	fi

	if [ -f "$XDG_CONFIG_HOME/sxmo/.configversion" ]; then
		CUR_VER="$(cat "$XDG_CONFIG_HOME/sxmo/.configversion")"
	else
		CUR_VER="UNKNOWN"
	fi

	if [ "$REQUIRED_VER" != "$CUR_VER" ]; then
		mv "$XDG_CONFIG_HOME/sxmo" "$XDG_CONFIG_HOME/sxmo.old-$CUR_VER"
		mkdir -p "$XDG_CONFIG_HOME/sxmo"
		printf %s "$REQUIRED_VER" > "$XDG_CONFIG_HOME/sxmo/.configversion"
	fi
}

_sxmo_grab_session() {
	if ! _sxmo_is_running; then
		unset SWAYSOCK

M scripts/core/sxmo_migrate.sh => scripts/core/sxmo_migrate.sh +135 -26
@@ 28,38 28,126 @@ resolvedifference() {
	elif [ "e" = "$reply" ]; then
		$EDITOR "$userfile" "$defaultfile"
	fi

	#finish the migration, removing .needs-migration and moving to right place
	case "$userfile" in
		*needs-migration)
			mv -f "$userfile" "${userfile%.needs-migration}"
			;;
	esac
	printf "\n"
}

defaultconfig() {
	if [ ! -r "$2" ]; then
		mkdir -p "$(dirname "$2")"
		cp "$1" "$2"
		chmod "$3" "$2"
	else
		if ! diff "$2" "$1" > /dev/null; then
			resolvedifference "$2" "$1"
checkconfigversion() {
	userfile="$1"
	reffile="$2"
	if [ ! -e "$userfile" ] || [ ! -e "$reffile" ]; then
		#if the userfile doesn't exist then we revert to default anyway so it's considered up to date
		return 0
	fi
	refversion=$(grep -e "^[#;]\\s*configversion\\s*[:=]" "$reffile" |  tr -d '#;:=[:space:]')
	userversion=$(grep -e "^[#;]\\s*configversion\\s*[:=]" "$userfile" |  tr -d '#;:=[:space:]')
	if [ -z "$userversion" ]; then
		#no user version found, check file contents instead
		tmpreffile="${XDG_RUNTIME_DIR}/versioncheck"
		grep -ve "^[#;]\\s*configversion\\s*[:=]" "$reffile" > "$tmpreffile"
		if diff "$tmpreffile" "$userfile" > /dev/null; then
			rm "$tmpreffile"
			return 0
		else
			rm "$tmpreffile"
			return 1
		fi
	else
		[ "$refversion" = "$userversion" ]
	fi
}

defaultconfig() {
	defaultfile="$1"
	userfile="$2"
	filemode="$3"
	if [ -e "$userfile.needs-migration" ] && ([ "$MODE" = "interactive" ] || [ "$MODE" = "all" ]); then
		resolvedifference "$userfile.needs-migration" "$defaultfile"
		chmod "$filemode" "$userfile" 2> /dev/null
	elif [ ! -r "$userfile" ]; then
		mkdir -p "$(dirname "$userfile")"
		sxmo_log "Installing default configuration $userfile..."
		cp "$defaultfile" "$userfile"
		chmod "$filemode" "$userfile"
	elif [ "$MODE" = "reset" ]; then
		mv -f "$userfile" "$userfile.backup"
		cp "$defaultfile" "$userfile"
		chmod "$filemode" "$userfile"
	elif ! checkconfigversion "$userfile" "$defaultfile" || [ "$MODE" = "all" ]; then
		case "$MODE" in
			"interactive"|"all")
				resolvedifference "$userfile" "$defaultfile"
				;;
			"sync")
				sxmo_log "$userfile is out of date, disabling and marked as needing migration..."
				[ ! -e "$userfile.needs-migration" ] && cp "$userfile" "$userfile.needs-migration" #never overwrite older .needs-migration files, they take precendence
				chmod "$filemode" "$userfile.needs-migration"
				cp "$defaultfile" "$userfile"
				chmod "$filemode" "$userfile"
				;;
		esac
	fi
}

checkhooks() {
	if [ -e "$XDG_CONFIG_HOME/sxmo/hooks/" ]; then
		for hook in "$XDG_CONFIG_HOME/sxmo/hooks/"*; do
			defaulthook="/usr/share/sxmo/default_hooks/$(basename "$hook")"
			if [ "$MODE" = "reset" ]; then
				mv -f "$hook" "$hook.backup" #move the hook away
				continue
			fi
			case "$hook" in
				*.needs-migration)
					defaulthook="/usr/share/sxmo/default_hooks/$(basename "$hook" ".needs-migration")"
					[ "$MODE" = sync ] && continue # ignore this already synced hook
					;;
				*.backup)
					#skip
					continue
					;;
				*)
					#if there is already one marked as needing migration, use that one instead and skip this one
					[ -e "$hook.needs-migration" ] && continue
					defaulthook="/usr/share/sxmo/default_hooks/$(basename "$hook")"
					;;
			esac
			if [ -f "$defaulthook" ]; then
				if ! diff "$hook" "$defaulthook" > /dev/null; then
					resolvedifference "$hook" "$defaulthook"
				else
				if diff "$hook" "$defaulthook" > /dev/null && [ "$MODE" != "sync" ]; then
					printf "\e[33mHook %s is identical to the default, so you don't need a custom hook, remove it? [Y/n]\e[0m" "$hook"
					read -r reply < /dev/tty
					if [ "n" != "$reply" ]; then
						rm "$hook"
					fi
				elif ! checkconfigversion "$hook" "$defaulthook" || [ "$MODE" = "all" ]; then
					case "$MODE" in
						"interactive"|"all")
							resolvedifference "$hook" "$defaulthook"
							;;
						"sync")
							sxmo_log "$hook is out of date, disabling and marked as needing migration..."
							#never overwrite older .needs-migration files, they take precendence
							if [ ! -e "$hook.needs-migration" ]; then
								mv "$hook" "$hook.needs-migration"
							else
								rm "$hook"
							fi
							;;
					esac
				fi
			else
			elif [ "$MODE" != "sync" ]; then
				(
					printf "\e[31mThe file \e[32m%s\e[31m is unknown\e[0m\n" "$hook"
					smartdiff -ud "/dev/null" "$hook"
					printf "\e[31mThe hook \e[32m%s\e[31m does not exist (anymore), remove it? [Y/n] \e[0m\n" "$hook"
					read -r reply < /dev/tty
					if [ "n" != "$reply" ]; then
						rm "$hook"
					fi
				) | more
				printf "\n"
			fi


@@ 82,20 170,41 @@ xorg() {
	defaultconfig /usr/share/sxmo/appcfg/dunst.conf "$XDG_CONFIG_HOME/dunst/dunstrc" 644
}

case "$SXMO_WM" in
	sway)
		common
		sway

MODE="interactive" #default mode
[ -n "$1" ] && MODE="$1"

case "$MODE" in
	"interactive"|"all"|"sync"|"reset")
		case "$SXMO_WM" in
			sway)
				common
				sway
				;;
			dwm)
				common
				xorg
				;;
			*)
				common
				sway
				xorg
				;;
		esac

		checkhooks
		;;
	dwm)
		common
		xorg
	"state")
		NEED_MIGRATION="$(find "$XDG_CONFIG_HOME/sxmo/" -name "*.needs-migration")"
		if [ -n "$NEED_MIGRATION" ]; then
			sxmo_log "The following configuration files need migration: $NEED_MIGRATION"
			exit "$(echo "$NEED_MIGRATION" | wc -l)" #exit code represents number of files needing migration
		else
			sxmo_log "All configuration files are up to date"
		fi
		;;
	*)
		common
		sway
		xorg
		sxmo_log "Invalid mode: $MODE"
		exit 2
		;;
esac

checkhooks

M scripts/core/sxmo_winit.sh => scripts/core/sxmo_winit.sh +1 -17
@@ 22,21 22,6 @@ defaults() {
	[ -e "$HOME"/.Xresources ] && xrdb -merge "$HOME"/.Xresources
}

defaultconfig() {
	if [ ! -r "$2" ]; then
		mkdir -p "$(dirname "$2")"
		cp "$1" "$2"
		chmod "$3" "$2"
	fi
}

defaultconfigs() {
	defaultconfig /usr/share/sxmo/appcfg/profile_template "$XDG_CONFIG_HOME/sxmo/profile" 644
	defaultconfig /usr/share/sxmo/appcfg/sway_template "$XDG_CONFIG_HOME/sxmo/sway" 644
	defaultconfig /usr/share/sxmo/appcfg/mako.conf "$XDG_CONFIG_HOME/mako/config" 644
	defaultconfig /usr/share/sxmo/appcfg/foot.ini "$XDG_CONFIG_HOME/foot/foot.ini" 644
}

start() {
	# shellcheck disable=SC2016
	dbus-run-session sh -c '


@@ 54,11 39,10 @@ cleanup() {
init() {
	_sxmo_load_environments
	_sxmo_prepare_dirs
	_sxmo_check_and_move_config
	envvars
	sxmo_migrate.sh sync

	defaults
	defaultconfigs

	# shellcheck disable=SC1090,SC1091
	. "$XDG_CONFIG_HOME/sxmo/profile"

M scripts/core/sxmo_xinit.sh => scripts/core/sxmo_xinit.sh +1 -15
@@ 42,19 42,6 @@ defaultkeyboard() {
	fi
}

defaultconfig() {
	if [ ! -r "$2" ]; then
		mkdir -p "$(dirname "$2")"
		cp "$1" "$2"
		chmod "$3" "$2"
	fi
}

defaultconfigs() {
	defaultconfig /usr/share/sxmo/appcfg/profile_template "$XDG_CONFIG_HOME/sxmo/profile" 644
	defaultconfig /usr/share/sxmo/appcfg/dunst.conf "$XDG_CONFIG_HOME/dunst/dunstrc" 644
	defaultconfig /usr/share/sxmo/appcfg/xinit_template "$XDG_CONFIG_HOME/sxmo/xinit" 644
}

start() {
	# shellcheck disable=SC2016


@@ 74,11 61,10 @@ cleanup() {
init() {
	_sxmo_load_environments
	_sxmo_prepare_dirs
	_sxmo_check_and_move_config
	envvars
	sxmo_migrate.sh sync

	defaults
	defaultconfigs

	# shellcheck disable=SC1090,SC1091
	. "$XDG_CONFIG_HOME/sxmo/profile"