@akanjs/cli 3.1.0-rc.0 → 3.1.0-rc.2
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/.build-stamp +1 -1
- package/{CodeAgent-q7a4srt6.js → CodeAgent-q50k4s45.js} +2 -2
- package/{CodeTui-ak23dxc9.js → CodeTui-62g35ztk.js} +1 -1
- package/TunnelShare-f1egrktx.js +13 -0
- package/{agent.command-f3gsc1gd.js → agent.command-adfd5299.js} +5 -5
- package/akanCode.js +2 -2
- package/{application.command-3v2d53gd.js → application.command-hj3gv1rx.js} +52 -41
- package/{applicationBuildRunner-1yaqsrdq.js → applicationBuildRunner-by10r9gf.js} +16 -30
- package/buildBatch.proc.js +22 -13
- package/{cloud.command-kk2rwkvw.js → cloud.command-k2fhaape.js} +60 -63
- package/cloud.script-z8p5dz7e.js +23 -0
- package/{code.command-30sca09d.js → code.command-kt5zgcc2.js} +6 -6
- package/{context.command-k7eqacq6.js → context.command-8fgn42jw.js} +16 -13
- package/context.runner-hnh0rg7m.js +24 -0
- package/{guideline.command-dtczrqpf.js → guideline.command-3yxxr2mm.js} +2 -2
- package/guidelines/conventions/conventions.instruction.md +30 -15
- package/guidelines/cssRule/cssRule.instruction.md +12 -6
- package/guidelines/mcpRule/mcpRule.instruction.md +32 -11
- package/guidelines/runtimeRule/runtimeRule.instruction.md +24 -15
- package/guidelines/transportRule/transportRule.instruction.md +28 -5
- package/incrementalBuilder.proc.js +9 -6
- package/{index-kjvrq3nc.js → index-0909wckt.js} +361 -282
- package/{index-p9ystjrp.js → index-20cebtth.js} +92 -52
- package/{index-55mynnqs.js → index-2etqq75t.js} +455 -206
- package/{index-6jjtnhkw.js → index-31ce9vhg.js} +51 -9
- package/{index-y8q31011.js → index-3reaaa1h.js} +42 -153
- package/{index-ww9bhp67.js → index-4pf8g7ff.js} +1 -1
- package/{index-qt22xstj.js → index-58vvz603.js} +5 -3
- package/index-6726qr1m.js +668 -0
- package/index-8935h3n1.js +33 -0
- package/{index-nz15k0b6.js → index-8zdwpxef.js} +1 -1
- package/{index-c2yrsxdh.js → index-azh29dc8.js} +96 -320
- package/{index-c1x0be5j.js → index-bea2gwc6.js} +2 -2
- package/{index-v29746vb.js → index-e9prnmyt.js} +3 -3
- package/{index-tn4rpqp1.js → index-fjsv10hd.js} +73 -49
- package/index-g0vwzy9m.js +241 -0
- package/{index-c76k723e.js → index-h85cxfkg.js} +2 -2
- package/{index-rwq3ekqc.js → index-hnby7p9n.js} +7 -7
- package/{index-2etcv0sc.js → index-jwqtchha.js} +1 -1
- package/index-kc8z134d.js +248 -0
- package/index-nq045px5.js +1124 -0
- package/{index-fm46xrf1.js → index-nq2ja5e0.js} +3 -3
- package/{index-0bm04n5g.js → index-pyxn1b53.js} +10 -8
- package/{index-05g3jayg.js → index-rhbabmja.js} +1 -1
- package/{index-2mx692qq.js → index-rk9sdys3.js} +2 -2
- package/{index-7bymavqk.js → index-sby8fe1p.js} +1 -1
- package/{index-ctanya41.js → index-sqfdmb5b.js} +4 -4
- package/{index-2t4tqd1c.js → index-t7nws9mx.js} +3 -3
- package/{index-jyv387qw.js → index-tv8fjaba.js} +4 -4
- package/{index-gnyf1fqy.js → index-tz5evhdr.js} +2 -2
- package/{index-aprt7pkz.js → index-xpdgxsj5.js} +229 -1327
- package/{index-e0cvze1s.js → index-z507nzhg.js} +4 -4
- package/index.js +22 -22
- package/{library.command-gaga5bmq.js → library.command-8tw59xzj.js} +3 -3
- package/{localRegistry.command-9e84e32q.js → localRegistry.command-tf3dmznd.js} +12 -9
- package/{module.command-7vpf6db4.js → module.command-9vm8hffy.js} +5 -5
- package/{package.command-twmvvf69.js → package.command-erbxe2tz.js} +3 -3
- package/package.json +2 -2
- package/{page.command-5asq7fdf.js → page.command-jpavcj0p.js} +3 -3
- package/{pipeline.command-d0v38572.js → pipeline.command-b8rzvjvv.js} +220 -219
- package/{primitive.command-dyrs2fm8.js → primitive.command-sm32chzk.js} +6 -6
- package/{projectTypecheck-cvtkrzp2.js → projectTypecheck-jamtx95x.js} +1 -1
- package/{quality.command-6emsqpbg.js → quality.command-pkd17n6y.js} +2 -2
- package/{repair.command-8t6aegv5.js → repair.command-8y4mq6hs.js} +5 -5
- package/{scalar.command-bnggy1x7.js → scalar.command-q8khmyx5.js} +4 -4
- package/{subspace.command-f02j5tge.js → subspace.command-51n0ykbx.js} +421 -362
- package/templates/app/env/env.client.type.ts +2 -3
- package/templates/app/page/styles.css.template +8 -0
- package/{tunnel.command-9x8nfn75.js → tunnel.command-7820vtxq.js} +7 -4
- package/{workflow.command-jx0wag5r.js → workflow.command-n5sv4wqz.js} +10 -10
- package/{workspace.command-cmj6mqyk.js → workspace.command-zdy60jh8.js} +33 -30
- package/TunnelShare-21mm6dr5.js +0 -9
- package/cloud.script-r7eyfey4.js +0 -20
- package/context.runner-hqd1ppqe.js +0 -21
- package/index-6w0rbr9d.js +0 -68
- package/index-r2tpeb3n.js +0 -392
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
LibExecutor,
|
|
11
11
|
PkgExecutor,
|
|
12
12
|
ModuleExecutor
|
|
13
|
-
} from "./index-
|
|
13
|
+
} from "./index-2etqq75t.js";
|
|
14
14
|
|
|
15
15
|
// pkgs/@akanjs/devkit/commandDecorators/camelToKebabCase.ts
|
|
16
16
|
var camelToKebabCase = (str) => str.replace(/([A-Z])/g, "-$1").toLowerCase();
|
|
@@ -40,7 +40,7 @@ var getArgMetas = (command, key) => {
|
|
|
40
40
|
const internalArgMetas = allArgMetas.filter((argMeta) => argMeta.type !== "Option");
|
|
41
41
|
return [allArgMetas, argMetas, internalArgMetas];
|
|
42
42
|
};
|
|
43
|
-
var createInternalArgToken = (type) =>
|
|
43
|
+
var createInternalArgToken = (type, option) => option ? { type, option } : { type };
|
|
44
44
|
var normalizePrimitiveArgType = (type) => {
|
|
45
45
|
if (type === String)
|
|
46
46
|
return "string";
|
|
@@ -54,6 +54,7 @@ var App = createInternalArgToken("App");
|
|
|
54
54
|
var Apps = createInternalArgToken("Apps");
|
|
55
55
|
var Lib = createInternalArgToken("Lib");
|
|
56
56
|
var Sys = createInternalArgToken("Sys");
|
|
57
|
+
var SysOrOnlyApp = createInternalArgToken("Sys", { onlyApp: true });
|
|
57
58
|
var Exec = createInternalArgToken("Exec");
|
|
58
59
|
var Pkg = createInternalArgToken("Pkg");
|
|
59
60
|
var Module = createInternalArgToken("Module");
|
|
@@ -160,6 +161,19 @@ class AppSelectionMemory {
|
|
|
160
161
|
}
|
|
161
162
|
}
|
|
162
163
|
|
|
164
|
+
// pkgs/@akanjs/devkit/commandDecorators/exitCodeError.ts
|
|
165
|
+
class ExitCodeError extends Error {
|
|
166
|
+
exitCode;
|
|
167
|
+
name = "ExitCodeError";
|
|
168
|
+
constructor(message, exitCode) {
|
|
169
|
+
super(message);
|
|
170
|
+
this.exitCode = exitCode;
|
|
171
|
+
}
|
|
172
|
+
static codeOf(error) {
|
|
173
|
+
return error instanceof ExitCodeError ? error.exitCode : null;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
163
177
|
// pkgs/@akanjs/devkit/commandDecorators/helpFormatter.ts
|
|
164
178
|
import chalk from "chalk";
|
|
165
179
|
var formatChoice = (choice) => typeof choice === "object" ? choice.label : choice.toString();
|
|
@@ -352,6 +366,7 @@ var select = (config, context) => prompts().then((m) => m.select(config, context
|
|
|
352
366
|
var confirm = (config, context) => prompts().then((m) => m.confirm(config, context));
|
|
353
367
|
var input = (config, context) => prompts().then((m) => m.input(config, context));
|
|
354
368
|
var checkbox = (config, context) => prompts().then((m) => m.checkbox(config, context));
|
|
369
|
+
var argLabelOf = (argMeta) => argMeta.type === "Option" ? `option '--${camelToKebabCase(argMeta.name)}'` : `argument '${argMeta.name}'`;
|
|
355
370
|
var assertEnumChoice = (argMeta, value) => {
|
|
356
371
|
const enumChoices = argMeta.argsOption.enum;
|
|
357
372
|
if (!enumChoices || typeof enumChoices === "function")
|
|
@@ -359,8 +374,12 @@ var assertEnumChoice = (argMeta, value) => {
|
|
|
359
374
|
const choices = normalizeEnumChoices(enumChoices);
|
|
360
375
|
if (choices.some((choice) => String(choice.value) === String(value)))
|
|
361
376
|
return;
|
|
362
|
-
|
|
363
|
-
|
|
377
|
+
throw new Error(`${argLabelOf(argMeta)} argument '${String(value)}' is invalid. Allowed choices are ${choices.map((choice) => choice.name).join(", ")}.`);
|
|
378
|
+
};
|
|
379
|
+
var assertPromptable = (problem, { choices = [], remedy = "pass it on the command line" } = {}) => {
|
|
380
|
+
if (process.stdin.isTTY)
|
|
381
|
+
return;
|
|
382
|
+
throw new Error(`${problem}: stdin is not a terminal to prompt on, so ${remedy}${choices.length ? ` (choices: ${choices.join(", ")})` : ""}.`);
|
|
364
383
|
};
|
|
365
384
|
var inputMessageOf = ({ name, argsOption: { desc, example, ask } }) => ask ? `${ask}: ` : desc ? `${desc}: ` : `Enter the ${name} value${example ? ` (example: ${example})` : ""}: `;
|
|
366
385
|
var getOptionValue = async (argMeta, opt, context) => {
|
|
@@ -379,8 +398,11 @@ var getOptionValue = async (argMeta, opt, context) => {
|
|
|
379
398
|
const choices = normalizeEnumChoices((typeof enumChoices === "function" ? await enumChoices(context) : enumChoices) ?? []);
|
|
380
399
|
if (choices.length === 1)
|
|
381
400
|
return choices[0]?.value;
|
|
401
|
+
assertPromptable(`Missing ${argLabelOf(argMeta)}`, { choices: choices.map((choice) => choice.value) });
|
|
382
402
|
return await select({ message: ask ?? desc ?? `Select the ${name} value`, choices });
|
|
383
|
-
}
|
|
403
|
+
}
|
|
404
|
+
assertPromptable(`Missing ${argLabelOf(argMeta)}`);
|
|
405
|
+
if (type === "boolean") {
|
|
384
406
|
const message = ask ?? desc ?? `Do you want to set ${name}? ${desc ? ` (${desc})` : ""}: `;
|
|
385
407
|
return await confirm({ message });
|
|
386
408
|
} else
|
|
@@ -395,6 +417,10 @@ var getArgumentValue = async (argMeta, value) => {
|
|
|
395
417
|
return defaultValue;
|
|
396
418
|
else if (nullable)
|
|
397
419
|
return null;
|
|
420
|
+
const { enum: enumChoices } = argMeta.argsOption;
|
|
421
|
+
assertPromptable(`Missing ${argLabelOf(argMeta)}`, {
|
|
422
|
+
choices: enumChoices && typeof enumChoices !== "function" ? normalizeEnumChoices(enumChoices).map(({ value }) => value) : []
|
|
423
|
+
});
|
|
398
424
|
return convertArgValue(await input({ message: inputMessageOf(argMeta) }), type ?? "string");
|
|
399
425
|
};
|
|
400
426
|
var assignCommandContext = (context, argMeta, value) => {
|
|
@@ -445,6 +471,7 @@ var getAppsArgumentValue = async (value, workspace) => {
|
|
|
445
471
|
}
|
|
446
472
|
if (appNames.length === 1 && appNames[0])
|
|
447
473
|
return [AppExecutor.from(workspace, appNames[0])];
|
|
474
|
+
assertPromptable("Missing apps", { choices: [...appNames, "all"] });
|
|
448
475
|
const remembered = new Set(await AppSelectionMemory.read(workspace.workspaceRoot));
|
|
449
476
|
const picked = await checkbox({
|
|
450
477
|
message: "Select the apps to run (space to toggle, enter to confirm)",
|
|
@@ -459,6 +486,7 @@ var getInternalArgumentValue = async (argMeta, value, workspace) => {
|
|
|
459
486
|
return workspace;
|
|
460
487
|
const sysType = argMeta.type.toLowerCase();
|
|
461
488
|
const [appNames, libNames, pkgNames] = await workspace.getExecs();
|
|
489
|
+
const problemOf = (kind) => value ? `Unknown ${kind} '${value}'` : `Missing ${kind}`;
|
|
462
490
|
if (sysType === "sys" || sysType === "exec") {
|
|
463
491
|
const pkgChoices = sysType === "exec" ? pkgNames : [];
|
|
464
492
|
const execOf = (name) => {
|
|
@@ -473,6 +501,11 @@ var getInternalArgumentValue = async (argMeta, value, workspace) => {
|
|
|
473
501
|
const found = value ? execOf(value) : null;
|
|
474
502
|
if (found)
|
|
475
503
|
return found;
|
|
504
|
+
if (!value && argMeta.option?.onlyApp && appNames.length === 1 && appNames[0])
|
|
505
|
+
return AppExecutor.from(workspace, appNames[0]);
|
|
506
|
+
assertPromptable(problemOf(sysType === "exec" ? "app, lib or pkg" : "app or lib"), {
|
|
507
|
+
choices: [...appNames, ...libNames, ...pkgChoices]
|
|
508
|
+
});
|
|
476
509
|
const name = await select({
|
|
477
510
|
message: sysType === "exec" ? `Select the App or Lib or Pkg name` : `Select the App or Lib name`,
|
|
478
511
|
choices: [...appNames, ...libNames, ...pkgChoices]
|
|
@@ -486,17 +519,20 @@ var getInternalArgumentValue = async (argMeta, value, workspace) => {
|
|
|
486
519
|
return AppExecutor.from(workspace, value);
|
|
487
520
|
if (!value && appNames.length === 1 && appNames[0])
|
|
488
521
|
return AppExecutor.from(workspace, appNames[0]);
|
|
522
|
+
assertPromptable(problemOf("app"), { choices: appNames });
|
|
489
523
|
const appName = await select({ message: `Select the ${sysType} name`, choices: appNames });
|
|
490
524
|
return AppExecutor.from(workspace, appName);
|
|
491
525
|
} else if (sysType === "lib") {
|
|
492
526
|
if (value && libNames.includes(value))
|
|
493
527
|
return LibExecutor.from(workspace, value);
|
|
528
|
+
assertPromptable(problemOf("lib"), { choices: libNames });
|
|
494
529
|
const libName = await select({ message: `Select the ${sysType} name`, choices: libNames });
|
|
495
530
|
return LibExecutor.from(workspace, libName);
|
|
496
531
|
} else if (sysType === "pkg") {
|
|
497
532
|
const pkgs = await workspace.getPkgs();
|
|
498
533
|
if (value && pkgs.includes(value))
|
|
499
534
|
return PkgExecutor.from(workspace, value);
|
|
535
|
+
assertPromptable(problemOf("pkg"), { choices: pkgs });
|
|
500
536
|
const pkgName = await select({ message: `Select the ${sysType} name`, choices: pkgs });
|
|
501
537
|
return PkgExecutor.from(workspace, pkgName);
|
|
502
538
|
} else if (sysType === "module") {
|
|
@@ -519,6 +555,7 @@ var getInternalArgumentValue = async (argMeta, value, workspace) => {
|
|
|
519
555
|
} else
|
|
520
556
|
throw new Error(`Invalid system name: ${sysName}`);
|
|
521
557
|
}
|
|
558
|
+
assertPromptable(problemOf("module"));
|
|
522
559
|
const { type, name } = await select({
|
|
523
560
|
message: `select the App or Lib name`,
|
|
524
561
|
choices: [
|
|
@@ -540,7 +577,7 @@ var runCommands = async (...commands) => {
|
|
|
540
577
|
let failureExitCode = 1;
|
|
541
578
|
process.on("unhandledRejection", (error) => {
|
|
542
579
|
printCliError(error);
|
|
543
|
-
process.exit(failureExitCode);
|
|
580
|
+
process.exit(ExitCodeError.codeOf(error) ?? failureExitCode);
|
|
544
581
|
});
|
|
545
582
|
const __dirname2 = getDirname2(import.meta.url);
|
|
546
583
|
const packageJsonCandidates = [`${path2.dirname(Bun.main)}/package.json`, `${__dirname2}/../package.json`];
|
|
@@ -647,7 +684,7 @@ It may cause unexpected behavior. Run \`akan update\` to update latest akanjs.`)
|
|
|
647
684
|
}
|
|
648
685
|
await program.parseAsync(process.argv).catch((error) => {
|
|
649
686
|
printCliError(error);
|
|
650
|
-
process.exit(failureExitCode);
|
|
687
|
+
process.exit(ExitCodeError.codeOf(error) ?? failureExitCode);
|
|
651
688
|
});
|
|
652
689
|
};
|
|
653
690
|
|
|
@@ -671,7 +708,12 @@ class TargetBuilder {
|
|
|
671
708
|
return this.#add(argMetaOf("Option", name, type, argsOption, this.#args.length));
|
|
672
709
|
}
|
|
673
710
|
with(...tokens) {
|
|
674
|
-
return this.#add(...tokens.map((token, offset) => ({
|
|
711
|
+
return this.#add(...tokens.map((token, offset) => ({
|
|
712
|
+
key: "",
|
|
713
|
+
idx: this.#args.length + offset,
|
|
714
|
+
type: token.type,
|
|
715
|
+
...token.option ? { option: token.option } : {}
|
|
716
|
+
})));
|
|
675
717
|
}
|
|
676
718
|
exec(handler) {
|
|
677
719
|
return {
|
|
@@ -718,4 +760,4 @@ function command(refName, depsOrBuilder, builder) {
|
|
|
718
760
|
}
|
|
719
761
|
return CommandBase;
|
|
720
762
|
}
|
|
721
|
-
export { getTargetMetas, getTargetCommandNames, App, Apps, Lib, Sys, Exec, Pkg, Module, Workspace, CommandContainer, runner, script, runCommands, command };
|
|
763
|
+
export { getTargetMetas, getTargetCommandNames, App, Apps, Lib, Sys, SysOrOnlyApp, Exec, Pkg, Module, Workspace, CommandContainer, runner, script, ExitCodeError, assertPromptable, runCommands, command };
|
|
@@ -3,24 +3,20 @@ import {
|
|
|
3
3
|
getDefaultHostConfig,
|
|
4
4
|
GlobalConfig,
|
|
5
5
|
CloudApi,
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
runner
|
|
10
|
-
} from "./index-6jjtnhkw.js";
|
|
6
|
+
CloudUpdates,
|
|
7
|
+
PipelineSecrets
|
|
8
|
+
} from "./index-6726qr1m.js";
|
|
11
9
|
import {
|
|
12
10
|
WorkspaceExecutor,
|
|
13
11
|
AppExecutor
|
|
14
|
-
} from "./index-
|
|
12
|
+
} from "./index-2etqq75t.js";
|
|
15
13
|
import {
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
runner,
|
|
15
|
+
assertPromptable
|
|
16
|
+
} from "./index-31ce9vhg.js";
|
|
18
17
|
import {
|
|
19
18
|
SubspaceConfig
|
|
20
|
-
} from "./index-
|
|
21
|
-
import {
|
|
22
|
-
resolveMobileTargets
|
|
23
|
-
} from "./index-q958tqqg.js";
|
|
19
|
+
} from "./index-4pf8g7ff.js";
|
|
24
20
|
import {
|
|
25
21
|
openBrowser
|
|
26
22
|
} from "./index-9dp031by.js";
|
|
@@ -657,11 +653,9 @@ class PlatformTestRun {
|
|
|
657
653
|
}
|
|
658
654
|
|
|
659
655
|
// pkgs/@akanjs/cli/cloud/cloud.runner.ts
|
|
660
|
-
import { rm } from "fs/promises";
|
|
661
|
-
import os3 from "os";
|
|
662
656
|
import path7 from "path";
|
|
663
|
-
import { confirm, input,
|
|
664
|
-
import { Logger,
|
|
657
|
+
import { confirm, input, select } from "@inquirer/prompts";
|
|
658
|
+
import { Logger, sleep } from "akanjs/common";
|
|
665
659
|
import chalk2 from "chalk";
|
|
666
660
|
import * as QRcode from "qrcode";
|
|
667
661
|
|
|
@@ -766,6 +760,9 @@ class CloudRunner extends runner("cloud") {
|
|
|
766
760
|
return { name: `${name} (${this.#getSshTarget(config)}${config.port ? `:${config.port}` : ""})`, value: name };
|
|
767
761
|
}
|
|
768
762
|
async#getRemoteEnvServerWithUsername() {
|
|
763
|
+
assertPromptable("The env archive goes over scp to a remote server picked here, as AKAN_WORKSPACE_ID is not set", {
|
|
764
|
+
remedy: "set AKAN_WORKSPACE_ID in the workspace .env to use Akan Cloud, or run it once in a terminal"
|
|
765
|
+
});
|
|
769
766
|
const remoteServer = await this.#selectRemoteEnvServer();
|
|
770
767
|
if (remoteServer.config.username)
|
|
771
768
|
return remoteServer;
|
|
@@ -891,6 +888,9 @@ ${run.format(results)}
|
|
|
891
888
|
const reason = result.infraError ?? "failing tests";
|
|
892
889
|
if (!interactive)
|
|
893
890
|
continue;
|
|
891
|
+
assertPromptable(`${result.platform} did not pass (${reason}) \u2014 ${run.logDir}`, {
|
|
892
|
+
remedy: "leave it out of --platforms to publish without it, or run it in a terminal to be asked"
|
|
893
|
+
});
|
|
894
894
|
const proceed = await confirm({
|
|
895
895
|
message: `${result.platform} did not pass (${reason}). Continue without it?`,
|
|
896
896
|
default: false
|
|
@@ -909,6 +909,9 @@ ${run.format(results)}
|
|
|
909
909
|
return results;
|
|
910
910
|
}
|
|
911
911
|
async#askWindowsTestTarget() {
|
|
912
|
+
assertPromptable("No Windows test target is configured", {
|
|
913
|
+
remedy: "run it once in a terminal, which saves the target to ~/.akan/config.json"
|
|
914
|
+
});
|
|
912
915
|
Logger.info("No Windows test target is configured yet; it is saved to ~/.akan/config.json once entered.");
|
|
913
916
|
const required = (value) => !!value.trim();
|
|
914
917
|
const host = await this.#ask("Windows host (ip or name): ", required);
|
|
@@ -944,11 +947,15 @@ ${run.format(results)}
|
|
|
944
947
|
for (const library of akanPkgs) {
|
|
945
948
|
const packageJson = await workspace.readJson(`pkgs/${library}/package.json`);
|
|
946
949
|
const newPackageJson = this.#normalizeAkanPackageJson(packageJson, library, nextVersion);
|
|
947
|
-
await workspace.
|
|
950
|
+
await workspace.writeJson(`pkgs/${library}/package.json`, newPackageJson);
|
|
948
951
|
const distPackageJson = await workspace.readJson(`dist/pkgs/${library}/package.json`);
|
|
949
952
|
const newDistPackageJson = this.#normalizeAkanPackageJson(distPackageJson, library, nextVersion);
|
|
950
953
|
await workspace.writeJson(`dist/pkgs/${library}/package.json`, newDistPackageJson);
|
|
951
954
|
}
|
|
955
|
+
if (confirmPublish)
|
|
956
|
+
assertPromptable(`Publishing ${akanPkgs.join(", ")}@${nextVersion} asks for a confirmation`, {
|
|
957
|
+
remedy: "run it in a terminal"
|
|
958
|
+
});
|
|
952
959
|
if (confirmPublish && !await confirm({ message: "Are you sure you want to deploy the libraries?" })) {
|
|
953
960
|
Logger.error("Deployment cancelled");
|
|
954
961
|
return;
|
|
@@ -1014,7 +1021,7 @@ ${run.format(results)}
|
|
|
1014
1021
|
async downloadEnv(cloudApi, workspace, workspaceId) {
|
|
1015
1022
|
await workspace.mkdir("local");
|
|
1016
1023
|
const localPath = await cloudApi.downloadEnv(workspaceId).catch((error) => {
|
|
1017
|
-
throw
|
|
1024
|
+
throw CloudApi.refusal(error, "downloading its env archive takes the project owner's own `akan login`");
|
|
1018
1025
|
});
|
|
1019
1026
|
const relativePath = path7.relative(workspace.workspaceRoot, localPath).split(path7.sep).join("/");
|
|
1020
1027
|
await workspace.spawn("tar", ["-xf", relativePath], { cwd: workspace.workspaceRoot });
|
|
@@ -1023,7 +1030,7 @@ ${run.format(results)}
|
|
|
1023
1030
|
async uploadEnv(cloudApi, workspaceId, filePath) {
|
|
1024
1031
|
const file = new File([Bun.file(filePath)], path7.basename(filePath));
|
|
1025
1032
|
await cloudApi.uploadEnv(workspaceId, file).catch((error) => {
|
|
1026
|
-
throw
|
|
1033
|
+
throw CloudApi.refusal(error, "an env archive is uploaded by a project member signed in with `akan login`");
|
|
1027
1034
|
});
|
|
1028
1035
|
}
|
|
1029
1036
|
async downloadEnvByScp(workspace) {
|
|
@@ -1096,7 +1103,7 @@ ${run.format(results)}
|
|
|
1096
1103
|
}));
|
|
1097
1104
|
return secretPaths.flat();
|
|
1098
1105
|
}
|
|
1099
|
-
async
|
|
1106
|
+
async projectOf(workspace, subspace, appName) {
|
|
1100
1107
|
if (!subspace) {
|
|
1101
1108
|
const workspaceId = workspace.getWorkspaceId({ allowEmpty: true });
|
|
1102
1109
|
if (!workspaceId)
|
|
@@ -1109,148 +1116,30 @@ ${run.format(results)}
|
|
|
1109
1116
|
const [declaration] = config.select([subspace]);
|
|
1110
1117
|
if (!declaration?.workspaceId)
|
|
1111
1118
|
throw new Error(`Subspace "${subspace}" declares no workspaceId in ${SubspaceConfig.source}.`);
|
|
1119
|
+
if (appName && !declaration.apps.includes(appName))
|
|
1120
|
+
throw new Error(`Subspace "${subspace}" declares no app ${appName} in ${SubspaceConfig.source}; it serves ${declaration.apps.join(", ")}.`);
|
|
1112
1121
|
return declaration.workspaceId;
|
|
1113
1122
|
}
|
|
1114
1123
|
async readSecretValue(name, file) {
|
|
1115
|
-
|
|
1116
|
-
const value = raw.replace(/\r?\n$/, "");
|
|
1117
|
-
if (!value)
|
|
1118
|
-
throw new Error(`The value of ${name} is empty.`);
|
|
1119
|
-
const bytes = new TextEncoder().encode(value).length;
|
|
1120
|
-
if (bytes > CloudRunner.#secretBytes)
|
|
1121
|
-
throw new Error(`The value of ${name} is ${bytes} bytes, over ${CloudRunner.#secretBytes}.`);
|
|
1122
|
-
return value;
|
|
1123
|
-
}
|
|
1124
|
-
async uploadSecret(cloudApi, devProjectId, { name, branch, value }) {
|
|
1125
|
-
CloudRunner.#assertSecretName(name);
|
|
1126
|
-
CloudRunner.#assertSecretBranch(branch);
|
|
1127
|
-
const saved = await cloudApi.uploadSecret(devProjectId, { scope: branch ?? "*", name, value }).catch((error) => {
|
|
1128
|
-
throw CloudRunner.#refusal(error, "a secret is written by a project member signed in with `akan login`", {
|
|
1129
|
-
unavailable: CloudRunner.#secretsUnavailable
|
|
1130
|
-
});
|
|
1131
|
-
});
|
|
1132
|
-
Logger.rawLog(`Saved ${saved.name} for ${saved.scope === "*" ? "every branch" : `branch ${saved.scope}`}. Builds that start from now on use it.`);
|
|
1124
|
+
return await PipelineSecrets.readValue(name, file);
|
|
1133
1125
|
}
|
|
1134
|
-
async
|
|
1135
|
-
|
|
1136
|
-
const listed = await cloudApi.listSecrets(devProjectId).catch((error) => {
|
|
1137
|
-
throw CloudRunner.#refusal(error, "the secrets are listed for a project member", {
|
|
1138
|
-
unavailable: CloudRunner.#secretsUnavailable
|
|
1139
|
-
});
|
|
1140
|
-
});
|
|
1141
|
-
const secrets = listed.filter((secret) => !branch || secret.scope === "*" || secret.scope === branch);
|
|
1142
|
-
Logger.rawLog(CloudRunner.formatSecrets(secrets));
|
|
1143
|
-
}
|
|
1144
|
-
async removeSecret(cloudApi, devProjectId, { name, branch }) {
|
|
1145
|
-
CloudRunner.#assertSecretName(name);
|
|
1146
|
-
CloudRunner.#assertSecretBranch(branch);
|
|
1147
|
-
const removed = await cloudApi.removeSecret(devProjectId, { scope: branch ?? "*", name }).catch((error) => {
|
|
1148
|
-
throw CloudRunner.#refusal(error, "a secret is removed by a project member signed in with `akan login`", {
|
|
1149
|
-
unavailable: CloudRunner.#secretsUnavailable
|
|
1150
|
-
});
|
|
1151
|
-
});
|
|
1152
|
-
Logger.rawLog(removed ? `Removed ${name} for ${branch ? `branch ${branch}` : "every branch"}.` : `No ${name} for ${branch ? `branch ${branch}` : "every branch"} to remove.`);
|
|
1126
|
+
async uploadSecret(cloudApi, devProjectId, secret) {
|
|
1127
|
+
await new PipelineSecrets(cloudApi, devProjectId).upload(secret);
|
|
1153
1128
|
}
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
const tar = Bun.spawn(["tar", "-czf", archive, "-C", pack.out, "."], { stdout: "ignore", stderr: "pipe" });
|
|
1157
|
-
if (await tar.exited !== 0)
|
|
1158
|
-
throw new Error(`tar of ${pack.out} failed: ${(await new Response(tar.stderr).text()).trim()}`);
|
|
1159
|
-
return archive;
|
|
1129
|
+
async listSecrets(cloudApi, devProjectId, branch) {
|
|
1130
|
+
await new PipelineSecrets(cloudApi, devProjectId).list(branch);
|
|
1160
1131
|
}
|
|
1161
|
-
async
|
|
1162
|
-
|
|
1163
|
-
const target = channel ?? pack.channel;
|
|
1164
|
-
try {
|
|
1165
|
-
const release = await new UpdatePackUpload(cloudApi, devProjectId).send(archive, { appName: pack.app, target: pack.target, platform: pack.platform, channel: target, notes }).catch((error) => {
|
|
1166
|
-
throw CloudRunner.#refusal(error, "a pack is uploaded by a project member signed in with `akan login`");
|
|
1167
|
-
});
|
|
1168
|
-
const label = `${pack.app} ${pack.target} ${pack.platform} on channel ${target}: release ${release.id}`;
|
|
1169
|
-
if (release.status === "rejected") {
|
|
1170
|
-
process.exitCode = 2;
|
|
1171
|
-
Logger.error(`The project rejected ${label}:
|
|
1172
|
-
${(release.problems ?? []).map((problem) => ` - ${problem}`).join(`
|
|
1173
|
-
`)}`);
|
|
1174
|
-
} else if (release.status === "published")
|
|
1175
|
-
Logger.rawLog(`Published ${label} (${pack.files} files, native API ${pack.nativeApi}).`);
|
|
1176
|
-
else
|
|
1177
|
-
Logger.rawLog(`Staged ${label} (${pack.files} files, native API ${pack.nativeApi}). Publish it from the project's release screen.`);
|
|
1178
|
-
return release;
|
|
1179
|
-
} finally {
|
|
1180
|
-
await rm(archive, { force: true });
|
|
1181
|
-
}
|
|
1132
|
+
async removeSecret(cloudApi, devProjectId, secret) {
|
|
1133
|
+
await new PipelineSecrets(cloudApi, devProjectId).remove(secret);
|
|
1182
1134
|
}
|
|
1183
|
-
async
|
|
1184
|
-
|
|
1185
|
-
if (!mobileTarget)
|
|
1186
|
-
throw new Error(`Native target '${pack.target}' was not found.`);
|
|
1187
|
-
const bundlePath = path7.join(new NativeApp(app, mobileTarget).outDir(pack.platform), "bundle.json");
|
|
1188
|
-
const bundleFile = Bun.file(bundlePath);
|
|
1189
|
-
if (!await bundleFile.exists())
|
|
1190
|
-
throw new Error(`No store build at ${bundlePath}: run \`akan release-${pack.platform} ${app.name} --target ${pack.target} --env ${env}\` first.`);
|
|
1191
|
-
const bundle = await bundleFile.json();
|
|
1192
|
-
if (!bundle.updates)
|
|
1193
|
-
throw new Error(`The store build at ${bundlePath} takes no updates: give the target native.updates.`);
|
|
1194
|
-
if (bundle.nativeApi?.hash !== pack.nativeApi)
|
|
1195
|
-
throw new Error(`The store build at ${bundlePath} provides native API ${bundle.nativeApi?.hash}, and this commit's pack needs ${pack.nativeApi}: build the store binary from this commit.`);
|
|
1196
|
-
const archive = await CloudRunner.#tarPack(pack);
|
|
1197
|
-
try {
|
|
1198
|
-
const binary = await cloudApi.registerAppBinary(devProjectId, {
|
|
1199
|
-
appName: pack.app,
|
|
1200
|
-
target: pack.target,
|
|
1201
|
-
distribution,
|
|
1202
|
-
bundle: bundleFile,
|
|
1203
|
-
pack: Bun.file(archive),
|
|
1204
|
-
...bundle.updates.channel ? { channel: bundle.updates.channel } : {}
|
|
1205
|
-
}).catch((error) => {
|
|
1206
|
-
throw CloudRunner.#refusal(error, "a binary is registered by a project member signed in with `akan login`");
|
|
1207
|
-
});
|
|
1208
|
-
Logger.rawLog(`Registered ${pack.app} ${pack.target} ${pack.platform} (${distribution}): binary ${binary.id}, native API ${pack.nativeApi}.`);
|
|
1209
|
-
return binary;
|
|
1210
|
-
} finally {
|
|
1211
|
-
await rm(archive, { force: true });
|
|
1212
|
-
}
|
|
1135
|
+
async readPack(app, dir, flags) {
|
|
1136
|
+
return await CloudUpdates.readPack(app, dir, flags);
|
|
1213
1137
|
}
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
return "No pipeline secrets.";
|
|
1217
|
-
const rows = [...secrets].sort((a, b) => a.name.localeCompare(b.name) || a.scope.localeCompare(b.scope)).map((secret) => [
|
|
1218
|
-
secret.name,
|
|
1219
|
-
secret.scope === "*" ? "every branch" : secret.scope,
|
|
1220
|
-
secret.updatedAt,
|
|
1221
|
-
secret.updatedBy
|
|
1222
|
-
]);
|
|
1223
|
-
const header = ["NAME", "SCOPE", "UPDATED", "BY"];
|
|
1224
|
-
const widths = header.map((title, column) => Math.max(title.length, ...rows.map((row) => row[column]?.length ?? 0)));
|
|
1225
|
-
return [header, ...rows].map((row) => row.map((cell, column) => cell.padEnd(widths[column] ?? 0)).join(" ").trimEnd()).join(`
|
|
1226
|
-
`);
|
|
1138
|
+
async pushUpdate(cloudApi, devProjectId, pack, options) {
|
|
1139
|
+
return await new CloudUpdates(cloudApi, devProjectId).push(pack, options);
|
|
1227
1140
|
}
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
static #secretsUnavailable = "The server is not configured for pipeline secrets yet.";
|
|
1231
|
-
static #assertSecretName(name) {
|
|
1232
|
-
const problem = PipelineNames.secretNameError(name);
|
|
1233
|
-
if (problem)
|
|
1234
|
-
throw new Error(problem);
|
|
1235
|
-
}
|
|
1236
|
-
static #assertSecretBranch(branch) {
|
|
1237
|
-
if (branch !== null && !CloudRunner.#secretBranches.includes(branch))
|
|
1238
|
-
throw new Error(`--branch ${branch} is not a deployed branch: ${CloudRunner.#secretBranches.join(", ")}.`);
|
|
1239
|
-
}
|
|
1240
|
-
static #refusal(error, who, { unavailable } = {}) {
|
|
1241
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
1242
|
-
const status = CloudApi.statusOf(error);
|
|
1243
|
-
if (status === 401)
|
|
1244
|
-
return new Error("The server did not accept your sign-in: run `akan login`, then try again.");
|
|
1245
|
-
if (status === 403)
|
|
1246
|
-
return new Error(`The project refused: ${who}.`);
|
|
1247
|
-
if (status === 400) {
|
|
1248
|
-
const key = /"error"\s*:\s*"([^"]+)"/.exec(message)?.[1];
|
|
1249
|
-
return new Error(`The project refused the request${key ? `: ${key}` : ""}.`);
|
|
1250
|
-
}
|
|
1251
|
-
if (status === 503 && unavailable)
|
|
1252
|
-
return new Error(unavailable);
|
|
1253
|
-
return error;
|
|
1141
|
+
async registerBinary(cloudApi, devProjectId, app, pack, options) {
|
|
1142
|
+
return await new CloudUpdates(cloudApi, devProjectId).registerBinary(app, pack, options);
|
|
1254
1143
|
}
|
|
1255
1144
|
async#syncSecretGitignore(workspace, appNames) {
|
|
1256
1145
|
const patterns = (await Promise.all(appNames.map(async (appName) => {
|
|
@@ -55,7 +55,7 @@ class SubspaceConfig {
|
|
|
55
55
|
assertPushable(branch) {
|
|
56
56
|
if (this.pushableBranches.includes(branch))
|
|
57
57
|
return;
|
|
58
|
-
throw new Error(`Branch "${branch}" is not pushable.
|
|
58
|
+
throw new Error(`Branch "${branch}" is not pushable. \`subspace.pushableBranches\` in the workspace root akan.config.ts allows: ${this.pushableBranches.join(", ")}`);
|
|
59
59
|
}
|
|
60
60
|
}
|
|
61
61
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
import {
|
|
3
3
|
AppInfo
|
|
4
|
-
} from "./index-
|
|
4
|
+
} from "./index-2etqq75t.js";
|
|
5
5
|
|
|
6
6
|
// pkgs/@akanjs/devkit/slicePlanner.ts
|
|
7
7
|
class SlicePlanner {
|
|
@@ -33,6 +33,9 @@ class SlicePlanner {
|
|
|
33
33
|
}
|
|
34
34
|
return found ? peers : null;
|
|
35
35
|
}
|
|
36
|
+
static rootEntriesOf(rootFiles) {
|
|
37
|
+
return [...new Set(rootFiles.map((file) => file.split("/")[0] ?? file))].sort();
|
|
38
|
+
}
|
|
36
39
|
static typesPackageOf(dependency) {
|
|
37
40
|
return dependency.startsWith("@") ? `@types/${dependency.slice(1).replace("/", "__")}` : `@types/${dependency}`;
|
|
38
41
|
}
|
|
@@ -131,7 +134,6 @@ class SlicePlanner {
|
|
|
131
134
|
};
|
|
132
135
|
}
|
|
133
136
|
}
|
|
134
|
-
var rootEntriesOf = (rootFiles) => [...new Set(rootFiles.map((file) => file.split("/")[0] ?? file))].sort();
|
|
135
137
|
function formatSlicePlan(plan) {
|
|
136
138
|
const libCounts = Object.entries(plan.libFiles).map(([lib, files]) => ` - libs/${lib}: ${files.length} files`);
|
|
137
139
|
const sections = [
|
|
@@ -139,7 +141,7 @@ function formatSlicePlan(plan) {
|
|
|
139
141
|
`app: ${plan.app} (apps/${plan.app}: ${plan.appFiles.length} files)`,
|
|
140
142
|
`libs: ${plan.libs.length ? plan.libs.join(", ") : "(none)"}`,
|
|
141
143
|
...libCounts,
|
|
142
|
-
`workspace shell: ${plan.rootFiles.length} files across ${rootEntriesOf(plan.rootFiles).join(", ")}`,
|
|
144
|
+
`workspace shell: ${plan.rootFiles.length} files across ${SlicePlanner.rootEntriesOf(plan.rootFiles).join(", ")}`,
|
|
143
145
|
"",
|
|
144
146
|
`Root dependencies this slice does not use (${plan.unusedDependencies.length}) \u2014 pruned:`,
|
|
145
147
|
"",
|