@bratel/dgit 0.0.13 → 0.0.14
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/.github/workflows/nodejs.yml +17 -17
- package/README.en_US.md +20 -22
- package/README.md +25 -26
- package/eslint.config.mjs +15 -0
- package/lib/cmd/action.d.ts +3 -3
- package/lib/cmd/action.js +80 -78
- package/lib/cmd/main.js +17 -15
- package/lib/cmd/prompt.d.ts +4 -4
- package/lib/cmd/prompt.js +82 -76
- package/lib/cmd/utils.d.ts +5 -5
- package/lib/cmd/utils.js +25 -19
- package/lib/dgit.d.ts +2 -2
- package/lib/dgit.js +162 -157
- package/lib/log.d.ts +2 -2
- package/lib/log.js +11 -9
- package/lib/repo.d.ts +1 -1
- package/lib/repo.js +6 -4
- package/lib/request.d.ts +6 -6
- package/lib/request.js +29 -26
- package/lib/type.d.ts +1 -1
- package/package.json +51 -55
- package/renovate.json +19 -0
- package/src/cmd/action.ts +115 -108
- package/src/cmd/main.ts +51 -49
- package/src/cmd/prompt.ts +93 -92
- package/src/cmd/type.ts +27 -27
- package/src/cmd/utils.ts +78 -73
- package/src/dgit.ts +238 -228
- package/src/log.ts +9 -7
- package/src/repo.ts +6 -4
- package/src/request.ts +91 -92
- package/src/type.ts +36 -36
- package/test/dgit.test.ts +122 -119
- package/tsconfig.json +51 -51
- package/tsconfig.tsbuildinfo +1 -0
- package/.eslintignore +0 -6
- package/.eslintrc.js +0 -30
- package/x-npmrc +0 -2
package/src/dgit.ts
CHANGED
|
@@ -1,256 +1,266 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
import {
|
|
8
|
-
DgitGlobalOption,
|
|
9
|
-
RepoOptionType,
|
|
10
|
-
RepoTreeNode,
|
|
11
|
-
DgitLifeCycle,
|
|
12
|
-
DgitLoadGitTree,
|
|
1
|
+
import type {
|
|
2
|
+
DgitGlobalOption,
|
|
3
|
+
DgitLifeCycle,
|
|
4
|
+
DgitLoadGitTree,
|
|
5
|
+
RepoOptionType,
|
|
6
|
+
RepoTreeNode,
|
|
13
7
|
} from './type';
|
|
8
|
+
import fs from 'node:fs';
|
|
9
|
+
import path from 'node:path';
|
|
10
|
+
import process from 'node:process';
|
|
11
|
+
import async from 'async';
|
|
14
12
|
import {
|
|
15
|
-
|
|
13
|
+
isHttpsLink,
|
|
14
|
+
MakeDirs,
|
|
15
|
+
ParseGithubHttpsLink,
|
|
16
16
|
} from './cmd/utils';
|
|
17
|
+
import { createLogger } from './log';
|
|
18
|
+
import repo from './repo';
|
|
19
|
+
import { requestGetPromise, requestOnStream } from './request';
|
|
17
20
|
|
|
18
21
|
const UserAgent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36';
|
|
19
22
|
const DEFAULT_PARALLEL_LIMIT = 10;
|
|
20
23
|
const MAX_PARALLEL_LIMIT = 100;
|
|
21
24
|
const JSON_STRINGIFY_PADDING = 2;
|
|
22
25
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
26
|
+
async function dgit(repoOption: RepoOptionType, dPath: string, dgitOptions?: DgitGlobalOption, hooks?: DgitLifeCycle & DgitLoadGitTree): Promise<void> {
|
|
27
|
+
const {
|
|
28
|
+
username,
|
|
29
|
+
password,
|
|
30
|
+
token,
|
|
31
|
+
githubLink,
|
|
32
|
+
proxy = '',
|
|
33
|
+
} = repoOption;
|
|
34
|
+
|
|
35
|
+
let {
|
|
36
|
+
owner,
|
|
37
|
+
repoName,
|
|
38
|
+
ref = 'master',
|
|
39
|
+
relativePath = '.',
|
|
40
|
+
} = repoOption;
|
|
41
|
+
|
|
42
|
+
if (githubLink && isHttpsLink(githubLink)) {
|
|
43
|
+
const parseResult = ParseGithubHttpsLink(githubLink);
|
|
44
|
+
owner = parseResult.owner;
|
|
45
|
+
repoName = parseResult.repoName;
|
|
46
|
+
ref = parseResult.ref;
|
|
47
|
+
relativePath = parseResult.relativePath;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (!owner || !repoName) {
|
|
51
|
+
throw new Error('invalid repo option.');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const logger = createLogger(dgitOptions);
|
|
55
|
+
|
|
56
|
+
const { exclude = [], include = [] } = dgitOptions || {};
|
|
57
|
+
|
|
58
|
+
let { parallelLimit = DEFAULT_PARALLEL_LIMIT } = dgitOptions || {};
|
|
59
|
+
if (!parallelLimit || parallelLimit <= 0) {
|
|
60
|
+
logger('parallelLimit value is invalid.');
|
|
61
|
+
parallelLimit = DEFAULT_PARALLEL_LIMIT;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
parallelLimit > MAX_PARALLEL_LIMIT && (parallelLimit = MAX_PARALLEL_LIMIT);
|
|
65
|
+
|
|
66
|
+
const {
|
|
67
|
+
onSuccess,
|
|
68
|
+
onError,
|
|
69
|
+
onProgress,
|
|
70
|
+
onFinish,
|
|
71
|
+
onRetry,
|
|
72
|
+
onResolved,
|
|
73
|
+
beforeLoadTree,
|
|
74
|
+
afterLoadTree,
|
|
75
|
+
} = hooks || {};
|
|
76
|
+
|
|
77
|
+
let onSuccessResolve: (data?: any) => void = () => {};
|
|
78
|
+
let onErrorReject: (err?: any) => void = () => {};
|
|
79
|
+
|
|
80
|
+
const prom: Promise<void> = new Promise((resolve, reject) => {
|
|
81
|
+
onSuccessResolve = resolve;
|
|
82
|
+
onErrorReject = reject;
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
const { getRepoTreeUrl, getDownloadUrl } = repo(owner, repoName, ref, proxy);
|
|
86
|
+
const url = getRepoTreeUrl();
|
|
87
|
+
|
|
88
|
+
const headers = {
|
|
89
|
+
'User-Agent': UserAgent,
|
|
90
|
+
'Authorization': token ? `token ${token}` : undefined,
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
const auth = username && password
|
|
94
|
+
? {
|
|
95
|
+
user: username,
|
|
96
|
+
pass: password,
|
|
97
|
+
sendImmediately: true,
|
|
98
|
+
}
|
|
99
|
+
: undefined;
|
|
100
|
+
|
|
101
|
+
const options = {
|
|
102
|
+
url,
|
|
103
|
+
headers,
|
|
104
|
+
auth,
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
const destPath = path.isAbsolute(dPath) ? dPath : path.resolve(process.cwd(), dPath);
|
|
108
|
+
|
|
109
|
+
logger(' request repo tree options.');
|
|
110
|
+
logger(JSON.stringify(options, null, JSON_STRINGIFY_PADDING));
|
|
111
|
+
|
|
112
|
+
try {
|
|
113
|
+
logger(' loading remote repo tree...');
|
|
114
|
+
beforeLoadTree && beforeLoadTree();
|
|
115
|
+
const body = await requestGetPromise(options, dgitOptions || {}, {
|
|
116
|
+
onRetry() {
|
|
117
|
+
logger(` request ${url} failed. Retrying...`);
|
|
118
|
+
onRetry && onRetry();
|
|
119
|
+
},
|
|
120
|
+
});
|
|
44
121
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
122
|
+
logger(' loading remote repo tree succeed.');
|
|
123
|
+
afterLoadTree && afterLoadTree();
|
|
124
|
+
const result = JSON.parse(body);
|
|
48
125
|
|
|
49
|
-
|
|
126
|
+
if (!result.tree || result.tree.length <= 0) {
|
|
127
|
+
throw new Error('404 repo not found!');
|
|
128
|
+
}
|
|
50
129
|
|
|
51
|
-
const
|
|
130
|
+
const treeNodeList: RepoTreeNode[] = result.tree;
|
|
131
|
+
const includeTreeNodeList = treeNodeList.filter((node) => {
|
|
132
|
+
const nPath = path.resolve(__dirname, node.path);
|
|
133
|
+
const rPath = path.resolve(__dirname, relativePath);
|
|
134
|
+
if (!nPath.startsWith(rPath) || node.type !== 'blob') {
|
|
135
|
+
return false;
|
|
136
|
+
}
|
|
137
|
+
if (
|
|
138
|
+
exclude.some(v => nPath.startsWith(path.resolve(rPath, v)))
|
|
139
|
+
&& include.every(v => !nPath.startsWith(path.resolve(rPath, v)))
|
|
140
|
+
) {
|
|
141
|
+
return false;
|
|
142
|
+
}
|
|
143
|
+
return true;
|
|
144
|
+
});
|
|
52
145
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
logger('parallelLimit value is invalid.');
|
|
56
|
-
parallelLimit = DEFAULT_PARALLEL_LIMIT;
|
|
146
|
+
if (includeTreeNodeList.length <= 0) {
|
|
147
|
+
throw new Error(`404 repo ${relativePath} not found!`);
|
|
57
148
|
}
|
|
58
149
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
150
|
+
const totalStatus = includeTreeNodeList.reduce(
|
|
151
|
+
(prev, cur) => {
|
|
152
|
+
if (cur.type === 'blob') {
|
|
153
|
+
prev.size += cur.size;
|
|
154
|
+
prev.count++;
|
|
155
|
+
}
|
|
156
|
+
return prev;
|
|
157
|
+
},
|
|
158
|
+
{ size: 0, count: 0 },
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
let currentSize = 0;
|
|
162
|
+
let currentCount = 0;
|
|
163
|
+
|
|
164
|
+
onResolved
|
|
165
|
+
&& onResolved({
|
|
166
|
+
currentSize,
|
|
167
|
+
currentCount,
|
|
168
|
+
totalSize: totalStatus.size,
|
|
169
|
+
totalCount: totalStatus.count,
|
|
78
170
|
});
|
|
79
171
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
const body = await requestGetPromise(options, dgitOptions || {}, {
|
|
109
|
-
onRetry () {
|
|
110
|
-
logger(` request ${ url } failed. Retrying...`);
|
|
111
|
-
onRetry && onRetry();
|
|
112
|
-
},
|
|
113
|
-
});
|
|
172
|
+
logger(' include files resolved.');
|
|
173
|
+
logger(
|
|
174
|
+
'',
|
|
175
|
+
JSON.stringify({
|
|
176
|
+
currentSize,
|
|
177
|
+
currentCount,
|
|
178
|
+
totalSize: totalStatus.size,
|
|
179
|
+
totalCount: totalStatus.count,
|
|
180
|
+
}),
|
|
181
|
+
);
|
|
182
|
+
|
|
183
|
+
async.eachLimit(
|
|
184
|
+
includeTreeNodeList,
|
|
185
|
+
parallelLimit,
|
|
186
|
+
(node, callback) => {
|
|
187
|
+
const downloadUrl = getDownloadUrl(node.path);
|
|
188
|
+
|
|
189
|
+
const rPath = path.resolve(destPath, relativePath);
|
|
190
|
+
const tPath = path.resolve(destPath, node.path);
|
|
191
|
+
const root = path.resolve(destPath, '.');
|
|
192
|
+
|
|
193
|
+
let targetPath: string;
|
|
194
|
+
if (rPath === tPath) {
|
|
195
|
+
targetPath = path.resolve(destPath, path.basename(tPath));
|
|
196
|
+
}
|
|
197
|
+
else {
|
|
198
|
+
targetPath = tPath.replace(rPath, root);
|
|
199
|
+
}
|
|
114
200
|
|
|
115
|
-
logger('
|
|
116
|
-
afterLoadTree && afterLoadTree();
|
|
117
|
-
const result = JSON.parse(body);
|
|
201
|
+
logger('', node.path, relativePath, targetPath);
|
|
118
202
|
|
|
119
|
-
if (!
|
|
120
|
-
|
|
203
|
+
if (!fs.existsSync(path.dirname(targetPath))) {
|
|
204
|
+
MakeDirs(path.dirname(targetPath));
|
|
121
205
|
}
|
|
122
206
|
|
|
123
|
-
const
|
|
124
|
-
const includeTreeNodeList = treeNodeList.filter(node => {
|
|
125
|
-
const nPath = path.resolve(__dirname, node.path);
|
|
126
|
-
const rPath = path.resolve(__dirname, relativePath);
|
|
127
|
-
if (!nPath.startsWith(rPath) || node.type !== 'blob') {
|
|
128
|
-
return false;
|
|
129
|
-
}
|
|
130
|
-
if (
|
|
131
|
-
exclude.some(v => nPath.startsWith(path.resolve(rPath, v))) &&
|
|
132
|
-
include.every(v => !nPath.startsWith(path.resolve(rPath, v)))
|
|
133
|
-
) {
|
|
134
|
-
return false;
|
|
135
|
-
}
|
|
136
|
-
return true;
|
|
137
|
-
});
|
|
207
|
+
const ws = fs.createWriteStream(targetPath);
|
|
138
208
|
|
|
139
|
-
|
|
140
|
-
throw new Error(`404 repo ${ relativePath } not found!`);
|
|
141
|
-
}
|
|
209
|
+
logger(` downloading from ${downloadUrl}...`);
|
|
142
210
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
{ size: 0, count: 0 },
|
|
152
|
-
);
|
|
153
|
-
|
|
154
|
-
let currentSize = 0;
|
|
155
|
-
let currentCount = 0;
|
|
156
|
-
|
|
157
|
-
onResolved &&
|
|
158
|
-
onResolved({
|
|
159
|
-
currentSize,
|
|
160
|
-
currentCount,
|
|
161
|
-
totalSize : totalStatus.size,
|
|
162
|
-
totalCount: totalStatus.count,
|
|
163
|
-
});
|
|
211
|
+
requestOnStream(downloadUrl, ws, dgitOptions || {}, {
|
|
212
|
+
onSuccess() {
|
|
213
|
+
currentCount++;
|
|
214
|
+
currentSize += node.size;
|
|
215
|
+
|
|
216
|
+
logger(` write file ${node.path} succeed.
|
|
217
|
+
size: [${currentSize}/${totalStatus.size}],
|
|
218
|
+
count: [${currentCount}/${totalStatus.count}]`);
|
|
164
219
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
220
|
+
onProgress
|
|
221
|
+
&& onProgress(
|
|
222
|
+
{
|
|
223
|
+
totalCount: totalStatus.count,
|
|
224
|
+
totalSize: totalStatus.size,
|
|
169
225
|
currentSize,
|
|
170
226
|
currentCount,
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
(
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
size: [${ currentSize }/${ totalStatus.size }],
|
|
210
|
-
count: [${ currentCount }/${ totalStatus.count }]`);
|
|
211
|
-
|
|
212
|
-
onProgress &&
|
|
213
|
-
onProgress(
|
|
214
|
-
{
|
|
215
|
-
totalCount: totalStatus.count,
|
|
216
|
-
totalSize : totalStatus.size,
|
|
217
|
-
currentSize,
|
|
218
|
-
currentCount,
|
|
219
|
-
},
|
|
220
|
-
node,
|
|
221
|
-
);
|
|
222
|
-
|
|
223
|
-
callback();
|
|
224
|
-
},
|
|
225
|
-
onError (err) {
|
|
226
|
-
logger('', err);
|
|
227
|
-
callback(new Error(` request ${ downloadUrl } failed.`));
|
|
228
|
-
},
|
|
229
|
-
onRetry () {
|
|
230
|
-
logger(` request ${ downloadUrl } failed. Retrying...`);
|
|
231
|
-
onRetry && onRetry();
|
|
232
|
-
},
|
|
233
|
-
});
|
|
234
|
-
},
|
|
235
|
-
err => {
|
|
236
|
-
if (err) {
|
|
237
|
-
onError && onError(err);
|
|
238
|
-
onFinish && onFinish();
|
|
239
|
-
onErrorReject(err);
|
|
240
|
-
} else {
|
|
241
|
-
onSuccess && onSuccess();
|
|
242
|
-
onFinish && onFinish();
|
|
243
|
-
onSuccessResolve();
|
|
244
|
-
}
|
|
245
|
-
},
|
|
246
|
-
);
|
|
247
|
-
} catch (error) {
|
|
248
|
-
onError && onError(error);
|
|
249
|
-
onFinish && onFinish();
|
|
250
|
-
onErrorReject(error);
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
return prom;
|
|
254
|
-
};
|
|
227
|
+
},
|
|
228
|
+
node,
|
|
229
|
+
);
|
|
230
|
+
|
|
231
|
+
callback();
|
|
232
|
+
},
|
|
233
|
+
onError(err) {
|
|
234
|
+
logger('', err);
|
|
235
|
+
callback(new Error(` request ${downloadUrl} failed.`));
|
|
236
|
+
},
|
|
237
|
+
onRetry() {
|
|
238
|
+
logger(` request ${downloadUrl} failed. Retrying...`);
|
|
239
|
+
onRetry && onRetry();
|
|
240
|
+
},
|
|
241
|
+
});
|
|
242
|
+
},
|
|
243
|
+
(err) => {
|
|
244
|
+
if (err) {
|
|
245
|
+
onError && onError(err);
|
|
246
|
+
onFinish && onFinish();
|
|
247
|
+
onErrorReject(err);
|
|
248
|
+
}
|
|
249
|
+
else {
|
|
250
|
+
onSuccess && onSuccess();
|
|
251
|
+
onFinish && onFinish();
|
|
252
|
+
onSuccessResolve();
|
|
253
|
+
}
|
|
254
|
+
},
|
|
255
|
+
);
|
|
256
|
+
}
|
|
257
|
+
catch (error) {
|
|
258
|
+
onError && onError(error);
|
|
259
|
+
onFinish && onFinish();
|
|
260
|
+
onErrorReject(error);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
return prom;
|
|
264
|
+
}
|
|
255
265
|
|
|
256
266
|
export default dgit;
|
package/src/log.ts
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
|
-
import { DgitGlobalOption } from './type';
|
|
1
|
+
import type { DgitGlobalOption } from './type';
|
|
2
2
|
|
|
3
3
|
const DEFAULT_PREFIX = '[dgit-logger]';
|
|
4
4
|
|
|
5
|
-
export
|
|
5
|
+
export function createLogger(option?: DgitGlobalOption) {
|
|
6
|
+
return (...message: any[]) => {
|
|
6
7
|
if (option && option.log) {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
const prefix = option
|
|
9
|
+
? option.logPrefix || DEFAULT_PREFIX
|
|
10
|
+
: DEFAULT_PREFIX;
|
|
11
|
+
console.log(prefix, ...message, '\n');
|
|
11
12
|
}
|
|
12
|
-
};
|
|
13
|
+
};
|
|
14
|
+
}
|
package/src/repo.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
}
|
|
1
|
+
function repoUtils(owner: string, repoName: string, ref: string, proxy: string) {
|
|
2
|
+
return {
|
|
3
|
+
getRepoTreeUrl: () => `https://api.github.com/repos/${owner}/${repoName}/git/trees/${ref}?recursive=1`,
|
|
4
|
+
getDownloadUrl: (path: string) => `${proxy ? `${proxy}/` : ''}https://raw.githubusercontent.com/${owner}/${repoName}/${ref}/${path}`,
|
|
5
|
+
};
|
|
6
|
+
}
|
|
5
7
|
|
|
6
8
|
export default repoUtils;
|