renovate 43.248.0 → 43.249.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/dist/util/cache/package/codec.js +22 -2
- package/dist/util/cache/package/codec.js.map +1 -1
- package/dist/util/cache/package/impl/base.js +20 -1
- package/dist/util/cache/package/impl/base.js.map +1 -1
- package/dist/util/cache/package/impl/file.js +34 -44
- package/dist/util/cache/package/impl/file.js.map +1 -1
- package/dist/util/cache/package/impl/redis.js +3 -26
- package/dist/util/cache/package/impl/redis.js.map +1 -1
- package/dist/util/cache/package/impl/sqlite.js +7 -22
- package/dist/util/cache/package/impl/sqlite.js.map +1 -1
- package/dist/util/cache/package/legacy.js.map +1 -1
- package/package.json +2 -2
- package/renovate-schema.json +2 -2
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { decompressFromBuffer } from "../../compress.js";
|
|
1
|
+
import { compressToBuffer, decompressFromBuffer } from "../../compress.js";
|
|
2
2
|
import { DateTime } from "luxon";
|
|
3
3
|
//#region lib/util/cache/package/codec.ts
|
|
4
4
|
const headerLength = 8;
|
|
@@ -19,9 +19,23 @@ const prefixWord = 294715648;
|
|
|
19
19
|
*/
|
|
20
20
|
const magic0 = 17;
|
|
21
21
|
const magic1 = 145;
|
|
22
|
+
const formatVersion = 1;
|
|
23
|
+
const reserved = 0;
|
|
22
24
|
function isEnvelope(data) {
|
|
23
25
|
return data[0] === magic0 && data[1] === magic1;
|
|
24
26
|
}
|
|
27
|
+
async function encodeEntry(value, cachedAt) {
|
|
28
|
+
const cachedAtSeconds = cachedAtToSeconds(cachedAt);
|
|
29
|
+
const compressed = await compressToBuffer(JSON.stringify(value));
|
|
30
|
+
const data = Buffer.alloc(headerLength + compressed.length);
|
|
31
|
+
data[0] = magic0;
|
|
32
|
+
data[1] = magic1;
|
|
33
|
+
data[2] = formatVersion;
|
|
34
|
+
data[3] = reserved;
|
|
35
|
+
data.writeUInt32BE(cachedAtSeconds, 4);
|
|
36
|
+
compressed.copy(data, headerLength);
|
|
37
|
+
return data;
|
|
38
|
+
}
|
|
25
39
|
async function decodeEntry(data) {
|
|
26
40
|
if (data.length < headerLength) throw new Error("Truncated package cache entry");
|
|
27
41
|
if (data.readUInt32BE(0) !== prefixWord) throw new Error("Unsupported package cache format");
|
|
@@ -32,7 +46,13 @@ async function decodeEntry(data) {
|
|
|
32
46
|
cachedAt: DateTime.fromSeconds(cachedAtSeconds)
|
|
33
47
|
};
|
|
34
48
|
}
|
|
49
|
+
function cachedAtToSeconds(cachedAt) {
|
|
50
|
+
if (!cachedAt.isValid) throw new Error("Invalid package cache timestamp");
|
|
51
|
+
const cachedAtSeconds = Math.floor(cachedAt.toSeconds());
|
|
52
|
+
if (cachedAtSeconds < 0 || cachedAtSeconds > 4294967295) throw new Error("Package cache timestamp is out of range");
|
|
53
|
+
return cachedAtSeconds;
|
|
54
|
+
}
|
|
35
55
|
//#endregion
|
|
36
|
-
export { decodeEntry, isEnvelope };
|
|
56
|
+
export { decodeEntry, encodeEntry, isEnvelope };
|
|
37
57
|
|
|
38
58
|
//# sourceMappingURL=codec.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"codec.js","names":[],"sources":["../../../../lib/util/cache/package/codec.ts"],"sourcesContent":["import { DateTime } from 'luxon';\nimport { compressToBuffer, decompressFromBuffer } from '../../compress.ts';\n\nconst headerLength = 8;\nconst prefixWord = 0x11910100;\n\n/**\n * Format detection must not collide with anything older Renovate versions\n * stored. Legacy entries are either JSON wrappers, which always start with `{`\n * (`0x7b`), or raw brotli blobs from the SQLite backend.\n *\n * Brotli streams start with the WBITS field (RFC 7932, section 9.1). Standard\n * brotli reserves `0010001` as an invalid bit pattern. The only byte values\n * whose low seven bits match that pattern are `0x11` and `0x91`, so no valid\n * legacy brotli stream can start with either byte.\n *\n * This does not account for `BROTLI_PARAM_LARGE_WINDOW`, which Renovate has\n * never enabled. Both bytes also fall outside the base64 alphabet and printable\n * JSON, giving the prefix extra margin against other legacy-looking payloads.\n */\nconst magic0 = 0x11;\nconst magic1 = 0x91;\n\nconst formatVersion = 0x01;\nconst reserved = 0x00;\n\nexport interface EnvelopeEntry {\n value: unknown;\n cachedAt: DateTime;\n}\n\nexport function isEnvelope(data: Buffer): boolean {\n return data[0] === magic0 && data[1] === magic1;\n}\n\nexport async function encodeEntry(\n value: unknown,\n cachedAt: DateTime,\n): Promise<Buffer> {\n const cachedAtSeconds = cachedAtToSeconds(cachedAt);\n const compressed = await compressToBuffer(JSON.stringify(value));\n const data = Buffer.alloc(headerLength + compressed.length);\n\n data[0] = magic0;\n data[1] = magic1;\n data[2] = formatVersion;\n data[3] = reserved;\n data.writeUInt32BE(cachedAtSeconds, 4);\n compressed.copy(data, headerLength);\n\n return data;\n}\n\nexport async function decodeEntry(data: Buffer): Promise<EnvelopeEntry> {\n if (data.length < headerLength) {\n throw new Error('Truncated package cache entry');\n }\n\n if (data.readUInt32BE(0) !== prefixWord) {\n throw new Error('Unsupported package cache format');\n }\n\n const cachedAtSeconds = data.readUInt32BE(4);\n const json = await decompressFromBuffer(data.subarray(headerLength));\n\n return {\n value: JSON.parse(json),\n cachedAt: DateTime.fromSeconds(cachedAtSeconds),\n };\n}\n\nfunction cachedAtToSeconds(cachedAt: DateTime): number {\n if (!cachedAt.isValid) {\n throw new Error('Invalid package cache timestamp');\n }\n\n const cachedAtSeconds = Math.floor(cachedAt.toSeconds());\n // UInt32 seconds are enough for cache timestamps until 2106.\n if (cachedAtSeconds < 0 || cachedAtSeconds > 0xffffffff) {\n throw new Error('Package cache timestamp is out of range');\n }\n\n return cachedAtSeconds;\n}\n"],"mappings":";;;AAGA,MAAM,eAAe;AACrB,MAAM,aAAa;;;;;;;;;;;;;;;AAgBnB,MAAM,SAAS;AACf,MAAM,SAAS;
|
|
1
|
+
{"version":3,"file":"codec.js","names":[],"sources":["../../../../lib/util/cache/package/codec.ts"],"sourcesContent":["import { DateTime } from 'luxon';\nimport { compressToBuffer, decompressFromBuffer } from '../../compress.ts';\n\nconst headerLength = 8;\nconst prefixWord = 0x11910100;\n\n/**\n * Format detection must not collide with anything older Renovate versions\n * stored. Legacy entries are either JSON wrappers, which always start with `{`\n * (`0x7b`), or raw brotli blobs from the SQLite backend.\n *\n * Brotli streams start with the WBITS field (RFC 7932, section 9.1). Standard\n * brotli reserves `0010001` as an invalid bit pattern. The only byte values\n * whose low seven bits match that pattern are `0x11` and `0x91`, so no valid\n * legacy brotli stream can start with either byte.\n *\n * This does not account for `BROTLI_PARAM_LARGE_WINDOW`, which Renovate has\n * never enabled. Both bytes also fall outside the base64 alphabet and printable\n * JSON, giving the prefix extra margin against other legacy-looking payloads.\n */\nconst magic0 = 0x11;\nconst magic1 = 0x91;\n\nconst formatVersion = 0x01;\nconst reserved = 0x00;\n\nexport interface EnvelopeEntry {\n value: unknown;\n cachedAt: DateTime;\n}\n\nexport function isEnvelope(data: Buffer): boolean {\n return data[0] === magic0 && data[1] === magic1;\n}\n\nexport async function encodeEntry(\n value: unknown,\n cachedAt: DateTime,\n): Promise<Buffer> {\n const cachedAtSeconds = cachedAtToSeconds(cachedAt);\n const compressed = await compressToBuffer(JSON.stringify(value));\n const data = Buffer.alloc(headerLength + compressed.length);\n\n data[0] = magic0;\n data[1] = magic1;\n data[2] = formatVersion;\n data[3] = reserved;\n data.writeUInt32BE(cachedAtSeconds, 4);\n compressed.copy(data, headerLength);\n\n return data;\n}\n\nexport async function decodeEntry(data: Buffer): Promise<EnvelopeEntry> {\n if (data.length < headerLength) {\n throw new Error('Truncated package cache entry');\n }\n\n if (data.readUInt32BE(0) !== prefixWord) {\n throw new Error('Unsupported package cache format');\n }\n\n const cachedAtSeconds = data.readUInt32BE(4);\n const json = await decompressFromBuffer(data.subarray(headerLength));\n\n return {\n value: JSON.parse(json),\n cachedAt: DateTime.fromSeconds(cachedAtSeconds),\n };\n}\n\nfunction cachedAtToSeconds(cachedAt: DateTime): number {\n if (!cachedAt.isValid) {\n throw new Error('Invalid package cache timestamp');\n }\n\n const cachedAtSeconds = Math.floor(cachedAt.toSeconds());\n // UInt32 seconds are enough for cache timestamps until 2106.\n if (cachedAtSeconds < 0 || cachedAtSeconds > 0xffffffff) {\n throw new Error('Package cache timestamp is out of range');\n }\n\n return cachedAtSeconds;\n}\n"],"mappings":";;;AAGA,MAAM,eAAe;AACrB,MAAM,aAAa;;;;;;;;;;;;;;;AAgBnB,MAAM,SAAS;AACf,MAAM,SAAS;AAEf,MAAM,gBAAgB;AACtB,MAAM,WAAW;AAOjB,SAAgB,WAAW,MAAuB;CAChD,OAAO,KAAK,OAAO,UAAU,KAAK,OAAO;AAC3C;AAEA,eAAsB,YACpB,OACA,UACiB;CACjB,MAAM,kBAAkB,kBAAkB,QAAQ;CAClD,MAAM,aAAa,MAAM,iBAAiB,KAAK,UAAU,KAAK,CAAC;CAC/D,MAAM,OAAO,OAAO,MAAM,eAAe,WAAW,MAAM;CAE1D,KAAK,KAAK;CACV,KAAK,KAAK;CACV,KAAK,KAAK;CACV,KAAK,KAAK;CACV,KAAK,cAAc,iBAAiB,CAAC;CACrC,WAAW,KAAK,MAAM,YAAY;CAElC,OAAO;AACT;AAEA,eAAsB,YAAY,MAAsC;CACtE,IAAI,KAAK,SAAS,cAChB,MAAM,IAAI,MAAM,+BAA+B;CAGjD,IAAI,KAAK,aAAa,CAAC,MAAM,YAC3B,MAAM,IAAI,MAAM,kCAAkC;CAGpD,MAAM,kBAAkB,KAAK,aAAa,CAAC;CAC3C,MAAM,OAAO,MAAM,qBAAqB,KAAK,SAAS,YAAY,CAAC;CAEnE,OAAO;EACL,OAAO,KAAK,MAAM,IAAI;EACtB,UAAU,SAAS,YAAY,eAAe;CAChD;AACF;AAEA,SAAS,kBAAkB,UAA4B;CACrD,IAAI,CAAC,SAAS,SACZ,MAAM,IAAI,MAAM,iCAAiC;CAGnD,MAAM,kBAAkB,KAAK,MAAM,SAAS,UAAU,CAAC;CAEvD,IAAI,kBAAkB,KAAK,kBAAkB,YAC3C,MAAM,IAAI,MAAM,yCAAyC;CAG3D,OAAO;AACT"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { logger } from "../../../../logger/index.js";
|
|
2
|
-
import { decodeEntry, isEnvelope } from "../codec.js";
|
|
2
|
+
import { decodeEntry, encodeEntry, isEnvelope } from "../codec.js";
|
|
3
3
|
import { decodeLegacyEntry } from "../legacy.js";
|
|
4
4
|
import { DateTime } from "luxon";
|
|
5
5
|
//#region lib/util/cache/package/impl/base.ts
|
|
@@ -33,6 +33,7 @@ var PackageCacheBase = class {
|
|
|
33
33
|
await this.removeInvalidEntry(namespace, key);
|
|
34
34
|
return;
|
|
35
35
|
}
|
|
36
|
+
await this.upgradeLegacyEntry(namespace, key, entry);
|
|
36
37
|
logger.trace({
|
|
37
38
|
namespace,
|
|
38
39
|
key
|
|
@@ -44,6 +45,24 @@ var PackageCacheBase = class {
|
|
|
44
45
|
return;
|
|
45
46
|
}
|
|
46
47
|
}
|
|
48
|
+
async set(namespace, key, value, hardTtlMinutes) {
|
|
49
|
+
logger.trace({
|
|
50
|
+
namespace,
|
|
51
|
+
key,
|
|
52
|
+
hardTtlMinutes
|
|
53
|
+
}, "Saving cached value");
|
|
54
|
+
const ttlSeconds = Math.floor(hardTtlMinutes * 60);
|
|
55
|
+
try {
|
|
56
|
+
if (ttlSeconds <= 0) {
|
|
57
|
+
await this.rm(namespace, key);
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
await this.writeRaw(namespace, key, await encodeEntry(value, DateTime.local()), ttlSeconds);
|
|
61
|
+
} catch (err) {
|
|
62
|
+
logger.once.warn({ err }, "Error while setting package cache value");
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
upgradeLegacyEntry(_namespace, _key, _entry) {}
|
|
47
66
|
async removeInvalidEntry(namespace, key) {
|
|
48
67
|
try {
|
|
49
68
|
await this.rm(namespace, key);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base.js","names":[],"sources":["../../../../../lib/util/cache/package/impl/base.ts"],"sourcesContent":["import { DateTime } from 'luxon';\nimport { logger } from '../../../../logger/index.ts';\nimport type { MaybePromise } from '../../../../types/index.ts';\nimport { decodeEntry, isEnvelope } from '../codec.ts';\nimport type { LegacyEntry } from '../legacy.ts';\nimport { decodeLegacyEntry } from '../legacy.ts';\nimport type { PackageCacheNamespace } from '../types.ts';\n\nexport abstract class PackageCacheBase {\n async get<T = unknown>(\n namespace: PackageCacheNamespace,\n key: string,\n ): Promise<T | undefined> {\n let raw: Buffer | undefined;\n try {\n raw = await this.readRaw(namespace, key);\n } catch (err) {\n logger.once.debug({ err }, 'Error while reading package cache value');\n return undefined;\n }\n\n if (!raw) {\n logger.trace({ namespace, key }, 'Cache miss');\n return undefined;\n }\n\n try {\n if (isEnvelope(raw)) {\n const entry = await decodeEntry(raw);\n logger.trace({ namespace, key }, 'Returning cached value');\n return entry.value as T;\n }\n\n const entry = await decodeLegacyEntry(raw);\n if (isExpiredLegacyEntry(entry)) {\n await this.removeInvalidEntry(namespace, key);\n return undefined;\n }\n\n logger.trace({ namespace, key }, 'Returning cached value');\n return entry.value as T;\n } catch (err) {\n logger.once.debug({ err }, 'Error while reading package cache value');\n await this.removeInvalidEntry(namespace, key);\n return undefined;\n }\n }\n\n
|
|
1
|
+
{"version":3,"file":"base.js","names":[],"sources":["../../../../../lib/util/cache/package/impl/base.ts"],"sourcesContent":["import { DateTime } from 'luxon';\nimport { logger } from '../../../../logger/index.ts';\nimport type { MaybePromise } from '../../../../types/index.ts';\nimport { decodeEntry, encodeEntry, isEnvelope } from '../codec.ts';\nimport type { LegacyEntry } from '../legacy.ts';\nimport { decodeLegacyEntry } from '../legacy.ts';\nimport type { PackageCacheNamespace } from '../types.ts';\n\nexport abstract class PackageCacheBase {\n async get<T = unknown>(\n namespace: PackageCacheNamespace,\n key: string,\n ): Promise<T | undefined> {\n let raw: Buffer | undefined;\n try {\n raw = await this.readRaw(namespace, key);\n } catch (err) {\n logger.once.debug({ err }, 'Error while reading package cache value');\n return undefined;\n }\n\n if (!raw) {\n logger.trace({ namespace, key }, 'Cache miss');\n return undefined;\n }\n\n try {\n if (isEnvelope(raw)) {\n const entry = await decodeEntry(raw);\n logger.trace({ namespace, key }, 'Returning cached value');\n return entry.value as T;\n }\n\n // TODO: Delete this legacy cache fallback once pre-envelope entries have\n // expired.\n const entry = await decodeLegacyEntry(raw);\n if (isExpiredLegacyEntry(entry)) {\n await this.removeInvalidEntry(namespace, key);\n return undefined;\n }\n\n await this.upgradeLegacyEntry(namespace, key, entry);\n\n logger.trace({ namespace, key }, 'Returning cached value');\n return entry.value as T;\n } catch (err) {\n logger.once.debug({ err }, 'Error while reading package cache value');\n await this.removeInvalidEntry(namespace, key);\n return undefined;\n }\n }\n\n async set(\n namespace: PackageCacheNamespace,\n key: string,\n value: unknown,\n hardTtlMinutes: number,\n ): Promise<void> {\n logger.trace({ namespace, key, hardTtlMinutes }, 'Saving cached value');\n\n const ttlSeconds = Math.floor(hardTtlMinutes * 60);\n\n try {\n if (ttlSeconds <= 0) {\n await this.rm(namespace, key);\n return;\n }\n\n await this.writeRaw(\n namespace,\n key,\n await encodeEntry(value, DateTime.local()),\n ttlSeconds,\n );\n } catch (err) {\n logger.once.warn({ err }, 'Error while setting package cache value');\n }\n }\n\n abstract destroy(): Promise<void>;\n\n protected abstract readRaw(\n namespace: PackageCacheNamespace,\n key: string,\n ): MaybePromise<Buffer | undefined>;\n\n protected abstract writeRaw(\n namespace: PackageCacheNamespace,\n key: string,\n data: Buffer,\n ttlSeconds: number,\n ): MaybePromise<void>;\n\n protected abstract rm(\n namespace: PackageCacheNamespace,\n key: string,\n ): MaybePromise<void>;\n\n // The file backend's cleanup can't read a legacy entry's in-payload expiry, so\n // it overrides this to rewrite the entry as an envelope on read and avoid\n // purging it. Best-effort: overrides must not throw or change the read result.\n // TODO: Delete with the legacy decoder once pre-envelope entries have expired.\n protected upgradeLegacyEntry(\n _namespace: PackageCacheNamespace,\n _key: string,\n _entry: LegacyEntry,\n ): MaybePromise<void> {\n // Default: no upgrade.\n }\n\n private async removeInvalidEntry(\n namespace: PackageCacheNamespace,\n key: string,\n ): Promise<void> {\n try {\n await this.rm(namespace, key);\n } catch (err) {\n logger.once.debug({ err }, 'Error while removing package cache value');\n }\n }\n}\n\n// TODO: Delete with the legacy JSON-wrapper decoder; envelopes use\n// backend-native TTL.\nfunction isExpiredLegacyEntry(entry: LegacyEntry): boolean {\n const { expiry } = entry;\n return (\n expiry !== undefined && (!expiry.isValid || DateTime.local() >= expiry)\n );\n}\n"],"mappings":";;;;;AAQA,IAAsB,mBAAtB,MAAuC;CACrC,MAAM,IACJ,WACA,KACwB;EACxB,IAAI;EACJ,IAAI;GACF,MAAM,MAAM,KAAK,QAAQ,WAAW,GAAG;EACzC,SAAS,KAAK;GACZ,OAAO,KAAK,MAAM,EAAE,IAAI,GAAG,yCAAyC;GACpE;EACF;EAEA,IAAI,CAAC,KAAK;GACR,OAAO,MAAM;IAAE;IAAW;GAAI,GAAG,YAAY;GAC7C;EACF;EAEA,IAAI;GACF,IAAI,WAAW,GAAG,GAAG;IACnB,MAAM,QAAQ,MAAM,YAAY,GAAG;IACnC,OAAO,MAAM;KAAE;KAAW;IAAI,GAAG,wBAAwB;IACzD,OAAO,MAAM;GACf;GAIA,MAAM,QAAQ,MAAM,kBAAkB,GAAG;GACzC,IAAI,qBAAqB,KAAK,GAAG;IAC/B,MAAM,KAAK,mBAAmB,WAAW,GAAG;IAC5C;GACF;GAEA,MAAM,KAAK,mBAAmB,WAAW,KAAK,KAAK;GAEnD,OAAO,MAAM;IAAE;IAAW;GAAI,GAAG,wBAAwB;GACzD,OAAO,MAAM;EACf,SAAS,KAAK;GACZ,OAAO,KAAK,MAAM,EAAE,IAAI,GAAG,yCAAyC;GACpE,MAAM,KAAK,mBAAmB,WAAW,GAAG;GAC5C;EACF;CACF;CAEA,MAAM,IACJ,WACA,KACA,OACA,gBACe;EACf,OAAO,MAAM;GAAE;GAAW;GAAK;EAAe,GAAG,qBAAqB;EAEtE,MAAM,aAAa,KAAK,MAAM,iBAAiB,EAAE;EAEjD,IAAI;GACF,IAAI,cAAc,GAAG;IACnB,MAAM,KAAK,GAAG,WAAW,GAAG;IAC5B;GACF;GAEA,MAAM,KAAK,SACT,WACA,KACA,MAAM,YAAY,OAAO,SAAS,MAAM,CAAC,GACzC,UACF;EACF,SAAS,KAAK;GACZ,OAAO,KAAK,KAAK,EAAE,IAAI,GAAG,yCAAyC;EACrE;CACF;CAyBA,mBACE,YACA,MACA,QACoB,CAEtB;CAEA,MAAc,mBACZ,WACA,KACe;EACf,IAAI;GACF,MAAM,KAAK,GAAG,WAAW,GAAG;EAC9B,SAAS,KAAK;GACZ,OAAO,KAAK,MAAM,EAAE,IAAI,GAAG,0CAA0C;EACvE;CACF;AACF;AAIA,SAAS,qBAAqB,OAA6B;CACzD,MAAM,EAAE,WAAW;CACnB,OACE,WAAW,KAAA,MAAc,CAAC,OAAO,WAAW,SAAS,MAAM,KAAK;AAEpE"}
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import { logger } from "../../../../logger/index.js";
|
|
2
|
-
import {
|
|
2
|
+
import { encodeEntry } from "../codec.js";
|
|
3
3
|
import { PackageCacheBase } from "./base.js";
|
|
4
4
|
import { DateTime } from "luxon";
|
|
5
5
|
import upath from "upath";
|
|
6
6
|
import cacache from "cacache";
|
|
7
|
-
import { LRUCache } from "lru-cache";
|
|
8
7
|
//#region lib/util/cache/package/impl/file.ts
|
|
9
8
|
var PackageCacheFile = class PackageCacheFile extends PackageCacheBase {
|
|
10
9
|
static create(cacheDir) {
|
|
@@ -13,7 +12,6 @@ var PackageCacheFile = class PackageCacheFile extends PackageCacheBase {
|
|
|
13
12
|
return new PackageCacheFile(cacheFileName);
|
|
14
13
|
}
|
|
15
14
|
cacheFileName;
|
|
16
|
-
expiryMap = new LRUCache({ max: 1e5 });
|
|
17
15
|
constructor(cacheFileName) {
|
|
18
16
|
super();
|
|
19
17
|
this.cacheFileName = cacheFileName;
|
|
@@ -21,21 +19,6 @@ var PackageCacheFile = class PackageCacheFile extends PackageCacheBase {
|
|
|
21
19
|
getKey(namespace, key) {
|
|
22
20
|
return `${namespace}-${key}`;
|
|
23
21
|
}
|
|
24
|
-
async set(namespace, key, value, hardTtlMinutes) {
|
|
25
|
-
logger.trace({
|
|
26
|
-
namespace,
|
|
27
|
-
key,
|
|
28
|
-
hardTtlMinutes
|
|
29
|
-
}, "Saving cached value");
|
|
30
|
-
const compressedValue = await compressToBase64(JSON.stringify(value));
|
|
31
|
-
const expiry = DateTime.local().plus({ minutes: hardTtlMinutes });
|
|
32
|
-
const payload = JSON.stringify({
|
|
33
|
-
value: compressedValue,
|
|
34
|
-
expiry
|
|
35
|
-
});
|
|
36
|
-
await cacache.put(this.cacheFileName, this.getKey(namespace, key), payload);
|
|
37
|
-
this.expiryMap.set(this.getKey(namespace, key), expiry);
|
|
38
|
-
}
|
|
39
22
|
async destroy() {
|
|
40
23
|
logger.debug("Checking file package cache for expired items");
|
|
41
24
|
let totalCount = 0;
|
|
@@ -45,28 +28,7 @@ var PackageCacheFile = class PackageCacheFile extends PackageCacheBase {
|
|
|
45
28
|
for await (const item of cacache.ls.stream(this.cacheFileName)) try {
|
|
46
29
|
totalCount += 1;
|
|
47
30
|
const cacheEntry = item;
|
|
48
|
-
|
|
49
|
-
if (cachedExpiry !== void 0) {
|
|
50
|
-
if (DateTime.local() <= cachedExpiry) continue;
|
|
51
|
-
await cacache.rm.entry(this.cacheFileName, cacheEntry.key);
|
|
52
|
-
await cacache.rm.content(this.cacheFileName, cacheEntry.integrity);
|
|
53
|
-
this.expiryMap.delete(cacheEntry.key);
|
|
54
|
-
deletedCount += 1;
|
|
55
|
-
continue;
|
|
56
|
-
}
|
|
57
|
-
const entry = await cacache.get(this.cacheFileName, cacheEntry.key);
|
|
58
|
-
let cached;
|
|
59
|
-
try {
|
|
60
|
-
const raw = entry.data.toString();
|
|
61
|
-
cached = JSON.parse(raw);
|
|
62
|
-
} catch {
|
|
63
|
-
logger.debug("Error parsing cached value - deleting");
|
|
64
|
-
}
|
|
65
|
-
if (cached) {
|
|
66
|
-
if (!cached.expiry) continue;
|
|
67
|
-
const expiry = DateTime.fromISO(cached.expiry);
|
|
68
|
-
if (expiry.isValid && DateTime.local() <= expiry) continue;
|
|
69
|
-
}
|
|
31
|
+
if (hasFutureExpiry(cacheEntry.metadata)) continue;
|
|
70
32
|
await cacache.rm.entry(this.cacheFileName, cacheEntry.key);
|
|
71
33
|
await cacache.rm.content(this.cacheFileName, cacheEntry.integrity);
|
|
72
34
|
deletedCount += 1;
|
|
@@ -81,21 +43,49 @@ var PackageCacheFile = class PackageCacheFile extends PackageCacheBase {
|
|
|
81
43
|
async readRaw(namespace, key) {
|
|
82
44
|
const cacheKey = this.getKey(namespace, key);
|
|
83
45
|
try {
|
|
84
|
-
|
|
46
|
+
const entry = await cacache.get(this.cacheFileName, cacheKey);
|
|
47
|
+
if (hasExpiredMetadata(entry.metadata)) return;
|
|
48
|
+
return entry.data;
|
|
85
49
|
} catch {
|
|
86
50
|
return;
|
|
87
51
|
}
|
|
88
52
|
}
|
|
53
|
+
async writeRaw(namespace, key, data, ttlSeconds) {
|
|
54
|
+
await this.putEntry(this.getKey(namespace, key), data, Date.now() + ttlSeconds * 1e3);
|
|
55
|
+
}
|
|
89
56
|
async rm(namespace, key) {
|
|
90
57
|
logger.trace({
|
|
91
58
|
namespace,
|
|
92
59
|
key
|
|
93
60
|
}, "Removing cache entry");
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
61
|
+
await cacache.rm.entry(this.cacheFileName, this.getKey(namespace, key));
|
|
62
|
+
}
|
|
63
|
+
async upgradeLegacyEntry(namespace, key, entry) {
|
|
64
|
+
const { expiry } = entry;
|
|
65
|
+
if (!expiry?.isValid) return;
|
|
66
|
+
try {
|
|
67
|
+
await this.putEntry(this.getKey(namespace, key), await encodeEntry(entry.value, DateTime.local()), expiry.toMillis());
|
|
68
|
+
} catch (err) {
|
|
69
|
+
logger.once.debug({ err }, "Error while upgrading legacy cache entry");
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
async putEntry(cacheKey, data, expiry) {
|
|
73
|
+
await cacache.put(this.cacheFileName, cacheKey, data, { metadata: { expiry } });
|
|
97
74
|
}
|
|
98
75
|
};
|
|
76
|
+
function getMetadataExpiry(metadata) {
|
|
77
|
+
if (!metadata || typeof metadata !== "object") return;
|
|
78
|
+
const { expiry } = metadata;
|
|
79
|
+
return typeof expiry === "number" ? expiry : void 0;
|
|
80
|
+
}
|
|
81
|
+
function hasExpiredMetadata(metadata) {
|
|
82
|
+
const expiry = getMetadataExpiry(metadata);
|
|
83
|
+
return expiry !== void 0 && Date.now() >= expiry;
|
|
84
|
+
}
|
|
85
|
+
function hasFutureExpiry(metadata) {
|
|
86
|
+
const expiry = getMetadataExpiry(metadata);
|
|
87
|
+
return expiry !== void 0 && Date.now() < expiry;
|
|
88
|
+
}
|
|
99
89
|
//#endregion
|
|
100
90
|
export { PackageCacheFile };
|
|
101
91
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file.js","names":[],"sources":["../../../../../lib/util/cache/package/impl/file.ts"],"sourcesContent":["import cacache from 'cacache';\nimport {
|
|
1
|
+
{"version":3,"file":"file.js","names":[],"sources":["../../../../../lib/util/cache/package/impl/file.ts"],"sourcesContent":["import cacache from 'cacache';\nimport { DateTime } from 'luxon';\nimport upath from 'upath';\nimport { logger } from '../../../../logger/index.ts';\nimport { encodeEntry } from '../codec.ts';\nimport type { LegacyEntry } from '../legacy.ts';\nimport type { PackageCacheNamespace } from '../types.ts';\nimport { PackageCacheBase } from './base.ts';\n\ninterface FileCacheMetadata {\n expiry?: unknown;\n}\n\nexport class PackageCacheFile extends PackageCacheBase {\n static create(cacheDir: string): PackageCacheFile {\n const cacheFileName = upath.join(cacheDir, '/renovate/renovate-cache-v1');\n logger.debug(`Initializing Renovate internal cache into ${cacheFileName}`);\n return new PackageCacheFile(cacheFileName);\n }\n\n private readonly cacheFileName: string;\n\n private constructor(cacheFileName: string) {\n super();\n this.cacheFileName = cacheFileName;\n }\n\n private getKey(namespace: PackageCacheNamespace, key: string): string {\n return `${namespace}-${key}`;\n }\n\n override async destroy(): Promise<void> {\n logger.debug('Checking file package cache for expired items');\n let totalCount = 0;\n let deletedCount = 0;\n let errorCount = 0;\n const startTime = Date.now();\n for await (const item of cacache.ls.stream(this.cacheFileName)) {\n try {\n totalCount += 1;\n const cacheEntry = item as unknown as cacache.CacheObject;\n if (hasFutureExpiry(cacheEntry.metadata)) {\n continue;\n }\n\n // Entries without native expiry metadata include legacy and foreign\n // entries. The sweep evicts them so future reads converge on envelopes.\n await cacache.rm.entry(this.cacheFileName, cacheEntry.key);\n await cacache.rm.content(this.cacheFileName, cacheEntry.integrity);\n deletedCount += 1;\n } catch (err) {\n logger.trace({ err }, 'Error cleaning up cache entry');\n errorCount += 1;\n }\n }\n if (errorCount > 0) {\n logger.debug(`Error count cleaning up cache: ${errorCount}`);\n }\n const durationMs = Date.now() - startTime;\n logger.debug(\n `Deleted ${deletedCount} of ${totalCount} file cached entries in ${durationMs}ms`,\n );\n }\n\n protected override async readRaw(\n namespace: PackageCacheNamespace,\n key: string,\n ): Promise<Buffer | undefined> {\n const cacheKey = this.getKey(namespace, key);\n try {\n // TODO: Once legacy file entries are unsupported, use get.info first and\n // require valid future expiry metadata before reading content.\n const entry = await cacache.get(this.cacheFileName, cacheKey);\n if (hasExpiredMetadata(entry.metadata)) {\n // Do not call rm here: rm.entry only tombstones the index entry, which\n // would hide this item from the sweep and orphan its content file.\n return undefined;\n }\n\n return entry.data;\n } catch {\n return undefined;\n }\n }\n\n protected override async writeRaw(\n namespace: PackageCacheNamespace,\n key: string,\n data: Buffer,\n ttlSeconds: number,\n ): Promise<void> {\n await this.putEntry(\n this.getKey(namespace, key),\n data,\n Date.now() + ttlSeconds * 1000,\n );\n }\n\n protected override async rm(\n namespace: PackageCacheNamespace,\n key: string,\n ): Promise<void> {\n logger.trace({ namespace, key }, 'Removing cache entry');\n await cacache.rm.entry(this.cacheFileName, this.getKey(namespace, key));\n }\n\n // TODO: Delete together with the legacy decoder once pre-envelope entries\n // have expired.\n protected override async upgradeLegacyEntry(\n namespace: PackageCacheNamespace,\n key: string,\n entry: LegacyEntry,\n ): Promise<void> {\n const { expiry } = entry;\n if (!expiry?.isValid) {\n return;\n }\n\n try {\n await this.putEntry(\n this.getKey(namespace, key),\n await encodeEntry(entry.value, DateTime.local()),\n expiry.toMillis(),\n );\n } catch (err) {\n logger.once.debug({ err }, 'Error while upgrading legacy cache entry');\n }\n }\n\n private async putEntry(\n cacheKey: string,\n data: Buffer,\n expiry: number,\n ): Promise<void> {\n await cacache.put(this.cacheFileName, cacheKey, data, {\n metadata: { expiry },\n });\n }\n}\n\nfunction getMetadataExpiry(metadata: unknown): number | undefined {\n if (!metadata || typeof metadata !== 'object') {\n return undefined;\n }\n\n const { expiry } = metadata as FileCacheMetadata;\n return typeof expiry === 'number' ? expiry : undefined;\n}\n\n// TODO: Treat missing or invalid expiry metadata as expired in readRaw once\n// legacy file entries are unsupported.\nfunction hasExpiredMetadata(metadata: unknown): boolean {\n const expiry = getMetadataExpiry(metadata);\n return expiry !== undefined && Date.now() >= expiry;\n}\n\nfunction hasFutureExpiry(metadata: unknown): boolean {\n const expiry = getMetadataExpiry(metadata);\n return expiry !== undefined && Date.now() < expiry;\n}\n"],"mappings":";;;;;;;AAaA,IAAa,mBAAb,MAAa,yBAAyB,iBAAiB;CACrD,OAAO,OAAO,UAAoC;EAChD,MAAM,gBAAgB,MAAM,KAAK,UAAU,6BAA6B;EACxE,OAAO,MAAM,6CAA6C,eAAe;EACzE,OAAO,IAAI,iBAAiB,aAAa;CAC3C;CAEA;CAEA,YAAoB,eAAuB;EACzC,MAAM;EACN,KAAK,gBAAgB;CACvB;CAEA,OAAe,WAAkC,KAAqB;EACpE,OAAO,GAAG,UAAU,GAAG;CACzB;CAEA,MAAe,UAAyB;EACtC,OAAO,MAAM,+CAA+C;EAC5D,IAAI,aAAa;EACjB,IAAI,eAAe;EACnB,IAAI,aAAa;EACjB,MAAM,YAAY,KAAK,IAAI;EAC3B,WAAW,MAAM,QAAQ,QAAQ,GAAG,OAAO,KAAK,aAAa,GAC3D,IAAI;GACF,cAAc;GACd,MAAM,aAAa;GACnB,IAAI,gBAAgB,WAAW,QAAQ,GACrC;GAKF,MAAM,QAAQ,GAAG,MAAM,KAAK,eAAe,WAAW,GAAG;GACzD,MAAM,QAAQ,GAAG,QAAQ,KAAK,eAAe,WAAW,SAAS;GACjE,gBAAgB;EAClB,SAAS,KAAK;GACZ,OAAO,MAAM,EAAE,IAAI,GAAG,+BAA+B;GACrD,cAAc;EAChB;EAEF,IAAI,aAAa,GACf,OAAO,MAAM,kCAAkC,YAAY;EAE7D,MAAM,aAAa,KAAK,IAAI,IAAI;EAChC,OAAO,MACL,WAAW,aAAa,MAAM,WAAW,0BAA0B,WAAW,GAChF;CACF;CAEA,MAAyB,QACvB,WACA,KAC6B;EAC7B,MAAM,WAAW,KAAK,OAAO,WAAW,GAAG;EAC3C,IAAI;GAGF,MAAM,QAAQ,MAAM,QAAQ,IAAI,KAAK,eAAe,QAAQ;GAC5D,IAAI,mBAAmB,MAAM,QAAQ,GAGnC;GAGF,OAAO,MAAM;EACf,QAAQ;GACN;EACF;CACF;CAEA,MAAyB,SACvB,WACA,KACA,MACA,YACe;EACf,MAAM,KAAK,SACT,KAAK,OAAO,WAAW,GAAG,GAC1B,MACA,KAAK,IAAI,IAAI,aAAa,GAC5B;CACF;CAEA,MAAyB,GACvB,WACA,KACe;EACf,OAAO,MAAM;GAAE;GAAW;EAAI,GAAG,sBAAsB;EACvD,MAAM,QAAQ,GAAG,MAAM,KAAK,eAAe,KAAK,OAAO,WAAW,GAAG,CAAC;CACxE;CAIA,MAAyB,mBACvB,WACA,KACA,OACe;EACf,MAAM,EAAE,WAAW;EACnB,IAAI,CAAC,QAAQ,SACX;EAGF,IAAI;GACF,MAAM,KAAK,SACT,KAAK,OAAO,WAAW,GAAG,GAC1B,MAAM,YAAY,MAAM,OAAO,SAAS,MAAM,CAAC,GAC/C,OAAO,SAAS,CAClB;EACF,SAAS,KAAK;GACZ,OAAO,KAAK,MAAM,EAAE,IAAI,GAAG,0CAA0C;EACvE;CACF;CAEA,MAAc,SACZ,UACA,MACA,QACe;EACf,MAAM,QAAQ,IAAI,KAAK,eAAe,UAAU,MAAM,EACpD,UAAU,EAAE,OAAO,EACrB,CAAC;CACH;AACF;AAEA,SAAS,kBAAkB,UAAuC;CAChE,IAAI,CAAC,YAAY,OAAO,aAAa,UACnC;CAGF,MAAM,EAAE,WAAW;CACnB,OAAO,OAAO,WAAW,WAAW,SAAS,KAAA;AAC/C;AAIA,SAAS,mBAAmB,UAA4B;CACtD,MAAM,SAAS,kBAAkB,QAAQ;CACzC,OAAO,WAAW,KAAA,KAAa,KAAK,IAAI,KAAK;AAC/C;AAEA,SAAS,gBAAgB,UAA4B;CACnD,MAAM,SAAS,kBAAkB,QAAQ;CACzC,OAAO,WAAW,KAAA,KAAa,KAAK,IAAI,IAAI;AAC9C"}
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import { regEx } from "../../../regex.js";
|
|
2
2
|
import { logger } from "../../../../logger/index.js";
|
|
3
3
|
import { parseUrl } from "../../../url.js";
|
|
4
|
-
import { compressToBase64 } from "../../../compress.js";
|
|
5
4
|
import { PackageCacheBase } from "./base.js";
|
|
6
|
-
import { DateTime } from "luxon";
|
|
7
5
|
import { RESP_TYPES, createClient, createCluster } from "@redis/client";
|
|
8
6
|
//#region lib/util/cache/package/impl/redis.ts
|
|
9
7
|
function normalizeRedisUrl(url) {
|
|
@@ -48,30 +46,6 @@ var PackageCacheRedis = class PackageCacheRedis extends PackageCacheBase {
|
|
|
48
46
|
getKey(namespace, key) {
|
|
49
47
|
return `${this.rprefix}${namespace}-${key}`;
|
|
50
48
|
}
|
|
51
|
-
async set(namespace, key, value, hardTtlMinutes) {
|
|
52
|
-
logger.trace({
|
|
53
|
-
rprefix: this.rprefix,
|
|
54
|
-
namespace,
|
|
55
|
-
key,
|
|
56
|
-
hardTtlMinutes
|
|
57
|
-
}, "Saving cached value");
|
|
58
|
-
const ttlSeconds = Math.floor(hardTtlMinutes * 60);
|
|
59
|
-
try {
|
|
60
|
-
if (ttlSeconds <= 0) {
|
|
61
|
-
await this.rm(namespace, key);
|
|
62
|
-
return;
|
|
63
|
-
}
|
|
64
|
-
const compressedValue = await compressToBase64(JSON.stringify(value));
|
|
65
|
-
const expiry = DateTime.local().plus({ minutes: hardTtlMinutes });
|
|
66
|
-
const payload = JSON.stringify({
|
|
67
|
-
value: compressedValue,
|
|
68
|
-
expiry
|
|
69
|
-
});
|
|
70
|
-
await this.client.set(this.getKey(namespace, key), payload, { EX: ttlSeconds });
|
|
71
|
-
} catch (err) {
|
|
72
|
-
logger.once.warn({ err }, "Error while setting Redis cache value");
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
49
|
destroy() {
|
|
76
50
|
try {
|
|
77
51
|
this.client.destroy();
|
|
@@ -83,6 +57,9 @@ var PackageCacheRedis = class PackageCacheRedis extends PackageCacheBase {
|
|
|
83
57
|
async readRaw(namespace, key) {
|
|
84
58
|
return await this.binaryClient.get(this.getKey(namespace, key)) ?? void 0;
|
|
85
59
|
}
|
|
60
|
+
async writeRaw(namespace, key, data, ttlSeconds) {
|
|
61
|
+
await this.client.set(this.getKey(namespace, key), data, { EX: ttlSeconds });
|
|
62
|
+
}
|
|
86
63
|
async rm(namespace, key) {
|
|
87
64
|
logger.trace({
|
|
88
65
|
rprefix: this.rprefix,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"redis.js","names":[],"sources":["../../../../../lib/util/cache/package/impl/redis.ts"],"sourcesContent":["import type { RedisClusterOptions } from '@redis/client';\nimport { RESP_TYPES, createClient, createCluster } from '@redis/client';\nimport {
|
|
1
|
+
{"version":3,"file":"redis.js","names":[],"sources":["../../../../../lib/util/cache/package/impl/redis.ts"],"sourcesContent":["import type { RedisClusterOptions } from '@redis/client';\nimport { RESP_TYPES, createClient, createCluster } from '@redis/client';\nimport { logger } from '../../../../logger/index.ts';\nimport { regEx } from '../../../regex.ts';\nimport { parseUrl } from '../../../url.ts';\nimport type { PackageCacheNamespace } from '../types.ts';\nimport { PackageCacheBase } from './base.ts';\n\nexport function normalizeRedisUrl(url: string): string {\n return url.replace(regEx(/^(rediss?)\\+cluster:\\/\\//), '$1://');\n}\n\ntype RedisClient =\n | ReturnType<typeof createClient>\n | ReturnType<typeof createCluster>;\n\ninterface RedisBinaryClient {\n get(key: string): Promise<Buffer | null>;\n}\n\nexport class PackageCacheRedis extends PackageCacheBase {\n static async create(\n url: string,\n prefix: string | undefined,\n ): Promise<PackageCacheRedis> {\n const rprefix = prefix ?? '';\n logger.debug('Redis cache init');\n\n const rewrittenUrl = normalizeRedisUrl(url);\n const clusteredMode = rewrittenUrl !== url;\n\n const config = {\n url: rewrittenUrl,\n socket: {\n reconnectStrategy: (retries: number) => Math.min(retries * 100, 3000),\n },\n pingInterval: 30000,\n };\n\n let client: RedisClient;\n\n if (clusteredMode) {\n const clusterConfig: RedisClusterOptions = { rootNodes: [config] };\n\n const parsedUrl = parseUrl(rewrittenUrl);\n if (parsedUrl?.username) {\n clusterConfig.defaults = {\n username: parsedUrl.username,\n };\n }\n\n if (parsedUrl?.password) {\n clusterConfig.defaults ??= {};\n clusterConfig.defaults.password = parsedUrl.password;\n }\n\n client = createCluster(clusterConfig);\n } else {\n client = createClient(config);\n }\n\n await client.connect();\n logger.debug('Redis cache connected');\n const binaryClient = client.withTypeMapping({\n [RESP_TYPES.BLOB_STRING]: Buffer,\n }) as RedisBinaryClient;\n return new PackageCacheRedis(client, binaryClient, rprefix);\n }\n\n private readonly client: RedisClient;\n private readonly binaryClient: RedisBinaryClient;\n private readonly rprefix: string;\n\n private constructor(\n client: RedisClient,\n binaryClient: RedisBinaryClient,\n rprefix: string,\n ) {\n super();\n this.client = client;\n this.binaryClient = binaryClient;\n this.rprefix = rprefix;\n }\n\n private getKey(namespace: PackageCacheNamespace, key: string): string {\n return `${this.rprefix}${namespace}-${key}`;\n }\n\n override destroy(): Promise<void> {\n try {\n // https://github.com/redis/node-redis#disconnecting\n this.client.destroy();\n } catch (err) {\n logger.warn({ err }, 'Redis cache destroy failed');\n }\n return Promise.resolve();\n }\n\n protected override async readRaw(\n namespace: PackageCacheNamespace,\n key: string,\n ): Promise<Buffer | undefined> {\n const raw = await this.binaryClient.get(this.getKey(namespace, key));\n\n return raw ?? undefined;\n }\n\n protected override async writeRaw(\n namespace: PackageCacheNamespace,\n key: string,\n data: Buffer,\n ttlSeconds: number,\n ): Promise<void> {\n await this.client.set(this.getKey(namespace, key), data, {\n EX: ttlSeconds,\n });\n }\n\n protected override async rm(\n namespace: PackageCacheNamespace,\n key: string,\n ): Promise<void> {\n logger.trace(\n { rprefix: this.rprefix, namespace, key },\n 'Removing cache entry',\n );\n await this.client.del(this.getKey(namespace, key));\n }\n}\n"],"mappings":";;;;;;AAQA,SAAgB,kBAAkB,KAAqB;CACrD,OAAO,IAAI,QAAQ,MAAM,0BAA0B,GAAG,OAAO;AAC/D;AAUA,IAAa,oBAAb,MAAa,0BAA0B,iBAAiB;CACtD,aAAa,OACX,KACA,QAC4B;EAC5B,MAAM,UAAU,UAAU;EAC1B,OAAO,MAAM,kBAAkB;EAE/B,MAAM,eAAe,kBAAkB,GAAG;EAC1C,MAAM,gBAAgB,iBAAiB;EAEvC,MAAM,SAAS;GACb,KAAK;GACL,QAAQ,EACN,oBAAoB,YAAoB,KAAK,IAAI,UAAU,KAAK,GAAI,EACtE;GACA,cAAc;EAChB;EAEA,IAAI;EAEJ,IAAI,eAAe;GACjB,MAAM,gBAAqC,EAAE,WAAW,CAAC,MAAM,EAAE;GAEjE,MAAM,YAAY,SAAS,YAAY;GACvC,IAAI,WAAW,UACb,cAAc,WAAW,EACvB,UAAU,UAAU,SACtB;GAGF,IAAI,WAAW,UAAU;IACvB,cAAc,aAAa,CAAC;IAC5B,cAAc,SAAS,WAAW,UAAU;GAC9C;GAEA,SAAS,cAAc,aAAa;EACtC,OACE,SAAS,aAAa,MAAM;EAG9B,MAAM,OAAO,QAAQ;EACrB,OAAO,MAAM,uBAAuB;EACpC,MAAM,eAAe,OAAO,gBAAgB,GACzC,WAAW,cAAc,OAC5B,CAAC;EACD,OAAO,IAAI,kBAAkB,QAAQ,cAAc,OAAO;CAC5D;CAEA;CACA;CACA;CAEA,YACE,QACA,cACA,SACA;EACA,MAAM;EACN,KAAK,SAAS;EACd,KAAK,eAAe;EACpB,KAAK,UAAU;CACjB;CAEA,OAAe,WAAkC,KAAqB;EACpE,OAAO,GAAG,KAAK,UAAU,UAAU,GAAG;CACxC;CAEA,UAAkC;EAChC,IAAI;GAEF,KAAK,OAAO,QAAQ;EACtB,SAAS,KAAK;GACZ,OAAO,KAAK,EAAE,IAAI,GAAG,4BAA4B;EACnD;EACA,OAAO,QAAQ,QAAQ;CACzB;CAEA,MAAyB,QACvB,WACA,KAC6B;EAG7B,OAAO,MAFW,KAAK,aAAa,IAAI,KAAK,OAAO,WAAW,GAAG,CAAC,KAErD,KAAA;CAChB;CAEA,MAAyB,SACvB,WACA,KACA,MACA,YACe;EACf,MAAM,KAAK,OAAO,IAAI,KAAK,OAAO,WAAW,GAAG,GAAG,MAAM,EACvD,IAAI,WACN,CAAC;CACH;CAEA,MAAyB,GACvB,WACA,KACe;EACf,OAAO,MACL;GAAE,SAAS,KAAK;GAAS;GAAW;EAAI,GACxC,sBACF;EACA,MAAM,KAAK,OAAO,IAAI,KAAK,OAAO,WAAW,GAAG,CAAC;CACnD;AACF"}
|
|
@@ -3,19 +3,10 @@ import { logger } from "../../../../logger/index.js";
|
|
|
3
3
|
import { ensureDir } from "../../../fs/index.js";
|
|
4
4
|
import { PackageCacheBase } from "./base.js";
|
|
5
5
|
import { parseInteger } from "../../../number.js";
|
|
6
|
-
import { promisify } from "node:util";
|
|
7
6
|
import fs from "fs-extra";
|
|
8
7
|
import upath from "upath";
|
|
9
|
-
import zlib, { constants } from "node:zlib";
|
|
10
8
|
//#region lib/util/cache/package/impl/sqlite.ts
|
|
11
9
|
const { exists } = fs;
|
|
12
|
-
const brotliCompress = promisify(zlib.brotliCompress);
|
|
13
|
-
function compress(input) {
|
|
14
|
-
return brotliCompress(JSON.stringify(input), { params: {
|
|
15
|
-
[constants.BROTLI_PARAM_MODE]: constants.BROTLI_MODE_TEXT,
|
|
16
|
-
[constants.BROTLI_PARAM_QUALITY]: 3
|
|
17
|
-
} });
|
|
18
|
-
}
|
|
19
10
|
var PackageCacheSqlite = class PackageCacheSqlite extends PackageCacheBase {
|
|
20
11
|
static async create(cacheDir) {
|
|
21
12
|
const { DatabaseSync: Sqlite } = await import("node:sqlite");
|
|
@@ -70,19 +61,13 @@ var PackageCacheSqlite = class PackageCacheSqlite extends PackageCacheBase {
|
|
|
70
61
|
`);
|
|
71
62
|
this.countStatement = client.prepare("SELECT COUNT(*) as total FROM package_cache");
|
|
72
63
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
data: compressedData,
|
|
81
|
-
ttlSeconds
|
|
82
|
-
});
|
|
83
|
-
} catch (err) {
|
|
84
|
-
logger.once.warn({ err }, "Error while setting SQLite cache value");
|
|
85
|
-
}
|
|
64
|
+
writeRaw(namespace, key, data, ttlSeconds) {
|
|
65
|
+
this.upsertStatement.run({
|
|
66
|
+
namespace,
|
|
67
|
+
key,
|
|
68
|
+
data,
|
|
69
|
+
ttlSeconds
|
|
70
|
+
});
|
|
86
71
|
}
|
|
87
72
|
readRaw(namespace, key) {
|
|
88
73
|
const row = this.getStatement.get({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sqlite.js","names":[],"sources":["../../../../../lib/util/cache/package/impl/sqlite.ts"],"sourcesContent":["import type { DatabaseSync, StatementSync } from 'node:sqlite';\nimport
|
|
1
|
+
{"version":3,"file":"sqlite.js","names":[],"sources":["../../../../../lib/util/cache/package/impl/sqlite.ts"],"sourcesContent":["import type { DatabaseSync, StatementSync } from 'node:sqlite';\nimport fs from 'fs-extra';\nimport upath from 'upath';\nimport { logger } from '../../../../logger/index.ts';\nimport { getEnv } from '../../../env.ts';\nimport { ensureDir } from '../../../fs/index.ts';\nimport { parseInteger } from '../../../number.ts';\nimport type { PackageCacheNamespace } from '../types.ts';\nimport { PackageCacheBase } from './base.ts';\n\nconst { exists } = fs;\n\nexport class PackageCacheSqlite extends PackageCacheBase {\n static async create(cacheDir: string): Promise<PackageCacheSqlite> {\n const { DatabaseSync: Sqlite } = await import('node:sqlite');\n const sqliteDir = upath.join(cacheDir, 'renovate/renovate-cache-sqlite');\n await ensureDir(sqliteDir);\n const sqliteFile = upath.join(sqliteDir, 'db.sqlite');\n\n if (await exists(sqliteFile)) {\n logger.debug(`Using SQLite package cache: ${sqliteFile}`);\n } else {\n logger.debug(`Creating SQLite package cache: ${sqliteFile}`);\n }\n\n const { RENOVATE_X_SQLITE_BUSY_TIMEOUT } = getEnv();\n const timeout = parseInteger(RENOVATE_X_SQLITE_BUSY_TIMEOUT, 5000);\n\n const client = new Sqlite(sqliteFile, { timeout });\n return new PackageCacheSqlite(client);\n }\n\n private readonly upsertStatement: StatementSync;\n private readonly getStatement: StatementSync;\n private readonly deleteStatement: StatementSync;\n private readonly deleteExpiredRows: StatementSync;\n private readonly countStatement: StatementSync;\n\n readonly client: DatabaseSync;\n\n private constructor(client: DatabaseSync) {\n super();\n this.client = client;\n client.exec('PRAGMA journal_mode = WAL');\n client.exec(\"PRAGMA encoding = 'UTF-8'\");\n\n client.exec(\n `\n CREATE TABLE IF NOT EXISTS package_cache (\n namespace TEXT NOT NULL,\n key TEXT NOT NULL,\n expiry INTEGER NOT NULL,\n data BLOB NOT NULL,\n PRIMARY KEY (namespace, key)\n )\n `,\n );\n client.exec('CREATE INDEX IF NOT EXISTS expiry ON package_cache (expiry)');\n\n this.upsertStatement = client.prepare(`\n INSERT INTO package_cache (namespace, key, data, expiry)\n VALUES (@namespace, @key, @data, unixepoch() + @ttlSeconds)\n ON CONFLICT (namespace, key) DO UPDATE SET\n data = @data,\n expiry = unixepoch() + @ttlSeconds\n `);\n\n this.getStatement = client.prepare(\n `\n SELECT data FROM package_cache\n WHERE\n namespace = @namespace AND key = @key AND expiry > unixepoch()\n `,\n );\n\n this.deleteStatement = client.prepare(`\n DELETE FROM package_cache\n WHERE namespace = @namespace AND key = @key\n `);\n\n this.deleteExpiredRows = client.prepare(`\n DELETE FROM package_cache\n WHERE expiry <= unixepoch()\n `);\n\n this.countStatement = client.prepare(\n 'SELECT COUNT(*) as total FROM package_cache',\n );\n }\n\n protected override writeRaw(\n namespace: PackageCacheNamespace,\n key: string,\n data: Buffer,\n ttlSeconds: number,\n ): void {\n this.upsertStatement.run({\n namespace,\n key,\n data,\n ttlSeconds,\n });\n }\n\n protected override readRaw(\n namespace: PackageCacheNamespace,\n key: string,\n ): Buffer | undefined {\n const row = this.getStatement.get({ namespace, key }) as\n | { data: Uint8Array }\n | undefined;\n\n if (!row) {\n return undefined;\n }\n\n // `Buffer.from(row.data)` copies the whole payload.\n // This call does zero copy.\n return Buffer.from(\n row.data.buffer,\n row.data.byteOffset,\n row.data.byteLength,\n );\n }\n\n protected override rm(namespace: PackageCacheNamespace, key: string): void {\n logger.trace({ namespace, key }, 'Removing cache entry');\n this.deleteStatement.run({ namespace, key });\n }\n\n override destroy(): Promise<void> {\n try {\n const startTime = Date.now();\n // `COUNT(*)` is always returning a row\n const totalCount = this.countStatement.get()!.total as number;\n const { changes: deletedCount } = this.deleteExpiredRows.run();\n const durationMs = Date.now() - startTime;\n logger.debug(\n `SQLite package cache: deleted ${deletedCount} of ${totalCount} entries in ${durationMs}ms`,\n );\n } catch (err) {\n logger.warn({ err }, 'SQLite package cache cleanup failed');\n }\n\n try {\n this.client.close();\n } catch (err) {\n logger.warn({ err }, 'SQLite package cache close failed');\n }\n\n return Promise.resolve();\n }\n}\n"],"mappings":";;;;;;;;AAUA,MAAM,EAAE,WAAW;AAEnB,IAAa,qBAAb,MAAa,2BAA2B,iBAAiB;CACvD,aAAa,OAAO,UAA+C;EACjE,MAAM,EAAE,cAAc,WAAW,MAAM,OAAO;EAC9C,MAAM,YAAY,MAAM,KAAK,UAAU,gCAAgC;EACvE,MAAM,UAAU,SAAS;EACzB,MAAM,aAAa,MAAM,KAAK,WAAW,WAAW;EAEpD,IAAI,MAAM,OAAO,UAAU,GACzB,OAAO,MAAM,+BAA+B,YAAY;OAExD,OAAO,MAAM,kCAAkC,YAAY;EAG7D,MAAM,EAAE,mCAAmC,OAAO;EAIlD,OAAO,IAAI,mBAAmB,IADX,OAAO,YAAY,EAAE,SAFxB,aAAa,gCAAgC,GAEf,EAAE,CACb,CAAC;CACtC;CAEA;CACA;CACA;CACA;CACA;CAEA;CAEA,YAAoB,QAAsB;EACxC,MAAM;EACN,KAAK,SAAS;EACd,OAAO,KAAK,2BAA2B;EACvC,OAAO,KAAK,2BAA2B;EAEvC,OAAO,KACL;;;;;;;;SASF;EACA,OAAO,KAAK,6DAA6D;EAEzE,KAAK,kBAAkB,OAAO,QAAQ;;;;;;KAMrC;EAED,KAAK,eAAe,OAAO,QACzB;;;;SAKF;EAEA,KAAK,kBAAkB,OAAO,QAAQ;;;KAGrC;EAED,KAAK,oBAAoB,OAAO,QAAQ;;;KAGvC;EAED,KAAK,iBAAiB,OAAO,QAC3B,6CACF;CACF;CAEA,SACE,WACA,KACA,MACA,YACM;EACN,KAAK,gBAAgB,IAAI;GACvB;GACA;GACA;GACA;EACF,CAAC;CACH;CAEA,QACE,WACA,KACoB;EACpB,MAAM,MAAM,KAAK,aAAa,IAAI;GAAE;GAAW;EAAI,CAAC;EAIpD,IAAI,CAAC,KACH;EAKF,OAAO,OAAO,KACZ,IAAI,KAAK,QACT,IAAI,KAAK,YACT,IAAI,KAAK,UACX;CACF;CAEA,GAAsB,WAAkC,KAAmB;EACzE,OAAO,MAAM;GAAE;GAAW;EAAI,GAAG,sBAAsB;EACvD,KAAK,gBAAgB,IAAI;GAAE;GAAW;EAAI,CAAC;CAC7C;CAEA,UAAkC;EAChC,IAAI;GACF,MAAM,YAAY,KAAK,IAAI;GAE3B,MAAM,aAAa,KAAK,eAAe,IAAI,CAAC,CAAE;GAC9C,MAAM,EAAE,SAAS,iBAAiB,KAAK,kBAAkB,IAAI;GAC7D,MAAM,aAAa,KAAK,IAAI,IAAI;GAChC,OAAO,MACL,iCAAiC,aAAa,MAAM,WAAW,cAAc,WAAW,GAC1F;EACF,SAAS,KAAK;GACZ,OAAO,KAAK,EAAE,IAAI,GAAG,qCAAqC;EAC5D;EAEA,IAAI;GACF,KAAK,OAAO,MAAM;EACpB,SAAS,KAAK;GACZ,OAAO,KAAK,EAAE,IAAI,GAAG,mCAAmC;EAC1D;EAEA,OAAO,QAAQ,QAAQ;CACzB;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"legacy.js","names":[],"sources":["../../../../lib/util/cache/package/legacy.ts"],"sourcesContent":["import { DateTime } from 'luxon';\nimport { decompressFromBase64, decompressFromBuffer } from '../../compress.ts';\n\n//
|
|
1
|
+
{"version":3,"file":"legacy.js","names":[],"sources":["../../../../lib/util/cache/package/legacy.ts"],"sourcesContent":["import { DateTime } from 'luxon';\nimport { decompressFromBase64, decompressFromBuffer } from '../../compress.ts';\n\n// TODO: Delete this decode-only support, legacy.spec.ts, and the single call\n// site in impl/base.ts once pre-envelope entries have expired.\n\nexport interface LegacyEntry {\n value: unknown;\n expiry: DateTime | undefined;\n}\n\ninterface LegacyJsonEntry {\n value: unknown;\n expiry: unknown;\n}\n\nexport async function decodeLegacyEntry(data: Buffer): Promise<LegacyEntry> {\n if (data[0] === 0x7b) {\n return await decodeLegacyJsonEntry(data);\n }\n\n return await decodeLegacySqliteEntry(data);\n}\n\nasync function decodeLegacyJsonEntry(data: Buffer): Promise<LegacyEntry> {\n const cached = JSON.parse(data.toString('utf8')) as LegacyJsonEntry;\n\n if (typeof cached.value !== 'string' || typeof cached.expiry !== 'string') {\n throw new Error('Invalid legacy JSON package cache entry');\n }\n\n const json = await decompressFromBase64(cached.value);\n\n return {\n value: JSON.parse(json),\n expiry: DateTime.fromISO(cached.expiry),\n };\n}\n\nasync function decodeLegacySqliteEntry(data: Buffer): Promise<LegacyEntry> {\n const json = await decompressFromBuffer(data);\n\n return {\n value: JSON.parse(json),\n expiry: undefined,\n };\n}\n"],"mappings":";;;AAgBA,eAAsB,kBAAkB,MAAoC;CAC1E,IAAI,KAAK,OAAO,KACd,OAAO,MAAM,sBAAsB,IAAI;CAGzC,OAAO,MAAM,wBAAwB,IAAI;AAC3C;AAEA,eAAe,sBAAsB,MAAoC;CACvE,MAAM,SAAS,KAAK,MAAM,KAAK,SAAS,MAAM,CAAC;CAE/C,IAAI,OAAO,OAAO,UAAU,YAAY,OAAO,OAAO,WAAW,UAC/D,MAAM,IAAI,MAAM,yCAAyC;CAG3D,MAAM,OAAO,MAAM,qBAAqB,OAAO,KAAK;CAEpD,OAAO;EACL,OAAO,KAAK,MAAM,IAAI;EACtB,QAAQ,SAAS,QAAQ,OAAO,MAAM;CACxC;AACF;AAEA,eAAe,wBAAwB,MAAoC;CACzE,MAAM,OAAO,MAAM,qBAAqB,IAAI;CAE5C,OAAO;EACL,OAAO,KAAK,MAAM,IAAI;EACtB,QAAQ,KAAA;CACV;AACF"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "renovate",
|
|
3
3
|
"description": "Automated dependency updates. Flexible so you don't need to be.",
|
|
4
|
-
"version": "43.
|
|
4
|
+
"version": "43.249.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"renovate": "dist/renovate.js",
|
|
@@ -218,7 +218,7 @@
|
|
|
218
218
|
"@ls-lint/ls-lint": "2.3.1",
|
|
219
219
|
"@openpgp/web-stream-tools": "0.3.1",
|
|
220
220
|
"@semantic-release/exec": "7.1.0",
|
|
221
|
-
"@smithy/util-stream": "4.7.
|
|
221
|
+
"@smithy/util-stream": "4.7.2",
|
|
222
222
|
"@types/adm-zip": "0.5.8",
|
|
223
223
|
"@types/aws4": "1.11.6",
|
|
224
224
|
"@types/breejs__later": "4.1.5",
|
package/renovate-schema.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$id": "https://docs.renovatebot.com/renovate-schema.json",
|
|
3
|
-
"title": "JSON schema for Renovate 43.
|
|
3
|
+
"title": "JSON schema for Renovate 43.249.0 config files (https://renovatebot.com/)",
|
|
4
4
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
5
|
-
"x-renovate-version": "43.
|
|
5
|
+
"x-renovate-version": "43.249.0",
|
|
6
6
|
"allowComments": true,
|
|
7
7
|
"type": "object",
|
|
8
8
|
"definitions": {
|