dsh-autorestart 0.6.2 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +460 -12
- package/assets/screenshot-1.png +0 -0
- package/assets/screenshot-2.png +0 -0
- package/assets/screenshot-3.png +0 -0
- package/assets/screenshot-4.png +0 -0
- package/assets/screenshot-5.png +0 -0
- package/client.js +6329 -3863
- package/cordis.patch.yml +44 -1
- package/docs/README.zh.md +297 -9
- package/docs/platform-matrix.md +92 -0
- package/docs/rescue-drill.md +606 -0
- package/index.js +2867 -199
- package/lib/access.js +212 -0
- package/lib/autostart-advice.js +198 -0
- package/lib/backup.js +131 -4
- package/lib/blockers.js +228 -0
- package/lib/checkpoint.js +147 -0
- package/lib/diagnostics.js +188 -0
- package/lib/health.js +264 -0
- package/lib/host-kind.js +145 -0
- package/lib/node-runtime.js +144 -0
- package/lib/rescue-paths.js +253 -0
- package/lib/rescue.js +596 -0
- package/lib/restart-verdict.js +327 -0
- package/lib/restart.js +823 -56
- package/lib/scheduler-capability.js +300 -0
- package/lib/sleep.js +46 -49
- package/lib/startup-mode.js +491 -0
- package/lib/supervisor-config.js +735 -0
- package/lib/supervisor-install.js +557 -0
- package/lib/supervisor.js +1276 -0
- package/lib/watchdog-arbiter.js +117 -0
- package/lib/watchdog-schedule.js +310 -0
- package/lib/watchdog-tier.js +182 -0
- package/lib/watchdog.js +479 -0
- package/locale/en.json +274 -64
- package/locale/zh.json +274 -64
- package/package.json +7 -2
- package/screenshots.json +6 -4
- package/scripts/README.md +65 -0
- package/scripts/drill-rescue.ps1 +508 -0
- package/scripts/emergency-disable.cmd +27 -0
- package/scripts/emergency-toggle.ps1 +373 -0
- package/scripts/restore-profile.cmd +44 -0
- package/scripts/restore-profile.ps1 +608 -0
- package/scripts/run-tests.ps1 +46 -0
- package/supervisor/supervisor.js +884 -0
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
<#
|
|
2
|
+
.SYNOPSIS
|
|
3
|
+
The dsh-autorestart emergency switch: take this plugin out (or put it back)
|
|
4
|
+
when DSH does not come up.
|
|
5
|
+
|
|
6
|
+
.DESCRIPTION
|
|
7
|
+
"I enabled the plugin and DSH does not start any more" is the hardest moment
|
|
8
|
+
for a plugin: fixing it needs the plugin itself. This script touches ONE
|
|
9
|
+
file - the package.json of the desktop profile - and ONE array in it:
|
|
10
|
+
`dsh.profile.bundles`.
|
|
11
|
+
|
|
12
|
+
- disable: take "dsh-autorestart" out of the bundles (it was not there
|
|
13
|
+
anyway -> report "already disabled", that is not a failure);
|
|
14
|
+
- enable : put "dsh-autorestart" back into the bundles (it was already
|
|
15
|
+
there -> report "already enabled");
|
|
16
|
+
- status : read only, prints whether it is in the bundles right now.
|
|
17
|
+
|
|
18
|
+
Two hard rules:
|
|
19
|
+
|
|
20
|
+
1. the file is backed up as `package.json.bak-emergency-<yyyyMMdd-HHmmss>`
|
|
21
|
+
before anything is written;
|
|
22
|
+
2. after writing, the JSON is parsed again and checked; if the check
|
|
23
|
+
fails, the backup is put back and the failure is reported honestly.
|
|
24
|
+
|
|
25
|
+
The indentation, the line endings and the trailing newline of the file are
|
|
26
|
+
kept as they are (the text is edited, the JSON is not re-serialized).
|
|
27
|
+
|
|
28
|
+
LANGUAGE: this file is pure ASCII on purpose. Windows PowerShell 5.1 reads a
|
|
29
|
+
UTF-8 file without a byte order mark as ANSI, so one Chinese character
|
|
30
|
+
inside the script turns into mojibake and the script dies with a ParserError
|
|
31
|
+
before it does anything. The Chinese wording lives in
|
|
32
|
+
scripts\rescue-strings.zh.json (UTF-8, no BOM) and is read at runtime; if
|
|
33
|
+
that file is missing or broken, every message falls back to English and the
|
|
34
|
+
script says so.
|
|
35
|
+
|
|
36
|
+
.EXAMPLE
|
|
37
|
+
powershell -NoProfile -ExecutionPolicy Bypass -File .\emergency-toggle.ps1 -Action status
|
|
38
|
+
|
|
39
|
+
.EXAMPLE
|
|
40
|
+
powershell -NoProfile -ExecutionPolicy Bypass -File .\emergency-toggle.ps1 -Action disable
|
|
41
|
+
|
|
42
|
+
.NOTES
|
|
43
|
+
Windows PowerShell 5.1 is enough (nothing from 6.0+ is used); the .cmd entry
|
|
44
|
+
point double-clicked by the user is that one too.
|
|
45
|
+
#>
|
|
46
|
+
[CmdletBinding()]
|
|
47
|
+
param(
|
|
48
|
+
# What to do.
|
|
49
|
+
[ValidateSet('disable', 'enable', 'status')]
|
|
50
|
+
[string] $Action = 'status',
|
|
51
|
+
|
|
52
|
+
# Only print what would be written, change nothing.
|
|
53
|
+
[switch] $DryRun,
|
|
54
|
+
|
|
55
|
+
# The package.json to work on (for tests and for other profiles).
|
|
56
|
+
[string] $PackageJson = (Join-Path $env:USERPROFILE '.dsh\profiles\desktop\package.json')
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
$ErrorActionPreference = 'Stop'
|
|
60
|
+
|
|
61
|
+
# The name of this plugin inside the bundles.
|
|
62
|
+
$PackageName = 'dsh-autorestart'
|
|
63
|
+
|
|
64
|
+
# The anchor it is put back after (the position does not matter, but an anchor
|
|
65
|
+
# is steadier than the end of the array).
|
|
66
|
+
$AnchorName = 'dsh-github-guard'
|
|
67
|
+
|
|
68
|
+
# ---------------------------------------------------------------------------
|
|
69
|
+
# The Chinese strings: read from rescue-strings.zh.json, fall back to English.
|
|
70
|
+
# ---------------------------------------------------------------------------
|
|
71
|
+
|
|
72
|
+
$script:Strings = $null
|
|
73
|
+
$script:StringsError = ''
|
|
74
|
+
|
|
75
|
+
function Initialize-Strings {
|
|
76
|
+
$path = Join-Path $PSScriptRoot 'rescue-strings.zh.json'
|
|
77
|
+
if (-not (Test-Path -LiteralPath $path -PathType Leaf)) {
|
|
78
|
+
$script:StringsError = (T 'common.stringsMissingEn' 'the Chinese strings file is missing: {0} (falling back to English messages)') -f $path
|
|
79
|
+
return
|
|
80
|
+
}
|
|
81
|
+
try {
|
|
82
|
+
# Read as UTF-8 explicitly: Get-Content would use the console code page.
|
|
83
|
+
$text = [System.IO.File]::ReadAllText($path, [System.Text.Encoding]::UTF8)
|
|
84
|
+
$script:Strings = $text | ConvertFrom-Json
|
|
85
|
+
} catch {
|
|
86
|
+
$script:StringsError = (T 'common.stringsBrokenEn' 'the Chinese strings file could not be read: {0} (falling back to English messages)') -f $_.Exception.Message
|
|
87
|
+
$script:Strings = $null
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
# Look a string up. The English fallback is right here in the script, so a
|
|
92
|
+
# missing key (or a broken strings file) never stops the rescue.
|
|
93
|
+
function T {
|
|
94
|
+
param([string] $Key, [string] $Fallback)
|
|
95
|
+
if ($null -ne $script:Strings) {
|
|
96
|
+
$node = $script:Strings
|
|
97
|
+
foreach ($part in $Key.Split('.')) {
|
|
98
|
+
if ($null -eq $node) { break }
|
|
99
|
+
$property = $node.PSObject.Properties[$part]
|
|
100
|
+
if ($null -eq $property) { $node = $null; break }
|
|
101
|
+
$node = $property.Value
|
|
102
|
+
}
|
|
103
|
+
if ($null -ne $node) {
|
|
104
|
+
$text = [string] $node
|
|
105
|
+
if ($text -ne '') { return $text }
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return $Fallback
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
Initialize-Strings
|
|
112
|
+
|
|
113
|
+
function Say {
|
|
114
|
+
param([string] $Text, [string] $Color = 'Gray')
|
|
115
|
+
Write-Host $Text -ForegroundColor $Color
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function Fail {
|
|
119
|
+
param([string] $Text)
|
|
120
|
+
Write-Host ((T 'emergency.failed' 'the emergency switch failed: {0}') -f $Text) -ForegroundColor Red
|
|
121
|
+
exit 1
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
# ---------------------------------------------------------------------------
|
|
125
|
+
# Text helpers: only the bundles array is touched, every other byte stays.
|
|
126
|
+
# ---------------------------------------------------------------------------
|
|
127
|
+
|
|
128
|
+
# The square brackets of the bundles array.
|
|
129
|
+
function Get-ArrayRange {
|
|
130
|
+
param([string] $Text)
|
|
131
|
+
$marker = '"bundles"'
|
|
132
|
+
$markerIndex = $Text.IndexOf($marker)
|
|
133
|
+
if ($markerIndex -lt 0) { throw 'package.json has no bundles entry' }
|
|
134
|
+
$open = $Text.IndexOf('[', $markerIndex)
|
|
135
|
+
if ($open -lt 0) { throw 'bundles is not an array' }
|
|
136
|
+
$depth = 0
|
|
137
|
+
$inString = $false
|
|
138
|
+
$escaped = $false
|
|
139
|
+
for ($i = $open; $i -lt $Text.Length; $i++) {
|
|
140
|
+
$c = $Text[$i]
|
|
141
|
+
if ($inString) {
|
|
142
|
+
if ($escaped) { $escaped = $false; continue }
|
|
143
|
+
if ($c -eq '\') { $escaped = $true; continue }
|
|
144
|
+
if ($c -eq '"') { $inString = $false }
|
|
145
|
+
continue
|
|
146
|
+
}
|
|
147
|
+
if ($c -eq '"') { $inString = $true; continue }
|
|
148
|
+
if ($c -eq '[') { $depth++; continue }
|
|
149
|
+
if ($c -eq ']') {
|
|
150
|
+
$depth--
|
|
151
|
+
if ($depth -eq 0) { return @($open, $i) }
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
throw 'the bundles array is not closed'
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
# Every item of the array: the name, the indentation, the comma, the position
|
|
158
|
+
# in the text.
|
|
159
|
+
function Get-Items {
|
|
160
|
+
param([string] $Text, [int] $Open, [int] $Close)
|
|
161
|
+
$body = $Text.Substring($Open + 1, $Close - $Open - 1)
|
|
162
|
+
$items = @()
|
|
163
|
+
$pattern = '(?m)^(?<indent>[ \t]*)"(?<name>[^"]*)"(?<comma>,?)[ \t]*\r?$'
|
|
164
|
+
foreach ($match in [regex]::Matches($body, $pattern)) {
|
|
165
|
+
$lineStart = $Open + 1 + $match.Index
|
|
166
|
+
$lineEnd = $lineStart + $match.Length
|
|
167
|
+
# The line ending of that line (\r\n or \n) belongs to the line.
|
|
168
|
+
if ($lineEnd -lt $Text.Length -and $Text[$lineEnd] -eq "`n") { $lineEnd++ }
|
|
169
|
+
$nameGroup = $match.Groups['name']
|
|
170
|
+
$commaGroup = $match.Groups['comma']
|
|
171
|
+
$items += [pscustomobject]@{
|
|
172
|
+
Name = $nameGroup.Value
|
|
173
|
+
Indent = $match.Groups['indent'].Value
|
|
174
|
+
HasComma = ($commaGroup.Value -eq ',')
|
|
175
|
+
CommaIndex = $commaGroup.Index
|
|
176
|
+
# The position right after the closing quote of the name (where the
|
|
177
|
+
# comma goes).
|
|
178
|
+
ContentEnd = $nameGroup.Index + $nameGroup.Length + 1
|
|
179
|
+
LineStart = $lineStart
|
|
180
|
+
LineEnd = $lineEnd
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return $items
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
# The indentation of a line (so a new line can look the same).
|
|
187
|
+
function Get-LineIndent {
|
|
188
|
+
param([string] $Text, [int] $Index)
|
|
189
|
+
$start = $Text.LastIndexOf("`n", [Math]::Max(0, $Index - 1))
|
|
190
|
+
if ($start -lt 0) { $start = 0 } else { $start++ }
|
|
191
|
+
$indent = ''
|
|
192
|
+
for ($i = $start; $i -lt $Text.Length; $i++) {
|
|
193
|
+
$c = $Text[$i]
|
|
194
|
+
if ($c -eq ' ' -or $c -eq "`t") { $indent += $c; continue }
|
|
195
|
+
break
|
|
196
|
+
}
|
|
197
|
+
return $indent
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
# The line ending the file uses.
|
|
201
|
+
function Get-LineEnding {
|
|
202
|
+
param([string] $Text)
|
|
203
|
+
if ($Text.Contains("`r`n")) { return "`r`n" }
|
|
204
|
+
return "`n"
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
# ---------------------------------------------------------------------------
|
|
208
|
+
# Start.
|
|
209
|
+
# ---------------------------------------------------------------------------
|
|
210
|
+
|
|
211
|
+
if ($script:StringsError -ne '') {
|
|
212
|
+
Write-Host ('WARNING: ' + $script:StringsError) -ForegroundColor Yellow
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
if (-not (Test-Path -LiteralPath $PackageJson -PathType Leaf)) {
|
|
216
|
+
Fail ((T 'emergency.notFound' 'the package.json of the profile cannot be found: {0}') -f $PackageJson)
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
$path = (Resolve-Path -LiteralPath $PackageJson).Path
|
|
220
|
+
$bytes = [System.IO.File]::ReadAllBytes($path)
|
|
221
|
+
$hasBom = ($bytes.Length -ge 3 -and $bytes[0] -eq 0xEF -and $bytes[1] -eq 0xBB -and $bytes[2] -eq 0xBF)
|
|
222
|
+
$raw = [System.IO.File]::ReadAllText($path)
|
|
223
|
+
|
|
224
|
+
try {
|
|
225
|
+
$document = $raw | ConvertFrom-Json
|
|
226
|
+
} catch {
|
|
227
|
+
Fail ((T 'emergency.badJson' 'this package.json is not valid JSON: {0}') -f $_.Exception.Message)
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
$bundles = @()
|
|
231
|
+
$hasBundles = $false
|
|
232
|
+
if ($null -ne $document -and $null -ne $document.dsh -and $null -ne $document.dsh.profile -and $null -ne $document.dsh.profile.bundles) {
|
|
233
|
+
$bundles = @($document.dsh.profile.bundles)
|
|
234
|
+
$hasBundles = $true
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
$present = ($bundles -contains $PackageName)
|
|
238
|
+
|
|
239
|
+
Say ((T 'emergency.file' 'file: {0}') -f $path)
|
|
240
|
+
|
|
241
|
+
if ($Action -eq 'status') {
|
|
242
|
+
if ($present) { Say ((T 'emergency.enabled' 'enabled: {0} is in the bundles.') -f $PackageName) 'Green' }
|
|
243
|
+
else { Say ((T 'emergency.disabled' 'disabled: {0} is not in the bundles.') -f $PackageName) 'Yellow' }
|
|
244
|
+
exit 0
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
if ($Action -eq 'enable' -and -not $hasBundles) {
|
|
248
|
+
Fail (T 'emergency.noBundles' 'this profile has no dsh.profile.bundles list, so the plugin cannot be put back (please add a line "dsh-autorestart" by hand).')
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
# ---------------------------------------------------------------------------
|
|
252
|
+
# Work out the new text.
|
|
253
|
+
# ---------------------------------------------------------------------------
|
|
254
|
+
|
|
255
|
+
$new = $raw
|
|
256
|
+
$range = Get-ArrayRange $raw
|
|
257
|
+
$items = Get-Items $raw $range[0] $range[1]
|
|
258
|
+
$eol = Get-LineEnding $raw
|
|
259
|
+
|
|
260
|
+
if ($Action -eq 'disable') {
|
|
261
|
+
if (-not $present) {
|
|
262
|
+
Say ((T 'emergency.alreadyDisabled' 'already disabled: {0} was not in the bundles anyway (no file was changed).') -f $PackageName) 'Yellow'
|
|
263
|
+
exit 0
|
|
264
|
+
}
|
|
265
|
+
$target = $items | Where-Object { $_.Name -eq $PackageName } | Select-Object -First 1
|
|
266
|
+
if ($null -eq $target) { Fail ((T 'emergency.notInBundles' '{0} cannot be found in the bundles') -f $PackageName) }
|
|
267
|
+
$shift = 0
|
|
268
|
+
if (-not $target.HasComma) {
|
|
269
|
+
# It is the last item of the array: the comma of the item before it has to
|
|
270
|
+
# go, otherwise the JSON is invalid.
|
|
271
|
+
$previous = $items | Where-Object { $_.LineEnd -le $target.LineStart } | Select-Object -Last 1
|
|
272
|
+
if ($null -ne $previous -and $previous.HasComma) {
|
|
273
|
+
$new = $new.Remove($previous.CommaIndex, 1)
|
|
274
|
+
$shift = -1
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
$new = $new.Remove($target.LineStart + $shift, $target.LineEnd - $target.LineStart)
|
|
278
|
+
$summary = (T 'emergency.summaryRemove' 'take {0} out of the bundles') -f $PackageName
|
|
279
|
+
} else {
|
|
280
|
+
# enable
|
|
281
|
+
if ($present) {
|
|
282
|
+
Say ((T 'emergency.alreadyEnabled' 'already enabled: {0} was in the bundles anyway (no file was changed).') -f $PackageName) 'Yellow'
|
|
283
|
+
exit 0
|
|
284
|
+
}
|
|
285
|
+
$anchor = $items | Where-Object { $_.Name -eq $AnchorName } | Select-Object -First 1
|
|
286
|
+
$line = ''
|
|
287
|
+
$insertAt = 0
|
|
288
|
+
if ($null -ne $anchor) {
|
|
289
|
+
if ($anchor.HasComma) {
|
|
290
|
+
$insertAt = $anchor.LineEnd
|
|
291
|
+
$line = $anchor.Indent + '"' + $PackageName + '",'
|
|
292
|
+
} else {
|
|
293
|
+
# The anchor is the last item: give it a comma first, then insert the new
|
|
294
|
+
# item (the new one has no comma).
|
|
295
|
+
$new = $new.Insert($anchor.ContentEnd, ',')
|
|
296
|
+
$insertAt = $anchor.LineEnd + 1
|
|
297
|
+
$line = $anchor.Indent + '"' + $PackageName + '"'
|
|
298
|
+
}
|
|
299
|
+
} else {
|
|
300
|
+
$last = $items | Select-Object -Last 1
|
|
301
|
+
if ($null -ne $last) {
|
|
302
|
+
if ($last.HasComma) {
|
|
303
|
+
$insertAt = $last.LineEnd
|
|
304
|
+
} else {
|
|
305
|
+
$new = $new.Insert($last.ContentEnd, ',')
|
|
306
|
+
$insertAt = $last.LineEnd + 1
|
|
307
|
+
}
|
|
308
|
+
$line = $last.Indent + '"' + $PackageName + '"'
|
|
309
|
+
} else {
|
|
310
|
+
# The array is empty.
|
|
311
|
+
$insertAt = $range[1]
|
|
312
|
+
$line = (Get-LineIndent $raw $range[1]) + ' "' + $PackageName + '"'
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
$new = $new.Insert($insertAt, $line + $eol)
|
|
316
|
+
$summary = (T 'emergency.summaryAdd' 'put {0} back into the bundles') -f $PackageName
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
if ($DryRun) {
|
|
320
|
+
Say ((T 'emergency.dryRunWill' '[dry run] would {0}.') -f $summary) 'Cyan'
|
|
321
|
+
Say ((T 'emergency.dryRunWrite' '[dry run] would write: {0}') -f $path) 'Cyan'
|
|
322
|
+
Say ((T 'emergency.dryRunBackup' '[dry run] would back it up as: {0}.bak-emergency-<timestamp>') -f $path) 'Cyan'
|
|
323
|
+
Say (T 'emergency.dryRunNoChange' '[dry run] no file was changed.') 'Cyan'
|
|
324
|
+
exit 0
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
# ---------------------------------------------------------------------------
|
|
328
|
+
# Write: back up first, write, then parse and check again; on a failure, roll
|
|
329
|
+
# back.
|
|
330
|
+
# ---------------------------------------------------------------------------
|
|
331
|
+
|
|
332
|
+
$backup = $path + '.bak-emergency-' + (Get-Date -Format 'yyyyMMdd-HHmmss')
|
|
333
|
+
try {
|
|
334
|
+
Copy-Item -LiteralPath $path -Destination $backup -Force
|
|
335
|
+
} catch {
|
|
336
|
+
Fail ((T 'emergency.backupFailed' 'the backup failed (the original file was not touched): {0}') -f $_.Exception.Message)
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
try {
|
|
340
|
+
$encoding = New-Object System.Text.UTF8Encoding($hasBom)
|
|
341
|
+
[System.IO.File]::WriteAllText($path, $new, $encoding)
|
|
342
|
+
} catch {
|
|
343
|
+
Fail ((T 'emergency.writeFailed' 'the write failed (the backup is at {0}): {1}') -f $backup, $_.Exception.Message)
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
try {
|
|
347
|
+
$written = [System.IO.File]::ReadAllText($path)
|
|
348
|
+
$parsed = $written | ConvertFrom-Json
|
|
349
|
+
$names = @()
|
|
350
|
+
if ($null -ne $parsed -and $null -ne $parsed.dsh -and $null -ne $parsed.dsh.profile -and $null -ne $parsed.dsh.profile.bundles) {
|
|
351
|
+
$names = @($parsed.dsh.profile.bundles)
|
|
352
|
+
}
|
|
353
|
+
if ($Action -eq 'enable' -and -not ($names -contains $PackageName)) {
|
|
354
|
+
throw ('after writing, the bundles still do not hold ' + $PackageName)
|
|
355
|
+
}
|
|
356
|
+
if ($Action -eq 'disable' -and ($names -contains $PackageName)) {
|
|
357
|
+
throw ('after writing, the bundles still hold ' + $PackageName)
|
|
358
|
+
}
|
|
359
|
+
} catch {
|
|
360
|
+
# The check failed: put the original file back, then report honestly.
|
|
361
|
+
try {
|
|
362
|
+
Copy-Item -LiteralPath $backup -Destination $path -Force
|
|
363
|
+
Say ((T 'emergency.rolledBack' 'the backup was put back: {0}') -f $backup) 'Yellow'
|
|
364
|
+
} catch {
|
|
365
|
+
Fail ((T 'emergency.verifyFailedNoRollback' 'the check failed and the rollback failed too (the backup is at {0}): {1}') -f $backup, $_.Exception.Message)
|
|
366
|
+
}
|
|
367
|
+
Fail ((T 'emergency.verifyFailedRolledBack' 'the check failed (it was rolled back): {0}') -f $_.Exception.Message)
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
Say ((T 'emergency.done' 'done: {0}.') -f $summary) 'Green'
|
|
371
|
+
Say ((T 'emergency.backup' 'backup: {0}') -f $backup) 'Gray'
|
|
372
|
+
Say (T 'emergency.restartHint' 'restart DSH for it to take effect.') 'Gray'
|
|
373
|
+
exit 0
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
@echo off
|
|
2
|
+
rem dsh-autorestart - restore the profile snapshot.
|
|
3
|
+
rem
|
|
4
|
+
rem Double-click this file when DSH does not start any more (a plugin upgrade
|
|
5
|
+
rem broke the profile, the configuration was damaged, node_modules is a mess):
|
|
6
|
+
rem it puts the newest snapshot that carries a manifest back over the profile.
|
|
7
|
+
rem
|
|
8
|
+
rem What it does, in this order:
|
|
9
|
+
rem 1. finds the newest snapshot with a manifest under <stateDir>\backups
|
|
10
|
+
rem 2. MOVES the current profile to <profile>\.broken-<timestamp> (nothing is
|
|
11
|
+
rem deleted, the evidence stays)
|
|
12
|
+
rem 3. copies the configuration files and the plugin tree back
|
|
13
|
+
rem 4. reads the result back: package.json, dsh.profile.bundles and the
|
|
14
|
+
rem version of dsh-autorestart have to be readable
|
|
15
|
+
rem
|
|
16
|
+
rem It never restarts DSH for you - you do that yourself.
|
|
17
|
+
rem
|
|
18
|
+
rem Optional arguments are passed straight to the PowerShell script, e.g.
|
|
19
|
+
rem restore-profile.cmd -DryRun
|
|
20
|
+
rem restore-profile.cmd -ProfileDir C:\temp\drill\profile -StateDir C:\temp\drill\state
|
|
21
|
+
rem
|
|
22
|
+
rem NOTE: this file is ASCII on purpose. cmd.exe reads a batch file with the
|
|
23
|
+
rem console code page, and a UTF-8 character in it would shift the rest of the
|
|
24
|
+
rem line and be run as a command. The Chinese explanation comes from the
|
|
25
|
+
rem PowerShell script below (and from scripts\README.md).
|
|
26
|
+
setlocal
|
|
27
|
+
set "SCRIPT=%~dp0restore-profile.ps1"
|
|
28
|
+
if not exist "%SCRIPT%" (
|
|
29
|
+
echo Cannot find "%SCRIPT%".
|
|
30
|
+
echo The rescue kit is incomplete - keep the plugin folder together.
|
|
31
|
+
pause
|
|
32
|
+
exit /b 1
|
|
33
|
+
)
|
|
34
|
+
powershell.exe -NoProfile -ExecutionPolicy Bypass -File "%SCRIPT%" %*
|
|
35
|
+
set "RC=%ERRORLEVEL%"
|
|
36
|
+
echo.
|
|
37
|
+
if "%RC%"=="0" (
|
|
38
|
+
echo Done. The result is above - open DSH again.
|
|
39
|
+
) else (
|
|
40
|
+
echo The restore did NOT finish. Read the red lines above.
|
|
41
|
+
echo Your old profile was not deleted - it is kept as a ".broken-<timestamp>" folder.
|
|
42
|
+
)
|
|
43
|
+
pause
|
|
44
|
+
exit /b %RC%
|