fix: crashed on plugin update if there were locked files (dll on windows)
Massimo Melina committed
Jun 1, 2023 at 19:05 UTC
2c512c95d20f2bf367746a1e46568670d1b0e230
4 files changed
+23
-7
src/github.ts
+15
-3
@@ -2,10 +2,18 @@
2
3
import events from './events'
4
import { httpsString, httpsStream, unzip } from './misc'
5
-import { getAvailablePlugins, mapPlugins, parsePluginSource, PATH as PLUGINS_PATH, rescan } from './plugins'
5
+import {
6
+ getAvailablePlugins,
7
+ mapPlugins,
8
+ parsePluginSource,
9
+ PATH as PLUGINS_PATH,
10
+ pluginsWatcher,
11
+ rescan,
12
+} from './plugins'
13
import { ApiError } from './apiMiddleware'
14
import _ from 'lodash'
15
import { DAY, HFS_REPO, HTTP_BAD_REQUEST, HTTP_CONFLICT } from './const'
16
+import { rm } from 'fs/promises'
17
18
const DIST_ROOT = 'dist'
19
@@ -43,12 +51,16 @@ export async function downloadPlugin(repo: string, branch='', overwrite?: boolea
51
rootWithinZip + '-' + process.platform,
52
rootWithinZip,
53
].map(x => x + '/')
54
+ pluginsWatcher.pause()
55
// this zip doesn't have content-length, so we cannot produce progress event
56
const stream = await httpsStream(`https://github.com/${repo}/archive/refs/heads/${branch}.zip`)
48
- await unzip(stream, path => {
57
+ await unzip(stream, async path => {
58
const folder = foldersToCopy.find(x => path.startsWith(x))
50
- return folder ? installPath + '/' + path.slice(folder.length) : false
59
+ if (!folder || path.endsWith('/')) return false
60
+ const dest = installPath + '/' + path.slice(folder.length)
61
+ return rm(dest).then(() => dest, () => false)
62
})
63
+ pluginsWatcher.unpause()
64
await rescan() // workaround: for some reason, operations are not triggering the rescan of the watched folder. Let's invoke it.
65
return folder
66
}
src/misc.ts
+1
@@ -17,6 +17,7 @@ export { debounceAsync }
17
18
export type Callback<IN=void, OUT=void> = (x:IN) => OUT
19
export type Dict<T = any> = Record<string, T>
20
+export type Promisable<T> = T | Promise<T>
21
22
export function enforceFinal(sub:string, s:string) {
23
return s.endsWith(sub) ? s : s+sub
src/update.ts
+1
-1
@@ -1,7 +1,7 @@
1
// This file is part of HFS - Copyright 2021-2023, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
2
3
import { getRepoInfo } from './github'
4
-import { argv, HFS_REPO, IS_BINARY, IS_WINDOWS, VERSION } from './const'
4
+import { argv, HFS_REPO, IS_BINARY, IS_WINDOWS } from './const'
5
import { basename, dirname, join } from 'path'
6
import { spawn, spawnSync } from 'child_process'
7
import { httpsStream, onProcessExit, unzip } from './misc'
src/util-files.ts
+6
-3
@@ -1,7 +1,7 @@
1
// This file is part of HFS - Copyright 2021-2023, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
2
3
import fs from 'fs/promises'
4
-import { wait } from './misc'
4
+import { Promisable, try_, wait } from './misc'
5
import { createWriteStream, mkdirSync, watch } from 'fs'
6
import { basename, dirname } from 'path'
7
import glob from 'fast-glob'
@@ -106,7 +106,7 @@ export async function* dirStream(path: string, deep=0) {
106
}
107
}
108
109
-export async function unzip(stream: Readable, cb: (path: string) => false | string) {
109
+export async function unzip(stream: Readable, cb: (path: string) => Promisable<false | string>) {
110
let pending: Promise<any> = Promise.resolve()
111
return new Promise(resolve =>
112
stream.pipe(unzipper.Parse())
@@ -114,7 +114,10 @@ export async function unzip(stream: Readable, cb: (path: string) => false | stri
114
.on('entry', (entry: any) =>
115
pending = pending.then(async () => { // don't overlap writings
116
const { path, type } = entry
117
- const dest = cb(path)
117
+ const dest = await try_(() => cb(path), e => {
118
+ console.warn(String(e))
119
+ return false
120
+ })
121
if (!dest || type !== 'File')
122
return entry.autodrain()
123
console.debug('unzip', dest)