@udondan/avanti 0.1.0 → 0.2.1
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/CHANGELOG.md +15 -0
- package/dist/config.d.ts +1 -1
- package/dist/config.js +51 -51
- package/dist/diff.js +11 -11
- package/dist/processors/post.js +4 -4
- package/dist/processors/replace.d.ts +1 -1
- package/dist/processors/replace.js +1 -1
- package/dist/sources/exec.js +3 -3
- package/dist/sources/github.js +15 -15
- package/dist/sources/gitlab.js +11 -11
- package/dist/sources/http.js +5 -5
- package/dist/sources/index.d.ts +1 -1
- package/dist/sources/index.js +15 -15
- package/dist/sources/local.js +4 -4
- package/dist/writer.js +3 -3
- package/package.json +19 -7
- package/.claude/settings.local.json +0 -9
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [0.2.1](https://github.com/udondan/avanti/compare/v0.2.0...v0.2.1) (2026-05-07)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* add repository url to package.json ([#5](https://github.com/udondan/avanti/issues/5)) ([842c843](https://github.com/udondan/avanti/commit/842c843f433243b1ef131f0e9192e10fb7816e98))
|
|
9
|
+
|
|
10
|
+
## [0.2.0](https://github.com/udondan/avanti/compare/v0.1.0...v0.2.0) (2026-05-07)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Features
|
|
14
|
+
|
|
15
|
+
* initial implementation ([#1](https://github.com/udondan/avanti/issues/1)) ([a97bbfa](https://github.com/udondan/avanti/commit/a97bbfa0fb07b9bb0dc6518bf8345aaf789bf09f))
|
package/dist/config.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { FileFerryConfig } from
|
|
1
|
+
import { FileFerryConfig } from './types';
|
|
2
2
|
export declare function resolveConfigPath(explicit?: string): string;
|
|
3
3
|
export declare function loadConfig(configPath: string): FileFerryConfig;
|
|
4
4
|
//# sourceMappingURL=config.d.ts.map
|
package/dist/config.js
CHANGED
|
@@ -28,10 +28,10 @@ const fs = __importStar(require("fs"));
|
|
|
28
28
|
const path = __importStar(require("path"));
|
|
29
29
|
const yaml = __importStar(require("js-yaml"));
|
|
30
30
|
const CONFIG_CANDIDATES = [
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
31
|
+
'.avanti.yml',
|
|
32
|
+
'.avanti.yaml',
|
|
33
|
+
'avanti.yml',
|
|
34
|
+
'avanti.yaml',
|
|
35
35
|
];
|
|
36
36
|
function resolveConfigPath(explicit) {
|
|
37
37
|
if (explicit)
|
|
@@ -53,51 +53,51 @@ function loadConfig(configPath) {
|
|
|
53
53
|
}
|
|
54
54
|
let raw;
|
|
55
55
|
try {
|
|
56
|
-
const content = fs.readFileSync(configPath,
|
|
56
|
+
const content = fs.readFileSync(configPath, 'utf8');
|
|
57
57
|
raw = yaml.load(content);
|
|
58
58
|
}
|
|
59
59
|
catch (err) {
|
|
60
60
|
const msg = err instanceof Error ? err.message : String(err);
|
|
61
61
|
throw new Error(`Failed to parse config file: ${msg}`, { cause: err });
|
|
62
62
|
}
|
|
63
|
-
if (!raw || typeof raw !==
|
|
64
|
-
throw new Error(
|
|
63
|
+
if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {
|
|
64
|
+
throw new Error('Config must be a YAML object');
|
|
65
65
|
}
|
|
66
66
|
const obj = raw;
|
|
67
|
-
if (!Array.isArray(obj[
|
|
67
|
+
if (!Array.isArray(obj['files'])) {
|
|
68
68
|
throw new Error('Config must have a "files" array');
|
|
69
69
|
}
|
|
70
|
-
const files = obj[
|
|
71
|
-
if (!entry || typeof entry !==
|
|
70
|
+
const files = obj['files'].map((entry, i) => {
|
|
71
|
+
if (!entry || typeof entry !== 'object' || Array.isArray(entry)) {
|
|
72
72
|
throw new Error(`files[${i}]: must be an object`);
|
|
73
73
|
}
|
|
74
74
|
const e = entry;
|
|
75
|
-
if (e[
|
|
75
|
+
if (e['src'] === undefined || e['src'] === null) {
|
|
76
76
|
throw new Error(`files[${i}]: "src" is required`);
|
|
77
77
|
}
|
|
78
|
-
const src = Array.isArray(e[
|
|
79
|
-
? e[
|
|
80
|
-
: parseSingleSrc(e[
|
|
78
|
+
const src = Array.isArray(e['src'])
|
|
79
|
+
? e['src'].map((item, j) => parseSingleSrc(item, i, j))
|
|
80
|
+
: parseSingleSrc(e['src'], i, undefined);
|
|
81
81
|
// list src must have an explicit target
|
|
82
|
-
if (Array.isArray(src) && !e[
|
|
82
|
+
if (Array.isArray(src) && !e['target']) {
|
|
83
83
|
throw new Error(`files[${i}]: "target" is required when "src" is a list`);
|
|
84
84
|
}
|
|
85
85
|
// exec sources must have a target
|
|
86
|
-
if (!Array.isArray(src) && isExecSrc(src) && !e[
|
|
86
|
+
if (!Array.isArray(src) && isExecSrc(src) && !e['target']) {
|
|
87
87
|
throw new Error(`files[${i}]: "target" is required for exec sources`);
|
|
88
88
|
}
|
|
89
89
|
const fileEntry = { src };
|
|
90
|
-
if (typeof e[
|
|
91
|
-
fileEntry.target = e[
|
|
92
|
-
if (typeof e[
|
|
93
|
-
fileEntry.mode = e[
|
|
94
|
-
if (typeof e[
|
|
95
|
-
fileEntry.post = e[
|
|
96
|
-
if (e[
|
|
97
|
-
if (!Array.isArray(e[
|
|
90
|
+
if (typeof e['target'] === 'string')
|
|
91
|
+
fileEntry.target = e['target'];
|
|
92
|
+
if (typeof e['mode'] === 'string')
|
|
93
|
+
fileEntry.mode = e['mode'];
|
|
94
|
+
if (typeof e['post'] === 'string')
|
|
95
|
+
fileEntry.post = e['post'];
|
|
96
|
+
if (e['replace'] !== undefined) {
|
|
97
|
+
if (!Array.isArray(e['replace'])) {
|
|
98
98
|
throw new Error(`files[${i}]: "replace" must be an array`);
|
|
99
99
|
}
|
|
100
|
-
fileEntry.replace = e[
|
|
100
|
+
fileEntry.replace = e['replace'].map((r, j) => parseReplaceRule(r, i, j));
|
|
101
101
|
}
|
|
102
102
|
return fileEntry;
|
|
103
103
|
});
|
|
@@ -107,75 +107,75 @@ exports.loadConfig = loadConfig;
|
|
|
107
107
|
function parseSingleSrc(raw, i, j) {
|
|
108
108
|
const loc = j !== undefined ? `files[${i}].src[${j}]` : `files[${i}].src`;
|
|
109
109
|
// Plain string → http/https URL or local path
|
|
110
|
-
if (typeof raw ===
|
|
110
|
+
if (typeof raw === 'string') {
|
|
111
111
|
return raw;
|
|
112
112
|
}
|
|
113
|
-
if (!raw || typeof raw !==
|
|
113
|
+
if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {
|
|
114
114
|
throw new Error(`${loc}: must be a string or a map with one of: exec, gitlab, github`);
|
|
115
115
|
}
|
|
116
116
|
const obj = raw;
|
|
117
|
-
if (
|
|
118
|
-
if (typeof obj[
|
|
117
|
+
if ('exec' in obj) {
|
|
118
|
+
if (typeof obj['exec'] !== 'string' || !obj['exec']) {
|
|
119
119
|
throw new Error(`${loc}.exec: must be a non-empty string`);
|
|
120
120
|
}
|
|
121
|
-
return { exec: obj[
|
|
121
|
+
return { exec: obj['exec'] };
|
|
122
122
|
}
|
|
123
|
-
if (
|
|
124
|
-
const gl = obj[
|
|
125
|
-
if (!gl || typeof gl !==
|
|
123
|
+
if ('gitlab' in obj) {
|
|
124
|
+
const gl = obj['gitlab'];
|
|
125
|
+
if (!gl || typeof gl !== 'object' || Array.isArray(gl)) {
|
|
126
126
|
throw new Error(`${loc}.gitlab: must be an object`);
|
|
127
127
|
}
|
|
128
128
|
const g = gl;
|
|
129
|
-
if (typeof g[
|
|
129
|
+
if (typeof g['project'] !== 'string' || !g['project']) {
|
|
130
130
|
throw new Error(`${loc}.gitlab.project: required string`);
|
|
131
131
|
}
|
|
132
|
-
if (typeof g[
|
|
132
|
+
if (typeof g['file'] !== 'string' || !g['file']) {
|
|
133
133
|
throw new Error(`${loc}.gitlab.file: required string`);
|
|
134
134
|
}
|
|
135
135
|
return {
|
|
136
136
|
gitlab: {
|
|
137
|
-
project: g[
|
|
138
|
-
file: g[
|
|
139
|
-
ref: typeof g[
|
|
137
|
+
project: g['project'],
|
|
138
|
+
file: g['file'],
|
|
139
|
+
ref: typeof g['ref'] === 'string' ? g['ref'] : undefined,
|
|
140
140
|
},
|
|
141
141
|
};
|
|
142
142
|
}
|
|
143
|
-
if (
|
|
144
|
-
const gh = obj[
|
|
145
|
-
if (!gh || typeof gh !==
|
|
143
|
+
if ('github' in obj) {
|
|
144
|
+
const gh = obj['github'];
|
|
145
|
+
if (!gh || typeof gh !== 'object' || Array.isArray(gh)) {
|
|
146
146
|
throw new Error(`${loc}.github: must be an object`);
|
|
147
147
|
}
|
|
148
148
|
const g = gh;
|
|
149
|
-
if (typeof g[
|
|
149
|
+
if (typeof g['repo'] !== 'string' || !g['repo']) {
|
|
150
150
|
throw new Error(`${loc}.github.repo: required string`);
|
|
151
151
|
}
|
|
152
|
-
if (typeof g[
|
|
152
|
+
if (typeof g['file'] !== 'string' || !g['file']) {
|
|
153
153
|
throw new Error(`${loc}.github.file: required string`);
|
|
154
154
|
}
|
|
155
155
|
return {
|
|
156
156
|
github: {
|
|
157
|
-
repo: g[
|
|
158
|
-
file: g[
|
|
159
|
-
ref: typeof g[
|
|
157
|
+
repo: g['repo'],
|
|
158
|
+
file: g['file'],
|
|
159
|
+
ref: typeof g['ref'] === 'string' ? g['ref'] : undefined,
|
|
160
160
|
},
|
|
161
161
|
};
|
|
162
162
|
}
|
|
163
163
|
throw new Error(`${loc}: unknown source type. Must be a string or map with exec/gitlab/github`);
|
|
164
164
|
}
|
|
165
165
|
function isExecSrc(src) {
|
|
166
|
-
return typeof src ===
|
|
166
|
+
return typeof src === 'object' && 'exec' in src;
|
|
167
167
|
}
|
|
168
168
|
function parseReplaceRule(r, i, j) {
|
|
169
|
-
if (!r || typeof r !==
|
|
169
|
+
if (!r || typeof r !== 'object' || Array.isArray(r)) {
|
|
170
170
|
throw new Error(`files[${i}].replace[${j}]: must be an object`);
|
|
171
171
|
}
|
|
172
172
|
const rule = r;
|
|
173
|
-
if (typeof rule[
|
|
173
|
+
if (typeof rule['from'] !== 'string') {
|
|
174
174
|
throw new Error(`files[${i}].replace[${j}]: "from" must be a string`);
|
|
175
175
|
}
|
|
176
|
-
if (typeof rule[
|
|
176
|
+
if (typeof rule['to'] !== 'string') {
|
|
177
177
|
throw new Error(`files[${i}].replace[${j}]: "to" must be a string`);
|
|
178
178
|
}
|
|
179
|
-
return { from: rule[
|
|
179
|
+
return { from: rule['from'], to: rule['to'] };
|
|
180
180
|
}
|
|
181
181
|
//# sourceMappingURL=config.js.map
|
package/dist/diff.js
CHANGED
|
@@ -33,34 +33,34 @@ const diff_1 = require("diff");
|
|
|
33
33
|
const chalk_1 = __importDefault(require("chalk"));
|
|
34
34
|
function computeDiff(targetPath, newContent) {
|
|
35
35
|
const isNew = !fs.existsSync(targetPath);
|
|
36
|
-
const oldContent = isNew ?
|
|
36
|
+
const oldContent = isNew ? '' : fs.readFileSync(targetPath, 'utf8');
|
|
37
37
|
const hasChanges = oldContent !== newContent;
|
|
38
|
-
const patch = (0, diff_1.createTwoFilesPatch)(isNew ?
|
|
38
|
+
const patch = (0, diff_1.createTwoFilesPatch)(isNew ? '/dev/null' : targetPath, targetPath, oldContent, newContent, isNew ? '' : undefined, isNew ? 'new file' : undefined);
|
|
39
39
|
return { targetPath, isNew, hasChanges, patch };
|
|
40
40
|
}
|
|
41
41
|
exports.computeDiff = computeDiff;
|
|
42
42
|
function formatDiff(diff) {
|
|
43
43
|
if (!diff.hasChanges)
|
|
44
|
-
return
|
|
45
|
-
const lines = diff.patch.split(
|
|
44
|
+
return '';
|
|
45
|
+
const lines = diff.patch.split('\n');
|
|
46
46
|
const colored = lines.map((line) => {
|
|
47
|
-
if (line.startsWith(
|
|
47
|
+
if (line.startsWith('+++') || line.startsWith('---'))
|
|
48
48
|
return chalk_1.default.bold(line);
|
|
49
|
-
if (line.startsWith(
|
|
49
|
+
if (line.startsWith('@@'))
|
|
50
50
|
return chalk_1.default.cyan(line);
|
|
51
|
-
if (line.startsWith(
|
|
51
|
+
if (line.startsWith('+'))
|
|
52
52
|
return chalk_1.default.green(line);
|
|
53
|
-
if (line.startsWith(
|
|
53
|
+
if (line.startsWith('-'))
|
|
54
54
|
return chalk_1.default.red(line);
|
|
55
55
|
return line;
|
|
56
56
|
});
|
|
57
|
-
return colored.join(
|
|
57
|
+
return colored.join('\n');
|
|
58
58
|
}
|
|
59
59
|
exports.formatDiff = formatDiff;
|
|
60
60
|
function printDiffs(diffs) {
|
|
61
61
|
const changed = diffs.filter((d) => d.hasChanges);
|
|
62
62
|
if (!changed.length) {
|
|
63
|
-
console.log(
|
|
63
|
+
console.log('No changes.');
|
|
64
64
|
return;
|
|
65
65
|
}
|
|
66
66
|
for (const d of changed) {
|
|
@@ -71,7 +71,7 @@ exports.printDiffs = printDiffs;
|
|
|
71
71
|
function resolveTargetPath(entry, relPath, baseDir) {
|
|
72
72
|
if (entry.target) {
|
|
73
73
|
// If target looks like a directory (ends with /) or the relPath has subdirs, join them
|
|
74
|
-
if (entry.target.endsWith(
|
|
74
|
+
if (entry.target.endsWith('/') || entry.target.endsWith(path.sep)) {
|
|
75
75
|
return path.resolve(baseDir, entry.target, relPath);
|
|
76
76
|
}
|
|
77
77
|
// Single file: use target directly
|
package/dist/processors/post.js
CHANGED
|
@@ -3,13 +3,13 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.applyPost = void 0;
|
|
4
4
|
const child_process_1 = require("child_process");
|
|
5
5
|
function applyPost(content, script) {
|
|
6
|
-
const result = (0, child_process_1.spawnSync)(
|
|
6
|
+
const result = (0, child_process_1.spawnSync)('sh', ['-c', script], {
|
|
7
7
|
input: content,
|
|
8
|
-
encoding:
|
|
8
|
+
encoding: 'utf8',
|
|
9
9
|
});
|
|
10
10
|
if (result.status !== null && result.status !== 0) {
|
|
11
|
-
const stderr = result.stderr?.trim() ??
|
|
12
|
-
throw new Error(`post script exited with code ${result.status}${stderr ?
|
|
11
|
+
const stderr = result.stderr?.trim() ?? '';
|
|
12
|
+
throw new Error(`post script exited with code ${result.status}${stderr ? ': ' + stderr : ''}`);
|
|
13
13
|
}
|
|
14
14
|
if (result.error) {
|
|
15
15
|
throw new Error(`post script failed to spawn: ${result.error.message}`);
|
|
@@ -12,7 +12,7 @@ function applyReplace(content, rules) {
|
|
|
12
12
|
let result = content;
|
|
13
13
|
for (const rule of rules) {
|
|
14
14
|
const pattern = parseFrom(rule.from);
|
|
15
|
-
if (typeof pattern ===
|
|
15
|
+
if (typeof pattern === 'string') {
|
|
16
16
|
result = result.split(pattern).join(rule.to);
|
|
17
17
|
}
|
|
18
18
|
else {
|
package/dist/sources/exec.js
CHANGED
|
@@ -3,13 +3,13 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.fetchExec = void 0;
|
|
4
4
|
const child_process_1 = require("child_process");
|
|
5
5
|
function fetchExec(command) {
|
|
6
|
-
const result = (0, child_process_1.spawnSync)(
|
|
6
|
+
const result = (0, child_process_1.spawnSync)('sh', ['-c', command], { encoding: 'utf8' });
|
|
7
7
|
if (result.error) {
|
|
8
8
|
throw new Error(`exec failed to spawn: ${result.error.message}`);
|
|
9
9
|
}
|
|
10
10
|
if (result.status !== 0) {
|
|
11
|
-
const stderr = result.stderr?.trim() ??
|
|
12
|
-
throw new Error(`exec exited with code ${result.status}${stderr ?
|
|
11
|
+
const stderr = result.stderr?.trim() ?? '';
|
|
12
|
+
throw new Error(`exec exited with code ${result.status}${stderr ? ': ' + stderr : ''}`);
|
|
13
13
|
}
|
|
14
14
|
return result.stdout;
|
|
15
15
|
}
|
package/dist/sources/github.js
CHANGED
|
@@ -27,47 +27,47 @@ exports.fetchGitHub = void 0;
|
|
|
27
27
|
const child_process_1 = require("child_process");
|
|
28
28
|
const path = __importStar(require("path"));
|
|
29
29
|
function ghRun(args) {
|
|
30
|
-
const result = (0, child_process_1.spawnSync)(
|
|
30
|
+
const result = (0, child_process_1.spawnSync)('gh', args, { encoding: 'utf8' });
|
|
31
31
|
if (result.error) {
|
|
32
|
-
const msg = result.error.message ??
|
|
33
|
-
if (msg.includes(
|
|
34
|
-
throw new Error(
|
|
32
|
+
const msg = result.error.message ?? '';
|
|
33
|
+
if (msg.includes('ENOENT')) {
|
|
34
|
+
throw new Error('gh CLI not found. Install it from https://cli.github.com');
|
|
35
35
|
}
|
|
36
36
|
throw new Error(`gh error: ${msg}`);
|
|
37
37
|
}
|
|
38
38
|
return {
|
|
39
|
-
stdout: result.stdout ??
|
|
40
|
-
stderr: result.stderr ??
|
|
39
|
+
stdout: result.stdout ?? '',
|
|
40
|
+
stderr: result.stderr ?? '',
|
|
41
41
|
status: result.status,
|
|
42
42
|
};
|
|
43
43
|
}
|
|
44
44
|
function fetchFile(repo, filePath, ref) {
|
|
45
45
|
const res = ghRun([
|
|
46
|
-
|
|
46
|
+
'api',
|
|
47
47
|
`repos/${repo}/contents/${filePath}?ref=${encodeURIComponent(ref)}`,
|
|
48
|
-
|
|
49
|
-
|
|
48
|
+
'--jq',
|
|
49
|
+
'.content',
|
|
50
50
|
]);
|
|
51
51
|
if (res.status !== 0) {
|
|
52
52
|
throw new Error(`Failed to fetch ${filePath} from ${repo}@${ref}: ${res.stderr}`);
|
|
53
53
|
}
|
|
54
|
-
const b64 = res.stdout.trim().replace(/\\n/g,
|
|
55
|
-
return Buffer.from(b64,
|
|
54
|
+
const b64 = res.stdout.trim().replace(/\\n/g, '').replace(/\n/g, '');
|
|
55
|
+
return Buffer.from(b64, 'base64').toString('utf8');
|
|
56
56
|
}
|
|
57
57
|
function listTree(repo, dirPath, ref) {
|
|
58
58
|
const res = ghRun([
|
|
59
|
-
|
|
59
|
+
'api',
|
|
60
60
|
`repos/${repo}/git/trees/${encodeURIComponent(ref)}?recursive=1`,
|
|
61
|
-
|
|
61
|
+
'--jq',
|
|
62
62
|
`.tree[] | select(.type == "blob") | select(.path | startswith("${dirPath}/")) | .path`,
|
|
63
63
|
]);
|
|
64
64
|
if (res.status !== 0) {
|
|
65
65
|
throw new Error(`Failed to list tree ${dirPath} in ${repo}@${ref}: ${res.stderr}`);
|
|
66
66
|
}
|
|
67
|
-
return res.stdout.trim().split(
|
|
67
|
+
return res.stdout.trim().split('\n').filter(Boolean);
|
|
68
68
|
}
|
|
69
69
|
function fetchGitHub(repo, file, ref) {
|
|
70
|
-
const resolvedRef = ref ??
|
|
70
|
+
const resolvedRef = ref ?? 'HEAD';
|
|
71
71
|
const files = new Map();
|
|
72
72
|
try {
|
|
73
73
|
const content = fetchFile(repo, file, resolvedRef);
|
package/dist/sources/gitlab.js
CHANGED
|
@@ -27,24 +27,24 @@ exports.fetchGitLab = void 0;
|
|
|
27
27
|
const child_process_1 = require("child_process");
|
|
28
28
|
const path = __importStar(require("path"));
|
|
29
29
|
function glabRun(args) {
|
|
30
|
-
const result = (0, child_process_1.spawnSync)(
|
|
30
|
+
const result = (0, child_process_1.spawnSync)('glab', args, { encoding: 'utf8' });
|
|
31
31
|
if (result.error) {
|
|
32
|
-
const msg = result.error.message ??
|
|
33
|
-
if (msg.includes(
|
|
34
|
-
throw new Error(
|
|
32
|
+
const msg = result.error.message ?? '';
|
|
33
|
+
if (msg.includes('ENOENT')) {
|
|
34
|
+
throw new Error('glab CLI not found. Install it from https://gitlab.com/gitlab-org/cli');
|
|
35
35
|
}
|
|
36
36
|
throw new Error(`glab error: ${msg}`);
|
|
37
37
|
}
|
|
38
38
|
return {
|
|
39
|
-
stdout: result.stdout ??
|
|
40
|
-
stderr: result.stderr ??
|
|
39
|
+
stdout: result.stdout ?? '',
|
|
40
|
+
stderr: result.stderr ?? '',
|
|
41
41
|
status: result.status,
|
|
42
42
|
};
|
|
43
43
|
}
|
|
44
44
|
function resolveRef(project, ref) {
|
|
45
|
-
if (!ref || ref ===
|
|
45
|
+
if (!ref || ref === '$latest') {
|
|
46
46
|
const res = glabRun([
|
|
47
|
-
|
|
47
|
+
'api',
|
|
48
48
|
`projects/${encodeURIComponent(project)}/repository/tags?order_by=version&sort=desc&per_page=1`,
|
|
49
49
|
]);
|
|
50
50
|
if (res.status !== 0) {
|
|
@@ -61,7 +61,7 @@ function resolveRef(project, ref) {
|
|
|
61
61
|
function fetchFile(project, filePath, ref) {
|
|
62
62
|
const encodedPath = encodeURIComponent(filePath);
|
|
63
63
|
const res = glabRun([
|
|
64
|
-
|
|
64
|
+
'api',
|
|
65
65
|
`projects/${encodeURIComponent(project)}/repository/files/${encodedPath}/raw?ref=${encodeURIComponent(ref)}`,
|
|
66
66
|
]);
|
|
67
67
|
if (res.status !== 0) {
|
|
@@ -71,14 +71,14 @@ function fetchFile(project, filePath, ref) {
|
|
|
71
71
|
}
|
|
72
72
|
function listTree(project, dirPath, ref) {
|
|
73
73
|
const res = glabRun([
|
|
74
|
-
|
|
74
|
+
'api',
|
|
75
75
|
`projects/${encodeURIComponent(project)}/repository/tree?path=${encodeURIComponent(dirPath)}&ref=${encodeURIComponent(ref)}&recursive=true&per_page=100`,
|
|
76
76
|
]);
|
|
77
77
|
if (res.status !== 0) {
|
|
78
78
|
throw new Error(`Failed to list tree ${dirPath} in ${project}@${ref}: ${res.stderr}`);
|
|
79
79
|
}
|
|
80
80
|
const items = JSON.parse(res.stdout);
|
|
81
|
-
return items.filter((i) => i.type ===
|
|
81
|
+
return items.filter((i) => i.type === 'blob').map((i) => i.path);
|
|
82
82
|
}
|
|
83
83
|
function fetchGitLab(project, file, ref) {
|
|
84
84
|
const resolvedRef = resolveRef(project, ref);
|
package/dist/sources/http.js
CHANGED
|
@@ -40,7 +40,7 @@ function inferFilenameFromUrl(url) {
|
|
|
40
40
|
exports.inferFilenameFromUrl = inferFilenameFromUrl;
|
|
41
41
|
async function fetchHttp(url) {
|
|
42
42
|
return new Promise((resolve, reject) => {
|
|
43
|
-
const mod = url.startsWith(
|
|
43
|
+
const mod = url.startsWith('https') ? https : http;
|
|
44
44
|
mod
|
|
45
45
|
.get(url, (res) => {
|
|
46
46
|
if (res.statusCode &&
|
|
@@ -55,11 +55,11 @@ async function fetchHttp(url) {
|
|
|
55
55
|
return;
|
|
56
56
|
}
|
|
57
57
|
const chunks = [];
|
|
58
|
-
res.on(
|
|
59
|
-
res.on(
|
|
60
|
-
res.on(
|
|
58
|
+
res.on('data', (chunk) => chunks.push(chunk));
|
|
59
|
+
res.on('end', () => resolve(Buffer.concat(chunks).toString('utf8')));
|
|
60
|
+
res.on('error', reject);
|
|
61
61
|
})
|
|
62
|
-
.on(
|
|
62
|
+
.on('error', reject);
|
|
63
63
|
});
|
|
64
64
|
}
|
|
65
65
|
exports.fetchHttp = fetchHttp;
|
package/dist/sources/index.d.ts
CHANGED
package/dist/sources/index.js
CHANGED
|
@@ -31,27 +31,27 @@ const exec_1 = require("./exec");
|
|
|
31
31
|
const gitlab_1 = require("./gitlab");
|
|
32
32
|
const github_1 = require("./github");
|
|
33
33
|
async function fetchOneSrc(src) {
|
|
34
|
-
if (typeof src ===
|
|
35
|
-
if (src.startsWith(
|
|
34
|
+
if (typeof src === 'string') {
|
|
35
|
+
if (src.startsWith('http://') || src.startsWith('https://')) {
|
|
36
36
|
return (0, http_1.fetchHttp)(src);
|
|
37
37
|
}
|
|
38
38
|
const result = (0, local_1.fetchLocal)(src);
|
|
39
39
|
// For a single-file local result, return the first (and only) value
|
|
40
40
|
const values = Array.from(result.files.values());
|
|
41
|
-
return values.join(
|
|
41
|
+
return values.join('\n');
|
|
42
42
|
}
|
|
43
|
-
if (
|
|
43
|
+
if ('exec' in src) {
|
|
44
44
|
return (0, exec_1.fetchExec)(src.exec);
|
|
45
45
|
}
|
|
46
|
-
if (
|
|
46
|
+
if ('gitlab' in src) {
|
|
47
47
|
const result = (0, gitlab_1.fetchGitLab)(src.gitlab.project, src.gitlab.file, src.gitlab.ref);
|
|
48
48
|
const values = Array.from(result.files.values());
|
|
49
|
-
return values.join(
|
|
49
|
+
return values.join('\n');
|
|
50
50
|
}
|
|
51
|
-
if (
|
|
51
|
+
if ('github' in src) {
|
|
52
52
|
const result = (0, github_1.fetchGitHub)(src.github.repo, src.github.file, src.github.ref);
|
|
53
53
|
const values = Array.from(result.files.values());
|
|
54
|
-
return values.join(
|
|
54
|
+
return values.join('\n');
|
|
55
55
|
}
|
|
56
56
|
throw new Error(`Unknown source type: ${JSON.stringify(src)}`);
|
|
57
57
|
}
|
|
@@ -70,15 +70,15 @@ async function fetchSource(entry) {
|
|
|
70
70
|
}
|
|
71
71
|
}
|
|
72
72
|
const filename = path.basename(entry.target);
|
|
73
|
-
return { files: new Map([[filename, parts.join(
|
|
73
|
+
return { files: new Map([[filename, parts.join('\n')]]) };
|
|
74
74
|
}
|
|
75
75
|
// Single src — original behaviour
|
|
76
|
-
if (typeof src ===
|
|
77
|
-
if (src.startsWith(
|
|
76
|
+
if (typeof src === 'string') {
|
|
77
|
+
if (src.startsWith('http://') || src.startsWith('https://')) {
|
|
78
78
|
const content = await (0, http_1.fetchHttp)(src);
|
|
79
79
|
const filename = entry.target
|
|
80
80
|
? path.basename(entry.target)
|
|
81
|
-
: ((0, http_1.inferFilenameFromUrl)(src) ??
|
|
81
|
+
: ((0, http_1.inferFilenameFromUrl)(src) ?? 'download');
|
|
82
82
|
return { files: new Map([[filename, content]]) };
|
|
83
83
|
}
|
|
84
84
|
// Local path (absolute, ~/, or relative)
|
|
@@ -86,16 +86,16 @@ async function fetchSource(entry) {
|
|
|
86
86
|
return { files: result.files };
|
|
87
87
|
}
|
|
88
88
|
// Map sources
|
|
89
|
-
if (
|
|
89
|
+
if ('exec' in src) {
|
|
90
90
|
const content = (0, exec_1.fetchExec)(src.exec);
|
|
91
91
|
const filename = path.basename(entry.target); // target required, validated in config
|
|
92
92
|
return { files: new Map([[filename, content]]) };
|
|
93
93
|
}
|
|
94
|
-
if (
|
|
94
|
+
if ('gitlab' in src) {
|
|
95
95
|
const result = (0, gitlab_1.fetchGitLab)(src.gitlab.project, src.gitlab.file, src.gitlab.ref);
|
|
96
96
|
return { files: result.files };
|
|
97
97
|
}
|
|
98
|
-
if (
|
|
98
|
+
if ('github' in src) {
|
|
99
99
|
const result = (0, github_1.fetchGitHub)(src.github.repo, src.github.file, src.github.ref);
|
|
100
100
|
return { files: result.files };
|
|
101
101
|
}
|
package/dist/sources/local.js
CHANGED
|
@@ -27,8 +27,8 @@ exports.fetchLocal = void 0;
|
|
|
27
27
|
const fs = __importStar(require("fs"));
|
|
28
28
|
const path = __importStar(require("path"));
|
|
29
29
|
function expandHome(p) {
|
|
30
|
-
if (p.startsWith(
|
|
31
|
-
return path.join(process.env[
|
|
30
|
+
if (p.startsWith('~/')) {
|
|
31
|
+
return path.join(process.env['HOME'] ?? '~', p.slice(2));
|
|
32
32
|
}
|
|
33
33
|
return p;
|
|
34
34
|
}
|
|
@@ -43,7 +43,7 @@ function fetchLocal(src) {
|
|
|
43
43
|
readDirRecursive(resolved, resolved, files);
|
|
44
44
|
}
|
|
45
45
|
else {
|
|
46
|
-
files.set(path.basename(resolved), fs.readFileSync(resolved,
|
|
46
|
+
files.set(path.basename(resolved), fs.readFileSync(resolved, 'utf8'));
|
|
47
47
|
}
|
|
48
48
|
return { files };
|
|
49
49
|
}
|
|
@@ -57,7 +57,7 @@ function readDirRecursive(base, current, out) {
|
|
|
57
57
|
readDirRecursive(base, full, out);
|
|
58
58
|
}
|
|
59
59
|
else {
|
|
60
|
-
out.set(rel, fs.readFileSync(full,
|
|
60
|
+
out.set(rel, fs.readFileSync(full, 'utf8'));
|
|
61
61
|
}
|
|
62
62
|
}
|
|
63
63
|
}
|
package/dist/writer.js
CHANGED
|
@@ -28,13 +28,13 @@ const fs = __importStar(require("fs"));
|
|
|
28
28
|
const os = __importStar(require("os"));
|
|
29
29
|
const path = __importStar(require("path"));
|
|
30
30
|
function atomicWrite(targets) {
|
|
31
|
-
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(),
|
|
31
|
+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'fileferry-'));
|
|
32
32
|
try {
|
|
33
33
|
// Stage all files to temp dir first
|
|
34
34
|
const staged = [];
|
|
35
35
|
for (const t of targets) {
|
|
36
|
-
const tmpFile = path.join(tmpDir, path.basename(t.targetPath) +
|
|
37
|
-
fs.writeFileSync(tmpFile, t.content,
|
|
36
|
+
const tmpFile = path.join(tmpDir, path.basename(t.targetPath) + '-' + staged.length);
|
|
37
|
+
fs.writeFileSync(tmpFile, t.content, 'utf8');
|
|
38
38
|
staged.push({ tmp: tmpFile, dest: t.targetPath, mode: t.mode });
|
|
39
39
|
}
|
|
40
40
|
// All staging succeeded — now write to real targets
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@udondan/avanti",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Assemble local files from any source via a declarative YAML spec",
|
|
5
5
|
"bin": {
|
|
6
6
|
"avanti": "dist/cli.js"
|
|
@@ -21,8 +21,20 @@
|
|
|
21
21
|
"files",
|
|
22
22
|
"yaml"
|
|
23
23
|
],
|
|
24
|
-
"author":
|
|
24
|
+
"author": {
|
|
25
|
+
"name": "Daniel Schroeder",
|
|
26
|
+
"email": "deemes79@googlemail.com",
|
|
27
|
+
"url": "https://udondan.com/"
|
|
28
|
+
},
|
|
29
|
+
"funding": {
|
|
30
|
+
"type": "github",
|
|
31
|
+
"url": "https://github.com/sponsors/udondan"
|
|
32
|
+
},
|
|
25
33
|
"license": "MIT",
|
|
34
|
+
"repository": {
|
|
35
|
+
"type": "git",
|
|
36
|
+
"url": "https://github.com/udondan/avanti.git"
|
|
37
|
+
},
|
|
26
38
|
"dependencies": {
|
|
27
39
|
"chalk": "5.3.0",
|
|
28
40
|
"commander": "12.1.0",
|
|
@@ -30,16 +42,16 @@
|
|
|
30
42
|
"js-yaml": "4.1.1"
|
|
31
43
|
},
|
|
32
44
|
"devDependencies": {
|
|
33
|
-
"@eslint/js": "
|
|
45
|
+
"@eslint/js": "10.0.1",
|
|
34
46
|
"@types/diff": "5.2.1",
|
|
35
47
|
"@types/js-yaml": "4.0.9",
|
|
36
48
|
"@types/node": "20.14.0",
|
|
37
|
-
"eslint": "
|
|
38
|
-
"eslint-config-prettier": "
|
|
39
|
-
"prettier": "
|
|
49
|
+
"eslint": "10.3.0",
|
|
50
|
+
"eslint-config-prettier": "10.1.8",
|
|
51
|
+
"prettier": "3.8.3",
|
|
40
52
|
"tsx": "4.15.0",
|
|
41
53
|
"typescript": "5.4.5",
|
|
42
|
-
"typescript-eslint": "
|
|
54
|
+
"typescript-eslint": "8.59.2",
|
|
43
55
|
"vitest": "1.6.0"
|
|
44
56
|
}
|
|
45
57
|
}
|