@fnndsc/salsa 4.4.4 → 4.5.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/plugins/index.d.ts +7 -0
- package/dist/plugins/index.js +7 -0
- package/dist/plugins/index.js.map +1 -1
- package/dist/plugins/plugin_add.d.ts +173 -0
- package/dist/plugins/plugin_add.js +325 -0
- package/dist/plugins/plugin_add.js.map +1 -0
- package/dist/plugins/plugin_docker.d.ts +90 -0
- package/dist/plugins/plugin_docker.js +158 -0
- package/dist/plugins/plugin_docker.js.map +1 -0
- package/dist/plugins/plugin_index.d.ts +157 -0
- package/dist/plugins/plugin_index.js +338 -0
- package/dist/plugins/plugin_index.js.map +1 -0
- package/dist/plugins/plugin_sync.d.ts +276 -0
- package/dist/plugins/plugin_sync.js +453 -0
- package/dist/plugins/plugin_sync.js.map +1 -0
- package/dist/plugins/plugin_validate.d.ts +224 -0
- package/dist/plugins/plugin_validate.js +395 -0
- package/dist/plugins/plugin_validate.js.map +1 -0
- package/dist/plugins/pool.d.ts +46 -0
- package/dist/plugins/pool.js +63 -0
- package/dist/plugins/pool.js.map +1 -0
- package/dist/plugins/tar_read.d.ts +73 -0
- package/dist/plugins/tar_read.js +190 -0
- package/dist/plugins/tar_read.js.map +1 -0
- package/package.json +3 -2
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @file Reading a plugin's descriptor out of its container image.
|
|
4
|
+
*
|
|
5
|
+
* The last way `plugin add` finds a descriptor: pull the image (or use the
|
|
6
|
+
* local copy) and ask it, trying the three ways ChRIS plugins have printed
|
|
7
|
+
* one over the years, in chrisomatic's order — `chris_plugin_info` with the
|
|
8
|
+
* image named, `chris_plugin_info` bare, and the old chrisapp `<CMD> --json`.
|
|
9
|
+
*
|
|
10
|
+
* Every docker call is an argument vector run without a shell: an image
|
|
11
|
+
* reference is operator input and never reaches a command line as text.
|
|
12
|
+
*
|
|
13
|
+
* Key components:
|
|
14
|
+
* - DockerRun: the seam that runs one docker command (tests stub it).
|
|
15
|
+
* - docker_isAvailable: whether a docker daemon answers.
|
|
16
|
+
* - dockerImage_ensure: the image locally, pulled when absent.
|
|
17
|
+
* - descriptorFromImage_extract: the image's descriptor JSON, by the three ways.
|
|
18
|
+
*
|
|
19
|
+
* Typical usage:
|
|
20
|
+
* ```ts
|
|
21
|
+
* if (await docker_isAvailable() && await dockerImage_ensure(image)) {
|
|
22
|
+
* const descriptor: Record<string, unknown> | null = await descriptorFromImage_extract(image);
|
|
23
|
+
* }
|
|
24
|
+
* ```
|
|
25
|
+
*
|
|
26
|
+
* Dependencies: Node.js `child_process`; a `docker` CLI on the host.
|
|
27
|
+
*
|
|
28
|
+
* @module
|
|
29
|
+
*/
|
|
30
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
31
|
+
exports.docker_run = void 0;
|
|
32
|
+
exports.docker_isAvailable = docker_isAvailable;
|
|
33
|
+
exports.dockerImage_ensure = dockerImage_ensure;
|
|
34
|
+
exports.descriptorFromImage_extract = descriptorFromImage_extract;
|
|
35
|
+
const node_child_process_1 = require("node:child_process");
|
|
36
|
+
/** Longest a descriptor print may take: some images (TensorFlow, MINC) take minutes. */
|
|
37
|
+
const DESCRIPTOR_TIMEOUT_MS = 10 * 60 * 1000;
|
|
38
|
+
/** Largest output read from a docker command. */
|
|
39
|
+
const OUTPUT_MAX_BYTES = 32 * 1024 * 1024;
|
|
40
|
+
/**
|
|
41
|
+
* Runs `docker` with an argument vector, no shell.
|
|
42
|
+
*
|
|
43
|
+
* @param args - The arguments after `docker`.
|
|
44
|
+
* @returns What it answered; never throws.
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* ```ts
|
|
48
|
+
* const answer: DockerAnswer = await docker_run(['image', 'inspect', 'alpine']);
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
const docker_run = (args) => new Promise((resolve) => {
|
|
52
|
+
(0, node_child_process_1.execFile)('docker', [...args], { timeout: DESCRIPTOR_TIMEOUT_MS, maxBuffer: OUTPUT_MAX_BYTES }, (error, stdout, stderr) => {
|
|
53
|
+
resolve({ ok: error === null, stdout: String(stdout).trim(), stderr: String(stderr || error?.message || '').trim() });
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
exports.docker_run = docker_run;
|
|
57
|
+
/**
|
|
58
|
+
* Reports whether a docker daemon answers.
|
|
59
|
+
*
|
|
60
|
+
* @param run - The docker seam.
|
|
61
|
+
* @returns True when `docker info` succeeds.
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* ```ts
|
|
65
|
+
* await docker_isAvailable(); // false on a host without docker
|
|
66
|
+
* ```
|
|
67
|
+
*/
|
|
68
|
+
async function docker_isAvailable(run = exports.docker_run) {
|
|
69
|
+
return (await run(['info', '--format', '{{.ServerVersion}}'])).ok;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Makes sure an image is present locally, pulling it when it is not.
|
|
73
|
+
*
|
|
74
|
+
* @param image - The image reference.
|
|
75
|
+
* @param run - The docker seam.
|
|
76
|
+
* @returns Null when the image is present; else why it could not be had.
|
|
77
|
+
*
|
|
78
|
+
* @example
|
|
79
|
+
* ```ts
|
|
80
|
+
* await dockerImage_ensure('fnndsc/pl-dircopy:2.1.1'); // null
|
|
81
|
+
* ```
|
|
82
|
+
*/
|
|
83
|
+
async function dockerImage_ensure(image, run = exports.docker_run) {
|
|
84
|
+
if ((await run(['image', 'inspect', '--format', '{{.Id}}', image])).ok)
|
|
85
|
+
return null;
|
|
86
|
+
const pulled = await run(['pull', '--quiet', image]);
|
|
87
|
+
return pulled.ok ? null : `docker pull ${image} failed: ${pulled.stderr || 'no reason given'}`;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Reads a plugin's descriptor out of its image.
|
|
91
|
+
*
|
|
92
|
+
* @param image - The image reference.
|
|
93
|
+
* @param publicRepo - The plugin's repository, passed to `chris_plugin_info` when known.
|
|
94
|
+
* @param run - The docker seam.
|
|
95
|
+
* @returns The descriptor, or null when none of the three ways printed one.
|
|
96
|
+
*
|
|
97
|
+
* @example
|
|
98
|
+
* ```ts
|
|
99
|
+
* const descriptor: Record<string, unknown> | null = await descriptorFromImage_extract('fnndsc/pl-dircopy:2.1.1');
|
|
100
|
+
* ```
|
|
101
|
+
*/
|
|
102
|
+
async function descriptorFromImage_extract(image, publicRepo, run = exports.docker_run) {
|
|
103
|
+
const withArgs = ['run', '--rm', image, 'chris_plugin_info', '--dock-image', image];
|
|
104
|
+
if (publicRepo !== undefined)
|
|
105
|
+
withArgs.push('--public-repo', publicRepo);
|
|
106
|
+
const attempts = [
|
|
107
|
+
() => run(withArgs),
|
|
108
|
+
() => run(['run', '--rm', image, 'chris_plugin_info']),
|
|
109
|
+
async () => {
|
|
110
|
+
const command = await imageCommand_get(image, run);
|
|
111
|
+
return command === null ? null : run(['run', '--rm', image, command, '--json']);
|
|
112
|
+
},
|
|
113
|
+
];
|
|
114
|
+
for (const attempt of attempts) {
|
|
115
|
+
const answer = await attempt();
|
|
116
|
+
if (answer === null || !answer.ok || answer.stdout === '')
|
|
117
|
+
continue;
|
|
118
|
+
const parsed = descriptorJson_parse(answer.stdout);
|
|
119
|
+
if (parsed !== null)
|
|
120
|
+
return parsed;
|
|
121
|
+
}
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Reads an image's default command, for the old chrisapp `--json` way.
|
|
126
|
+
*
|
|
127
|
+
* @param image - The image reference.
|
|
128
|
+
* @param run - The docker seam.
|
|
129
|
+
* @returns The first word of the image's CMD, or null when it has none.
|
|
130
|
+
*/
|
|
131
|
+
async function imageCommand_get(image, run) {
|
|
132
|
+
const answer = await run(['image', 'inspect', '--format', '{{json .Config.Cmd}}', image]);
|
|
133
|
+
if (!answer.ok)
|
|
134
|
+
return null;
|
|
135
|
+
try {
|
|
136
|
+
const command = JSON.parse(answer.stdout);
|
|
137
|
+
return Array.isArray(command) && typeof command[0] === 'string' ? command[0] : null;
|
|
138
|
+
}
|
|
139
|
+
catch {
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Parses a printed descriptor.
|
|
145
|
+
*
|
|
146
|
+
* @param text - What the image printed.
|
|
147
|
+
* @returns The descriptor object, or null when the text is not one.
|
|
148
|
+
*/
|
|
149
|
+
function descriptorJson_parse(text) {
|
|
150
|
+
try {
|
|
151
|
+
const parsed = JSON.parse(text);
|
|
152
|
+
return parsed !== null && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : null;
|
|
153
|
+
}
|
|
154
|
+
catch {
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
//# sourceMappingURL=plugin_docker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin_docker.js","sourceRoot":"","sources":["../../src/plugins/plugin_docker.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Plugin indexes: where descriptors come from, and reading them.
|
|
3
|
+
*
|
|
4
|
+
* A plugin index is a set of plugin descriptors, one JSON file per plugin at
|
|
5
|
+
* its latest version, in the form CUBE's administrative upload takes, with
|
|
6
|
+
* the JSON Schema they follow beside them. FNNDSC/chris-plugins is the one
|
|
7
|
+
* that matters; a directory of `.json` files or a `.tar.gz` holding one is
|
|
8
|
+
* an index too, on CUBE (how a surface with no host access syncs) or on the
|
|
9
|
+
* host.
|
|
10
|
+
*
|
|
11
|
+
* Every source reads to the same {@link PluginIndex}: its descriptors as
|
|
12
|
+
* text (so a later check can quote the file), the schema when one came with
|
|
13
|
+
* them, and the commit when the source can name one. GitHub's archive of a
|
|
14
|
+
* ref is fetched once, whole; its pax global header names the exact commit.
|
|
15
|
+
*
|
|
16
|
+
* Layout read from any source: descriptors are the `.json` files at the root
|
|
17
|
+
* or under `plugins/`; the schema is `schema/plugin.schema.json` at the root
|
|
18
|
+
* or one level up (a `plugins/` directory handed over on its own).
|
|
19
|
+
*
|
|
20
|
+
* Key components:
|
|
21
|
+
* - PluginIndexSource: a parsed source (GitHub repository at a ref, CUBE path, host path).
|
|
22
|
+
* - PluginIndex: the descriptors, schema and commit read from a source.
|
|
23
|
+
* - pluginIndexSource_parse: read a source word (`FNNDSC/chris-plugins@main`, `host:./plugins`).
|
|
24
|
+
* - pluginIndexArchive_read: read an index from archive bytes.
|
|
25
|
+
* - pluginIndex_load: read an index from any source.
|
|
26
|
+
*
|
|
27
|
+
* Typical usage:
|
|
28
|
+
* ```ts
|
|
29
|
+
* const source: PluginIndexSource | null = pluginIndexSource_parse('FNNDSC/chris-plugins@main', 'cube');
|
|
30
|
+
* const loaded: Result<PluginIndex> = source ? await pluginIndex_load(source) : Err();
|
|
31
|
+
* ```
|
|
32
|
+
*
|
|
33
|
+
* Dependencies: Node.js built-ins (`node:fs`, `node:path`), `fetch` for the
|
|
34
|
+
* GitHub archive, salsa's CUBE file reads for CUBE paths.
|
|
35
|
+
*
|
|
36
|
+
* @module
|
|
37
|
+
*/
|
|
38
|
+
import { type Result } from '@fnndsc/cumin';
|
|
39
|
+
/**
|
|
40
|
+
* Where an index is read from.
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```ts
|
|
44
|
+
* const source: PluginIndexSource = { kind: 'github', owner: 'FNNDSC', repo: 'chris-plugins', ref: 'main' };
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
export type PluginIndexSource = {
|
|
48
|
+
readonly kind: 'github';
|
|
49
|
+
readonly owner: string;
|
|
50
|
+
readonly repo: string;
|
|
51
|
+
readonly ref: string;
|
|
52
|
+
} | {
|
|
53
|
+
readonly kind: 'cube';
|
|
54
|
+
readonly path: string;
|
|
55
|
+
} | {
|
|
56
|
+
readonly kind: 'host';
|
|
57
|
+
readonly path: string;
|
|
58
|
+
};
|
|
59
|
+
/**
|
|
60
|
+
* One descriptor file of an index.
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* ```ts
|
|
64
|
+
* const descriptor: IndexDescriptor = { file: 'pl-dircopy.json', text: '{"name":"pl-dircopy"}' };
|
|
65
|
+
* ```
|
|
66
|
+
*/
|
|
67
|
+
export interface IndexDescriptor {
|
|
68
|
+
/** The file's name (`pl-dircopy.json`), which rule 3 holds to the plugin's name. */
|
|
69
|
+
readonly file: string;
|
|
70
|
+
/** The file's text, as read. */
|
|
71
|
+
readonly text: string;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* An index, read.
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
77
|
+
* ```ts
|
|
78
|
+
* const index: PluginIndex = pluginIndexArchive_read(bytes, 'FNNDSC/chris-plugins@main');
|
|
79
|
+
* index.descriptors.length;
|
|
80
|
+
* ```
|
|
81
|
+
*/
|
|
82
|
+
export interface PluginIndex {
|
|
83
|
+
/** The source as the operator would write it. */
|
|
84
|
+
readonly source: string;
|
|
85
|
+
/** A short name for headers: `chris-plugins @ 8c9a1f6`. */
|
|
86
|
+
readonly label: string;
|
|
87
|
+
/** The full commit the descriptors were read at, when the source names one. */
|
|
88
|
+
readonly commit: string | null;
|
|
89
|
+
/** Every descriptor, sorted by file name. */
|
|
90
|
+
readonly descriptors: IndexDescriptor[];
|
|
91
|
+
/** The parsed JSON Schema, or null when none came with the descriptors. */
|
|
92
|
+
readonly schema: unknown;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Parses an index source word.
|
|
96
|
+
*
|
|
97
|
+
* `owner/repo[@ref]` is a GitHub repository; `host:<path>` a host path;
|
|
98
|
+
* `cube:<path>` a CUBE path. Any other word is a path, read where `bare`
|
|
99
|
+
* says: `plugin sync` reads a bare path on CUBE, `plugin validate` (which
|
|
100
|
+
* needs no CUBE) on the host.
|
|
101
|
+
*
|
|
102
|
+
* @param word - The source as typed.
|
|
103
|
+
* @param bare - Where a path with no prefix is read.
|
|
104
|
+
* @param ref - A ref overriding the word's own (`--ref`), for a GitHub source.
|
|
105
|
+
* @returns The parsed source, or null for an empty word.
|
|
106
|
+
*
|
|
107
|
+
* @example
|
|
108
|
+
* ```ts
|
|
109
|
+
* pluginIndexSource_parse('FNNDSC/chris-plugins@v1', 'cube'); // { kind: 'github', …, ref: 'v1' }
|
|
110
|
+
* pluginIndexSource_parse('host:./plugins', 'cube'); // { kind: 'host', path: './plugins' }
|
|
111
|
+
* ```
|
|
112
|
+
*/
|
|
113
|
+
export declare function pluginIndexSource_parse(word: string, bare: 'cube' | 'host', ref?: string): PluginIndexSource | null;
|
|
114
|
+
/**
|
|
115
|
+
* Renders a source as the operator would write it.
|
|
116
|
+
*
|
|
117
|
+
* @param source - The parsed source.
|
|
118
|
+
* @returns The source word (`FNNDSC/chris-plugins@main`, `host:./plugins`, `cube:/home/u/plugins`).
|
|
119
|
+
*
|
|
120
|
+
* @example
|
|
121
|
+
* ```ts
|
|
122
|
+
* pluginIndexSource_format({ kind: 'host', path: './plugins' }); // 'host:./plugins'
|
|
123
|
+
* ```
|
|
124
|
+
*/
|
|
125
|
+
export declare function pluginIndexSource_format(source: PluginIndexSource): string;
|
|
126
|
+
/**
|
|
127
|
+
* Reads an index from archive bytes.
|
|
128
|
+
*
|
|
129
|
+
* A single top folder (GitHub's `FNNDSC-chris-plugins-<sha>/`) is the root;
|
|
130
|
+
* the pax global header's `comment` is the commit.
|
|
131
|
+
*
|
|
132
|
+
* @param bytes - The archive, gzipped or plain.
|
|
133
|
+
* @param source - The source word, for the index's `source` and `label`.
|
|
134
|
+
* @returns The index.
|
|
135
|
+
* @throws {Error} When the archive is unreadable, an entry's path is unsafe,
|
|
136
|
+
* or the schema it carries is not JSON.
|
|
137
|
+
*
|
|
138
|
+
* @example
|
|
139
|
+
* ```ts
|
|
140
|
+
* const index: PluginIndex = pluginIndexArchive_read(readFileSync('index.tar.gz'), 'FNNDSC/chris-plugins@main');
|
|
141
|
+
* ```
|
|
142
|
+
*/
|
|
143
|
+
export declare function pluginIndexArchive_read(bytes: Buffer, source: string): PluginIndex;
|
|
144
|
+
/**
|
|
145
|
+
* Reads an index from its source.
|
|
146
|
+
*
|
|
147
|
+
* @param source - The parsed source.
|
|
148
|
+
* @param fetcher - The fetch used for GitHub; the global one by default.
|
|
149
|
+
* @returns The index, or an error on the error stack (the fetch failed, the
|
|
150
|
+
* path is not there, the archive is unreadable).
|
|
151
|
+
*
|
|
152
|
+
* @example
|
|
153
|
+
* ```ts
|
|
154
|
+
* const loaded: Result<PluginIndex> = await pluginIndex_load({ kind: 'host', path: './plugins' });
|
|
155
|
+
* ```
|
|
156
|
+
*/
|
|
157
|
+
export declare function pluginIndex_load(source: PluginIndexSource, fetcher?: typeof fetch): Promise<Result<PluginIndex>>;
|
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @file Plugin indexes: where descriptors come from, and reading them.
|
|
4
|
+
*
|
|
5
|
+
* A plugin index is a set of plugin descriptors, one JSON file per plugin at
|
|
6
|
+
* its latest version, in the form CUBE's administrative upload takes, with
|
|
7
|
+
* the JSON Schema they follow beside them. FNNDSC/chris-plugins is the one
|
|
8
|
+
* that matters; a directory of `.json` files or a `.tar.gz` holding one is
|
|
9
|
+
* an index too, on CUBE (how a surface with no host access syncs) or on the
|
|
10
|
+
* host.
|
|
11
|
+
*
|
|
12
|
+
* Every source reads to the same {@link PluginIndex}: its descriptors as
|
|
13
|
+
* text (so a later check can quote the file), the schema when one came with
|
|
14
|
+
* them, and the commit when the source can name one. GitHub's archive of a
|
|
15
|
+
* ref is fetched once, whole; its pax global header names the exact commit.
|
|
16
|
+
*
|
|
17
|
+
* Layout read from any source: descriptors are the `.json` files at the root
|
|
18
|
+
* or under `plugins/`; the schema is `schema/plugin.schema.json` at the root
|
|
19
|
+
* or one level up (a `plugins/` directory handed over on its own).
|
|
20
|
+
*
|
|
21
|
+
* Key components:
|
|
22
|
+
* - PluginIndexSource: a parsed source (GitHub repository at a ref, CUBE path, host path).
|
|
23
|
+
* - PluginIndex: the descriptors, schema and commit read from a source.
|
|
24
|
+
* - pluginIndexSource_parse: read a source word (`FNNDSC/chris-plugins@main`, `host:./plugins`).
|
|
25
|
+
* - pluginIndexArchive_read: read an index from archive bytes.
|
|
26
|
+
* - pluginIndex_load: read an index from any source.
|
|
27
|
+
*
|
|
28
|
+
* Typical usage:
|
|
29
|
+
* ```ts
|
|
30
|
+
* const source: PluginIndexSource | null = pluginIndexSource_parse('FNNDSC/chris-plugins@main', 'cube');
|
|
31
|
+
* const loaded: Result<PluginIndex> = source ? await pluginIndex_load(source) : Err();
|
|
32
|
+
* ```
|
|
33
|
+
*
|
|
34
|
+
* Dependencies: Node.js built-ins (`node:fs`, `node:path`), `fetch` for the
|
|
35
|
+
* GitHub archive, salsa's CUBE file reads for CUBE paths.
|
|
36
|
+
*
|
|
37
|
+
* @module
|
|
38
|
+
*/
|
|
39
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
40
|
+
if (k2 === undefined) k2 = k;
|
|
41
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
42
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
43
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
44
|
+
}
|
|
45
|
+
Object.defineProperty(o, k2, desc);
|
|
46
|
+
}) : (function(o, m, k, k2) {
|
|
47
|
+
if (k2 === undefined) k2 = k;
|
|
48
|
+
o[k2] = m[k];
|
|
49
|
+
}));
|
|
50
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
51
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
52
|
+
}) : function(o, v) {
|
|
53
|
+
o["default"] = v;
|
|
54
|
+
});
|
|
55
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
56
|
+
var ownKeys = function(o) {
|
|
57
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
58
|
+
var ar = [];
|
|
59
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
60
|
+
return ar;
|
|
61
|
+
};
|
|
62
|
+
return ownKeys(o);
|
|
63
|
+
};
|
|
64
|
+
return function (mod) {
|
|
65
|
+
if (mod && mod.__esModule) return mod;
|
|
66
|
+
var result = {};
|
|
67
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
68
|
+
__setModuleDefault(result, mod);
|
|
69
|
+
return result;
|
|
70
|
+
};
|
|
71
|
+
})();
|
|
72
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
73
|
+
exports.pluginIndexSource_parse = pluginIndexSource_parse;
|
|
74
|
+
exports.pluginIndexSource_format = pluginIndexSource_format;
|
|
75
|
+
exports.pluginIndexArchive_read = pluginIndexArchive_read;
|
|
76
|
+
exports.pluginIndex_load = pluginIndex_load;
|
|
77
|
+
const node_fs_1 = require("node:fs");
|
|
78
|
+
const nodePath = __importStar(require("node:path"));
|
|
79
|
+
const cumin_1 = require("@fnndsc/cumin");
|
|
80
|
+
const index_js_1 = require("../files/index.js");
|
|
81
|
+
const tar_read_js_1 = require("./tar_read.js");
|
|
82
|
+
/** Where GitHub serves a repository's archive of a ref. */
|
|
83
|
+
const GITHUB_TARBALL_URL = 'https://api.github.com/repos';
|
|
84
|
+
/** The ref read when a GitHub source names none. */
|
|
85
|
+
const DEFAULT_REF = 'main';
|
|
86
|
+
/** File name of the index's JSON Schema. */
|
|
87
|
+
const SCHEMA_FILE = 'plugin.schema.json';
|
|
88
|
+
/** Archive extensions read as a single archive rather than a directory. */
|
|
89
|
+
const ARCHIVE_PATTERN = /\.(tar\.gz|tgz|tar)$/i;
|
|
90
|
+
/** A GitHub repository word: `owner/repo`, optionally `@ref`. */
|
|
91
|
+
const GITHUB_PATTERN = /^([A-Za-z0-9_.-]+)\/([A-Za-z0-9_.-]+)(?:@(\S+))?$/;
|
|
92
|
+
/**
|
|
93
|
+
* Parses an index source word.
|
|
94
|
+
*
|
|
95
|
+
* `owner/repo[@ref]` is a GitHub repository; `host:<path>` a host path;
|
|
96
|
+
* `cube:<path>` a CUBE path. Any other word is a path, read where `bare`
|
|
97
|
+
* says: `plugin sync` reads a bare path on CUBE, `plugin validate` (which
|
|
98
|
+
* needs no CUBE) on the host.
|
|
99
|
+
*
|
|
100
|
+
* @param word - The source as typed.
|
|
101
|
+
* @param bare - Where a path with no prefix is read.
|
|
102
|
+
* @param ref - A ref overriding the word's own (`--ref`), for a GitHub source.
|
|
103
|
+
* @returns The parsed source, or null for an empty word.
|
|
104
|
+
*
|
|
105
|
+
* @example
|
|
106
|
+
* ```ts
|
|
107
|
+
* pluginIndexSource_parse('FNNDSC/chris-plugins@v1', 'cube'); // { kind: 'github', …, ref: 'v1' }
|
|
108
|
+
* pluginIndexSource_parse('host:./plugins', 'cube'); // { kind: 'host', path: './plugins' }
|
|
109
|
+
* ```
|
|
110
|
+
*/
|
|
111
|
+
function pluginIndexSource_parse(word, bare, ref) {
|
|
112
|
+
const trimmed = word.trim();
|
|
113
|
+
if (trimmed === '')
|
|
114
|
+
return null;
|
|
115
|
+
if (trimmed.startsWith('host:'))
|
|
116
|
+
return { kind: 'host', path: trimmed.slice('host:'.length) };
|
|
117
|
+
if (trimmed.startsWith('cube:'))
|
|
118
|
+
return { kind: 'cube', path: trimmed.slice('cube:'.length) };
|
|
119
|
+
const github = trimmed.startsWith('.') ? null : GITHUB_PATTERN.exec(trimmed);
|
|
120
|
+
if (github !== null && github[1] !== undefined && github[2] !== undefined) {
|
|
121
|
+
return { kind: 'github', owner: github[1], repo: github[2], ref: ref ?? github[3] ?? DEFAULT_REF };
|
|
122
|
+
}
|
|
123
|
+
return { kind: bare, path: trimmed };
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Renders a source as the operator would write it.
|
|
127
|
+
*
|
|
128
|
+
* @param source - The parsed source.
|
|
129
|
+
* @returns The source word (`FNNDSC/chris-plugins@main`, `host:./plugins`, `cube:/home/u/plugins`).
|
|
130
|
+
*
|
|
131
|
+
* @example
|
|
132
|
+
* ```ts
|
|
133
|
+
* pluginIndexSource_format({ kind: 'host', path: './plugins' }); // 'host:./plugins'
|
|
134
|
+
* ```
|
|
135
|
+
*/
|
|
136
|
+
function pluginIndexSource_format(source) {
|
|
137
|
+
if (source.kind === 'github')
|
|
138
|
+
return `${source.owner}/${source.repo}@${source.ref}`;
|
|
139
|
+
return `${source.kind}:${source.path}`;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Reads an index from archive bytes.
|
|
143
|
+
*
|
|
144
|
+
* A single top folder (GitHub's `FNNDSC-chris-plugins-<sha>/`) is the root;
|
|
145
|
+
* the pax global header's `comment` is the commit.
|
|
146
|
+
*
|
|
147
|
+
* @param bytes - The archive, gzipped or plain.
|
|
148
|
+
* @param source - The source word, for the index's `source` and `label`.
|
|
149
|
+
* @returns The index.
|
|
150
|
+
* @throws {Error} When the archive is unreadable, an entry's path is unsafe,
|
|
151
|
+
* or the schema it carries is not JSON.
|
|
152
|
+
*
|
|
153
|
+
* @example
|
|
154
|
+
* ```ts
|
|
155
|
+
* const index: PluginIndex = pluginIndexArchive_read(readFileSync('index.tar.gz'), 'FNNDSC/chris-plugins@main');
|
|
156
|
+
* ```
|
|
157
|
+
*/
|
|
158
|
+
function pluginIndexArchive_read(bytes, source) {
|
|
159
|
+
const archive = (0, tar_read_js_1.tarArchive_read)(bytes);
|
|
160
|
+
const root = archiveRoot_find(archive.files);
|
|
161
|
+
const entries = archive.files
|
|
162
|
+
.filter((file) => file.path.startsWith(root))
|
|
163
|
+
.map((file) => ({ path: file.path.slice(root.length), text: file.content.toString('utf8') }));
|
|
164
|
+
const commit = archive.globals['comment'] ?? null;
|
|
165
|
+
return pluginIndex_assemble(source, commit, entries);
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Reads an index from its source.
|
|
169
|
+
*
|
|
170
|
+
* @param source - The parsed source.
|
|
171
|
+
* @param fetcher - The fetch used for GitHub; the global one by default.
|
|
172
|
+
* @returns The index, or an error on the error stack (the fetch failed, the
|
|
173
|
+
* path is not there, the archive is unreadable).
|
|
174
|
+
*
|
|
175
|
+
* @example
|
|
176
|
+
* ```ts
|
|
177
|
+
* const loaded: Result<PluginIndex> = await pluginIndex_load({ kind: 'host', path: './plugins' });
|
|
178
|
+
* ```
|
|
179
|
+
*/
|
|
180
|
+
async function pluginIndex_load(source, fetcher = fetch) {
|
|
181
|
+
const word = pluginIndexSource_format(source);
|
|
182
|
+
try {
|
|
183
|
+
if (source.kind === 'github')
|
|
184
|
+
return (0, cumin_1.Ok)(await pluginIndexGitHub_fetch(source, fetcher));
|
|
185
|
+
if (source.kind === 'host')
|
|
186
|
+
return (0, cumin_1.Ok)(await pluginIndexHost_read(source.path, word));
|
|
187
|
+
return await pluginIndexCube_read(source.path, word);
|
|
188
|
+
}
|
|
189
|
+
catch (error) {
|
|
190
|
+
cumin_1.errorStack.stack_push('error', `plugin index ${word}: ${error instanceof Error ? error.message : String(error)}`);
|
|
191
|
+
return (0, cumin_1.Err)();
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Fetches a GitHub repository's archive of a ref and reads it.
|
|
196
|
+
*
|
|
197
|
+
* @param source - The GitHub source.
|
|
198
|
+
* @param fetcher - The fetch to use.
|
|
199
|
+
* @returns The index.
|
|
200
|
+
* @throws {Error} When GitHub does not answer with the archive.
|
|
201
|
+
*/
|
|
202
|
+
async function pluginIndexGitHub_fetch(source, fetcher) {
|
|
203
|
+
const url = `${GITHUB_TARBALL_URL}/${source.owner}/${source.repo}/tarball/${encodeURIComponent(source.ref)}`;
|
|
204
|
+
const response = await fetcher(url, { headers: { Accept: 'application/vnd.github+json' } });
|
|
205
|
+
if (!response.ok)
|
|
206
|
+
throw new Error(`GitHub answered HTTP ${response.status} for ${url}`);
|
|
207
|
+
const bytes = Buffer.from(await response.arrayBuffer());
|
|
208
|
+
return pluginIndexArchive_read(bytes, `${source.owner}/${source.repo}@${source.ref}`);
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Reads an index from a host path: a directory or an archive.
|
|
212
|
+
*
|
|
213
|
+
* @param path - The host path.
|
|
214
|
+
* @param word - The source word.
|
|
215
|
+
* @returns The index.
|
|
216
|
+
* @throws {Error} When the path cannot be read.
|
|
217
|
+
*/
|
|
218
|
+
async function pluginIndexHost_read(path, word) {
|
|
219
|
+
if (ARCHIVE_PATTERN.test(path))
|
|
220
|
+
return pluginIndexArchive_read(await node_fs_1.promises.readFile(path), word);
|
|
221
|
+
const entries = [];
|
|
222
|
+
for (const [relative, directory] of [['', path], ['plugins/', nodePath.join(path, 'plugins')]]) {
|
|
223
|
+
const names = await directoryNames_read(directory);
|
|
224
|
+
for (const name of names.filter((candidate) => candidate.endsWith('.json'))) {
|
|
225
|
+
entries.push({ path: `${relative}${name}`, text: await node_fs_1.promises.readFile(nodePath.join(directory, name), 'utf8') });
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
for (const relative of [`schema/${SCHEMA_FILE}`, `../schema/${SCHEMA_FILE}`]) {
|
|
229
|
+
const text = await node_fs_1.promises.readFile(nodePath.join(path, relative), 'utf8').catch(() => null);
|
|
230
|
+
if (text !== null)
|
|
231
|
+
entries.push({ path: relative, text });
|
|
232
|
+
}
|
|
233
|
+
return pluginIndex_assemble(word, null, entries);
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Lists a host directory's file names, or none when it is not there.
|
|
237
|
+
*
|
|
238
|
+
* @param directory - The host directory.
|
|
239
|
+
* @returns Its entries' names; empty when the directory does not exist.
|
|
240
|
+
* @throws {Error} On a read failure other than absence.
|
|
241
|
+
*/
|
|
242
|
+
async function directoryNames_read(directory) {
|
|
243
|
+
try {
|
|
244
|
+
return (await node_fs_1.promises.readdir(directory)).sort();
|
|
245
|
+
}
|
|
246
|
+
catch (error) {
|
|
247
|
+
if (error.code === 'ENOENT' || error.code === 'ENOTDIR')
|
|
248
|
+
return [];
|
|
249
|
+
throw error;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Reads an index from a CUBE path: a directory or an uploaded archive.
|
|
254
|
+
*
|
|
255
|
+
* @param path - The CUBE path, absolute.
|
|
256
|
+
* @param word - The source word.
|
|
257
|
+
* @returns The index, or an error on the error stack.
|
|
258
|
+
*/
|
|
259
|
+
async function pluginIndexCube_read(path, word) {
|
|
260
|
+
if (ARCHIVE_PATTERN.test(path)) {
|
|
261
|
+
const bytes = await (0, index_js_1.fileContent_getBinary)(path);
|
|
262
|
+
return bytes.ok ? (0, cumin_1.Ok)(pluginIndexArchive_read(bytes.value, word)) : (0, cumin_1.Err)();
|
|
263
|
+
}
|
|
264
|
+
const root = path.replace(/\/+$/, '');
|
|
265
|
+
const items = await (0, index_js_1.files_listRecursive)(root);
|
|
266
|
+
const entries = [];
|
|
267
|
+
for (const item of items.filter((candidate) => candidate.type === 'file')) {
|
|
268
|
+
const relative = item.path.replace(/^\/+/, '').slice(root.replace(/^\/+/, '').length + 1);
|
|
269
|
+
if (!/^(plugins\/)?[^/]+\.json$/.test(relative) && relative !== `schema/${SCHEMA_FILE}`)
|
|
270
|
+
continue;
|
|
271
|
+
const text = await (0, index_js_1.fileContent_get)(`${root}/${relative}`);
|
|
272
|
+
if (!text.ok)
|
|
273
|
+
return (0, cumin_1.Err)();
|
|
274
|
+
entries.push({ path: relative, text: text.value });
|
|
275
|
+
}
|
|
276
|
+
if (!entries.some((entry) => entry.path.endsWith(SCHEMA_FILE))) {
|
|
277
|
+
const above = await (0, index_js_1.fileContent_get)(`${nodePath.posix.dirname(root)}/schema/${SCHEMA_FILE}`);
|
|
278
|
+
// An index handed over without its schema is still an index; rule 1
|
|
279
|
+
// then says it was not checked rather than this read failing.
|
|
280
|
+
if (above.ok)
|
|
281
|
+
entries.push({ path: `../schema/${SCHEMA_FILE}`, text: above.value });
|
|
282
|
+
else
|
|
283
|
+
cumin_1.errorStack.stack_pop();
|
|
284
|
+
}
|
|
285
|
+
return (0, cumin_1.Ok)(pluginIndex_assemble(word, null, entries));
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* Finds an archive's single top folder, the index's root.
|
|
289
|
+
*
|
|
290
|
+
* @param files - The archive's files.
|
|
291
|
+
* @returns The root with its trailing slash, or '' when the files share none.
|
|
292
|
+
*/
|
|
293
|
+
function archiveRoot_find(files) {
|
|
294
|
+
const tops = new Set(files.map((file) => file.path.split('/')[0] ?? ''));
|
|
295
|
+
const top = [...tops][0];
|
|
296
|
+
return tops.size === 1 && top !== undefined && files.every((file) => file.path.includes('/')) ? `${top}/` : '';
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Builds an index from its files, by the layout every source shares.
|
|
300
|
+
*
|
|
301
|
+
* @param source - The source word.
|
|
302
|
+
* @param commit - The commit, when the source names one.
|
|
303
|
+
* @param entries - The files, relative to the root.
|
|
304
|
+
* @returns The index.
|
|
305
|
+
* @throws {Error} When the schema file is not JSON.
|
|
306
|
+
*/
|
|
307
|
+
function pluginIndex_assemble(source, commit, entries) {
|
|
308
|
+
const descriptors = entries
|
|
309
|
+
.filter((entry) => /^(plugins\/)?[^/]+\.json$/.test(entry.path) && !entry.path.endsWith(SCHEMA_FILE))
|
|
310
|
+
.map((entry) => ({ file: entry.path.replace(/^plugins\//, ''), text: entry.text }))
|
|
311
|
+
.sort((a, b) => a.file.localeCompare(b.file));
|
|
312
|
+
const schemaEntry = entries.find((entry) => entry.path === `schema/${SCHEMA_FILE}`)
|
|
313
|
+
?? entries.find((entry) => entry.path === `../schema/${SCHEMA_FILE}`);
|
|
314
|
+
let schema = null;
|
|
315
|
+
if (schemaEntry !== undefined) {
|
|
316
|
+
try {
|
|
317
|
+
schema = JSON.parse(schemaEntry.text);
|
|
318
|
+
}
|
|
319
|
+
catch {
|
|
320
|
+
throw new Error(`${schemaEntry.path} is not JSON`);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
return { source, label: indexLabel_build(source, commit), commit, descriptors, schema };
|
|
324
|
+
}
|
|
325
|
+
/**
|
|
326
|
+
* Builds an index's short name for headers.
|
|
327
|
+
*
|
|
328
|
+
* @param source - The source word.
|
|
329
|
+
* @param commit - The commit, when known.
|
|
330
|
+
* @returns `chris-plugins @ 8c9a1f6` for a repository, else the source word.
|
|
331
|
+
*/
|
|
332
|
+
function indexLabel_build(source, commit) {
|
|
333
|
+
const github = GITHUB_PATTERN.exec(source);
|
|
334
|
+
if (github === null || source.includes(':'))
|
|
335
|
+
return commit === null ? source : `${source} @ ${commit.slice(0, 7)}`;
|
|
336
|
+
return `${github[2] ?? source} @ ${commit === null ? (github[3] ?? DEFAULT_REF) : commit.slice(0, 7)}`;
|
|
337
|
+
}
|
|
338
|
+
//# sourceMappingURL=plugin_index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin_index.js","sourceRoot":"","sources":["../../src/plugins/plugin_index.ts"],"names":[],"mappings":"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"}
|