fix(skills): never prune shell installers' current-run backups
The four standalone installers pruned the oldest excess stamp directories regardless of origin, so a migration retiring more than five batches destroyed its own rollback material mid-run — the same data loss already fixed for Go via the run-root registry and present in install.js/install.ps1 as currentRunBackupRoots. Every installer now records the stamp directories it creates and pruning only removes earlier-run batches, which is what the changelog already promises.
This commit is contained in:
@@ -95,8 +95,8 @@ refresh_cache_tree() {
|
||||
backup_skill_dir() {
|
||||
victim="$1"
|
||||
[ -e "$victim" ] || [ -L "$victim" ] || { printf '\n'; return 0; }
|
||||
# Prune before creating this run's archive, so the backup the caller is about
|
||||
# to depend on for rollback can never be a prune candidate.
|
||||
# Prune before creating this run's archive; every stamp this run has already
|
||||
# created is registered, so pruning can only remove earlier-run batches.
|
||||
prune_skill_backups
|
||||
stamp="$(date -u +%Y%m%d-%H%M%S)"
|
||||
name="$(printf '%s' "$victim" | sed 's#[/\\]#-#g; s#^-##')"
|
||||
@@ -110,18 +110,34 @@ backup_skill_dir() {
|
||||
return 1
|
||||
fi
|
||||
done
|
||||
record_current_run_backup_stamp "$backup_root"
|
||||
mkdir -p "$backup_root" || return 1
|
||||
mv "$victim" "$backup" || return 1
|
||||
printf '%s\n' "$backup"
|
||||
}
|
||||
|
||||
# prune_skill_backups keeps only the newest SKILL_BACKUP_KEEP stamped backup
|
||||
# directories under $HOME/.dws/skill-backups, matching Go's skillBackupKeep /
|
||||
# pruneSkillBackups. Glob order is lexicographic and the stamps are UTC
|
||||
# YYYYmmdd-HHMMSS, so the oldest sort first. Best-effort: a removal failure is
|
||||
# reported and never fatal.
|
||||
# directories from earlier runs under $HOME/.dws/skill-backups, matching Go's
|
||||
# skillBackupKeep / pruneSkillBackups. Stamp directories created by the
|
||||
# current process are never pruned (mirroring Go's run-root registry and
|
||||
# install.js's currentRunBackupRoots), so a migration that retires more than
|
||||
# SKILL_BACKUP_KEEP batches stays reversible. Glob order is lexicographic and
|
||||
# the stamps are UTC YYYYmmdd-HHMMSS, so the oldest sort first. Best-effort:
|
||||
# a removal failure is reported and never fatal.
|
||||
SKILL_BACKUP_KEEP=5
|
||||
|
||||
# Stamp directories created by this run, recorded by backup_skill_dir so
|
||||
# pruning can never delete a backup the running migration may still need to
|
||||
# roll back to.
|
||||
CURRENT_RUN_BACKUP_STAMPS=""
|
||||
|
||||
record_current_run_backup_stamp() {
|
||||
case " $CURRENT_RUN_BACKUP_STAMPS " in
|
||||
*" $1 "*) return 0 ;;
|
||||
esac
|
||||
CURRENT_RUN_BACKUP_STAMPS="${CURRENT_RUN_BACKUP_STAMPS} $1"
|
||||
}
|
||||
|
||||
# is_skill_backup_stamp accepts only directory names DWS itself writes: UTC
|
||||
# YYYYmmdd-HHMMSS, with an optional -N collision suffix. Any other entry in
|
||||
# the backup root is foreign (user data, unrelated tooling) and is preserved
|
||||
@@ -153,13 +169,15 @@ prune_skill_backups() {
|
||||
done
|
||||
[ "$prune_total" -gt "$SKILL_BACKUP_KEEP" ] || return 0
|
||||
prune_drop=$((prune_total - SKILL_BACKUP_KEEP))
|
||||
prune_seen=0
|
||||
for prune_entry in "$prune_root"/*; do
|
||||
[ "$prune_drop" -gt 0 ] || break
|
||||
[ -d "$prune_entry" ] || continue
|
||||
is_skill_backup_stamp "${prune_entry##*/}" || continue
|
||||
prune_seen=$((prune_seen + 1))
|
||||
[ "$prune_seen" -le "$prune_drop" ] || break
|
||||
case " $CURRENT_RUN_BACKUP_STAMPS " in
|
||||
*" $prune_entry "*) continue ;;
|
||||
esac
|
||||
rm -rf "$prune_entry" || printf ' ⚠️ 旧 Skill 备份清理失败: %s\n' "$prune_entry" >&2
|
||||
prune_drop=$((prune_drop - 1))
|
||||
done
|
||||
}
|
||||
|
||||
|
||||
@@ -162,8 +162,8 @@ refresh_cache_tree() {
|
||||
backup_skill_dir() {
|
||||
victim="$1"
|
||||
[ -e "$victim" ] || [ -L "$victim" ] || { printf '\n'; return 0; }
|
||||
# Prune before creating this run's archive, so the backup the caller is about
|
||||
# to depend on for rollback can never be a prune candidate.
|
||||
# Prune before creating this run's archive; every stamp this run has already
|
||||
# created is registered, so pruning can only remove earlier-run batches.
|
||||
prune_skill_backups
|
||||
stamp="$(date -u +%Y%m%d-%H%M%S)"
|
||||
name="$(printf '%s' "$victim" | sed 's#[/\\]#-#g; s#^-##')"
|
||||
@@ -179,18 +179,34 @@ backup_skill_dir() {
|
||||
return 1
|
||||
fi
|
||||
done
|
||||
record_current_run_backup_stamp "$backup_root"
|
||||
mkdir -p "$backup_root" || return 1
|
||||
mv "$victim" "$backup" || return 1
|
||||
printf '%s\n' "$backup"
|
||||
}
|
||||
|
||||
# prune_skill_backups keeps only the newest SKILL_BACKUP_KEEP stamped backup
|
||||
# directories under $HOME/.dws/skill-backups, matching Go's skillBackupKeep /
|
||||
# pruneSkillBackups. Glob order is lexicographic and the stamps are UTC
|
||||
# YYYYmmdd-HHMMSS, so the oldest sort first. Best-effort: a removal failure is
|
||||
# reported and never fatal.
|
||||
# directories from earlier runs under $HOME/.dws/skill-backups, matching Go's
|
||||
# skillBackupKeep / pruneSkillBackups. Stamp directories created by the
|
||||
# current process are never pruned (mirroring Go's run-root registry and
|
||||
# install.js's currentRunBackupRoots), so a migration that retires more than
|
||||
# SKILL_BACKUP_KEEP batches stays reversible. Glob order is lexicographic and
|
||||
# the stamps are UTC YYYYmmdd-HHMMSS, so the oldest sort first. Best-effort:
|
||||
# a removal failure is reported and never fatal.
|
||||
SKILL_BACKUP_KEEP=5
|
||||
|
||||
# Stamp directories created by this run, recorded by backup_skill_dir so
|
||||
# pruning can never delete a backup the running migration may still need to
|
||||
# roll back to.
|
||||
CURRENT_RUN_BACKUP_STAMPS=""
|
||||
|
||||
record_current_run_backup_stamp() {
|
||||
case " $CURRENT_RUN_BACKUP_STAMPS " in
|
||||
*" $1 "*) return 0 ;;
|
||||
esac
|
||||
CURRENT_RUN_BACKUP_STAMPS="${CURRENT_RUN_BACKUP_STAMPS} $1"
|
||||
}
|
||||
|
||||
# is_skill_backup_stamp accepts only directory names DWS itself writes: UTC
|
||||
# YYYYmmdd-HHMMSS, with an optional -N collision suffix. Any other entry in
|
||||
# the backup root is foreign (user data, unrelated tooling) and is preserved
|
||||
@@ -222,13 +238,15 @@ prune_skill_backups() {
|
||||
done
|
||||
[ "$prune_total" -gt "$SKILL_BACKUP_KEEP" ] || return 0
|
||||
prune_drop=$((prune_total - SKILL_BACKUP_KEEP))
|
||||
prune_seen=0
|
||||
for prune_entry in "$prune_root"/*; do
|
||||
[ "$prune_drop" -gt 0 ] || break
|
||||
[ -d "$prune_entry" ] || continue
|
||||
is_skill_backup_stamp "${prune_entry##*/}" || continue
|
||||
prune_seen=$((prune_seen + 1))
|
||||
[ "$prune_seen" -le "$prune_drop" ] || break
|
||||
case " $CURRENT_RUN_BACKUP_STAMPS " in
|
||||
*" $prune_entry "*) continue ;;
|
||||
esac
|
||||
rm -rf "$prune_entry" || printf ' ⚠️ 旧 Skill 备份清理失败: %s\n' "$prune_entry" >&2
|
||||
prune_drop=$((prune_drop - 1))
|
||||
done
|
||||
}
|
||||
|
||||
|
||||
@@ -67,6 +67,7 @@ backup_and_remove_skill_dir() {
|
||||
return 1
|
||||
fi
|
||||
done
|
||||
record_current_run_backup_stamp "$(dirname "$_bed_target")"
|
||||
mkdir -p "$(dirname "$_bed_target")" 2>/dev/null || {
|
||||
printf ' ⚠️ 无法创建备份目录,保留原目录 %s\n' "$_bed_dir"
|
||||
return 1
|
||||
@@ -80,13 +81,27 @@ backup_and_remove_skill_dir() {
|
||||
return 1
|
||||
}
|
||||
|
||||
# prune_skill_backups keeps only the newest DWS_SKILL_BACKUP_KEEP stamped backup
|
||||
# directories under $HOME/.dws/skill-backups, matching Go's skillBackupKeep /
|
||||
# pruneSkillBackups. Best-effort: a removal failure is reported, never fatal.
|
||||
# Only safe to call once every backup manifest of this run has been consumed,
|
||||
# because a single transaction can span more than one timestamp.
|
||||
# prune_skill_backups keeps only the newest DWS_SKILL_BACKUP_KEEP stamped
|
||||
# backup directories from earlier runs under $HOME/.dws/skill-backups,
|
||||
# matching Go's skillBackupKeep / pruneSkillBackups. Stamp directories created
|
||||
# by the current process are never pruned (mirroring Go's run-root registry
|
||||
# and install.js's currentRunBackupRoots), so a migration that retires more
|
||||
# than DWS_SKILL_BACKUP_KEEP batches stays reversible. Best-effort: a removal
|
||||
# failure is reported, never fatal.
|
||||
DWS_SKILL_BACKUP_KEEP=5
|
||||
|
||||
# Stamp directories created by this run, recorded by
|
||||
# backup_and_remove_skill_dir so pruning can never delete a backup the
|
||||
# running migration may still need to roll back to.
|
||||
DWS_CURRENT_RUN_BACKUP_STAMPS=""
|
||||
|
||||
record_current_run_backup_stamp() {
|
||||
case " $DWS_CURRENT_RUN_BACKUP_STAMPS " in
|
||||
*" $1 "*) return 0 ;;
|
||||
esac
|
||||
DWS_CURRENT_RUN_BACKUP_STAMPS="${DWS_CURRENT_RUN_BACKUP_STAMPS} $1"
|
||||
}
|
||||
|
||||
# is_skill_backup_stamp accepts only directory names DWS itself writes: UTC
|
||||
# YYYYmmdd-HHMMSS, with an optional -N collision suffix. Any other entry in
|
||||
# the backup root is foreign (user data, unrelated tooling) and is preserved
|
||||
@@ -118,13 +133,15 @@ prune_skill_backups() {
|
||||
done
|
||||
[ "$_psb_total" -gt "$DWS_SKILL_BACKUP_KEEP" ] || return 0
|
||||
_psb_drop=$((_psb_total - DWS_SKILL_BACKUP_KEEP))
|
||||
_psb_seen=0
|
||||
for _psb_entry in "$_psb_root"/*; do
|
||||
[ "$_psb_drop" -gt 0 ] || break
|
||||
[ -d "$_psb_entry" ] || continue
|
||||
is_skill_backup_stamp "${_psb_entry##*/}" || continue
|
||||
_psb_seen=$((_psb_seen + 1))
|
||||
[ "$_psb_seen" -le "$_psb_drop" ] || break
|
||||
case " $DWS_CURRENT_RUN_BACKUP_STAMPS " in
|
||||
*" $_psb_entry "*) continue ;;
|
||||
esac
|
||||
rm -rf "$_psb_entry" || printf ' ⚠️ 旧 Skill 备份清理失败: %s\n' "$_psb_entry"
|
||||
_psb_drop=$((_psb_drop - 1))
|
||||
done
|
||||
}
|
||||
|
||||
|
||||
+25
-8
@@ -85,6 +85,7 @@ backup_and_remove_skill_dir() {
|
||||
return 1
|
||||
fi
|
||||
done
|
||||
record_current_run_backup_stamp "$(dirname "$_bed_target")"
|
||||
mkdir -p "$(dirname "$_bed_target")" 2>/dev/null || {
|
||||
say " ⚠️ 无法创建备份目录,保留原目录 $_bed_dir"
|
||||
return 1
|
||||
@@ -98,13 +99,27 @@ backup_and_remove_skill_dir() {
|
||||
return 1
|
||||
}
|
||||
|
||||
# prune_skill_backups keeps only the newest DWS_SKILL_BACKUP_KEEP stamped backup
|
||||
# directories under $HOME/.dws/skill-backups, matching Go's skillBackupKeep /
|
||||
# pruneSkillBackups. Best-effort: a removal failure is reported, never fatal.
|
||||
# Only safe to call once every backup manifest of this run has been consumed,
|
||||
# because a single transaction can span more than one timestamp.
|
||||
# prune_skill_backups keeps only the newest DWS_SKILL_BACKUP_KEEP stamped
|
||||
# backup directories from earlier runs under $HOME/.dws/skill-backups,
|
||||
# matching Go's skillBackupKeep / pruneSkillBackups. Stamp directories created
|
||||
# by the current process are never pruned (mirroring Go's run-root registry
|
||||
# and install.js's currentRunBackupRoots), so a migration that retires more
|
||||
# than DWS_SKILL_BACKUP_KEEP batches stays reversible. Best-effort: a removal
|
||||
# failure is reported, never fatal.
|
||||
DWS_SKILL_BACKUP_KEEP=5
|
||||
|
||||
# Stamp directories created by this run, recorded by
|
||||
# backup_and_remove_skill_dir so pruning can never delete a backup the
|
||||
# running migration may still need to roll back to.
|
||||
DWS_CURRENT_RUN_BACKUP_STAMPS=""
|
||||
|
||||
record_current_run_backup_stamp() {
|
||||
case " $DWS_CURRENT_RUN_BACKUP_STAMPS " in
|
||||
*" $1 "*) return 0 ;;
|
||||
esac
|
||||
DWS_CURRENT_RUN_BACKUP_STAMPS="${DWS_CURRENT_RUN_BACKUP_STAMPS} $1"
|
||||
}
|
||||
|
||||
# is_skill_backup_stamp accepts only directory names DWS itself writes: UTC
|
||||
# YYYYmmdd-HHMMSS, with an optional -N collision suffix. Any other entry in
|
||||
# the backup root is foreign (user data, unrelated tooling) and is preserved
|
||||
@@ -136,13 +151,15 @@ prune_skill_backups() {
|
||||
done
|
||||
[ "$_psb_total" -gt "$DWS_SKILL_BACKUP_KEEP" ] || return 0
|
||||
_psb_drop=$((_psb_total - DWS_SKILL_BACKUP_KEEP))
|
||||
_psb_seen=0
|
||||
for _psb_entry in "$_psb_root"/*; do
|
||||
[ "$_psb_drop" -gt 0 ] || break
|
||||
[ -d "$_psb_entry" ] || continue
|
||||
is_skill_backup_stamp "${_psb_entry##*/}" || continue
|
||||
_psb_seen=$((_psb_seen + 1))
|
||||
[ "$_psb_seen" -le "$_psb_drop" ] || break
|
||||
case " $DWS_CURRENT_RUN_BACKUP_STAMPS " in
|
||||
*" $_psb_entry "*) continue ;;
|
||||
esac
|
||||
rm -rf "$_psb_entry" || say " ⚠️ 旧 Skill 备份清理失败: $_psb_entry"
|
||||
_psb_drop=$((_psb_drop - 1))
|
||||
done
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user