@magland/mochi 0.3.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/LICENSE +201 -0
- package/README.md +108 -0
- package/dist/ansi.js +174 -0
- package/dist/api/admin.js +416 -0
- package/dist/api/auth.js +166 -0
- package/dist/api/backup.js +598 -0
- package/dist/api/ci.js +336 -0
- package/dist/api/contents.js +339 -0
- package/dist/api/issues.js +165 -0
- package/dist/api/pulls.js +244 -0
- package/dist/api/releases.js +83 -0
- package/dist/api/repos.js +156 -0
- package/dist/api/write.js +518 -0
- package/dist/api.js +326 -0
- package/dist/assets.js +29 -0
- package/dist/atom.js +32 -0
- package/dist/atomic.js +171 -0
- package/dist/avatar.js +81 -0
- package/dist/browse.js +630 -0
- package/dist/build-info.json +4 -0
- package/dist/ci/actionref.js +86 -0
- package/dist/ci/api.js +829 -0
- package/dist/ci/artifacts.js +201 -0
- package/dist/ci/dispatch.js +30 -0
- package/dist/ci/engine.js +1321 -0
- package/dist/ci/expr.js +526 -0
- package/dist/ci/manual.js +199 -0
- package/dist/ci/present.js +82 -0
- package/dist/ci/protocol.js +6 -0
- package/dist/ci/runners.js +256 -0
- package/dist/ci/runs.js +208 -0
- package/dist/ci/trigger.js +28 -0
- package/dist/ci/views.js +441 -0
- package/dist/ci/wake.js +194 -0
- package/dist/ci/web.js +617 -0
- package/dist/ci/workflow.js +436 -0
- package/dist/cli/admin-cmd.js +324 -0
- package/dist/cli/api-cmd.js +128 -0
- package/dist/cli/backup-cmd.js +1500 -0
- package/dist/cli/exit.js +69 -0
- package/dist/cli/input.js +64 -0
- package/dist/cli/issue-cmd.js +243 -0
- package/dist/cli/output.js +93 -0
- package/dist/cli/parse.js +317 -0
- package/dist/cli/pr-cmd.js +289 -0
- package/dist/cli/release-cmd.js +171 -0
- package/dist/cli/repo-cmd.js +763 -0
- package/dist/cli/repo.js +101 -0
- package/dist/cli/run-cmd.js +438 -0
- package/dist/cli/target.js +54 -0
- package/dist/cli-api.js +84 -0
- package/dist/compare.js +111 -0
- package/dist/config.js +212 -0
- package/dist/credentials.js +235 -0
- package/dist/deploy-cli.js +859 -0
- package/dist/deploy-runner-cli.js +592 -0
- package/dist/diff.js +171 -0
- package/dist/discussion.js +253 -0
- package/dist/egress.js +559 -0
- package/dist/filecache.js +68 -0
- package/dist/find.js +162 -0
- package/dist/forms.js +737 -0
- package/dist/git.js +547 -0
- package/dist/githttp.js +428 -0
- package/dist/html.js +87 -0
- package/dist/icons.js +101 -0
- package/dist/import-cli.js +316 -0
- package/dist/index.js +752 -0
- package/dist/issues.js +308 -0
- package/dist/issueweb.js +447 -0
- package/dist/job-cli.js +197 -0
- package/dist/jobtoken.js +96 -0
- package/dist/languages.js +383 -0
- package/dist/layout.js +100 -0
- package/dist/lfs.js +438 -0
- package/dist/lfsstore.js +425 -0
- package/dist/limit.js +259 -0
- package/dist/logo.js +61 -0
- package/dist/markdown.js +382 -0
- package/dist/migrate.js +334 -0
- package/dist/multipart.js +90 -0
- package/dist/ops.js +869 -0
- package/dist/pagescript.js +465 -0
- package/dist/perms.js +370 -0
- package/dist/pointer.js +55 -0
- package/dist/profile.js +106 -0
- package/dist/pulls.js +320 -0
- package/dist/pullweb.js +461 -0
- package/dist/redirects.js +455 -0
- package/dist/releases.js +435 -0
- package/dist/render.js +233 -0
- package/dist/runner/actions.js +448 -0
- package/dist/runner/client.js +428 -0
- package/dist/runner/context.js +247 -0
- package/dist/runner/docker.js +197 -0
- package/dist/runner/externals.js +175 -0
- package/dist/runner/job.js +290 -0
- package/dist/runner/manual-run.js +272 -0
- package/dist/runner/overrides.js +554 -0
- package/dist/runner/steps.js +571 -0
- package/dist/runner/wake.js +84 -0
- package/dist/runner-cli.js +405 -0
- package/dist/scan.js +231 -0
- package/dist/server.js +424 -0
- package/dist/session.js +267 -0
- package/dist/site.js +259 -0
- package/dist/siteshost.js +94 -0
- package/dist/source.js +90 -0
- package/dist/style.js +1295 -0
- package/dist/themes.js +369 -0
- package/dist/vault.js +442 -0
- package/dist/version.js +88 -0
- package/dist/views.js +1007 -0
- package/dist/web.js +182 -0
- package/dist/webops.js +1402 -0
- package/package.json +71 -0
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.importCmd = importCmd;
|
|
37
|
+
exports.collectionAddCmd = collectionAddCmd;
|
|
38
|
+
exports.collectionListCmd = collectionListCmd;
|
|
39
|
+
const child_process_1 = require("child_process");
|
|
40
|
+
const fs = __importStar(require("fs"));
|
|
41
|
+
const os = __importStar(require("os"));
|
|
42
|
+
const path = __importStar(require("path"));
|
|
43
|
+
const cli_api_1 = require("./cli-api");
|
|
44
|
+
const scan_1 = require("./scan");
|
|
45
|
+
const source_1 = require("./source");
|
|
46
|
+
function parseImportArgs(args, usage) {
|
|
47
|
+
const out = {
|
|
48
|
+
src: null,
|
|
49
|
+
collection: null,
|
|
50
|
+
name: null,
|
|
51
|
+
host: null,
|
|
52
|
+
token: null,
|
|
53
|
+
lfs: false,
|
|
54
|
+
description: null,
|
|
55
|
+
noDescription: false,
|
|
56
|
+
};
|
|
57
|
+
let positional = 0;
|
|
58
|
+
for (let i = 0; i < args.length; i++) {
|
|
59
|
+
const a = args[i];
|
|
60
|
+
if (a === '-h' || a === '--help')
|
|
61
|
+
usage();
|
|
62
|
+
else if (a === '--host')
|
|
63
|
+
out.host = args[++i];
|
|
64
|
+
else if (a === '--token')
|
|
65
|
+
out.token = args[++i];
|
|
66
|
+
else if (a === '--collection')
|
|
67
|
+
out.collection = args[++i];
|
|
68
|
+
else if (a === '--name')
|
|
69
|
+
out.name = args[++i];
|
|
70
|
+
else if (a === '--lfs')
|
|
71
|
+
out.lfs = true;
|
|
72
|
+
else if (a === '--description')
|
|
73
|
+
out.description = args[++i];
|
|
74
|
+
else if (a === '--no-description')
|
|
75
|
+
out.noDescription = true;
|
|
76
|
+
else if (a.startsWith('-')) {
|
|
77
|
+
console.error(`Unknown option: ${a}`);
|
|
78
|
+
process.exit(1);
|
|
79
|
+
}
|
|
80
|
+
else if (positional === 0) {
|
|
81
|
+
out.src = a;
|
|
82
|
+
positional++;
|
|
83
|
+
}
|
|
84
|
+
else if (positional === 1) {
|
|
85
|
+
// The destination is written the way a repository is addressed
|
|
86
|
+
// everywhere else in mochi: `collection` or `collection/repo`.
|
|
87
|
+
const slash = a.indexOf('/');
|
|
88
|
+
if (slash === -1)
|
|
89
|
+
out.collection = a;
|
|
90
|
+
else {
|
|
91
|
+
out.collection = a.slice(0, slash);
|
|
92
|
+
out.name = a.slice(slash + 1);
|
|
93
|
+
}
|
|
94
|
+
positional++;
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
console.error(`Unexpected argument: ${a}`);
|
|
98
|
+
process.exit(1);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return out;
|
|
102
|
+
}
|
|
103
|
+
/** Run git with its output going straight to the terminal, so a long clone shows progress. */
|
|
104
|
+
function git(args, env) {
|
|
105
|
+
return new Promise((resolve, reject) => {
|
|
106
|
+
const child = (0, child_process_1.spawn)('git', args, { stdio: 'inherit', env: env ?? process.env });
|
|
107
|
+
child.on('error', (e) => reject(new Error(e.code === 'ENOENT' ? 'git is not on PATH' : String(e.message))));
|
|
108
|
+
child.on('close', (code) => resolve(code ?? 1));
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
// The token is handed to the push through a helper that reads it from the
|
|
112
|
+
// environment rather than through the command line, where every process on the
|
|
113
|
+
// machine could read it. The empty helper first clears the list, so the answer
|
|
114
|
+
// comes from here and nowhere else, and no prompt can appear.
|
|
115
|
+
function pushEnv(username, token) {
|
|
116
|
+
return { ...process.env, MOCHI_USER: username, MOCHI_TOKEN: token, GIT_TERMINAL_PROMPT: '0' };
|
|
117
|
+
}
|
|
118
|
+
const CREDENTIAL_HELPER = `!f() { test "$1" = get && printf 'username=%s\\npassword=%s\\n' "$MOCHI_USER" "$MOCHI_TOKEN"; }; f`;
|
|
119
|
+
function pushArgs(dir) {
|
|
120
|
+
return ['-C', dir, '-c', 'credential.helper=', '-c', `credential.helper=${CREDENTIAL_HELPER}`];
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* A repository's one-line description is not part of its git data, so a clone
|
|
124
|
+
* and a mirror push carry everything except that. For a GitHub source we can
|
|
125
|
+
* ask for it: one unauthenticated call to the REST API, which answers for a
|
|
126
|
+
* public repository and declines for a private one. Nothing here is worth
|
|
127
|
+
* failing an import that has already arrived, so every failure is a null and
|
|
128
|
+
* the caller says so in one line.
|
|
129
|
+
*/
|
|
130
|
+
async function githubDescription(gh) {
|
|
131
|
+
const url = `https://api.github.com/repos/${encodeURIComponent(gh.owner)}/${encodeURIComponent(gh.repo)}`;
|
|
132
|
+
try {
|
|
133
|
+
const resp = await fetch(url, {
|
|
134
|
+
headers: { accept: 'application/vnd.github+json', 'user-agent': 'mochi-import' },
|
|
135
|
+
signal: AbortSignal.timeout(10000),
|
|
136
|
+
});
|
|
137
|
+
if (!resp.ok)
|
|
138
|
+
return null;
|
|
139
|
+
const data = (await resp.json());
|
|
140
|
+
const description = typeof data.description === 'string' ? data.description.trim() : '';
|
|
141
|
+
return description === '' ? null : description;
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
async function importCmd(args, usage) {
|
|
148
|
+
const a = parseImportArgs(args, usage);
|
|
149
|
+
if (!a.src) {
|
|
150
|
+
console.error('Usage: mochi import <source> <collection>[/<name>]');
|
|
151
|
+
console.error(" e.g. mochi import https://github.com/octocat/Hello-World mycollection");
|
|
152
|
+
process.exit(1);
|
|
153
|
+
}
|
|
154
|
+
const source = (0, source_1.parseSource)(a.src);
|
|
155
|
+
if (!source) {
|
|
156
|
+
console.error(`Not a source we can clone: ${a.src}`);
|
|
157
|
+
console.error('Give an https or ssh git URL, owner/repo for GitHub, or the path to a repository here.');
|
|
158
|
+
process.exit(1);
|
|
159
|
+
}
|
|
160
|
+
const collection = a.collection ?? '';
|
|
161
|
+
const name = a.name ?? source.name;
|
|
162
|
+
if (!collection) {
|
|
163
|
+
console.error(`Which collection should ${name} go into?`);
|
|
164
|
+
console.error(` mochi import ${a.src} mycollection`);
|
|
165
|
+
process.exit(1);
|
|
166
|
+
}
|
|
167
|
+
if (!(0, scan_1.isValidName)(collection) || !(0, scan_1.isValidName)(name)) {
|
|
168
|
+
console.error('Collection and repository names may use letters, digits, dot, underscore, and dash,');
|
|
169
|
+
console.error('and must not be reserved words.');
|
|
170
|
+
process.exit(1);
|
|
171
|
+
}
|
|
172
|
+
const target = await (0, cli_api_1.remoteTarget)(a);
|
|
173
|
+
// Who the push will be as, and whether the token still works: worth knowing
|
|
174
|
+
// before a clone that may take minutes.
|
|
175
|
+
const who = await (0, cli_api_1.api)(target, 'GET', '/api/whoami');
|
|
176
|
+
const username = String(who.username ?? '');
|
|
177
|
+
if (!username) {
|
|
178
|
+
console.error(`${target.host} did not say who this token belongs to.`);
|
|
179
|
+
process.exit(1);
|
|
180
|
+
}
|
|
181
|
+
// A mirror push at an existing repository would replace its refs, so an
|
|
182
|
+
// existing name stops the import here rather than after the clone.
|
|
183
|
+
const listing = `/api/collections/${encodeURIComponent(collection)}`;
|
|
184
|
+
const existing = await (0, cli_api_1.apiTry)(target, 'GET', listing);
|
|
185
|
+
if (existing.ok && (existing.data.repos ?? []).includes(name)) {
|
|
186
|
+
console.error(`${collection}/${name} already exists on ${target.host}.`);
|
|
187
|
+
console.error('A mirror push would replace its branches and tags, so this stops here. Import');
|
|
188
|
+
console.error('under another name,');
|
|
189
|
+
console.error(` mochi import ${a.src} ${collection}/another-name`);
|
|
190
|
+
console.error('or delete the existing repository first, from its settings page.');
|
|
191
|
+
process.exit(1);
|
|
192
|
+
}
|
|
193
|
+
if (!existing.ok && existing.status !== 404)
|
|
194
|
+
(0, cli_api_1.apiFailed)(target, listing, existing);
|
|
195
|
+
const dest = `${target.host}/${encodeURIComponent(collection)}/${encodeURIComponent(name)}`;
|
|
196
|
+
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'mochi-import-'));
|
|
197
|
+
const cleanup = () => {
|
|
198
|
+
try {
|
|
199
|
+
fs.rmSync(tmp, { recursive: true, force: true });
|
|
200
|
+
}
|
|
201
|
+
catch {
|
|
202
|
+
// A leftover temporary directory is not worth failing the import over.
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
try {
|
|
206
|
+
// --bare rather than --mirror: mirroring a GitHub repository drags in
|
|
207
|
+
// refs/pull/*, thousands of refs the vault has no use for. A bare clone
|
|
208
|
+
// carries branches and tags, which is what the push then mirrors.
|
|
209
|
+
console.log(`Cloning ${source.url}`);
|
|
210
|
+
if ((await git(['clone', '--bare', '--', source.url, tmp])) !== 0) {
|
|
211
|
+
throw new Error(`Could not clone ${source.url}.`);
|
|
212
|
+
}
|
|
213
|
+
if (a.lfs) {
|
|
214
|
+
console.log('Fetching Git LFS objects');
|
|
215
|
+
if ((await git(['-C', tmp, 'lfs', 'fetch', '--all'])) !== 0) {
|
|
216
|
+
throw new Error('Could not fetch the LFS objects. Is git-lfs installed?');
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
console.log(`Pushing to ${dest} as ${username}`);
|
|
220
|
+
const env = pushEnv(username, target.token);
|
|
221
|
+
if ((await git([...pushArgs(tmp), 'push', '--mirror', dest], env)) !== 0) {
|
|
222
|
+
throw new Error(`Could not push to ${dest}.\nIf it refused the token, check that you may create in ${collection}.`);
|
|
223
|
+
}
|
|
224
|
+
if (a.lfs) {
|
|
225
|
+
console.log('Pushing Git LFS objects');
|
|
226
|
+
if ((await git([...pushArgs(tmp), 'lfs', 'push', '--all', dest], env)) !== 0) {
|
|
227
|
+
throw new Error('The repository arrived, but its LFS objects did not.');
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
finally {
|
|
232
|
+
// Reported through an exception rather than process.exit, since exiting
|
|
233
|
+
// here would skip this and leave the bare clone behind.
|
|
234
|
+
cleanup();
|
|
235
|
+
}
|
|
236
|
+
// The description is set after the push rather than before it, since the
|
|
237
|
+
// repository does not exist until the push creates it.
|
|
238
|
+
let described = a.description?.trim() || null;
|
|
239
|
+
if (!described && !a.noDescription && source.github)
|
|
240
|
+
described = await githubDescription(source.github);
|
|
241
|
+
if (described) {
|
|
242
|
+
const patch = await (0, cli_api_1.apiTry)(target, 'PATCH', `/api/repos/${encodeURIComponent(collection)}/${encodeURIComponent(name)}`, {
|
|
243
|
+
description: described,
|
|
244
|
+
});
|
|
245
|
+
if (!patch.ok) {
|
|
246
|
+
console.error(`The repository arrived, but its description could not be set: ${patch.data.error ?? `HTTP ${patch.status}`}`);
|
|
247
|
+
described = null;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
console.log('');
|
|
251
|
+
console.log(`Imported ${collection}/${name}`);
|
|
252
|
+
console.log(` ${dest}`);
|
|
253
|
+
// Worth saying rather than leaving to be discovered: a mirror push carries
|
|
254
|
+
// LFS pointer files, so the files look present and read as missing.
|
|
255
|
+
if (described)
|
|
256
|
+
console.log(` ${described}`);
|
|
257
|
+
else if (source.github && !a.noDescription)
|
|
258
|
+
console.log(' (no description came across; set one in repository settings)');
|
|
259
|
+
if (!a.lfs)
|
|
260
|
+
console.log(' (Git LFS objects, if it has any, were not carried over; import again with --lfs)');
|
|
261
|
+
}
|
|
262
|
+
function parseCollectionArgs(args, usage) {
|
|
263
|
+
const out = { name: null, host: null, token: null };
|
|
264
|
+
for (let i = 0; i < args.length; i++) {
|
|
265
|
+
const a = args[i];
|
|
266
|
+
if (a === '-h' || a === '--help')
|
|
267
|
+
usage();
|
|
268
|
+
else if (a === '--host')
|
|
269
|
+
out.host = args[++i];
|
|
270
|
+
else if (a === '--token')
|
|
271
|
+
out.token = args[++i];
|
|
272
|
+
else if (a.startsWith('-')) {
|
|
273
|
+
console.error(`Unknown option: ${a}`);
|
|
274
|
+
process.exit(1);
|
|
275
|
+
}
|
|
276
|
+
else if (!out.name)
|
|
277
|
+
out.name = a;
|
|
278
|
+
else {
|
|
279
|
+
console.error(`Unexpected argument: ${a}`);
|
|
280
|
+
process.exit(1);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
return out;
|
|
284
|
+
}
|
|
285
|
+
async function collectionAddCmd(args, usage) {
|
|
286
|
+
const a = parseCollectionArgs(args, usage);
|
|
287
|
+
if (!a.name) {
|
|
288
|
+
console.error('Usage: mochi collection add <name>');
|
|
289
|
+
process.exit(1);
|
|
290
|
+
}
|
|
291
|
+
const target = await (0, cli_api_1.remoteTarget)(a);
|
|
292
|
+
const data = await (0, cli_api_1.api)(target, 'POST', '/api/collections', { name: a.name });
|
|
293
|
+
console.log(`Created collection '${data.name}' on ${target.host}`);
|
|
294
|
+
console.log(` ${target.host}/${encodeURIComponent(String(data.name))}`);
|
|
295
|
+
console.log('');
|
|
296
|
+
console.log('It has no repositories yet. Put one in it with');
|
|
297
|
+
console.log(` mochi import https://github.com/owner/repo ${data.name}`);
|
|
298
|
+
}
|
|
299
|
+
async function collectionListCmd(args, usage) {
|
|
300
|
+
const a = parseCollectionArgs(args, usage);
|
|
301
|
+
if (a.name) {
|
|
302
|
+
console.error(`Unexpected argument: ${a.name}`);
|
|
303
|
+
process.exit(1);
|
|
304
|
+
}
|
|
305
|
+
const target = await (0, cli_api_1.remoteTarget)(a);
|
|
306
|
+
const data = await (0, cli_api_1.api)(target, 'GET', '/api/collections');
|
|
307
|
+
const collections = (data.collections ?? []);
|
|
308
|
+
if (collections.length === 0) {
|
|
309
|
+
console.log(`No collections on ${target.host}`);
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
const width = Math.max(...collections.map((c) => c.name.length));
|
|
313
|
+
for (const c of collections) {
|
|
314
|
+
console.log(`${c.name.padEnd(width)} ${c.repoCount} ${c.repoCount === 1 ? 'repository' : 'repositories'}`);
|
|
315
|
+
}
|
|
316
|
+
}
|