querysub 0.709.0 → 0.711.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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "querysub",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.711.0",
|
|
4
4
|
"main": "index.js",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"note1": "note on node-forge fork, see https://github.com/digitalbazaar/forge/issues/744 for details",
|
|
@@ -79,7 +79,7 @@
|
|
|
79
79
|
"node-forge": "https://github.com/sliftist/forge#e618181b469b07bdc70b968b0391beb8ef5fecd6",
|
|
80
80
|
"pako": "^2.1.0",
|
|
81
81
|
"peggy": "^5.0.6",
|
|
82
|
-
"sliftutils": "^1.
|
|
82
|
+
"sliftutils": "^1.8.20",
|
|
83
83
|
"socket-function": "^1.3.1",
|
|
84
84
|
"terser": "^5.31.0",
|
|
85
85
|
"typenode": "^6.6.1",
|
|
@@ -9,7 +9,8 @@ import { runPromise } from "../functional/runCommand";
|
|
|
9
9
|
import { getExternalIP } from "socket-function/src/networking";
|
|
10
10
|
import { errorToUndefined, errorToUndefinedSilent } from "../errors";
|
|
11
11
|
import { getDomain } from "../config";
|
|
12
|
-
import { formatTime } from "socket-function/src/formatting/format";
|
|
12
|
+
import { formatDateTime, formatTime } from "socket-function/src/formatting/format";
|
|
13
|
+
import { httpsRequest, HttpsResponseInfo } from "socket-function/src/https";
|
|
13
14
|
import { sort, timeInMinute, timeInSecond, watchSlowPromise } from "socket-function/src/misc";
|
|
14
15
|
import { isDefined, setPreciseTimeout } from "../misc";
|
|
15
16
|
import { logLoadTime } from "../logModuleLoadTimes";
|
|
@@ -247,6 +248,47 @@ function checkGitSyncedAndClean(gitFolder: string, gitRef: string): Promise<bool
|
|
|
247
248
|
return cached;
|
|
248
249
|
}
|
|
249
250
|
|
|
251
|
+
async function recordMissingDependency(config: { gitFolder: string; screenName: string; error: unknown }) {
|
|
252
|
+
let { gitFolder, screenName } = config;
|
|
253
|
+
let match = String((config.error as Error)?.stack || config.error).match(/(https?:\/\/\S+?\.tgz): Request failed "404 Not Found"/);
|
|
254
|
+
if (!match) return;
|
|
255
|
+
let failedUrl = new URL(match[1]);
|
|
256
|
+
let tarballUrl = NPM_REGISTRY_ORIGIN + failedUrl.pathname;
|
|
257
|
+
let outResponse: HttpsResponseInfo = { headers: {} };
|
|
258
|
+
try {
|
|
259
|
+
await httpsRequest(tarballUrl, undefined, "HEAD", false, { outResponse });
|
|
260
|
+
} catch { }
|
|
261
|
+
if (outResponse.statusCode !== 404) {
|
|
262
|
+
console.warn(`Yarn install for ${screenName} failed on ${match[1]} returning 404, but npm returned ${outResponse.statusCode} for ${tarballUrl}, so it is not missing and we are not backing off`);
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
let failures = (missingDependencyRetries.get(gitFolder)?.failures || 0) + 1;
|
|
266
|
+
let nextAttemptTime = failures >= MISSING_DEPENDENCY_MAX_FAILURES && Number.POSITIVE_INFINITY || Date.now() + MISSING_DEPENDENCY_RETRY_DELAY;
|
|
267
|
+
missingDependencyRetries.set(gitFolder, { failures, nextAttemptTime, tarballUrl });
|
|
268
|
+
if (failures >= MISSING_DEPENDENCY_MAX_FAILURES) {
|
|
269
|
+
console.error(`Giving up yarn installing for ${screenName}, ${tarballUrl} is still missing from npm after ${failures} failed installs. Retrying only on a config change.`);
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
console.warn(`Yarn install for ${screenName} failed because ${tarballUrl} is missing from npm (failure ${failures}/${MISSING_DEPENDENCY_MAX_FAILURES}), retrying at ${formatDateTime(nextAttemptTime)}`);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
async function getQuerysubVersionMismatch(gitFolder: string): Promise<string> {
|
|
276
|
+
let packageJson = JSON.parse(await fs.promises.readFile(gitFolder + "package.json", "utf8")) as { dependencies?: { [name: string]: string } };
|
|
277
|
+
let requested = packageJson.dependencies?.querysub;
|
|
278
|
+
if (!requested || !/^[\^~]?\d+\.\d+\.\d+$/.test(requested)) return "";
|
|
279
|
+
let installedPath = gitFolder + "node_modules/querysub/package.json";
|
|
280
|
+
if (!await fsExistsAsync(installedPath)) return `requested ${requested}, not installed`;
|
|
281
|
+
let installed = (JSON.parse(await fs.promises.readFile(installedPath, "utf8")) as { version: string }).version;
|
|
282
|
+
let installedParts = installed.split(".").map(Number);
|
|
283
|
+
let requestedParts = requested.replace(/^[\^~]/, "").split(".").map(Number);
|
|
284
|
+
for (let i = 0; i < requestedParts.length; i++) {
|
|
285
|
+
if (installedParts[i] === requestedParts[i]) continue;
|
|
286
|
+
if (installedParts[i] > requestedParts[i]) return "";
|
|
287
|
+
return `requested ${requested}, installed ${installed}`;
|
|
288
|
+
}
|
|
289
|
+
return "";
|
|
290
|
+
}
|
|
291
|
+
|
|
250
292
|
const ensureGitSynced = measureWrap(async function ensureGitSynced(config: {
|
|
251
293
|
// Forces the checkout at gitFolder to gitRef. Escalates through increasingly destructive recoveries, but only ever destroys the working tree as an absolute last resort — a network blip must never wipe node_modules out from under a running service.
|
|
252
294
|
gitFolder: string;
|
|
@@ -305,6 +347,11 @@ const RESYNC_INTERVAL = timeInSecond * 30;
|
|
|
305
347
|
const CONFIG_REFRESH_INTERVAL = RESYNC_INTERVAL * 6;
|
|
306
348
|
|
|
307
349
|
const LAUNCH_VERIFY_DELAY = 2000;
|
|
350
|
+
|
|
351
|
+
const NPM_REGISTRY_ORIGIN = "https://registry.npmjs.org";
|
|
352
|
+
const MISSING_DEPENDENCY_RETRY_DELAY = timeInMinute * 5;
|
|
353
|
+
const MISSING_DEPENDENCY_MAX_FAILURES = 5;
|
|
354
|
+
let missingDependencyRetries = new Map<string, { failures: number; nextAttemptTime: number; tarballUrl: string }>();
|
|
308
355
|
// How far a re-read start time may drift from the recorded one and still be the same process. Well under any interval a pid could be reused in, and wide enough for sources that only resolve to the second.
|
|
309
356
|
const START_TIME_TOLERANCE = timeInSecond * 5;
|
|
310
357
|
|
|
@@ -484,11 +531,24 @@ async function ensureScreen(config: {
|
|
|
484
531
|
} catch { }
|
|
485
532
|
await ensureGitSynced({ gitFolder, repoUrl: parameters.repoUrl, gitRef: parameters.gitRef });
|
|
486
533
|
let afterGitRef = await getGitRefLive(gitFolder);
|
|
487
|
-
// Reinstall when the ref changed OR node_modules is missing. A recovery re-clone can land on the same commit, so a ref-only check would leave the service with no node_modules.
|
|
488
534
|
let nodeModulesMissing = !await fsExistsAsync(gitFolder + "node_modules");
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
535
|
+
let querysubMismatch = await getQuerysubVersionMismatch(gitFolder);
|
|
536
|
+
let missingRetry = missingDependencyRetries.get(gitFolder);
|
|
537
|
+
if (afterGitRef !== prevGitRef || nodeModulesMissing || querysubMismatch || missingRetry) {
|
|
538
|
+
if (missingRetry && Date.now() < missingRetry.nextAttemptTime) {
|
|
539
|
+
throw new Error(`Not yarn installing for ${screenName}, ${missingRetry.tarballUrl} was missing from npm after ${missingRetry.failures} failed installs. ${missingRetry.failures >= MISSING_DEPENDENCY_MAX_FAILURES && "Gave up, waiting for a config change to retry" || `Next attempt at ${formatDateTime(missingRetry.nextAttemptTime)}`}`);
|
|
540
|
+
}
|
|
541
|
+
console.log(green(`Yarn installing for ${magenta(screenName)} (ref ${prevGitRef} -> ${afterGitRef}, nodeModulesMissing=${nodeModulesMissing}, querysubMismatch=${querysubMismatch || "none"}, missingDependencyFailures=${missingRetry?.failures || 0})`));
|
|
542
|
+
try {
|
|
543
|
+
await runPromise(`yarn install --mutex network`, { cwd: gitFolder });
|
|
544
|
+
} catch (e) {
|
|
545
|
+
await recordMissingDependency({ gitFolder, screenName, error: e });
|
|
546
|
+
throw e;
|
|
547
|
+
}
|
|
548
|
+
if (missingRetry) {
|
|
549
|
+
console.log(green(`Yarn install for ${magenta(screenName)} succeeded after ${missingRetry.failures} failures from ${missingRetry.tarballUrl} missing from npm`));
|
|
550
|
+
missingDependencyRetries.delete(gitFolder);
|
|
551
|
+
}
|
|
492
552
|
}
|
|
493
553
|
}
|
|
494
554
|
|
|
@@ -637,6 +697,10 @@ const resyncServicesBase = runInSerial(measureWrap(async function resyncServices
|
|
|
637
697
|
/** A resync that re-reads the configs, however recently they were last read - for when something tells us they changed. */
|
|
638
698
|
async function resyncServicesWithFreshConfigs() {
|
|
639
699
|
configRefreshRequested = true;
|
|
700
|
+
if (missingDependencyRetries.size) {
|
|
701
|
+
console.log(`Config changed, clearing the missing dependency backoff for ${Array.from(missingDependencyRetries.keys()).join(", ")}`);
|
|
702
|
+
missingDependencyRetries.clear();
|
|
703
|
+
}
|
|
640
704
|
await resyncServices();
|
|
641
705
|
}
|
|
642
706
|
async function resyncServices() {
|