@akanjs/cli 3.1.0-rc.1 → 3.1.0-rc.11
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-kyprmy06.js → CodeAgent-rkjyxgec.js} +2 -2
- package/{CodeTui-21mtze3j.js → CodeTui-h7pyj6xa.js} +1 -1
- package/TunnelShare-661cxz5e.js +14 -0
- package/{agent.command-f3gsc1gd.js → agent.command-3qxf4xcj.js} +5 -5
- package/akanCode.js +2 -2
- package/{application.command-5s780vnz.js → application.command-m1kcd7jf.js} +54 -42
- package/{applicationBuildRunner-1yaqsrdq.js → applicationBuildRunner-dj0xkrep.js} +26 -33
- package/buildBatch.proc.js +24 -15
- package/{cloud.command-yj6xb2a7.js → cloud.command-9cp1t0f5.js} +60 -67
- package/cloud.script-dgstqt1m.js +24 -0
- package/{code.command-3wkn4ja5.js → code.command-0ad6gw2a.js} +6 -6
- package/{context.command-9jjn3gcw.js → context.command-0qx447mb.js} +17 -13
- package/context.runner-1vsjztjg.js +25 -0
- package/{dependencyScanner-06z2dmrr.js → dependencyScanner-3afj2vt1.js} +1 -1
- package/{guideline.command-dtczrqpf.js → guideline.command-cv5bmrx2.js} +2 -2
- package/guidelines/agentRule/agentRule.instruction.md +3 -3
- package/guidelines/conventions/conventions.instruction.md +457 -1071
- package/guidelines/cssRule/cssRule.instruction.md +28 -7
- package/guidelines/fieldRule/fieldRule.instruction.md +62 -0
- package/guidelines/framework/framework.instruction.md +4 -7
- package/guidelines/mcpRule/mcpRule.instruction.md +43 -20
- package/guidelines/modelConstant/modelConstant.instruction.md +2 -1
- package/guidelines/modelStore/modelStore.instruction.md +9 -0
- package/guidelines/pageRule/pageRule.instruction.md +80 -0
- package/guidelines/queryRule/queryRule.instruction.md +44 -11
- package/guidelines/runtimeRule/runtimeRule.instruction.md +113 -17
- package/guidelines/ssrRule/ssrRule.instruction.md +13 -0
- package/guidelines/transportRule/transportRule.instruction.md +135 -13
- package/guidelines/workspaceOnboarding/workspaceOnboarding.instruction.md +4 -4
- package/guidelines/workspaceRecipes/workspaceRecipes.instruction.md +68 -7
- package/incrementalBuilder.proc.js +10 -7
- package/index-192ha4xh.js +1173 -0
- package/{index-c76k723e.js → index-1p98exm2.js} +2 -2
- package/{index-rwq3ekqc.js → index-1xj2t7cb.js} +7 -7
- package/{index-rtct7d43.js → index-20bxxyvn.js} +80 -80
- package/{index-qt22xstj.js → index-35hc95h5.js} +5 -3
- package/{index-8r4d7cmt.js → index-4mhy6qme.js} +4 -4
- package/{index-ww9bhp67.js → index-4pf8g7ff.js} +1 -1
- package/{index-2mx692qq.js → index-5edzv8fn.js} +5 -5
- package/{index-k9rrten6.js → index-5nd1wtkv.js} +92 -94
- package/index-62md4dwy.js +260 -0
- package/index-665zp9rs.js +271 -0
- package/{index-2etcv0sc.js → index-86jt2a0e.js} +1 -1
- package/{index-krq7btbt.js → index-8fp3b47h.js} +419 -418
- package/{index-c2yrsxdh.js → index-9z426msb.js} +109 -328
- package/{index-ctanya41.js → index-awbdfcf2.js} +4 -4
- package/{index-3hx1z96c.js → index-bqbf654f.js} +1 -1
- package/{index-rc5rwyps.js → index-bspm21y1.js} +71 -60
- package/index-cpb3nvma.js +667 -0
- package/{index-jx4bzvwg.js → index-d47ypebv.js} +97 -55
- package/{index-ae5pwhgs.js → index-d72hfmdm.js} +15 -13
- package/{index-e0cvze1s.js → index-fmzcs8v6.js} +4 -4
- package/{index-8ggztp8k.js → index-fyq1z6kt.js} +68 -201
- package/{index-1w2qaee2.js → index-g1mbppa9.js} +331 -259
- package/{index-gnyf1fqy.js → index-j237xtew.js} +6 -6
- package/{index-fm46xrf1.js → index-j4mkhz93.js} +3 -3
- package/{index-6jjtnhkw.js → index-jw1zm273.js} +74 -34
- package/{index-q958tqqg.js → index-m9jj544v.js} +1 -1
- package/index-mdjc93e1.js +33 -0
- package/{index-c1x0be5j.js → index-pydbnfez.js} +2 -2
- package/{index-56d2g8ms.js → index-qwa5k1j8.js} +1 -1
- package/{index-v29746vb.js → index-tamvyvj3.js} +3 -3
- package/{index-05g3jayg.js → index-trpasv9q.js} +260 -259
- package/{index-2ahj896a.js → index-tvehn2sp.js} +1 -1
- package/{index-55mynnqs.js → index-va06nr10.js} +1434 -1173
- package/index-vk0pa0mn.js +240 -0
- package/{index-aprt7pkz.js → index-w44cy4vs.js} +2271 -3369
- package/index.js +22 -22
- package/{library.command-gaga5bmq.js → library.command-erp2f56n.js} +3 -3
- package/{localRegistry.command-vsreyy42.js → localRegistry.command-y176p558.js} +15 -11
- package/{module.command-7vpf6db4.js → module.command-gs2r615e.js} +5 -5
- package/{package.command-twmvvf69.js → package.command-j10r76ax.js} +4 -4
- package/package.json +21 -21
- package/{page.command-5asq7fdf.js → page.command-4h4a8pnh.js} +3 -3
- package/{pipeline.command-d0v38572.js → pipeline.command-a2nrk26m.js} +221 -220
- package/{primitive.command-dyrs2fm8.js → primitive.command-w6t0asqz.js} +6 -6
- package/{projectTypecheck-cvtkrzp2.js → projectTypecheck-f534enqj.js} +1 -1
- package/{quality.command-6emsqpbg.js → quality.command-77x7vbbx.js} +4 -4
- package/{repair.command-8t6aegv5.js → repair.command-y6sstev7.js} +5 -5
- package/{scalar.command-bnggy1x7.js → scalar.command-20b8cy0k.js} +4 -4
- package/{subspace.command-md2teygq.js → subspace.command-5c7yasd7.js} +424 -364
- package/templates/appSample/lib/task/Task.Util.tsx +7 -7
- package/templates/appSample/lib/task/Task.Zone.tsx +1 -1
- package/templates/appSample/ui/QuantityControl.tsx +2 -2
- package/templates/crudSinglePage/page.tsx +1 -1
- package/templates/lib/cnst.ts +0 -1
- package/templates/workspaceRoot/.gitignore.template +23 -52
- package/templates/workspaceRoot/biome.json.template +1 -1
- package/{tunnel.command-jx3tdm2d.js → tunnel.command-2kw8vt9g.js} +8 -4
- package/{workflow.command-jx0wag5r.js → workflow.command-2djwmpqf.js} +10 -10
- package/{workspace.command-yxd7rr7g.js → workspace.command-6qfgq4b4.js} +38 -34
- package/TunnelShare-q4jq08qg.js +0 -9
- package/cloud.script-h6pvcpa2.js +0 -20
- package/context.runner-atcrzz5b.js +0 -21
- package/index-6w0rbr9d.js +0 -68
- package/index-n68jx79w.js +0 -120
- package/index-pgj2rt78.js +0 -404
|
@@ -3,28 +3,43 @@ 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-cpb3nvma.js";
|
|
11
9
|
import {
|
|
12
10
|
WorkspaceExecutor,
|
|
13
11
|
AppExecutor
|
|
14
|
-
} from "./index-
|
|
12
|
+
} from "./index-va06nr10.js";
|
|
15
13
|
import {
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
runner,
|
|
15
|
+
assertPromptable
|
|
16
|
+
} from "./index-jw1zm273.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";
|
|
27
23
|
|
|
24
|
+
// pkgs/@akanjs/cli/npmRegistry.ts
|
|
25
|
+
var defaultNpmRegistry = "https://registry.npmjs.org";
|
|
26
|
+
var getNpmRegistryUrl = (registryUrl = process.env.AKAN_NPM_REGISTRY ?? defaultNpmRegistry) => registryUrl.replace(/\/+$/, "");
|
|
27
|
+
async function getLatestPackageVersion(packageName, tag = "latest", registryUrl) {
|
|
28
|
+
const registry = getNpmRegistryUrl(registryUrl);
|
|
29
|
+
const url = `${registry}/${encodeURIComponent(packageName).replace(/^%40/, "@")}`;
|
|
30
|
+
const res = await fetch(url);
|
|
31
|
+
if (!res.ok)
|
|
32
|
+
throw new Error(`Failed to fetch ${packageName} metadata from ${registry}`);
|
|
33
|
+
const metadata = await res.json();
|
|
34
|
+
const version = metadata["dist-tags"]?.[tag];
|
|
35
|
+
if (!version)
|
|
36
|
+
throw new Error(`No npm dist-tag "${tag}" found for ${packageName}`);
|
|
37
|
+
return version;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// pkgs/@akanjs/cli/cloud/cloud.runner.ts
|
|
41
|
+
import path7 from "path";
|
|
42
|
+
|
|
28
43
|
// pkgs/@akanjs/devkit/platformTest/PlatformTestRun.ts
|
|
29
44
|
import path6 from "path";
|
|
30
45
|
import chalk from "chalk";
|
|
@@ -212,11 +227,13 @@ class PlatformTestTarget {
|
|
|
212
227
|
const install = await this.exec("bun install --frozen-lockfile", logPath, PlatformTestTarget.installIdleMs);
|
|
213
228
|
if (install.exitCode === 0)
|
|
214
229
|
return;
|
|
215
|
-
const
|
|
216
|
-
|
|
230
|
+
const failedPackages = [
|
|
231
|
+
...install.output.matchAll(/(?:pre|post)?install script from "([^"]+)" exited|failed to link binaries for package: (@?[^@\s]+)@/g)
|
|
232
|
+
].map((match) => match[1] ?? match[2] ?? "");
|
|
233
|
+
const tolerable = failedPackages.length > 0 && failedPackages.every((pkg) => this.tolerateScriptFailures.includes(pkg));
|
|
217
234
|
if (!tolerable)
|
|
218
235
|
throw new Error(`bun install failed \u2014 see ${logPath}`);
|
|
219
|
-
envWarnings.push(`install
|
|
236
|
+
envWarnings.push(`install failed for ${failedPackages.join(", ")} \u2014 tolerated on ${this.platform}`);
|
|
220
237
|
const relink = await this.exec("bun install --frozen-lockfile --ignore-scripts", this.#logPath(context, "install-relink"), PlatformTestTarget.installIdleMs);
|
|
221
238
|
if (relink.exitCode !== 0)
|
|
222
239
|
throw new Error(`bun install --ignore-scripts failed \u2014 see ${logPath}`);
|
|
@@ -253,7 +270,7 @@ class LinuxDockerTarget extends PlatformTestTarget {
|
|
|
253
270
|
}
|
|
254
271
|
static dockerfile = [
|
|
255
272
|
`FROM oven/bun:${Bun.version}-slim`,
|
|
256
|
-
"RUN apt-get update && apt-get install -y --no-install-recommends git ca-certificates procps lsof && rm -rf /var/lib/apt/lists/*"
|
|
273
|
+
"RUN sed -i 's|http://|https://|g' /etc/apt/sources.list.d/debian.sources && apt-get update && apt-get install -y --no-install-recommends git ca-certificates procps lsof && rm -rf /var/lib/apt/lists/*"
|
|
257
274
|
].join(`
|
|
258
275
|
`);
|
|
259
276
|
get image() {
|
|
@@ -416,7 +433,7 @@ import path5 from "path";
|
|
|
416
433
|
class WindowsSshTarget extends PlatformTestTarget {
|
|
417
434
|
static utmctlPath = "/Applications/UTM.app/Contents/MacOS/utmctl";
|
|
418
435
|
static defaultWorkRoot = "C:\\akan-test";
|
|
419
|
-
static defaultTolerateScriptFailures = ["sharp", "puppeteer"];
|
|
436
|
+
static defaultTolerateScriptFailures = ["sharp", "puppeteer", "msgpackr"];
|
|
420
437
|
static #runDirPattern = "^\\d{8}-\\d{6}$";
|
|
421
438
|
static #keptRuns = 2;
|
|
422
439
|
platform = "windows";
|
|
@@ -657,31 +674,10 @@ class PlatformTestRun {
|
|
|
657
674
|
}
|
|
658
675
|
|
|
659
676
|
// pkgs/@akanjs/cli/cloud/cloud.runner.ts
|
|
660
|
-
import {
|
|
661
|
-
import
|
|
662
|
-
import path7 from "path";
|
|
663
|
-
import { confirm, input, password, select } from "@inquirer/prompts";
|
|
664
|
-
import { Logger, PipelineNames, sleep } from "akanjs/common";
|
|
677
|
+
import { confirm, input, select } from "@inquirer/prompts";
|
|
678
|
+
import { Logger, sleep } from "akanjs/common";
|
|
665
679
|
import chalk2 from "chalk";
|
|
666
680
|
import * as QRcode from "qrcode";
|
|
667
|
-
|
|
668
|
-
// pkgs/@akanjs/cli/npmRegistry.ts
|
|
669
|
-
var defaultNpmRegistry = "https://registry.npmjs.org";
|
|
670
|
-
var getNpmRegistryUrl = (registryUrl = process.env.AKAN_NPM_REGISTRY ?? defaultNpmRegistry) => registryUrl.replace(/\/+$/, "");
|
|
671
|
-
async function getLatestPackageVersion(packageName, tag = "latest", registryUrl) {
|
|
672
|
-
const registry = getNpmRegistryUrl(registryUrl);
|
|
673
|
-
const url = `${registry}/${encodeURIComponent(packageName).replace(/^%40/, "@")}`;
|
|
674
|
-
const res = await fetch(url);
|
|
675
|
-
if (!res.ok)
|
|
676
|
-
throw new Error(`Failed to fetch ${packageName} metadata from ${registry}`);
|
|
677
|
-
const metadata = await res.json();
|
|
678
|
-
const version = metadata["dist-tags"]?.[tag];
|
|
679
|
-
if (!version)
|
|
680
|
-
throw new Error(`No npm dist-tag "${tag}" found for ${packageName}`);
|
|
681
|
-
return version;
|
|
682
|
-
}
|
|
683
|
-
|
|
684
|
-
// pkgs/@akanjs/cli/cloud/cloud.runner.ts
|
|
685
681
|
var addRemoteEnvServerValue = "__addRemoteEnvServer";
|
|
686
682
|
var removeRemoteEnvServerValue = "__removeRemoteEnvServer";
|
|
687
683
|
|
|
@@ -766,6 +762,9 @@ class CloudRunner extends runner("cloud") {
|
|
|
766
762
|
return { name: `${name} (${this.#getSshTarget(config)}${config.port ? `:${config.port}` : ""})`, value: name };
|
|
767
763
|
}
|
|
768
764
|
async#getRemoteEnvServerWithUsername() {
|
|
765
|
+
assertPromptable("The env archive goes over scp to a remote server picked here, as AKAN_WORKSPACE_ID is not set", {
|
|
766
|
+
remedy: "set AKAN_WORKSPACE_ID in the workspace .env to use Akan Cloud, or run it once in a terminal"
|
|
767
|
+
});
|
|
769
768
|
const remoteServer = await this.#selectRemoteEnvServer();
|
|
770
769
|
if (remoteServer.config.username)
|
|
771
770
|
return remoteServer;
|
|
@@ -891,6 +890,9 @@ ${run.format(results)}
|
|
|
891
890
|
const reason = result.infraError ?? "failing tests";
|
|
892
891
|
if (!interactive)
|
|
893
892
|
continue;
|
|
893
|
+
assertPromptable(`${result.platform} did not pass (${reason}) \u2014 ${run.logDir}`, {
|
|
894
|
+
remedy: "leave it out of --platforms to publish without it, or run it in a terminal to be asked"
|
|
895
|
+
});
|
|
894
896
|
const proceed = await confirm({
|
|
895
897
|
message: `${result.platform} did not pass (${reason}). Continue without it?`,
|
|
896
898
|
default: false
|
|
@@ -909,6 +911,9 @@ ${run.format(results)}
|
|
|
909
911
|
return results;
|
|
910
912
|
}
|
|
911
913
|
async#askWindowsTestTarget() {
|
|
914
|
+
assertPromptable("No Windows test target is configured", {
|
|
915
|
+
remedy: "run it once in a terminal, which saves the target to ~/.akan/config.json"
|
|
916
|
+
});
|
|
912
917
|
Logger.info("No Windows test target is configured yet; it is saved to ~/.akan/config.json once entered.");
|
|
913
918
|
const required = (value) => !!value.trim();
|
|
914
919
|
const host = await this.#ask("Windows host (ip or name): ", required);
|
|
@@ -944,11 +949,15 @@ ${run.format(results)}
|
|
|
944
949
|
for (const library of akanPkgs) {
|
|
945
950
|
const packageJson = await workspace.readJson(`pkgs/${library}/package.json`);
|
|
946
951
|
const newPackageJson = this.#normalizeAkanPackageJson(packageJson, library, nextVersion);
|
|
947
|
-
await workspace.
|
|
952
|
+
await workspace.writeJson(`pkgs/${library}/package.json`, newPackageJson);
|
|
948
953
|
const distPackageJson = await workspace.readJson(`dist/pkgs/${library}/package.json`);
|
|
949
954
|
const newDistPackageJson = this.#normalizeAkanPackageJson(distPackageJson, library, nextVersion);
|
|
950
955
|
await workspace.writeJson(`dist/pkgs/${library}/package.json`, newDistPackageJson);
|
|
951
956
|
}
|
|
957
|
+
if (confirmPublish)
|
|
958
|
+
assertPromptable(`Publishing ${akanPkgs.join(", ")}@${nextVersion} asks for a confirmation`, {
|
|
959
|
+
remedy: "run it in a terminal"
|
|
960
|
+
});
|
|
952
961
|
if (confirmPublish && !await confirm({ message: "Are you sure you want to deploy the libraries?" })) {
|
|
953
962
|
Logger.error("Deployment cancelled");
|
|
954
963
|
return;
|
|
@@ -1014,7 +1023,7 @@ ${run.format(results)}
|
|
|
1014
1023
|
async downloadEnv(cloudApi, workspace, workspaceId) {
|
|
1015
1024
|
await workspace.mkdir("local");
|
|
1016
1025
|
const localPath = await cloudApi.downloadEnv(workspaceId).catch((error) => {
|
|
1017
|
-
throw
|
|
1026
|
+
throw CloudApi.refusal(error, "downloading its env archive takes the project owner's own `akan login`");
|
|
1018
1027
|
});
|
|
1019
1028
|
const relativePath = path7.relative(workspace.workspaceRoot, localPath).split(path7.sep).join("/");
|
|
1020
1029
|
await workspace.spawn("tar", ["-xf", relativePath], { cwd: workspace.workspaceRoot });
|
|
@@ -1023,7 +1032,7 @@ ${run.format(results)}
|
|
|
1023
1032
|
async uploadEnv(cloudApi, workspaceId, filePath) {
|
|
1024
1033
|
const file = new File([Bun.file(filePath)], path7.basename(filePath));
|
|
1025
1034
|
await cloudApi.uploadEnv(workspaceId, file).catch((error) => {
|
|
1026
|
-
throw
|
|
1035
|
+
throw CloudApi.refusal(error, "an env archive is uploaded by a project member signed in with `akan login`");
|
|
1027
1036
|
});
|
|
1028
1037
|
}
|
|
1029
1038
|
async downloadEnvByScp(workspace) {
|
|
@@ -1096,7 +1105,7 @@ ${run.format(results)}
|
|
|
1096
1105
|
}));
|
|
1097
1106
|
return secretPaths.flat();
|
|
1098
1107
|
}
|
|
1099
|
-
async
|
|
1108
|
+
async projectOf(workspace, subspace, appName) {
|
|
1100
1109
|
if (!subspace) {
|
|
1101
1110
|
const workspaceId = workspace.getWorkspaceId({ allowEmpty: true });
|
|
1102
1111
|
if (!workspaceId)
|
|
@@ -1109,172 +1118,30 @@ ${run.format(results)}
|
|
|
1109
1118
|
const [declaration] = config.select([subspace]);
|
|
1110
1119
|
if (!declaration?.workspaceId)
|
|
1111
1120
|
throw new Error(`Subspace "${subspace}" declares no workspaceId in ${SubspaceConfig.source}.`);
|
|
1121
|
+
if (appName && !declaration.apps.includes(appName))
|
|
1122
|
+
throw new Error(`Subspace "${subspace}" declares no app ${appName} in ${SubspaceConfig.source}; it serves ${declaration.apps.join(", ")}.`);
|
|
1112
1123
|
return declaration.workspaceId;
|
|
1113
1124
|
}
|
|
1114
1125
|
async readSecretValue(name, file) {
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
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.`);
|
|
1126
|
+
return await PipelineSecrets.readValue(name, file);
|
|
1127
|
+
}
|
|
1128
|
+
async uploadSecret(cloudApi, devProjectId, secret) {
|
|
1129
|
+
await new PipelineSecrets(cloudApi, devProjectId).upload(secret);
|
|
1133
1130
|
}
|
|
1134
1131
|
async listSecrets(cloudApi, devProjectId, branch) {
|
|
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.`);
|
|
1153
|
-
}
|
|
1154
|
-
static #packRecords = ["manifest.template.json", "bundle.json", "pack.json", "compat.json"];
|
|
1155
|
-
static async#tarPack(pack) {
|
|
1156
|
-
const templateFile = Bun.file(path7.join(pack.out, "manifest.template.json"));
|
|
1157
|
-
if (!await templateFile.exists())
|
|
1158
|
-
throw new Error(`${pack.out} holds no manifest.template.json: give the folder \`akan pack-update\` wrote.`);
|
|
1159
|
-
const template = await templateFile.json();
|
|
1160
|
-
const hashes = new Set((template.files ?? []).map((file) => file.sha256));
|
|
1161
|
-
if (![...hashes].every((hash) => typeof hash === "string" && /^[0-9a-f]{64}$/.test(hash)))
|
|
1162
|
-
throw new Error(`${pack.out}/manifest.template.json lists a file whose sha256 is not 64 hex digits.`);
|
|
1163
|
-
const present = await Promise.all(CloudRunner.#packRecords.map((name) => Bun.file(path7.join(pack.out, name)).exists()));
|
|
1164
|
-
const entries = [
|
|
1165
|
-
...CloudRunner.#packRecords.filter((_, idx) => present[idx]),
|
|
1166
|
-
...[...hashes].map((hash) => `files/${hash}`)
|
|
1167
|
-
];
|
|
1168
|
-
const base = path7.join(os3.tmpdir(), `akan-pack-${pack.app}-${pack.target}-${pack.platform}-${process.pid}`);
|
|
1169
|
-
await Bun.write(`${base}.list`, entries.map((entry) => `./${entry}
|
|
1170
|
-
`).join(""));
|
|
1171
|
-
try {
|
|
1172
|
-
const tar = Bun.spawn(["tar", "-czf", `${base}.tgz`, "-C", pack.out, "-T", `${base}.list`], {
|
|
1173
|
-
stdout: "ignore",
|
|
1174
|
-
stderr: "pipe"
|
|
1175
|
-
});
|
|
1176
|
-
if (await tar.exited !== 0)
|
|
1177
|
-
throw new Error(`tar of ${pack.out} failed: ${(await new Response(tar.stderr).text()).trim()}`);
|
|
1178
|
-
} finally {
|
|
1179
|
-
await rm(`${base}.list`, { force: true });
|
|
1180
|
-
}
|
|
1181
|
-
return `${base}.tgz`;
|
|
1132
|
+
await new PipelineSecrets(cloudApi, devProjectId).list(branch);
|
|
1182
1133
|
}
|
|
1183
|
-
async
|
|
1184
|
-
|
|
1185
|
-
const target = channel ?? pack.channel;
|
|
1186
|
-
try {
|
|
1187
|
-
const release = await new UpdatePackUpload(cloudApi, devProjectId).send(archive, { appName: pack.app, target: pack.target, platform: pack.platform, channel: target, notes }).catch((error) => {
|
|
1188
|
-
throw CloudRunner.#refusal(error, "a pack is uploaded by a project member signed in with `akan login`");
|
|
1189
|
-
});
|
|
1190
|
-
const label = `${pack.app} ${pack.target} ${pack.platform} on channel ${target}: release ${release.id}`;
|
|
1191
|
-
if (release.status === "rejected") {
|
|
1192
|
-
process.exitCode = 2;
|
|
1193
|
-
Logger.error(`The project rejected ${label}:
|
|
1194
|
-
${(release.problems ?? []).map((problem) => ` - ${problem}`).join(`
|
|
1195
|
-
`)}`);
|
|
1196
|
-
} else if (release.status === "published")
|
|
1197
|
-
Logger.rawLog(`Published ${label} (${pack.files} files, native API ${pack.nativeApi}).`);
|
|
1198
|
-
else
|
|
1199
|
-
Logger.rawLog(`Staged ${label} (${pack.files} files, native API ${pack.nativeApi}). Publish it from the project's release screen.`);
|
|
1200
|
-
return release;
|
|
1201
|
-
} finally {
|
|
1202
|
-
await rm(archive, { force: true });
|
|
1203
|
-
}
|
|
1134
|
+
async removeSecret(cloudApi, devProjectId, secret) {
|
|
1135
|
+
await new PipelineSecrets(cloudApi, devProjectId).remove(secret);
|
|
1204
1136
|
}
|
|
1205
|
-
async
|
|
1206
|
-
|
|
1207
|
-
if (!mobileTarget)
|
|
1208
|
-
throw new Error(`Native target '${pack.target}' was not found.`);
|
|
1209
|
-
const bundlePath = path7.join(new NativeApp(app, mobileTarget).outDir(pack.platform), "bundle.json");
|
|
1210
|
-
const bundleFile = Bun.file(bundlePath);
|
|
1211
|
-
if (!await bundleFile.exists())
|
|
1212
|
-
throw new Error(`No store build at ${bundlePath}: run \`akan release-${pack.platform} ${app.name} --target ${pack.target} --env ${env}\` first.`);
|
|
1213
|
-
const bundle = await bundleFile.json();
|
|
1214
|
-
if (!bundle.updates)
|
|
1215
|
-
throw new Error(`The store build at ${bundlePath} takes no updates: give the target native.updates.`);
|
|
1216
|
-
if (bundle.nativeApi?.hash !== pack.nativeApi)
|
|
1217
|
-
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.`);
|
|
1218
|
-
const archive = await CloudRunner.#tarPack(pack);
|
|
1219
|
-
const channel = bundle.updates.channel ?? pack.channel;
|
|
1220
|
-
const upload = new UpdatePackUpload(cloudApi, devProjectId);
|
|
1221
|
-
try {
|
|
1222
|
-
const binary = await upload.stage(archive, { appName: pack.app, target: pack.target, platform: pack.platform, channel, notes: null }).then((packUploadId) => upload.settle(() => cloudApi.registerAppBinary(devProjectId, {
|
|
1223
|
-
appName: pack.app,
|
|
1224
|
-
target: pack.target,
|
|
1225
|
-
distribution,
|
|
1226
|
-
bundle: bundleFile,
|
|
1227
|
-
packUploadId,
|
|
1228
|
-
channel
|
|
1229
|
-
}))).catch((error) => {
|
|
1230
|
-
throw CloudRunner.#refusal(error, "a binary is registered by a project member signed in with `akan login`");
|
|
1231
|
-
});
|
|
1232
|
-
Logger.rawLog(`Registered ${pack.app} ${pack.target} ${pack.platform} (${distribution}): binary ${binary.id}, native API ${pack.nativeApi}.`);
|
|
1233
|
-
return binary;
|
|
1234
|
-
} finally {
|
|
1235
|
-
await rm(archive, { force: true });
|
|
1236
|
-
}
|
|
1137
|
+
async readPack(app, dir, flags) {
|
|
1138
|
+
return await CloudUpdates.readPack(app, dir, flags);
|
|
1237
1139
|
}
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
return "No pipeline secrets.";
|
|
1241
|
-
const rows = [...secrets].sort((a, b) => a.name.localeCompare(b.name) || a.scope.localeCompare(b.scope)).map((secret) => [
|
|
1242
|
-
secret.name,
|
|
1243
|
-
secret.scope === "*" ? "every branch" : secret.scope,
|
|
1244
|
-
secret.updatedAt,
|
|
1245
|
-
secret.updatedBy
|
|
1246
|
-
]);
|
|
1247
|
-
const header = ["NAME", "SCOPE", "UPDATED", "BY"];
|
|
1248
|
-
const widths = header.map((title, column) => Math.max(title.length, ...rows.map((row) => row[column]?.length ?? 0)));
|
|
1249
|
-
return [header, ...rows].map((row) => row.map((cell, column) => cell.padEnd(widths[column] ?? 0)).join(" ").trimEnd()).join(`
|
|
1250
|
-
`);
|
|
1140
|
+
async pushUpdate(cloudApi, devProjectId, pack, options) {
|
|
1141
|
+
return await new CloudUpdates(cloudApi, devProjectId).push(pack, options);
|
|
1251
1142
|
}
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
static #secretsUnavailable = "The server is not configured for pipeline secrets yet.";
|
|
1255
|
-
static #assertSecretName(name) {
|
|
1256
|
-
const problem = PipelineNames.secretNameError(name);
|
|
1257
|
-
if (problem)
|
|
1258
|
-
throw new Error(problem);
|
|
1259
|
-
}
|
|
1260
|
-
static #assertSecretBranch(branch) {
|
|
1261
|
-
if (branch !== null && !CloudRunner.#secretBranches.includes(branch))
|
|
1262
|
-
throw new Error(`--branch ${branch} is not a deployed branch: ${CloudRunner.#secretBranches.join(", ")}.`);
|
|
1263
|
-
}
|
|
1264
|
-
static #refusal(error, who, { unavailable } = {}) {
|
|
1265
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
1266
|
-
const status = CloudApi.statusOf(error);
|
|
1267
|
-
if (status === 401)
|
|
1268
|
-
return new Error("The server did not accept your sign-in: run `akan login`, then try again.");
|
|
1269
|
-
if (status === 403)
|
|
1270
|
-
return new Error(`The project refused: ${who}.`);
|
|
1271
|
-
if (status === 400) {
|
|
1272
|
-
const key = /"error"\s*:\s*"([^"]+)"/.exec(message)?.[1];
|
|
1273
|
-
return new Error(`The project refused the request${key ? `: ${key}` : ""}.`);
|
|
1274
|
-
}
|
|
1275
|
-
if (status === 503 && unavailable)
|
|
1276
|
-
return new Error(unavailable);
|
|
1277
|
-
return error;
|
|
1143
|
+
async registerBinary(cloudApi, devProjectId, app, pack, options) {
|
|
1144
|
+
return await new CloudUpdates(cloudApi, devProjectId).registerBinary(app, pack, options);
|
|
1278
1145
|
}
|
|
1279
1146
|
async#syncSecretGitignore(workspace, appNames) {
|
|
1280
1147
|
const patterns = (await Promise.all(appNames.map(async (appName) => {
|