fix(skills): guard pruneSkillBackups against non-DWS directories
Restrict backup pruning to directories whose names match the DWS stamp format (YYYYmmdd-HHMMSS with optional -N suffix) across all 8 installer surfaces (Go, npm, 4 shell, 2 PowerShell). Unknown directories in ~/.dws/skill-backups are now preserved. Also fixes Windows coverage test portability and covers the remaining macOS changed-code gap (retire warning loop in runUpgrade).
This commit is contained in:
@@ -290,7 +290,11 @@ $script:DevSkillBackupRootsThisRun = [System.Collections.Generic.HashSet[string]
|
||||
function Remove-OldDevSkillBackups {
|
||||
$root = Join-Path $HOME ".dws\skill-backups"
|
||||
if (!(Test-Path -LiteralPath $root -PathType Container)) { return }
|
||||
# Only directories whose names match the DWS backup stamp format (UTC
|
||||
# yyyyMMdd-HHmmss, optional -N collision suffix) are candidates; any
|
||||
# other entry is foreign data and is preserved.
|
||||
$dirs = @(Get-ChildItem -LiteralPath $root -Directory -Force -ErrorAction SilentlyContinue |
|
||||
Where-Object { $_.Name -match '^[0-9]{8}-[0-9]{6}(-[0-9]+)?$' } |
|
||||
Sort-Object -Property Name)
|
||||
$excess = $dirs.Count - $DevSkillBackupKeep
|
||||
foreach ($dir in $dirs) {
|
||||
|
||||
@@ -121,12 +121,34 @@ backup_skill_dir() {
|
||||
# YYYYmmdd-HHMMSS, so the oldest sort first. Best-effort: a removal failure is
|
||||
# reported and never fatal.
|
||||
SKILL_BACKUP_KEEP=5
|
||||
|
||||
# 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
|
||||
# so pruning can never remove a directory it cannot prove DWS created.
|
||||
is_skill_backup_stamp() {
|
||||
case "$1" in
|
||||
[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]-[0-9][0-9][0-9][0-9][0-9][0-9])
|
||||
return 0 ;;
|
||||
[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]-[0-9][0-9][0-9][0-9][0-9][0-9]-*)
|
||||
# base stamp is YYYYMMDD-HHMMSS (15 chars) + suffix dash (16th); strip
|
||||
# those 16 chars and require the remainder to be all digits.
|
||||
_isbs_suffix="${1#????????????????}"
|
||||
case "$_isbs_suffix" in
|
||||
""|*[!0-9]*) return 1 ;;
|
||||
esac
|
||||
return 0 ;;
|
||||
esac
|
||||
return 1
|
||||
}
|
||||
|
||||
prune_skill_backups() {
|
||||
prune_root="$HOME/.dws/skill-backups"
|
||||
[ -d "$prune_root" ] || return 0
|
||||
prune_total=0
|
||||
for prune_entry in "$prune_root"/*; do
|
||||
[ -d "$prune_entry" ] || continue
|
||||
is_skill_backup_stamp "${prune_entry##*/}" || continue
|
||||
prune_total=$((prune_total + 1))
|
||||
done
|
||||
[ "$prune_total" -gt "$SKILL_BACKUP_KEEP" ] || return 0
|
||||
@@ -134,6 +156,7 @@ prune_skill_backups() {
|
||||
prune_seen=0
|
||||
for prune_entry in "$prune_root"/*; do
|
||||
[ -d "$prune_entry" ] || continue
|
||||
is_skill_backup_stamp "${prune_entry##*/}" || continue
|
||||
prune_seen=$((prune_seen + 1))
|
||||
[ "$prune_seen" -le "$prune_drop" ] || break
|
||||
rm -rf "$prune_entry" || printf ' ⚠️ 旧 Skill 备份清理失败: %s\n' "$prune_entry" >&2
|
||||
|
||||
@@ -190,12 +190,34 @@ backup_skill_dir() {
|
||||
# YYYYmmdd-HHMMSS, so the oldest sort first. Best-effort: a removal failure is
|
||||
# reported and never fatal.
|
||||
SKILL_BACKUP_KEEP=5
|
||||
|
||||
# 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
|
||||
# so pruning can never remove a directory it cannot prove DWS created.
|
||||
is_skill_backup_stamp() {
|
||||
case "$1" in
|
||||
[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]-[0-9][0-9][0-9][0-9][0-9][0-9])
|
||||
return 0 ;;
|
||||
[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]-[0-9][0-9][0-9][0-9][0-9][0-9]-*)
|
||||
# base stamp is YYYYMMDD-HHMMSS (15 chars) + suffix dash (16th); strip
|
||||
# those 16 chars and require the remainder to be all digits.
|
||||
_isbs_suffix="${1#????????????????}"
|
||||
case "$_isbs_suffix" in
|
||||
""|*[!0-9]*) return 1 ;;
|
||||
esac
|
||||
return 0 ;;
|
||||
esac
|
||||
return 1
|
||||
}
|
||||
|
||||
prune_skill_backups() {
|
||||
prune_root="$HOME/.dws/skill-backups"
|
||||
[ -d "$prune_root" ] || return 0
|
||||
prune_total=0
|
||||
for prune_entry in "$prune_root"/*; do
|
||||
[ -d "$prune_entry" ] || continue
|
||||
is_skill_backup_stamp "${prune_entry##*/}" || continue
|
||||
prune_total=$((prune_total + 1))
|
||||
done
|
||||
[ "$prune_total" -gt "$SKILL_BACKUP_KEEP" ] || return 0
|
||||
@@ -203,6 +225,7 @@ prune_skill_backups() {
|
||||
prune_seen=0
|
||||
for prune_entry in "$prune_root"/*; do
|
||||
[ -d "$prune_entry" ] || continue
|
||||
is_skill_backup_stamp "${prune_entry##*/}" || continue
|
||||
prune_seen=$((prune_seen + 1))
|
||||
[ "$prune_seen" -le "$prune_drop" ] || break
|
||||
rm -rf "$prune_entry" || printf ' ⚠️ 旧 Skill 备份清理失败: %s\n' "$prune_entry" >&2
|
||||
|
||||
@@ -86,12 +86,34 @@ backup_and_remove_skill_dir() {
|
||||
# Only safe to call once every backup manifest of this run has been consumed,
|
||||
# because a single transaction can span more than one timestamp.
|
||||
DWS_SKILL_BACKUP_KEEP=5
|
||||
|
||||
# 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
|
||||
# so pruning can never remove a directory it cannot prove DWS created.
|
||||
is_skill_backup_stamp() {
|
||||
case "$1" in
|
||||
[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]-[0-9][0-9][0-9][0-9][0-9][0-9])
|
||||
return 0 ;;
|
||||
[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]-[0-9][0-9][0-9][0-9][0-9][0-9]-*)
|
||||
# base stamp is YYYYMMDD-HHMMSS (15 chars) + suffix dash (16th); strip
|
||||
# those 16 chars and require the remainder to be all digits.
|
||||
_isbs_suffix="${1#????????????????}"
|
||||
case "$_isbs_suffix" in
|
||||
""|*[!0-9]*) return 1 ;;
|
||||
esac
|
||||
return 0 ;;
|
||||
esac
|
||||
return 1
|
||||
}
|
||||
|
||||
prune_skill_backups() {
|
||||
_psb_root="${HOME}/.dws/skill-backups"
|
||||
[ -d "$_psb_root" ] || return 0
|
||||
_psb_total=0
|
||||
for _psb_entry in "$_psb_root"/*; do
|
||||
[ -d "$_psb_entry" ] || continue
|
||||
is_skill_backup_stamp "${_psb_entry##*/}" || continue
|
||||
_psb_total=$((_psb_total + 1))
|
||||
done
|
||||
[ "$_psb_total" -gt "$DWS_SKILL_BACKUP_KEEP" ] || return 0
|
||||
@@ -99,6 +121,7 @@ prune_skill_backups() {
|
||||
_psb_seen=0
|
||||
for _psb_entry in "$_psb_root"/*; do
|
||||
[ -d "$_psb_entry" ] || continue
|
||||
is_skill_backup_stamp "${_psb_entry##*/}" || continue
|
||||
_psb_seen=$((_psb_seen + 1))
|
||||
[ "$_psb_seen" -le "$_psb_drop" ] || break
|
||||
rm -rf "$_psb_entry" || printf ' ⚠️ 旧 Skill 备份清理失败: %s\n' "$_psb_entry"
|
||||
|
||||
@@ -744,7 +744,11 @@ $script:SkillBackupRootsThisRun = [System.Collections.Generic.HashSet[string]]::
|
||||
function Remove-OldSkillBackups {
|
||||
$root = Join-Path $HOME ".dws\skill-backups"
|
||||
if (!(Test-Path -LiteralPath $root -PathType Container)) { return }
|
||||
# Only directories whose names match the DWS backup stamp format (UTC
|
||||
# yyyyMMdd-HHmmss, optional -N collision suffix) are candidates; any
|
||||
# other entry is foreign data and is preserved.
|
||||
$dirs = @(Get-ChildItem -LiteralPath $root -Directory -Force -ErrorAction SilentlyContinue |
|
||||
Where-Object { $_.Name -match '^[0-9]{8}-[0-9]{6}(-[0-9]+)?$' } |
|
||||
Sort-Object -Property Name)
|
||||
$excess = $dirs.Count - $SkillBackupKeep
|
||||
foreach ($dir in $dirs) {
|
||||
|
||||
@@ -104,12 +104,34 @@ backup_and_remove_skill_dir() {
|
||||
# Only safe to call once every backup manifest of this run has been consumed,
|
||||
# because a single transaction can span more than one timestamp.
|
||||
DWS_SKILL_BACKUP_KEEP=5
|
||||
|
||||
# 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
|
||||
# so pruning can never remove a directory it cannot prove DWS created.
|
||||
is_skill_backup_stamp() {
|
||||
case "$1" in
|
||||
[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]-[0-9][0-9][0-9][0-9][0-9][0-9])
|
||||
return 0 ;;
|
||||
[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]-[0-9][0-9][0-9][0-9][0-9][0-9]-*)
|
||||
# base stamp is YYYYMMDD-HHMMSS (15 chars) + suffix dash (16th); strip
|
||||
# those 16 chars and require the remainder to be all digits.
|
||||
_isbs_suffix="${1#????????????????}"
|
||||
case "$_isbs_suffix" in
|
||||
""|*[!0-9]*) return 1 ;;
|
||||
esac
|
||||
return 0 ;;
|
||||
esac
|
||||
return 1
|
||||
}
|
||||
|
||||
prune_skill_backups() {
|
||||
_psb_root="${HOME}/.dws/skill-backups"
|
||||
[ -d "$_psb_root" ] || return 0
|
||||
_psb_total=0
|
||||
for _psb_entry in "$_psb_root"/*; do
|
||||
[ -d "$_psb_entry" ] || continue
|
||||
is_skill_backup_stamp "${_psb_entry##*/}" || continue
|
||||
_psb_total=$((_psb_total + 1))
|
||||
done
|
||||
[ "$_psb_total" -gt "$DWS_SKILL_BACKUP_KEEP" ] || return 0
|
||||
@@ -117,6 +139,7 @@ prune_skill_backups() {
|
||||
_psb_seen=0
|
||||
for _psb_entry in "$_psb_root"/*; do
|
||||
[ -d "$_psb_entry" ] || continue
|
||||
is_skill_backup_stamp "${_psb_entry##*/}" || continue
|
||||
_psb_seen=$((_psb_seen + 1))
|
||||
[ "$_psb_seen" -le "$_psb_drop" ] || break
|
||||
rm -rf "$_psb_entry" || say " ⚠️ 旧 Skill 备份清理失败: $_psb_entry"
|
||||
|
||||
Reference in New Issue
Block a user