aboutsummaryrefslogtreecommitdiff
path: root/vcpkg/scripts/azure-pipelines/owners-db
diff options
context:
space:
mode:
Diffstat (limited to 'vcpkg/scripts/azure-pipelines/owners-db')
-rw-r--r--vcpkg/scripts/azure-pipelines/owners-db/README.md96
-rw-r--r--vcpkg/scripts/azure-pipelines/owners-db/file_script.ts93
-rw-r--r--vcpkg/scripts/azure-pipelines/owners-db/file_script_from_cache.ts184
-rw-r--r--vcpkg/scripts/azure-pipelines/owners-db/package-lock.json219
-rw-r--r--vcpkg/scripts/azure-pipelines/owners-db/package.json19
5 files changed, 611 insertions, 0 deletions
diff --git a/vcpkg/scripts/azure-pipelines/owners-db/README.md b/vcpkg/scripts/azure-pipelines/owners-db/README.md
new file mode 100644
index 0000000..023058e
--- /dev/null
+++ b/vcpkg/scripts/azure-pipelines/owners-db/README.md
@@ -0,0 +1,96 @@
+Owners DB helpers
+=================
+
+Summary
+-------
+This directory contains two small Node.js CLI helpers used by the Azure Pipelines owners-db step to build two plain-text database files used by downstream tooling:
+
+- `file_script.ts` — builds databases from a local `info` directory (used in CI runs where vcpkg is installed locally).
+- `file_script_from_cache.ts` — builds databases by downloading package ZIPs from a binary cache (used in PR runs that can consult prebuilt artifacts).
+
+Both scripts produce the same output file formats described below:
+
+- `VCPKGDatabase.txt` — a newline-separated list of entries of the form `port:triplet:/path/inside/package`.
+- `VCPKGHeadersDatabase.txt` — a newline-separated list of entries of the form `port:triplet:relative/header/path`
+
+These files are emitted to `--out-dir` (default `scripts/list_files`).
+
+Usage
+-----
+
+file_script.ts (local info-dir mode)
+
+```text
+file_script.ts --info-dir <path-to-info-dir> [--out-dir <path>]
+```
+
+Behavior and input format:
+
+- `--info-dir <path>` should point at a directory containing vcpkg-generated `.list` files (the same layout created by `vcpkg` under `installed/<triplet>/vcpkg/info/`).
+- Each file in that directory is expected to follow the filename convention used by vcpkg info files. The script parses the filename by splitting on underscores and constructs a package identifier using the first and third components:
+
+ <package>_<...>_<triplet>.list --> package id = `<package>:<triplet>`
+
+- Each `.list` file is plain text with one relative file path per line. Lines that are empty, or which end in `/` are ignored. If a line contains any prefix before a `/`, the script strips the prefix and uses only the path starting at the first `/`.
+
+Examples of lines processed from `.list` files:
+
+- `share/zlib/include/zlib.h` -> entry `zlib:x64-windows:/share/zlib/include/zlib.h`
+- `someprefix/share/zlib/include/zlib.h` -> same as above (prefix before first `/` is dropped)
+
+file_script_from_cache.ts (PR cache mode)
+
+```text
+file_script_from_cache.ts --pr-hashes <pr-hashes.json> --blob-base-url <blob-base-url> [--target-branch <branch>] [--out-dir <path>]
+```
+
+Required inputs:
+
+- `--pr-hashes <pr-hashes.json>` is a JSON file produced by the vcpkg tooling that lists changed ports and their ABIs. The script expects a top-level JSON array of objects where each object contains at least the following fields:
+
+ {
+ "name": "<port-name>",
+ "triplet": "<triplet>",
+ "state": "<state>",
+ "abi": "<sha>"
+ }
+
+ Important: in this script the `abi` field is expected to be the 64-hex SHA string used to name the ZIP blob in the binary cache (the script validates `abi` against `/^[a-f0-9]{64}$/`).
+
+- `--blob-base-url <blob-base-url>` should be a URL that points to a binary cache container and include any required SAS token (for example: `https://<account>.blob.core.windows.net/cache?<sas>`). The script will insert `/<sha>.zip` into that base URL to download the package ZIP for each port/abi pair and then enumerate files inside each ZIP.
+
+Output file formats
+-------------------
+
+Both scripts write two files into the chosen `--out-dir` (default `scripts/list_files`):
+
+- `VCPKGDatabase.txt`
+ - Each line has the form: `<port>:<triplet>:<filepath>`
+ - `<filepath>` begins with a leading `/` when sourced from `.list` files or ZIP entries; it is the path inside the package (for example `/share/zlib/include/zlib.h`).
+
+- `VCPKGHeadersDatabase.txt`
+ - Each line has the form: `<port>:<triplet>:<relative/header/path>`
+ - Only files whose path starts with `/include/` are recorded here and the `/include/` prefix is removed from the path. For example, an entry for `/include/zlib.h` will produce `zlib:x64-windows:zlib.h`.
+
+Exit codes and errors
+---------------------
+
+- Both scripts print an error and exit non-zero on fatal problems (invalid arguments, invalid `pr-hashes.json`, or failed git diff in the cache variant).
+- `file_script_from_cache.ts` will attempt to download each expected ZIP; failures to download or process a single package are reported as warnings and the script continues — missing entries will simply be absent from the output.
+
+Examples
+--------
+
+Local info-dir:
+
+```sh
+npx ts-node ./file_script.ts --info-dir /mnt/vcpkg-ci/installed/vcpkg/info --out-dir ./scripts/list_files
+```
+
+PR cache mode (pipeline example using `BCACHE_SAS_TOKEN` set as a secret variable):
+
+```sh
+# pipeline constructs the URL from the secret token and passes it to the script
+blob="https://vcpkgbinarycachewus.blob.core.windows.net/cache?${BCACHE_SAS_TOKEN}"
+npx --yes ts-node ./file_script_from_cache.ts --pr-hashes /path/to/pr-hashes.json --blob-base-url "$blob" --target-branch origin/master --out-dir ./scripts/list_files
+```
diff --git a/vcpkg/scripts/azure-pipelines/owners-db/file_script.ts b/vcpkg/scripts/azure-pipelines/owners-db/file_script.ts
new file mode 100644
index 0000000..abb79dc
--- /dev/null
+++ b/vcpkg/scripts/azure-pipelines/owners-db/file_script.ts
@@ -0,0 +1,93 @@
+#!/usr/bin/env node
+import * as fs from "fs";
+import * as path from "path";
+
+const include_subpath = "/include/";
+
+function getFiles(dirPath: string): string[] {
+ const files = fs.readdirSync(dirPath);
+ return files.filter((f) => !f.startsWith("."));
+}
+
+function genAllFileStrings(
+ dirPath: string,
+ files: string[],
+ headersStream: fs.WriteStream,
+ outputStream: fs.WriteStream
+) {
+ for (const file of files) {
+ const components = file.split("_");
+ const pkg = components[0] + ":" + components[2].replace(".list", "");
+ const content = fs.readFileSync(path.join(dirPath, file), "utf8");
+ const lines = content.split(/\r?\n/);
+ for (const raw of lines) {
+ if (!raw) continue;
+ const line = raw.trim();
+ if (line.length === 0) continue;
+ if (line.endsWith("/")) continue;
+ // Remove the leading triplet directory
+ const idx = line.indexOf("/");
+ const filepath = idx >= 0 ? line.substring(idx) : line;
+ outputStream.write(pkg + ":" + filepath + "\n");
+ if (filepath.startsWith(include_subpath)) {
+ headersStream.write(pkg + ":" + filepath.substring(include_subpath.length) + "\n");
+ }
+ }
+ }
+}
+
+function usage() {
+ console.error("Usage: file_script.ts --info-dir <path-to-info-dir> [--out-dir <path>]");
+}
+
+function parseArgs(argv: string[]) {
+ let infoDir: string | undefined;
+ let outDir = "scripts/list_files";
+ for (let i = 0; i < argv.length; i++) {
+ const a = argv[i];
+ if (a === "--info-dir") {
+ i++;
+ infoDir = argv[i];
+ } else if (a === "--out-dir") {
+ i++;
+ outDir = argv[i];
+ } else if (a.startsWith("--")) {
+ console.error(`Unknown argument: ${a}`);
+ usage();
+ process.exit(2);
+ } else {
+ console.error(`Unexpected positional argument: ${a}`);
+ usage();
+ process.exit(2);
+ }
+ }
+ if (!infoDir) {
+ console.error("info-dir is required");
+ usage();
+ process.exit(2);
+ }
+ return { infoDir, outDir };
+}
+
+function main() {
+ const { infoDir: dir, outDir } = parseArgs(process.argv.slice(2));
+ try {
+ fs.mkdirSync(outDir, { recursive: true });
+ } catch {
+ // ignore
+ }
+
+ const headersPath = path.join(outDir, "VCPKGHeadersDatabase.txt");
+ const dbPath = path.join(outDir, "VCPKGDatabase.txt");
+ const headers = fs.createWriteStream(headersPath, { encoding: "utf8" });
+ const output = fs.createWriteStream(dbPath, { encoding: "utf8" });
+ try {
+ const files = getFiles(dir);
+ genAllFileStrings(dir, files, headers, output);
+ } finally {
+ headers.end();
+ output.end();
+ }
+}
+
+main();
diff --git a/vcpkg/scripts/azure-pipelines/owners-db/file_script_from_cache.ts b/vcpkg/scripts/azure-pipelines/owners-db/file_script_from_cache.ts
new file mode 100644
index 0000000..7b91c1f
--- /dev/null
+++ b/vcpkg/scripts/azure-pipelines/owners-db/file_script_from_cache.ts
@@ -0,0 +1,184 @@
+#!/usr/bin/env node
+import * as fs from "fs";
+import * as path from "path";
+import * as https from "https";
+import AdmZip from "adm-zip";
+import { execSync } from "child_process";
+
+const keyword = "/include/";
+
+function writeOutputLines(outDir: string, dbLines: string[], headerLines: string[]) {
+ fs.mkdirSync(outDir, { recursive: true });
+ fs.writeFileSync(path.join(outDir, "VCPKGDatabase.txt"), dbLines.join("\n") + (dbLines.length ? "\n" : ""));
+ fs.writeFileSync(path.join(outDir, "VCPKGHeadersDatabase.txt"), headerLines.join("\n") + (headerLines.length ? "\n" : ""));
+}
+
+function listZipFiles(buffer: Buffer, pkgName: string, dbLines: string[], headerLines: string[]) {
+ const zip = new AdmZip(buffer);
+ const entries = zip.getEntries();
+ for (const e of entries) {
+ if (e.isDirectory) continue;
+ const entryName = "/" + e.entryName.replace(/\\/g, "/");
+ if (entryName === "/BUILD_INFO" || entryName === "/CONTROL") continue;
+ dbLines.push(`${pkgName}:${entryName}`);
+ if (entryName.startsWith(keyword)) {
+ headerLines.push(`${pkgName}:${entryName.substring(keyword.length)}`);
+ }
+ }
+}
+
+function downloadUrlToBuffer(url: string): Promise<Buffer> {
+ return new Promise((resolve, reject) => {
+ https.get(url, (res) => {
+ if (res.statusCode && res.statusCode >= 400) {
+ reject(new Error(`HTTP ${res.statusCode} while fetching ${url}`));
+ return;
+ }
+ const chunks: Buffer[] = [];
+ res.on("data", (c) => chunks.push(c));
+ res.on("end", () => resolve(Buffer.concat(chunks)));
+ }).on("error", reject);
+ });
+}
+
+function usage() {
+ console.error("Usage: file_script_from_cache.ts --pr-hashes <pr-hashes.json> --blob-base-url <blob-base-url> [--target-branch <branch>] [--out-dir <path>]");
+ console.error("blob-base-url should include SAS token (e.g. https://<account>.blob.core.windows.net/<container>/?<sas>)");
+}
+
+function parseArgs(argv: string[]) {
+ // supports: --pr-hashes <path> --blob-base-url <url> [--target-branch <branch>] [--out-dir <path>]
+ // legacy: positional: <pr-hashes> <blob-base-url> [target-branch]
+ let prHashesPath: string | undefined;
+ let blobBaseUrl: string | undefined;
+ let targetBranch = "master";
+ let outDir = "scripts/list_files";
+ for (let i = 0; i < argv.length; i++) {
+ const a = argv[i];
+ if (a === "--pr-hashes") {
+ i++;
+ prHashesPath = argv[i];
+ } else if (a === "--blob-base-url") {
+ i++;
+ blobBaseUrl = argv[i];
+ } else if (a === "--target-branch") {
+ i++;
+ targetBranch = argv[i];
+ } else if (a === "--out-dir") {
+ i++;
+ outDir = argv[i];
+ } else if (a.startsWith("--")) {
+ console.error(`Unknown argument: ${a}`);
+ usage();
+ process.exit(2);
+ } else if (!prHashesPath) {
+ prHashesPath = a;
+ } else if (!blobBaseUrl) {
+ blobBaseUrl = a.replace(/[\/\\]+$/g, "");
+ } else if (targetBranch === "master") {
+ targetBranch = a;
+ } else {
+ console.error(`Unexpected positional argument: ${a}`);
+ usage();
+ process.exit(2);
+ }
+ }
+ if (!prHashesPath || !blobBaseUrl) {
+ usage();
+ process.exit(2);
+ }
+ return { prHashesPath, blobBaseUrl, targetBranch, outDir };
+}
+
+async function main() {
+ const { prHashesPath, blobBaseUrl, targetBranch, outDir } = parseArgs(process.argv.slice(2));
+
+ const prHashes = JSON.parse(fs.readFileSync(prHashesPath, "utf8")) as Array<{ name: string; triplet: string; state: string; abi: string }>;
+ // Expect vcpkg-tool produced format: array of objects
+ // [ { "name": "zlib", "triplet": "x64-windows", "state": "pass", "abi": "zlib:x64-windows:<sha>" }, ... ]
+ if (!Array.isArray(prHashes)) {
+ console.error(
+ `Invalid pr-hashes.json format: expected a top-level JSON array (vcpkg-tool output).`
+ );
+ process.exit(2);
+ }
+
+ const dbLines: string[] = [];
+ const headerLines: string[] = [];
+
+ // Determine list of ports to process from git-diff (only folders under ports/ that changed)
+ let changedPorts: string[] = [];
+ try {
+ const mergebase = execSync(`git merge-base ${targetBranch} HEAD`, { encoding: "utf8" }).trim();
+ // Find repository root by locating .vcpkg-root in or above cwd
+ function findRepoRoot(): string {
+ let dir = process.cwd();
+ while (true) {
+ if (fs.existsSync(path.join(dir, ".vcpkg-root"))) return dir;
+ const parent = path.dirname(dir);
+ if (parent === dir) break;
+ dir = parent;
+ }
+ throw new Error("Could not find .vcpkg-root in or above current working directory");
+ }
+
+ const repoRoot = findRepoRoot();
+ const diffOut = execSync(`git diff --name-only ${mergebase}...HEAD -- ports/`, { encoding: "utf8", cwd: repoRoot });
+ const files = diffOut.split(/\r?\n/).filter((l) => l.length > 0);
+ const set = new Set<string>();
+ for (const f of files) {
+ const m = f.match(/^ports\/([^\/]+)/);
+ if (m) set.add(m[1]);
+ }
+ changedPorts = Array.from(set);
+ if (changedPorts.length === 0) {
+ console.log(`git diff found no changed ports under ports/ for range ${mergebase}...HEAD; exiting.`);
+ writeOutputLines(outDir, dbLines, headerLines);
+ return;
+ }
+ } catch (e) {
+ console.error(`git diff failed (${e}); this is fatal in PR cache mode.`);
+ process.exit(2);
+ }
+
+ for (const port of changedPorts) {
+ for (const item of prHashes) {
+ if (item.name !== port) continue;
+ // Validate sha format
+ const sha1Regex = /^[a-f0-9]{64}$/;
+ if (!sha1Regex.test(item.abi)) {
+ throw new Error(`Invalid SHA format in pr-hashes.json for port ${port}: ${item.abi}`);
+ }
+ const abi = item.abi;
+ // blob named <sha>.zip
+ // Ensure we append the ABI path before the SAS query string, i.e.:
+ // https://.../<container>/<sha>.zip?<sas>
+ let blobUrl: string;
+ try {
+ const u = new URL(blobBaseUrl);
+ const sas = u.search; // includes leading '?' or empty
+ // build base path without query and without trailing slash
+ const baseNoQuery = `${u.origin}${u.pathname.replace(/[\/\\]+$/g, "")}`;
+ blobUrl = sas ? `${baseNoQuery}/${abi}.zip${sas}` : `${baseNoQuery}/${abi}.zip`;
+ } catch (e) {
+ console.error(`Invalid blob base URL provided: ${blobBaseUrl} -- ${e}`);
+ process.exit(2);
+ }
+ console.log(`Downloading ${blobUrl} for port ${port}...`);
+ try {
+ const buf = await downloadUrlToBuffer(blobUrl);
+ listZipFiles(buf, `${port}:${item.triplet}`, dbLines, headerLines);
+ } catch (err) {
+ console.warn(`Failed to download or process blob for ${port}: ${err}`);
+ }
+ }
+ }
+
+ writeOutputLines(outDir, dbLines, headerLines);
+ console.log(`Wrote ${path.join(outDir, "VCPKGDatabase.txt")} and ${path.join(outDir, "VCPKGHeadersDatabase.txt")}`);
+}
+
+await main().catch((e) => {
+ console.error("Error in script:", e);
+ process.exit(1);
+});
diff --git a/vcpkg/scripts/azure-pipelines/owners-db/package-lock.json b/vcpkg/scripts/azure-pipelines/owners-db/package-lock.json
new file mode 100644
index 0000000..78139ee
--- /dev/null
+++ b/vcpkg/scripts/azure-pipelines/owners-db/package-lock.json
@@ -0,0 +1,219 @@
+{
+ "name": "owners-db",
+ "version": "1.0.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "owners-db",
+ "version": "1.0.0",
+ "license": "MIT",
+ "dependencies": {
+ "adm-zip": "^0.5.9",
+ "ts-node": "^10.9.1",
+ "typescript": "^4.9.5"
+ },
+ "devDependencies": {
+ "@types/adm-zip": "^0.5.7"
+ },
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/@cspotcode/source-map-support": {
+ "version": "0.8.1",
+ "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
+ "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
+ "dependencies": {
+ "@jridgewell/trace-mapping": "0.3.9"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@jridgewell/resolve-uri": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
+ "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.5.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
+ "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="
+ },
+ "node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.9",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
+ "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
+ "dependencies": {
+ "@jridgewell/resolve-uri": "^3.0.3",
+ "@jridgewell/sourcemap-codec": "^1.4.10"
+ }
+ },
+ "node_modules/@tsconfig/node10": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz",
+ "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw=="
+ },
+ "node_modules/@tsconfig/node12": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz",
+ "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag=="
+ },
+ "node_modules/@tsconfig/node14": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz",
+ "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow=="
+ },
+ "node_modules/@tsconfig/node16": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz",
+ "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA=="
+ },
+ "node_modules/@types/adm-zip": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/@types/adm-zip/-/adm-zip-0.5.7.tgz",
+ "integrity": "sha512-DNEs/QvmyRLurdQPChqq0Md4zGvPwHerAJYWk9l2jCbD1VPpnzRJorOdiq4zsw09NFbYnhfsoEhWtxIzXpn2yw==",
+ "dev": true,
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/node": {
+ "version": "24.5.2",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-24.5.2.tgz",
+ "integrity": "sha512-FYxk1I7wPv3K2XBaoyH2cTnocQEu8AOZ60hPbsyukMPLv5/5qr7V1i8PLHdl6Zf87I+xZXFvPCXYjiTFq+YSDQ==",
+ "dependencies": {
+ "undici-types": "~7.12.0"
+ }
+ },
+ "node_modules/acorn": {
+ "version": "8.15.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
+ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/acorn-walk": {
+ "version": "8.3.4",
+ "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz",
+ "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==",
+ "dependencies": {
+ "acorn": "^8.11.0"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/adm-zip": {
+ "version": "0.5.16",
+ "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.16.tgz",
+ "integrity": "sha512-TGw5yVi4saajsSEgz25grObGHEUaDrniwvA2qwSC060KfqGPdglhvPMA2lPIoxs3PQIItj2iag35fONcQqgUaQ==",
+ "engines": {
+ "node": ">=12.0"
+ }
+ },
+ "node_modules/arg": {
+ "version": "4.1.3",
+ "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
+ "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA=="
+ },
+ "node_modules/create-require": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
+ "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ=="
+ },
+ "node_modules/diff": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
+ "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
+ "engines": {
+ "node": ">=0.3.1"
+ }
+ },
+ "node_modules/make-error": {
+ "version": "1.3.6",
+ "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
+ "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw=="
+ },
+ "node_modules/ts-node": {
+ "version": "10.9.2",
+ "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz",
+ "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==",
+ "dependencies": {
+ "@cspotcode/source-map-support": "^0.8.0",
+ "@tsconfig/node10": "^1.0.7",
+ "@tsconfig/node12": "^1.0.7",
+ "@tsconfig/node14": "^1.0.0",
+ "@tsconfig/node16": "^1.0.2",
+ "acorn": "^8.4.1",
+ "acorn-walk": "^8.1.1",
+ "arg": "^4.1.0",
+ "create-require": "^1.1.0",
+ "diff": "^4.0.1",
+ "make-error": "^1.1.1",
+ "v8-compile-cache-lib": "^3.0.1",
+ "yn": "3.1.1"
+ },
+ "bin": {
+ "ts-node": "dist/bin.js",
+ "ts-node-cwd": "dist/bin-cwd.js",
+ "ts-node-esm": "dist/bin-esm.js",
+ "ts-node-script": "dist/bin-script.js",
+ "ts-node-transpile-only": "dist/bin-transpile.js",
+ "ts-script": "dist/bin-script-deprecated.js"
+ },
+ "peerDependencies": {
+ "@swc/core": ">=1.2.50",
+ "@swc/wasm": ">=1.2.50",
+ "@types/node": "*",
+ "typescript": ">=2.7"
+ },
+ "peerDependenciesMeta": {
+ "@swc/core": {
+ "optional": true
+ },
+ "@swc/wasm": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/typescript": {
+ "version": "4.9.5",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
+ "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=4.2.0"
+ }
+ },
+ "node_modules/undici-types": {
+ "version": "7.12.0",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.12.0.tgz",
+ "integrity": "sha512-goOacqME2GYyOZZfb5Lgtu+1IDmAlAEu5xnD3+xTzS10hT0vzpf0SPjkXwAw9Jm+4n/mQGDP3LO8CPbYROeBfQ=="
+ },
+ "node_modules/v8-compile-cache-lib": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
+ "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg=="
+ },
+ "node_modules/yn": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
+ "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
+ "engines": {
+ "node": ">=6"
+ }
+ }
+ }
+}
diff --git a/vcpkg/scripts/azure-pipelines/owners-db/package.json b/vcpkg/scripts/azure-pipelines/owners-db/package.json
new file mode 100644
index 0000000..abfeda0
--- /dev/null
+++ b/vcpkg/scripts/azure-pipelines/owners-db/package.json
@@ -0,0 +1,19 @@
+{
+ "name": "owners-db",
+ "version": "1.0.0",
+ "description": "Utility to generate VCPKG file lists (TypeScript)",
+ "private": true,
+ "type": "module",
+ "license": "MIT",
+ "engines": {
+ "node": ">=22"
+ },
+ "dependencies": {
+ "adm-zip": "^0.5.9",
+ "ts-node": "^10.9.1",
+ "typescript": "^4.9.5"
+ },
+ "devDependencies": {
+ "@types/adm-zip": "^0.5.7"
+ }
+}