@samitouri / QOSami-HFS / commits / 2c512c95

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)