@udondan/avanti 0.24.0 → 0.26.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/README.md +488 -64
- package/dist/commands/diff.d.ts.map +1 -1
- package/dist/commands/diff.js +82 -18
- package/dist/commands/diff.js.map +1 -1
- package/dist/commands/lock.d.ts.map +1 -1
- package/dist/commands/lock.js +6 -4
- package/dist/commands/lock.js.map +1 -1
- package/dist/commands/log.d.ts.map +1 -1
- package/dist/commands/log.js +7 -5
- package/dist/commands/log.js.map +1 -1
- package/dist/commands/pull.d.ts.map +1 -1
- package/dist/commands/pull.js +682 -42
- package/dist/commands/pull.js.map +1 -1
- package/dist/commands/reset.d.ts.map +1 -1
- package/dist/commands/reset.js +43 -11
- package/dist/commands/reset.js.map +1 -1
- package/dist/commands/revert.d.ts.map +1 -1
- package/dist/commands/revert.js +68 -14
- package/dist/commands/revert.js.map +1 -1
- package/dist/condition.d.ts.map +1 -1
- package/dist/condition.js +9 -6
- package/dist/condition.js.map +1 -1
- package/dist/config-writeback.d.ts.map +1 -1
- package/dist/config-writeback.js +17 -0
- package/dist/config-writeback.js.map +1 -1
- package/dist/config.d.ts +12 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +346 -3
- package/dist/config.js.map +1 -1
- package/dist/diff.d.ts +19 -0
- package/dist/diff.d.ts.map +1 -1
- package/dist/diff.js +317 -12
- package/dist/diff.js.map +1 -1
- package/dist/extract.d.ts +4 -0
- package/dist/extract.d.ts.map +1 -0
- package/dist/extract.js +142 -0
- package/dist/extract.js.map +1 -0
- package/dist/filter.d.ts +3 -0
- package/dist/filter.d.ts.map +1 -0
- package/dist/filter.js +126 -0
- package/dist/filter.js.map +1 -0
- package/dist/history.d.ts +7 -1
- package/dist/history.d.ts.map +1 -1
- package/dist/history.js +89 -9
- package/dist/history.js.map +1 -1
- package/dist/paths.d.ts +4 -0
- package/dist/paths.d.ts.map +1 -1
- package/dist/paths.js +97 -0
- package/dist/paths.js.map +1 -1
- package/dist/processors/ini.d.ts +33 -0
- package/dist/processors/ini.d.ts.map +1 -0
- package/dist/processors/ini.js +500 -0
- package/dist/processors/ini.js.map +1 -0
- package/dist/processors/insert.d.ts.map +1 -1
- package/dist/processors/insert.js +338 -15
- package/dist/processors/insert.js.map +1 -1
- package/dist/processors/on.d.ts +4 -0
- package/dist/processors/on.d.ts.map +1 -0
- package/dist/processors/on.js +54 -0
- package/dist/processors/on.js.map +1 -0
- package/dist/ref.d.ts +21 -0
- package/dist/ref.d.ts.map +1 -0
- package/dist/ref.js +65 -0
- package/dist/ref.js.map +1 -0
- package/dist/sources/bitbucket.d.ts.map +1 -1
- package/dist/sources/bitbucket.js +51 -12
- package/dist/sources/bitbucket.js.map +1 -1
- package/dist/sources/git.d.ts.map +1 -1
- package/dist/sources/git.js +54 -6
- package/dist/sources/git.js.map +1 -1
- package/dist/sources/github.d.ts.map +1 -1
- package/dist/sources/github.js +188 -51
- package/dist/sources/github.js.map +1 -1
- package/dist/sources/gitlab.d.ts.map +1 -1
- package/dist/sources/gitlab.js +242 -44
- package/dist/sources/gitlab.js.map +1 -1
- package/dist/sources/index.d.ts +4 -2
- package/dist/sources/index.d.ts.map +1 -1
- package/dist/sources/index.js +220 -49
- package/dist/sources/index.js.map +1 -1
- package/dist/sources/local.d.ts +3 -0
- package/dist/sources/local.d.ts.map +1 -1
- package/dist/sources/local.js +44 -0
- package/dist/sources/local.js.map +1 -1
- package/dist/types.d.ts +38 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/variables-remote.d.ts +1 -1
- package/dist/variables-remote.d.ts.map +1 -1
- package/dist/variables-remote.js +4 -3
- package/dist/variables-remote.js.map +1 -1
- package/dist/variables.d.ts +1 -0
- package/dist/variables.d.ts.map +1 -1
- package/dist/variables.js +37 -2
- package/dist/variables.js.map +1 -1
- package/dist/writer.d.ts +17 -0
- package/dist/writer.d.ts.map +1 -1
- package/dist/writer.js +848 -20
- package/dist/writer.js.map +1 -1
- package/package.json +14 -10
package/dist/config.d.ts
CHANGED
|
@@ -1,8 +1,20 @@
|
|
|
1
1
|
import { AvantiConfig, Via } from './types';
|
|
2
2
|
export declare const SELF_KEY = "$self";
|
|
3
3
|
export declare function isRemoteConfigSpec(s: string): boolean;
|
|
4
|
+
export declare function deriveConfigBase(configPath: string): string;
|
|
5
|
+
export declare function resolveRelativeSrc(src: string, configBase: string): string;
|
|
4
6
|
export declare function normalizeConfigKey(spec: string): string;
|
|
5
7
|
export declare function resolveConfigPath(explicit?: string): string;
|
|
8
|
+
export declare function parseGitHubSpec(spec: string): {
|
|
9
|
+
repo: string;
|
|
10
|
+
file: string;
|
|
11
|
+
ref: string | undefined;
|
|
12
|
+
};
|
|
13
|
+
export declare function parseGitLabSpec(spec: string): {
|
|
14
|
+
project: string;
|
|
15
|
+
file: string;
|
|
16
|
+
ref: string | undefined;
|
|
17
|
+
};
|
|
6
18
|
export declare function parseConfigContent(content: string): AvantiConfig;
|
|
7
19
|
export declare function loadConfig(configPath: string, via?: Via | Via[]): Promise<AvantiConfig>;
|
|
8
20
|
export declare function parseVia(value: unknown, loc: string): Via | Via[] | undefined;
|
package/dist/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAIA,OAAO,EACL,YAAY,
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAIA,OAAO,EACL,YAAY,EAiCZ,GAAG,EACJ,MAAM,SAAS,CAAC;AASjB,eAAO,MAAM,QAAQ,UAAU,CAAC;AAiBhC,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAQrD;AAED,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAkC3D;AAED,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAkC1E;AA6CD,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAcvD;AAED,wBAAgB,iBAAiB,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAgB3D;AAGD,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG;IAC7C,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,GAAG,SAAS,CAAC;CACzB,CAmBA;AAGD,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG;IAC7C,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,GAAG,SAAS,CAAC;CACzB,CAmBA;AAkDD,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,YAAY,CAkdhE;AAED,wBAAsB,UAAU,CAC9B,UAAU,EAAE,MAAM,EAClB,GAAG,CAAC,EAAE,GAAG,GAAG,GAAG,EAAE,GAChB,OAAO,CAAC,YAAY,CAAC,CAQvB;AA8JD,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,GAAG,GAAG,GAAG,EAAE,GAAG,SAAS,CAgC7E"}
|
package/dist/config.js
CHANGED
|
@@ -35,8 +35,12 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.SELF_KEY = void 0;
|
|
37
37
|
exports.isRemoteConfigSpec = isRemoteConfigSpec;
|
|
38
|
+
exports.deriveConfigBase = deriveConfigBase;
|
|
39
|
+
exports.resolveRelativeSrc = resolveRelativeSrc;
|
|
38
40
|
exports.normalizeConfigKey = normalizeConfigKey;
|
|
39
41
|
exports.resolveConfigPath = resolveConfigPath;
|
|
42
|
+
exports.parseGitHubSpec = parseGitHubSpec;
|
|
43
|
+
exports.parseGitLabSpec = parseGitLabSpec;
|
|
40
44
|
exports.parseConfigContent = parseConfigContent;
|
|
41
45
|
exports.loadConfig = loadConfig;
|
|
42
46
|
exports.parseVia = parseVia;
|
|
@@ -51,6 +55,7 @@ const http_1 = require("./sources/http");
|
|
|
51
55
|
const github_1 = require("./sources/github");
|
|
52
56
|
const gitlab_1 = require("./sources/gitlab");
|
|
53
57
|
const git_1 = require("./sources/git");
|
|
58
|
+
const local_1 = require("./sources/local");
|
|
54
59
|
exports.SELF_KEY = '$self';
|
|
55
60
|
const CONFIG_CANDIDATES = [
|
|
56
61
|
'.avanti.yml',
|
|
@@ -58,6 +63,14 @@ const CONFIG_CANDIDATES = [
|
|
|
58
63
|
'avanti.yml',
|
|
59
64
|
'avanti.yaml',
|
|
60
65
|
];
|
|
66
|
+
function isLocalFileSrc(src) {
|
|
67
|
+
if (typeof src === 'string') {
|
|
68
|
+
return !(0, local_1.isNonLocalSrcString)(src);
|
|
69
|
+
}
|
|
70
|
+
if (!('path' in src))
|
|
71
|
+
return false;
|
|
72
|
+
return !(0, local_1.isNonLocalSrcString)(src.path);
|
|
73
|
+
}
|
|
61
74
|
function isRemoteConfigSpec(s) {
|
|
62
75
|
return (s.startsWith('http://') ||
|
|
63
76
|
s.startsWith('https://') ||
|
|
@@ -65,6 +78,104 @@ function isRemoteConfigSpec(s) {
|
|
|
65
78
|
s.startsWith('github:') ||
|
|
66
79
|
s.startsWith('gitlab:'));
|
|
67
80
|
}
|
|
81
|
+
function deriveConfigBase(configPath) {
|
|
82
|
+
if (configPath.startsWith('http://') || configPath.startsWith('https://')) {
|
|
83
|
+
const url = new URL(configPath);
|
|
84
|
+
const lastSlash = url.pathname.lastIndexOf('/');
|
|
85
|
+
url.pathname = lastSlash >= 0 ? url.pathname.slice(0, lastSlash + 1) : '/';
|
|
86
|
+
return url.toString();
|
|
87
|
+
}
|
|
88
|
+
if (configPath.startsWith('github:')) {
|
|
89
|
+
const { repo, file, ref } = parseGitHubSpec(configPath);
|
|
90
|
+
const normalizedFile = file.replace(/\\/g, '/');
|
|
91
|
+
const dir = normalizedFile.includes('/')
|
|
92
|
+
? normalizedFile.slice(0, normalizedFile.lastIndexOf('/'))
|
|
93
|
+
: '';
|
|
94
|
+
const refSuffix = ref !== undefined ? `@${ref}` : '';
|
|
95
|
+
return `github:${repo}:${dir}${refSuffix}`;
|
|
96
|
+
}
|
|
97
|
+
if (configPath.startsWith('gitlab:')) {
|
|
98
|
+
const { project, file, ref } = parseGitLabSpec(configPath);
|
|
99
|
+
const normalizedFile = file.replace(/\\/g, '/');
|
|
100
|
+
const dir = normalizedFile.includes('/')
|
|
101
|
+
? normalizedFile.slice(0, normalizedFile.lastIndexOf('/'))
|
|
102
|
+
: '';
|
|
103
|
+
const refSuffix = ref !== undefined ? `@${ref}` : '';
|
|
104
|
+
return `gitlab:${project}:${dir}${refSuffix}`;
|
|
105
|
+
}
|
|
106
|
+
if ((0, git_1.isGitRemoteUrl)(configPath)) {
|
|
107
|
+
const { repo, file, ref } = (0, git_1.parseGitRemoteSpec)(configPath);
|
|
108
|
+
const normalizedFile = file.replace(/\\/g, '/');
|
|
109
|
+
const dir = path.posix.dirname(normalizedFile);
|
|
110
|
+
const dirPart = dir === '.' ? '' : dir;
|
|
111
|
+
const refSuffix = ref !== undefined ? `@${ref}` : '';
|
|
112
|
+
return `${repo}//${dirPart}${refSuffix}`;
|
|
113
|
+
}
|
|
114
|
+
return path.dirname(configPath);
|
|
115
|
+
}
|
|
116
|
+
function resolveRelativeSrc(src, configBase) {
|
|
117
|
+
if (src.startsWith('/') ||
|
|
118
|
+
path.isAbsolute(src) ||
|
|
119
|
+
src.startsWith('~/') ||
|
|
120
|
+
(0, local_1.isNonLocalSrcString)(src)) {
|
|
121
|
+
return src;
|
|
122
|
+
}
|
|
123
|
+
if (configBase.startsWith('http://') || configBase.startsWith('https://')) {
|
|
124
|
+
const normalizedSrc = src.replace(/\\/g, '/');
|
|
125
|
+
const resolvedUrl = new URL(normalizedSrc, configBase);
|
|
126
|
+
const baseUrl = new URL(configBase);
|
|
127
|
+
if (baseUrl.search) {
|
|
128
|
+
const baseParams = new URLSearchParams(baseUrl.search);
|
|
129
|
+
const resolvedParams = new URLSearchParams(resolvedUrl.search);
|
|
130
|
+
const overriddenKeys = new Set(resolvedParams.keys());
|
|
131
|
+
for (const [key, val] of baseParams.entries()) {
|
|
132
|
+
if (!overriddenKeys.has(key))
|
|
133
|
+
resolvedParams.append(key, val);
|
|
134
|
+
}
|
|
135
|
+
resolvedUrl.search = resolvedParams.toString();
|
|
136
|
+
}
|
|
137
|
+
return resolvedUrl.href;
|
|
138
|
+
}
|
|
139
|
+
if (configBase.startsWith('github:')) {
|
|
140
|
+
return resolveRelativeGitHostSpec(src, configBase, 'github');
|
|
141
|
+
}
|
|
142
|
+
if (configBase.startsWith('gitlab:')) {
|
|
143
|
+
return resolveRelativeGitHostSpec(src, configBase, 'gitlab');
|
|
144
|
+
}
|
|
145
|
+
if ((0, git_1.isGitRemoteUrl)(configBase)) {
|
|
146
|
+
return resolveRelativeGitRemoteSpec(src, configBase);
|
|
147
|
+
}
|
|
148
|
+
return path.resolve(configBase, src);
|
|
149
|
+
}
|
|
150
|
+
function resolveGitRelativePath(rest, src, configBase) {
|
|
151
|
+
const atIdx = rest.lastIndexOf('@');
|
|
152
|
+
const dir = atIdx === -1 ? rest : rest.slice(0, atIdx);
|
|
153
|
+
const ref = atIdx === -1 ? undefined : rest.slice(atIdx + 1);
|
|
154
|
+
const normalizedSrc = src.replace(/\\/g, '/');
|
|
155
|
+
const file = path.posix.join(dir, normalizedSrc);
|
|
156
|
+
if (file.startsWith('../') || file === '..') {
|
|
157
|
+
throw new Error(`Relative source path '${src}' escapes the repository root for config base '${configBase}'`);
|
|
158
|
+
}
|
|
159
|
+
const refSuffix = ref !== undefined ? `@${ref}` : '';
|
|
160
|
+
return { file, refSuffix };
|
|
161
|
+
}
|
|
162
|
+
function resolveRelativeGitHostSpec(src, configBase, scheme) {
|
|
163
|
+
const prefix = `${scheme}:`;
|
|
164
|
+
const body = configBase.slice(prefix.length);
|
|
165
|
+
const colonIdx = body.indexOf(':');
|
|
166
|
+
const id = colonIdx >= 0 ? body.slice(0, colonIdx) : body;
|
|
167
|
+
const rest = colonIdx >= 0 ? body.slice(colonIdx + 1) : '';
|
|
168
|
+
const { file, refSuffix } = resolveGitRelativePath(rest, src, configBase);
|
|
169
|
+
return `${prefix}${id}:${file}${refSuffix}`;
|
|
170
|
+
}
|
|
171
|
+
function resolveRelativeGitRemoteSpec(src, configBase) {
|
|
172
|
+
const schemeEnd = configBase.indexOf('://') + 3;
|
|
173
|
+
const separatorIdx = configBase.indexOf('//', schemeEnd);
|
|
174
|
+
const repo = separatorIdx >= 0 ? configBase.slice(0, separatorIdx) : configBase;
|
|
175
|
+
const rest = separatorIdx >= 0 ? configBase.slice(separatorIdx + 2) : '';
|
|
176
|
+
const { file, refSuffix } = resolveGitRelativePath(rest, src, configBase);
|
|
177
|
+
return `${repo}//${file}${refSuffix}`;
|
|
178
|
+
}
|
|
68
179
|
function normalizeConfigKey(spec) {
|
|
69
180
|
if (spec.startsWith('github:') || spec.startsWith('gitlab:')) {
|
|
70
181
|
const atIdx = spec.lastIndexOf('@');
|
|
@@ -260,16 +371,76 @@ function parseConfigContent(content) {
|
|
|
260
371
|
}
|
|
261
372
|
if (typeof e['backup'] === 'string')
|
|
262
373
|
fileEntry.backup = e['backup'];
|
|
263
|
-
if (
|
|
264
|
-
|
|
374
|
+
if (e['post'] !== undefined) {
|
|
375
|
+
throw new Error(`files["${target}"].post: removed — use on.write instead`);
|
|
376
|
+
}
|
|
377
|
+
if (e['on'] !== undefined) {
|
|
378
|
+
if (typeof e['on'] !== 'object' ||
|
|
379
|
+
e['on'] === null ||
|
|
380
|
+
Array.isArray(e['on']) ||
|
|
381
|
+
(Object.getPrototypeOf(e['on']) !== Object.prototype &&
|
|
382
|
+
Object.getPrototypeOf(e['on']) !== null)) {
|
|
383
|
+
throw new Error(`files["${target}"].on: must be a mapping`);
|
|
384
|
+
}
|
|
385
|
+
const validKeys = new Set([
|
|
386
|
+
'write',
|
|
387
|
+
'beforeWrite',
|
|
388
|
+
'beforeCreate',
|
|
389
|
+
'beforeUpdate',
|
|
390
|
+
'create',
|
|
391
|
+
'update',
|
|
392
|
+
]);
|
|
393
|
+
const onObj = e['on'];
|
|
394
|
+
const onHooks = Object.create(null);
|
|
395
|
+
for (const key of Object.keys(onObj)) {
|
|
396
|
+
if (!validKeys.has(key)) {
|
|
397
|
+
throw new Error(`files["${target}"].on: unknown key "${key}"`);
|
|
398
|
+
}
|
|
399
|
+
if (typeof onObj[key] !== 'string') {
|
|
400
|
+
throw new Error(`files["${target}"].on.${key}: must be a string`);
|
|
401
|
+
}
|
|
402
|
+
onHooks[key] = onObj[key];
|
|
403
|
+
}
|
|
404
|
+
fileEntry.on = onHooks;
|
|
405
|
+
}
|
|
265
406
|
if (typeof e['writeInPlace'] === 'boolean')
|
|
266
407
|
fileEntry.writeInPlace = e['writeInPlace'];
|
|
408
|
+
if (typeof e['followSymlink'] === 'boolean')
|
|
409
|
+
fileEntry.followSymlink = e['followSymlink'];
|
|
410
|
+
if (e['symlink'] !== undefined) {
|
|
411
|
+
if (e['symlink'] !== true &&
|
|
412
|
+
e['symlink'] !== 'absolute' &&
|
|
413
|
+
e['symlink'] !== 'relative') {
|
|
414
|
+
throw new Error(`files["${target}"].symlink: must be true, "absolute", or "relative"`);
|
|
415
|
+
}
|
|
416
|
+
fileEntry.symlink = e['symlink'];
|
|
417
|
+
}
|
|
418
|
+
if (e['sudo'] !== undefined) {
|
|
419
|
+
if (e['sudo'] === true) {
|
|
420
|
+
fileEntry.sudo = true;
|
|
421
|
+
}
|
|
422
|
+
else if (typeof e['sudo'] === 'string' && e['sudo'].trim()) {
|
|
423
|
+
if (e['sudo'].trim().startsWith('-')) {
|
|
424
|
+
throw new Error(`files["${target}"].sudo: username must not start with '-'`);
|
|
425
|
+
}
|
|
426
|
+
fileEntry.sudo = e['sudo'].trim();
|
|
427
|
+
}
|
|
428
|
+
else if (e['sudo'] !== false) {
|
|
429
|
+
throw new Error(`files["${target}"].sudo: must be true or a non-empty username string`);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
267
432
|
if (e['strategy'] !== undefined) {
|
|
268
433
|
if (e['strategy'] !== 'replace' && e['strategy'] !== 'insert') {
|
|
269
434
|
throw new Error(`files["${target}"].strategy: must be "replace" or "insert"`);
|
|
270
435
|
}
|
|
271
436
|
fileEntry.strategy = e['strategy'];
|
|
272
437
|
}
|
|
438
|
+
if (fileEntry.strategy === 'insert' && fileEntry.sudo) {
|
|
439
|
+
throw new Error(`files["${target}"]: strategy "insert" cannot be combined with sudo — ` +
|
|
440
|
+
`insert mode reads the existing file without privilege escalation, ` +
|
|
441
|
+
`which silently treats an unreadable privileged file as absent. ` +
|
|
442
|
+
`Use a non-insert strategy, or manage the file without sudo.`);
|
|
443
|
+
}
|
|
273
444
|
if (e['replace'] !== undefined) {
|
|
274
445
|
if (!Array.isArray(e['replace'])) {
|
|
275
446
|
throw new Error(`files["${target}"]: "replace" must be an array`);
|
|
@@ -303,6 +474,15 @@ function parseConfigContent(content) {
|
|
|
303
474
|
fileEntry.toml = parseTomlMergeOptions(rawToml, `files["${target}"]`);
|
|
304
475
|
}
|
|
305
476
|
}
|
|
477
|
+
if (e['ini'] !== undefined) {
|
|
478
|
+
const rawIni = e['ini'];
|
|
479
|
+
if (rawIni === true || rawIni === false) {
|
|
480
|
+
fileEntry.ini = rawIni;
|
|
481
|
+
}
|
|
482
|
+
else {
|
|
483
|
+
fileEntry.ini = parseIniMergeOptions(rawIni, `files["${target}"]`);
|
|
484
|
+
}
|
|
485
|
+
}
|
|
306
486
|
if (e['template'] !== undefined) {
|
|
307
487
|
const rawTemplate = e['template'];
|
|
308
488
|
if (rawTemplate === true) {
|
|
@@ -316,6 +496,106 @@ function parseConfigContent(content) {
|
|
|
316
496
|
fileEntry.template = rawTemplate;
|
|
317
497
|
}
|
|
318
498
|
}
|
|
499
|
+
if (e['extract'] !== undefined) {
|
|
500
|
+
if (fileEntry.symlink) {
|
|
501
|
+
throw new Error(`files["${target}"].symlink: cannot be combined with extract:`);
|
|
502
|
+
}
|
|
503
|
+
if (Array.isArray(src)) {
|
|
504
|
+
throw new Error(`files["${target}"].extract: cannot be used with a list of sources`);
|
|
505
|
+
}
|
|
506
|
+
if (!target.endsWith('/') && !target.endsWith(path.sep)) {
|
|
507
|
+
throw new Error(`files["${target}"].extract: target must be a directory (end with "/") — archive extraction writes multiple files`);
|
|
508
|
+
}
|
|
509
|
+
const rawExtract = e['extract'];
|
|
510
|
+
if (rawExtract === true) {
|
|
511
|
+
fileEntry.extract = true;
|
|
512
|
+
}
|
|
513
|
+
else if (Array.isArray(rawExtract)) {
|
|
514
|
+
if (rawExtract.length === 0) {
|
|
515
|
+
throw new Error(`files["${target}"].extract: must not be an empty array`);
|
|
516
|
+
}
|
|
517
|
+
fileEntry.extract = rawExtract.map((pat, i) => {
|
|
518
|
+
if (typeof pat !== 'string' || !pat) {
|
|
519
|
+
throw new Error(`files["${target}"].extract[${i}]: must be a non-empty string`);
|
|
520
|
+
}
|
|
521
|
+
if (pat.length > 2 && pat.startsWith('/') && pat.endsWith('/')) {
|
|
522
|
+
try {
|
|
523
|
+
new RegExp(pat.slice(1, -1));
|
|
524
|
+
}
|
|
525
|
+
catch (err) {
|
|
526
|
+
throw new Error(`files["${target}"].extract[${i}]: invalid regex`, { cause: err });
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
return pat;
|
|
530
|
+
});
|
|
531
|
+
}
|
|
532
|
+
else {
|
|
533
|
+
throw new Error(`files["${target}"].extract: must be true or a non-empty array of patterns`);
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
if (fileEntry.symlink) {
|
|
537
|
+
if (target === exports.SELF_KEY) {
|
|
538
|
+
throw new Error(`files["${exports.SELF_KEY}"].symlink: $self cannot be a symlink entry`);
|
|
539
|
+
}
|
|
540
|
+
if (target.endsWith('/') || target.endsWith(path.sep)) {
|
|
541
|
+
throw new Error(`files["${target}"].symlink: target must be a single file path, not a directory pattern (remove the trailing slash)`);
|
|
542
|
+
}
|
|
543
|
+
if (Array.isArray(fileEntry.src)) {
|
|
544
|
+
throw new Error(`files["${target}"].symlink: cannot be combined with a list of sources`);
|
|
545
|
+
}
|
|
546
|
+
if (!isLocalFileSrc(fileEntry.src)) {
|
|
547
|
+
throw new Error(`files["${target}"].symlink: src must be a local path — ` +
|
|
548
|
+
`http, exec, github, gitlab, and other remote sources are not supported`);
|
|
549
|
+
}
|
|
550
|
+
if (typeof fileEntry.src !== 'string' && 'path' in fileEntry.src) {
|
|
551
|
+
const localSrc = fileEntry.src;
|
|
552
|
+
if (localSrc.sha) {
|
|
553
|
+
throw new Error(`files["${target}"].symlink: cannot be combined with src.sha`);
|
|
554
|
+
}
|
|
555
|
+
if (localSrc.filter) {
|
|
556
|
+
throw new Error(`files["${target}"].symlink: cannot be combined with src.filter`);
|
|
557
|
+
}
|
|
558
|
+
if (localSrc.if) {
|
|
559
|
+
throw new Error(`files["${target}"].symlink: cannot be combined with src.if`);
|
|
560
|
+
}
|
|
561
|
+
if (localSrc.ifAny) {
|
|
562
|
+
throw new Error(`files["${target}"].symlink: cannot be combined with src.ifAny`);
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
if (fileEntry.replace) {
|
|
566
|
+
throw new Error(`files["${target}"].symlink: cannot be combined with replace:`);
|
|
567
|
+
}
|
|
568
|
+
if (fileEntry.template) {
|
|
569
|
+
throw new Error(`files["${target}"].symlink: cannot be combined with template:`);
|
|
570
|
+
}
|
|
571
|
+
if (fileEntry.json) {
|
|
572
|
+
throw new Error(`files["${target}"].symlink: cannot be combined with json:`);
|
|
573
|
+
}
|
|
574
|
+
if (fileEntry.yaml) {
|
|
575
|
+
throw new Error(`files["${target}"].symlink: cannot be combined with yaml:`);
|
|
576
|
+
}
|
|
577
|
+
if (fileEntry.toml) {
|
|
578
|
+
throw new Error(`files["${target}"].symlink: cannot be combined with toml:`);
|
|
579
|
+
}
|
|
580
|
+
if (fileEntry.ini) {
|
|
581
|
+
throw new Error(`files["${target}"].symlink: cannot be combined with ini:`);
|
|
582
|
+
}
|
|
583
|
+
if (fileEntry.on?.write) {
|
|
584
|
+
throw new Error(`files["${target}"].symlink: cannot be combined with on.write:`);
|
|
585
|
+
}
|
|
586
|
+
if (fileEntry.writeInPlace) {
|
|
587
|
+
throw new Error(`files["${target}"].symlink: cannot be combined with writeInPlace:`);
|
|
588
|
+
}
|
|
589
|
+
if (fileEntry.strategy) {
|
|
590
|
+
throw new Error(`files["${target}"].symlink: cannot be combined with strategy:`);
|
|
591
|
+
}
|
|
592
|
+
if (fileEntry.followSymlink) {
|
|
593
|
+
throw new Error(`files["${target}"].symlink: cannot be combined with followSymlink:`);
|
|
594
|
+
}
|
|
595
|
+
if (fileEntry.mode) {
|
|
596
|
+
throw new Error(`files["${target}"].symlink: cannot be combined with mode — symlinks do not have independent permission bits on POSIX`);
|
|
597
|
+
}
|
|
598
|
+
}
|
|
319
599
|
let expandedTargets;
|
|
320
600
|
try {
|
|
321
601
|
expandedTargets = (0, paths_1.expandBraces)(target);
|
|
@@ -336,6 +616,11 @@ function parseConfigContent(content) {
|
|
|
336
616
|
const suffix = parts.length > 0 ? ` (${parts.join('; ')})` : '';
|
|
337
617
|
throw new Error(`files["${expandedTarget}"]: duplicate target${suffix}`);
|
|
338
618
|
}
|
|
619
|
+
if (fileEntry.symlink &&
|
|
620
|
+
expandedTarget !== target &&
|
|
621
|
+
(expandedTarget.endsWith('/') || expandedTarget.endsWith(path.sep))) {
|
|
622
|
+
throw new Error(`files["${expandedTarget}"] (expanded from "${target}").symlink: target must be a single file path, not a directory pattern (remove the trailing slash)`);
|
|
623
|
+
}
|
|
339
624
|
files[expandedTarget] = { ...fileEntry, target: expandedTarget };
|
|
340
625
|
if (expandedTarget !== target) {
|
|
341
626
|
fileOrigins[expandedTarget] = target;
|
|
@@ -460,6 +745,15 @@ function parseVariableEntry(obj, varName) {
|
|
|
460
745
|
entry.toml = parseTomlMergeOptions(rawToml, loc);
|
|
461
746
|
}
|
|
462
747
|
}
|
|
748
|
+
if (obj['ini'] !== undefined) {
|
|
749
|
+
const rawIni = obj['ini'];
|
|
750
|
+
if (rawIni === true || rawIni === false) {
|
|
751
|
+
entry.ini = rawIni;
|
|
752
|
+
}
|
|
753
|
+
else {
|
|
754
|
+
entry.ini = parseIniMergeOptions(rawIni, loc);
|
|
755
|
+
}
|
|
756
|
+
}
|
|
463
757
|
if (obj['template'] !== undefined) {
|
|
464
758
|
const rawTemplate = obj['template'];
|
|
465
759
|
if (rawTemplate === true) {
|
|
@@ -519,7 +813,13 @@ function parseVia(value, loc) {
|
|
|
519
813
|
}
|
|
520
814
|
throw new Error(`${loc}.via: must be a string or array`);
|
|
521
815
|
}
|
|
522
|
-
const VALID_OS_PLATFORMS = [
|
|
816
|
+
const VALID_OS_PLATFORMS = [
|
|
817
|
+
'linux',
|
|
818
|
+
'mac',
|
|
819
|
+
'windows',
|
|
820
|
+
'darwin',
|
|
821
|
+
'win32',
|
|
822
|
+
];
|
|
523
823
|
function parseCondition(raw, loc) {
|
|
524
824
|
if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {
|
|
525
825
|
throw new Error(`${loc}: must be an object`);
|
|
@@ -600,6 +900,30 @@ function parseConditionField(obj, loc) {
|
|
|
600
900
|
}
|
|
601
901
|
return result;
|
|
602
902
|
}
|
|
903
|
+
function parseFilter(obj, loc) {
|
|
904
|
+
if (obj['filter'] === undefined)
|
|
905
|
+
return undefined;
|
|
906
|
+
if (!Array.isArray(obj['filter'])) {
|
|
907
|
+
throw new Error(`${loc}.filter: must be an array`);
|
|
908
|
+
}
|
|
909
|
+
if (obj['filter'].length === 0) {
|
|
910
|
+
throw new Error(`${loc}.filter: must not be an empty array`);
|
|
911
|
+
}
|
|
912
|
+
return obj['filter'].map((entry, i) => {
|
|
913
|
+
if (typeof entry !== 'string' || !entry) {
|
|
914
|
+
throw new Error(`${loc}.filter[${i}]: must be a non-empty string`);
|
|
915
|
+
}
|
|
916
|
+
if (entry.length > 2 && entry.startsWith('/') && entry.endsWith('/')) {
|
|
917
|
+
try {
|
|
918
|
+
new RegExp(entry.slice(1, -1));
|
|
919
|
+
}
|
|
920
|
+
catch (err) {
|
|
921
|
+
throw new Error(`${loc}.filter[${i}]: invalid regex: ${err instanceof Error ? err.message : String(err)}`, { cause: err });
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
return entry;
|
|
925
|
+
});
|
|
926
|
+
}
|
|
603
927
|
function parseSingleSrc(raw, loc) {
|
|
604
928
|
// Plain string → http/https URL or local path
|
|
605
929
|
if (typeof raw === 'string') {
|
|
@@ -620,6 +944,9 @@ function parseSingleSrc(raw, loc) {
|
|
|
620
944
|
}
|
|
621
945
|
result.optional = obj['optional'];
|
|
622
946
|
}
|
|
947
|
+
const pathFilter = parseFilter(obj, loc);
|
|
948
|
+
if (pathFilter !== undefined)
|
|
949
|
+
result.filter = pathFilter;
|
|
623
950
|
const pathSha = parseSha(obj['sha'], loc);
|
|
624
951
|
if (pathSha !== undefined)
|
|
625
952
|
result.sha = pathSha;
|
|
@@ -735,6 +1062,7 @@ function parseSingleSrc(raw, loc) {
|
|
|
735
1062
|
throw new Error(`${loc}.gitlab.host: must be a non-empty string`);
|
|
736
1063
|
}
|
|
737
1064
|
const gitlabConds = parseConditionField(obj, loc);
|
|
1065
|
+
const gitlabFilter = parseFilter(obj, loc);
|
|
738
1066
|
if (hasRelease) {
|
|
739
1067
|
return {
|
|
740
1068
|
gitlab: {
|
|
@@ -744,6 +1072,7 @@ function parseSingleSrc(raw, loc) {
|
|
|
744
1072
|
host: typeof g['host'] === 'string' ? g['host'] : undefined,
|
|
745
1073
|
via: parseVia(g['via'], `${loc}.gitlab`),
|
|
746
1074
|
},
|
|
1075
|
+
...(gitlabFilter !== undefined ? { filter: gitlabFilter } : {}),
|
|
747
1076
|
...gitlabConds,
|
|
748
1077
|
};
|
|
749
1078
|
}
|
|
@@ -756,6 +1085,7 @@ function parseSingleSrc(raw, loc) {
|
|
|
756
1085
|
host: typeof g['host'] === 'string' ? g['host'] : undefined,
|
|
757
1086
|
via: parseVia(g['via'], `${loc}.gitlab`),
|
|
758
1087
|
},
|
|
1088
|
+
...(gitlabFilter !== undefined ? { filter: gitlabFilter } : {}),
|
|
759
1089
|
...gitlabConds,
|
|
760
1090
|
};
|
|
761
1091
|
}
|
|
@@ -799,6 +1129,7 @@ function parseSingleSrc(raw, loc) {
|
|
|
799
1129
|
throw new Error(`${loc}.github.host: must be a non-empty string`);
|
|
800
1130
|
}
|
|
801
1131
|
const githubConds = parseConditionField(obj, loc);
|
|
1132
|
+
const githubFilter = parseFilter(obj, loc);
|
|
802
1133
|
if (hasRelease) {
|
|
803
1134
|
return {
|
|
804
1135
|
github: {
|
|
@@ -808,6 +1139,7 @@ function parseSingleSrc(raw, loc) {
|
|
|
808
1139
|
host: typeof g['host'] === 'string' ? g['host'] : undefined,
|
|
809
1140
|
via: parseVia(g['via'], `${loc}.github`),
|
|
810
1141
|
},
|
|
1142
|
+
...(githubFilter !== undefined ? { filter: githubFilter } : {}),
|
|
811
1143
|
...githubConds,
|
|
812
1144
|
};
|
|
813
1145
|
}
|
|
@@ -820,6 +1152,7 @@ function parseSingleSrc(raw, loc) {
|
|
|
820
1152
|
host: typeof g['host'] === 'string' ? g['host'] : undefined,
|
|
821
1153
|
via: parseVia(g['via'], `${loc}.github`),
|
|
822
1154
|
},
|
|
1155
|
+
...(githubFilter !== undefined ? { filter: githubFilter } : {}),
|
|
823
1156
|
...githubConds,
|
|
824
1157
|
};
|
|
825
1158
|
}
|
|
@@ -843,6 +1176,7 @@ function parseSingleSrc(raw, loc) {
|
|
|
843
1176
|
throw new Error(`${loc}.bitbucket.host: must be a non-empty string`);
|
|
844
1177
|
}
|
|
845
1178
|
const bitbucketConds = parseConditionField(obj, loc);
|
|
1179
|
+
const bitbucketFilter = parseFilter(obj, loc);
|
|
846
1180
|
return {
|
|
847
1181
|
bitbucket: {
|
|
848
1182
|
workspace: b['workspace'],
|
|
@@ -852,6 +1186,7 @@ function parseSingleSrc(raw, loc) {
|
|
|
852
1186
|
sha: parseSha(b['sha'], `${loc}.bitbucket`),
|
|
853
1187
|
host: typeof b['host'] === 'string' ? b['host'] : undefined,
|
|
854
1188
|
},
|
|
1189
|
+
...(bitbucketFilter !== undefined ? { filter: bitbucketFilter } : {}),
|
|
855
1190
|
...bitbucketConds,
|
|
856
1191
|
};
|
|
857
1192
|
}
|
|
@@ -868,6 +1203,7 @@ function parseSingleSrc(raw, loc) {
|
|
|
868
1203
|
throw new Error(`${loc}.git.file: required string`);
|
|
869
1204
|
}
|
|
870
1205
|
const gitConds = parseConditionField(obj, loc);
|
|
1206
|
+
const gitFilter = parseFilter(obj, loc);
|
|
871
1207
|
return {
|
|
872
1208
|
git: {
|
|
873
1209
|
repo: gt['repo'],
|
|
@@ -875,6 +1211,7 @@ function parseSingleSrc(raw, loc) {
|
|
|
875
1211
|
ref: typeof gt['ref'] === 'string' ? gt['ref'] : undefined,
|
|
876
1212
|
sha: parseSha(gt['sha'], `${loc}.git`),
|
|
877
1213
|
},
|
|
1214
|
+
...(gitFilter !== undefined ? { filter: gitFilter } : {}),
|
|
878
1215
|
...gitConds,
|
|
879
1216
|
};
|
|
880
1217
|
}
|
|
@@ -883,6 +1220,9 @@ function parseSingleSrc(raw, loc) {
|
|
|
883
1220
|
throw new Error(`${loc}.aws_s3: must be a non-empty string (s3:// URI)`);
|
|
884
1221
|
}
|
|
885
1222
|
const result = { aws_s3: obj['aws_s3'] };
|
|
1223
|
+
const s3Filter = parseFilter(obj, loc);
|
|
1224
|
+
if (s3Filter !== undefined)
|
|
1225
|
+
result.filter = s3Filter;
|
|
886
1226
|
const s3Sha = parseSha(obj['sha'], loc);
|
|
887
1227
|
if (s3Sha !== undefined)
|
|
888
1228
|
result.sha = s3Sha;
|
|
@@ -1030,6 +1370,9 @@ function parseYamlMergeOptions(raw, loc) {
|
|
|
1030
1370
|
function parseTomlMergeOptions(raw, loc) {
|
|
1031
1371
|
return parseMergeOptions(raw, loc, 'toml', ['abort', 'first_wins', 'last_wins'], ['replace', 'concat', 'dedupe'], ['replace', 'merge']);
|
|
1032
1372
|
}
|
|
1373
|
+
function parseIniMergeOptions(raw, loc) {
|
|
1374
|
+
return parseMergeOptions(raw, loc, 'ini', ['abort', 'first_wins', 'last_wins'], ['replace', 'concat', 'dedupe'], ['replace', 'merge']);
|
|
1375
|
+
}
|
|
1033
1376
|
function parseReplaceRule(r, target, j) {
|
|
1034
1377
|
if (!r || typeof r !== 'object' || Array.isArray(r)) {
|
|
1035
1378
|
throw new Error(`files["${target}"].replace[${j}]: must be an object`);
|