@jskit-ai/jskit-cli 0.2.128 → 0.2.130
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/package.json +6 -5
- package/src/server/cliRuntime/appState.js +5 -2
- package/src/server/cliRuntime/ci/composer.js +171 -0
- package/src/server/cliRuntime/ci/contract.js +218 -0
- package/src/server/cliRuntime/ci/githubWorkflow.js +136 -0
- package/src/server/cliRuntime/ci/managedWorkflow.js +549 -0
- package/src/server/cliRuntime/descriptorValidation.js +11 -0
- package/src/server/commandHandlers/app.js +4 -5
- package/src/server/commandHandlers/appCommandCatalog.js +20 -51
- package/src/server/commandHandlers/appCommands/syncCi.js +40 -0
- package/src/server/commandHandlers/appCommands/updatePackages.js +9 -1
- package/src/server/commandHandlers/appCommands/verify.js +12 -1
- package/src/server/commandHandlers/health.js +8 -0
- package/src/server/commandHandlers/packageCommands/add.js +28 -0
- package/src/server/commandHandlers/packageCommands/remove.js +13 -0
- package/src/server/core/argParser.js +0 -5
- package/src/server/core/buildCommandDeps.js +7 -0
- package/src/server/core/createCliRunner.js +16 -0
- package/src/server/index.js +11 -0
- package/src/server/commandHandlers/appCommands/preparePreviewUser.js +0 -215
|
@@ -90,7 +90,23 @@ const APP_COMMAND_DEFINITIONS = Object.freeze({
|
|
|
90
90
|
defaults: Object.freeze([
|
|
91
91
|
"Runtime and dev @jskit-ai dependencies are updated separately so package.json sections stay correct.",
|
|
92
92
|
"Each package is moved to the latest available major.x range and npm saves the resolved exact version.",
|
|
93
|
-
"Managed migrations are refreshed afterwards unless --dry-run is used."
|
|
93
|
+
"Managed migrations and the composed CI workflow are refreshed afterwards unless --dry-run is used."
|
|
94
|
+
])
|
|
95
|
+
}),
|
|
96
|
+
"sync-ci": Object.freeze({
|
|
97
|
+
name: "sync-ci",
|
|
98
|
+
summary: "Regenerate the JSKIT-managed GitHub verification workflow from installed package contracts.",
|
|
99
|
+
usage: "jskit app sync-ci [--force]",
|
|
100
|
+
options: Object.freeze([
|
|
101
|
+
Object.freeze({
|
|
102
|
+
label: "--force",
|
|
103
|
+
description: "Replace a modified workflow that is already recorded as JSKIT-managed."
|
|
104
|
+
})
|
|
105
|
+
]),
|
|
106
|
+
defaults: Object.freeze([
|
|
107
|
+
"Recomposes CI requirements from installed package descriptors and records the generated content hash in .jskit/lock.json.",
|
|
108
|
+
"Refuses to replace a modified workflow unless --force is explicit; application-specific CI belongs in a separate workflow.",
|
|
109
|
+
"Never claims an unrecorded jskit-verify.yml or removes a customized legacy verify.yml."
|
|
94
110
|
])
|
|
95
111
|
}),
|
|
96
112
|
"link-local-packages": Object.freeze({
|
|
@@ -109,46 +125,6 @@ const APP_COMMAND_DEFINITIONS = Object.freeze({
|
|
|
109
125
|
"Clears node_modules/.vite so Vite does not keep stale prebundled paths."
|
|
110
126
|
])
|
|
111
127
|
}),
|
|
112
|
-
"prepare-preview-user": Object.freeze({
|
|
113
|
-
name: "prepare-preview-user",
|
|
114
|
-
summary: "Prepare the JSKIT preview auth user and optional first workspace.",
|
|
115
|
-
usage: "jskit app prepare-preview-user [--profile-file <path>] [--email <email>] [--username <username>] [--display-name <name>] [--auth-provider <provider>] [--auth-provider-user-sid <sid>] [--ensure-workspace]",
|
|
116
|
-
options: Object.freeze([
|
|
117
|
-
Object.freeze({
|
|
118
|
-
label: "--profile-file <path>",
|
|
119
|
-
description: "JSON profile path to write for the preview auth bridge. Defaults to VIBE64_PREVIEW_AUTH_PROFILE_FILE."
|
|
120
|
-
}),
|
|
121
|
-
Object.freeze({
|
|
122
|
-
label: "--email <email>",
|
|
123
|
-
description: "Email address for the preview user."
|
|
124
|
-
}),
|
|
125
|
-
Object.freeze({
|
|
126
|
-
label: "--username <username>",
|
|
127
|
-
description: "Preferred username for the preview user."
|
|
128
|
-
}),
|
|
129
|
-
Object.freeze({
|
|
130
|
-
label: "--display-name <name>",
|
|
131
|
-
description: "Display name for the preview user."
|
|
132
|
-
}),
|
|
133
|
-
Object.freeze({
|
|
134
|
-
label: "--auth-provider <provider>",
|
|
135
|
-
description: "Auth provider id stored on the preview user."
|
|
136
|
-
}),
|
|
137
|
-
Object.freeze({
|
|
138
|
-
label: "--auth-provider-user-sid <sid>",
|
|
139
|
-
description: "Provider user id stored on the preview user."
|
|
140
|
-
}),
|
|
141
|
-
Object.freeze({
|
|
142
|
-
label: "--ensure-workspace",
|
|
143
|
-
description: "When workspace tables and tenancy config exist, ensure the preview user owns a first workspace."
|
|
144
|
-
})
|
|
145
|
-
]),
|
|
146
|
-
defaults: Object.freeze([
|
|
147
|
-
"Runs after migrations and before the app server starts.",
|
|
148
|
-
"Exits successfully without changes when the app has no knexfile.js or users table.",
|
|
149
|
-
"Uses the app knexfile and app-local JSKIT package provisioning contracts instead of booting the HTTP server."
|
|
150
|
-
])
|
|
151
|
-
}),
|
|
152
128
|
release: Object.freeze({
|
|
153
129
|
name: "release",
|
|
154
130
|
summary: "Run the JSKIT release helper for an app repository.",
|
|
@@ -241,6 +217,9 @@ function buildAppCommandOptionMeta(subcommandName = "") {
|
|
|
241
217
|
if (definition.name === "adopt-managed-scripts") {
|
|
242
218
|
optionMeta.force = { inputType: "flag" };
|
|
243
219
|
}
|
|
220
|
+
if (definition.name === "sync-ci") {
|
|
221
|
+
optionMeta.force = { inputType: "flag" };
|
|
222
|
+
}
|
|
244
223
|
if (definition.name === "update-packages" || definition.name === "release") {
|
|
245
224
|
optionMeta.registry = { inputType: "text" };
|
|
246
225
|
}
|
|
@@ -255,16 +234,6 @@ function buildAppCommandOptionMeta(subcommandName = "") {
|
|
|
255
234
|
if (definition.name === "link-local-packages") {
|
|
256
235
|
optionMeta["repo-root"] = { inputType: "text" };
|
|
257
236
|
}
|
|
258
|
-
if (definition.name === "prepare-preview-user") {
|
|
259
|
-
optionMeta["profile-file"] = { inputType: "text" };
|
|
260
|
-
optionMeta.email = { inputType: "text" };
|
|
261
|
-
optionMeta.username = { inputType: "text" };
|
|
262
|
-
optionMeta["display-name"] = { inputType: "text" };
|
|
263
|
-
optionMeta["auth-provider"] = { inputType: "text" };
|
|
264
|
-
optionMeta["auth-provider-user-sid"] = { inputType: "text" };
|
|
265
|
-
optionMeta["ensure-workspace"] = { inputType: "flag" };
|
|
266
|
-
}
|
|
267
|
-
|
|
268
237
|
return optionMeta;
|
|
269
238
|
}
|
|
270
239
|
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
async function runAppSyncCiCommand(ctx = {}, { appRoot = "", options = {}, stdout }) {
|
|
2
|
+
const {
|
|
3
|
+
createCliError,
|
|
4
|
+
synchronizeAppCiWorkflow
|
|
5
|
+
} = ctx;
|
|
6
|
+
const force = String(options?.inlineOptions?.force || "").trim().toLowerCase() === "true";
|
|
7
|
+
const result = await synchronizeAppCiWorkflow({
|
|
8
|
+
appRoot,
|
|
9
|
+
allowManagedOverwrite: force
|
|
10
|
+
});
|
|
11
|
+
if (!result.applicable) {
|
|
12
|
+
throw createCliError("jskit app sync-ci only works in a JSKIT app root.");
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
if (options.json) {
|
|
16
|
+
stdout.write(`${JSON.stringify({
|
|
17
|
+
path: result.path,
|
|
18
|
+
hash: result.hash,
|
|
19
|
+
changed: result.changed,
|
|
20
|
+
removedLegacyWorkflow: result.removedLegacyWorkflow,
|
|
21
|
+
replacedModifiedWorkflow: result.replacedModifiedWorkflow
|
|
22
|
+
}, null, 2)}\n`);
|
|
23
|
+
return 0;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (result.replacedModifiedWorkflow) {
|
|
27
|
+
stdout.write(`Replaced the modified JSKIT-managed CI workflow: ${result.path}\n`);
|
|
28
|
+
} else if (result.changed) {
|
|
29
|
+
stdout.write(`Synchronized the JSKIT-managed CI workflow: ${result.path}\n`);
|
|
30
|
+
} else {
|
|
31
|
+
stdout.write(`The JSKIT-managed CI workflow is already current: ${result.path}\n`);
|
|
32
|
+
}
|
|
33
|
+
if (result.removedLegacyWorkflow) {
|
|
34
|
+
stdout.write("Removed the superseded .github/workflows/verify.yml scaffold.\n");
|
|
35
|
+
}
|
|
36
|
+
stdout.write(`Recorded content hash: ${result.hash}\n`);
|
|
37
|
+
return 0;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export { runAppSyncCiCommand };
|
|
@@ -32,9 +32,11 @@ function resolveInstallSpecs(packageNames = [], resolveMajorRange) {
|
|
|
32
32
|
async function runAppUpdatePackagesCommand(ctx = {}, { appRoot = "", options = {}, stdout, stderr }) {
|
|
33
33
|
const {
|
|
34
34
|
createCliError,
|
|
35
|
-
loadAppPackageJson
|
|
35
|
+
loadAppPackageJson,
|
|
36
|
+
assertAppManagedCiWorkflowUnmodified
|
|
36
37
|
} = ctx;
|
|
37
38
|
|
|
39
|
+
await assertAppManagedCiWorkflowUnmodified({ appRoot });
|
|
38
40
|
const { packageJson } = await loadAppPackageJson(appRoot);
|
|
39
41
|
const runtimePackages = collectJskitPackageNames(packageJson?.dependencies);
|
|
40
42
|
const devPackages = collectJskitPackageNames(packageJson?.devDependencies);
|
|
@@ -93,6 +95,12 @@ async function runAppUpdatePackagesCommand(ctx = {}, { appRoot = "", options = {
|
|
|
93
95
|
stderr,
|
|
94
96
|
createCliError
|
|
95
97
|
});
|
|
98
|
+
stdout.write("[jskit:update] synchronizing the managed CI workflow.\n");
|
|
99
|
+
await runLocalJskit(appRoot, ["app", "sync-ci"], {
|
|
100
|
+
stdout,
|
|
101
|
+
stderr,
|
|
102
|
+
createCliError
|
|
103
|
+
});
|
|
96
104
|
}
|
|
97
105
|
|
|
98
106
|
stdout.write("[jskit:update] done.\n");
|
|
@@ -8,7 +8,10 @@ const BASELINE_VERIFY_SCRIPTS = Object.freeze([
|
|
|
8
8
|
]);
|
|
9
9
|
|
|
10
10
|
async function runAppVerifyCommand(ctx = {}, { appRoot = "", options = {}, stdout, stderr }) {
|
|
11
|
-
const {
|
|
11
|
+
const {
|
|
12
|
+
createCliError,
|
|
13
|
+
validateAppCiWorkflow
|
|
14
|
+
} = ctx;
|
|
12
15
|
const inlineOptions =
|
|
13
16
|
options?.inlineOptions && typeof options.inlineOptions === "object" ? options.inlineOptions : {};
|
|
14
17
|
const against = String(inlineOptions.against || "").trim();
|
|
@@ -17,6 +20,14 @@ async function runAppVerifyCommand(ctx = {}, { appRoot = "", options = {}, stdou
|
|
|
17
20
|
throw createCliError("jskit app verify does not support --dry-run.", { exitCode: 1 });
|
|
18
21
|
}
|
|
19
22
|
|
|
23
|
+
const ciValidation = await validateAppCiWorkflow({ appRoot });
|
|
24
|
+
if (!ciValidation.valid) {
|
|
25
|
+
throw createCliError(
|
|
26
|
+
ciValidation.issues.map((issue) => issue.message).join("\n"),
|
|
27
|
+
{ exitCode: 1 }
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
|
|
20
31
|
for (const scriptName of BASELINE_VERIFY_SCRIPTS) {
|
|
21
32
|
runExternalCommand("npm", ["run", "--if-present", scriptName], {
|
|
22
33
|
cwd: appRoot,
|
|
@@ -26,6 +26,7 @@ function createHealthCommands(ctx = {}) {
|
|
|
26
26
|
loadAppLocalPackageRegistry,
|
|
27
27
|
mergePackageRegistries,
|
|
28
28
|
hydratePackageRegistryFromInstalledNodeModules,
|
|
29
|
+
validateManagedCiWorkflow,
|
|
29
30
|
inspectPackageOfferings,
|
|
30
31
|
fileExists,
|
|
31
32
|
normalizeRelativePath,
|
|
@@ -2130,6 +2131,13 @@ function createHealthCommands(ctx = {}) {
|
|
|
2130
2131
|
seedPackageIds: Object.keys(installed)
|
|
2131
2132
|
});
|
|
2132
2133
|
|
|
2134
|
+
const ciValidation = await validateManagedCiWorkflow({
|
|
2135
|
+
appRoot,
|
|
2136
|
+
lock,
|
|
2137
|
+
packageRegistry: combinedPackageRegistry
|
|
2138
|
+
});
|
|
2139
|
+
issues.push(...ciValidation.issues.map((issue) => issue.message));
|
|
2140
|
+
|
|
2133
2141
|
for (const [packageId, lockEntryValue] of Object.entries(installed)) {
|
|
2134
2142
|
const lockEntry = ensureObject(lockEntryValue);
|
|
2135
2143
|
if (!combinedPackageRegistry.has(packageId)) {
|
|
@@ -360,6 +360,9 @@ async function runPackageAddCommand(ctx = {}, { positional, options, cwd, io })
|
|
|
360
360
|
resolvePackageOptions,
|
|
361
361
|
applyPackageInstall,
|
|
362
362
|
adoptAppLocalPackageDependencies,
|
|
363
|
+
assertManagedCiWorkflowUnmodified,
|
|
364
|
+
composeInstalledPackageCi,
|
|
365
|
+
synchronizeManagedCiWorkflow,
|
|
363
366
|
writeJsonFile,
|
|
364
367
|
readFileBufferIfExists,
|
|
365
368
|
runNpmInstall,
|
|
@@ -485,6 +488,10 @@ async function runPackageAddCommand(ctx = {}, { positional, options, cwd, io })
|
|
|
485
488
|
const combinedPackageRegistry = mergePackageRegistries(packageRegistry, appLocalRegistry);
|
|
486
489
|
const { packageJsonPath, packageJson } = await loadAppPackageJson(appRoot);
|
|
487
490
|
const { lockPath, lock } = await loadLockFile(appRoot);
|
|
491
|
+
await assertManagedCiWorkflowUnmodified({
|
|
492
|
+
appRoot,
|
|
493
|
+
lock
|
|
494
|
+
});
|
|
488
495
|
const resolvedTargetPackageId = targetType === "package"
|
|
489
496
|
? await resolvePackageIdFromRegistryOrNodeModules({
|
|
490
497
|
appRoot,
|
|
@@ -556,6 +563,19 @@ async function runPackageAddCommand(ctx = {}, { positional, options, cwd, io })
|
|
|
556
563
|
combinedPackageRegistry,
|
|
557
564
|
`${invocationMode} ${targetType} ${targetId}`
|
|
558
565
|
);
|
|
566
|
+
for (const packageId of plannedInstalledPackageIds) {
|
|
567
|
+
const packageEntry = combinedPackageRegistry.get(packageId);
|
|
568
|
+
if (!packageEntry) {
|
|
569
|
+
throw createCliError(
|
|
570
|
+
`[ci:descriptor-missing] Installed package descriptor not found for ${packageId}. Restore the package in node_modules or the JSKIT catalog before changing installed package requirements.`
|
|
571
|
+
);
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
composeInstalledPackageCi({
|
|
575
|
+
lock,
|
|
576
|
+
packageRegistry: combinedPackageRegistry,
|
|
577
|
+
installedPackageIds: plannedInstalledPackageIds
|
|
578
|
+
});
|
|
559
579
|
|
|
560
580
|
if (targetType === "bundle") {
|
|
561
581
|
validateInlineOptionsForBundle({
|
|
@@ -766,6 +786,14 @@ async function runPackageAddCommand(ctx = {}, { positional, options, cwd, io })
|
|
|
766
786
|
});
|
|
767
787
|
}
|
|
768
788
|
|
|
789
|
+
await synchronizeManagedCiWorkflow({
|
|
790
|
+
appRoot,
|
|
791
|
+
lock,
|
|
792
|
+
packageRegistry: combinedPackageRegistry,
|
|
793
|
+
touchedFiles,
|
|
794
|
+
dryRun: options.dryRun === true
|
|
795
|
+
});
|
|
796
|
+
|
|
769
797
|
const touchedFileList = sortStrings([...touchedFiles]);
|
|
770
798
|
const successLabel = invocationMode === "generate"
|
|
771
799
|
? "Generated with"
|
|
@@ -27,6 +27,8 @@ async function runPackageRemoveCommand(ctx = {}, { positional, options, cwd, io
|
|
|
27
27
|
removeEnvValue,
|
|
28
28
|
writeFile,
|
|
29
29
|
removeManagedViteProxyEntries,
|
|
30
|
+
assertManagedCiWorkflowUnmodified,
|
|
31
|
+
synchronizeManagedCiWorkflow,
|
|
30
32
|
hashBuffer,
|
|
31
33
|
rm,
|
|
32
34
|
writeJsonFile,
|
|
@@ -45,6 +47,10 @@ async function runPackageRemoveCommand(ctx = {}, { positional, options, cwd, io
|
|
|
45
47
|
const combinedPackageRegistry = mergePackageRegistries(packageRegistry, appLocalRegistry);
|
|
46
48
|
const { packageJsonPath, packageJson } = await loadAppPackageJson(appRoot);
|
|
47
49
|
const { lockPath, lock } = await loadLockFile(appRoot);
|
|
50
|
+
await assertManagedCiWorkflowUnmodified({
|
|
51
|
+
appRoot,
|
|
52
|
+
lock
|
|
53
|
+
});
|
|
48
54
|
const installed = ensureObject(lock.installedPackages);
|
|
49
55
|
await hydratePackageRegistryFromInstalledNodeModules({
|
|
50
56
|
appRoot,
|
|
@@ -159,6 +165,13 @@ async function runPackageRemoveCommand(ctx = {}, { positional, options, cwd, io
|
|
|
159
165
|
}
|
|
160
166
|
|
|
161
167
|
delete installed[resolvedTargetId];
|
|
168
|
+
await synchronizeManagedCiWorkflow({
|
|
169
|
+
appRoot,
|
|
170
|
+
lock,
|
|
171
|
+
packageRegistry: combinedPackageRegistry,
|
|
172
|
+
touchedFiles,
|
|
173
|
+
dryRun: options.dryRun === true
|
|
174
|
+
});
|
|
162
175
|
const touchedFileList = sortStrings([...touchedFiles]);
|
|
163
176
|
|
|
164
177
|
if (!options.dryRun) {
|
|
@@ -137,11 +137,6 @@ function parseArgs(argv, { createCliError } = {}) {
|
|
|
137
137
|
options.inlineOptions["skip-main-sync"] = "true";
|
|
138
138
|
continue;
|
|
139
139
|
}
|
|
140
|
-
if (token === "--ensure-workspace") {
|
|
141
|
-
options.inlineOptions["ensure-workspace"] = "true";
|
|
142
|
-
continue;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
140
|
if (token.startsWith("--")) {
|
|
146
141
|
const withoutPrefix = token.slice(2);
|
|
147
142
|
const hasInlineValue = withoutPrefix.includes("=");
|
|
@@ -27,6 +27,13 @@ function createCommandHandlerDeps(deps = {}) {
|
|
|
27
27
|
applyPackageMigrationsOnly: deps.applyPackageMigrationsOnly,
|
|
28
28
|
applyPackagePositioning: deps.applyPackagePositioning,
|
|
29
29
|
adoptAppLocalPackageDependencies: deps.adoptAppLocalPackageDependencies,
|
|
30
|
+
assertAppManagedCiWorkflowUnmodified: deps.assertAppManagedCiWorkflowUnmodified,
|
|
31
|
+
assertManagedCiWorkflowUnmodified: deps.assertManagedCiWorkflowUnmodified,
|
|
32
|
+
composeInstalledPackageCi: deps.composeInstalledPackageCi,
|
|
33
|
+
synchronizeAppCiWorkflow: deps.synchronizeAppCiWorkflow,
|
|
34
|
+
synchronizeManagedCiWorkflow: deps.synchronizeManagedCiWorkflow,
|
|
35
|
+
validateAppCiWorkflow: deps.validateAppCiWorkflow,
|
|
36
|
+
validateManagedCiWorkflow: deps.validateManagedCiWorkflow,
|
|
30
37
|
loadAppPackageJson: deps.loadAppPackageJson,
|
|
31
38
|
resolveLocalPackageId: deps.resolveLocalPackageId,
|
|
32
39
|
createLocalPackageScaffoldFiles: deps.createLocalPackageScaffoldFiles,
|
|
@@ -93,6 +93,15 @@ import {
|
|
|
93
93
|
import {
|
|
94
94
|
removeManagedViteProxyEntries
|
|
95
95
|
} from "../cliRuntime/viteProxy.js";
|
|
96
|
+
import {
|
|
97
|
+
assertAppManagedCiWorkflowUnmodified,
|
|
98
|
+
assertManagedCiWorkflowUnmodified,
|
|
99
|
+
composeInstalledPackageCi,
|
|
100
|
+
synchronizeAppCiWorkflow,
|
|
101
|
+
synchronizeManagedCiWorkflow,
|
|
102
|
+
validateAppCiWorkflow,
|
|
103
|
+
validateManagedCiWorkflow
|
|
104
|
+
} from "../cliRuntime/ci/managedWorkflow.js";
|
|
96
105
|
|
|
97
106
|
const commandHandlers = createCommandHandlers(
|
|
98
107
|
createCommandHandlerDeps({
|
|
@@ -123,6 +132,13 @@ const commandHandlers = createCommandHandlers(
|
|
|
123
132
|
applyPackageMigrationsOnly,
|
|
124
133
|
applyPackagePositioning,
|
|
125
134
|
adoptAppLocalPackageDependencies,
|
|
135
|
+
assertAppManagedCiWorkflowUnmodified,
|
|
136
|
+
assertManagedCiWorkflowUnmodified,
|
|
137
|
+
composeInstalledPackageCi,
|
|
138
|
+
synchronizeAppCiWorkflow,
|
|
139
|
+
synchronizeManagedCiWorkflow,
|
|
140
|
+
validateAppCiWorkflow,
|
|
141
|
+
validateManagedCiWorkflow,
|
|
126
142
|
loadAppPackageJson,
|
|
127
143
|
resolveLocalPackageId,
|
|
128
144
|
createLocalPackageScaffoldFiles,
|
package/src/server/index.js
CHANGED
|
@@ -1,2 +1,13 @@
|
|
|
1
1
|
export { runCli } from "./core/createCliRunner.js";
|
|
2
2
|
export * from "./appBlueprint.js";
|
|
3
|
+
export {
|
|
4
|
+
synchronizeAppCiWorkflow,
|
|
5
|
+
validateAppCiWorkflow
|
|
6
|
+
} from "./cliRuntime/ci/managedWorkflow.js";
|
|
7
|
+
export {
|
|
8
|
+
composeCiContributions
|
|
9
|
+
} from "./cliRuntime/ci/composer.js";
|
|
10
|
+
export {
|
|
11
|
+
JSKIT_CI_WORKFLOW_RELATIVE_PATH,
|
|
12
|
+
renderGithubWorkflow
|
|
13
|
+
} from "./cliRuntime/ci/githubWorkflow.js";
|
|
@@ -1,215 +0,0 @@
|
|
|
1
|
-
import { access, mkdir, writeFile } from "node:fs/promises";
|
|
2
|
-
import { createRequire } from "node:module";
|
|
3
|
-
import path from "node:path";
|
|
4
|
-
import { pathToFileURL } from "node:url";
|
|
5
|
-
import { loadAppConfigFromAppRoot } from "@jskit-ai/kernel/server/support";
|
|
6
|
-
|
|
7
|
-
const TENANCY_MODE_NONE = "none";
|
|
8
|
-
|
|
9
|
-
function normalizeText(value = "") {
|
|
10
|
-
return String(value || "").trim();
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
function normalizeLowerText(value = "") {
|
|
14
|
-
return normalizeText(value).toLowerCase();
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function profileFromOptions(options = {}) {
|
|
18
|
-
const inlineOptions = options?.inlineOptions && typeof options.inlineOptions === "object" ? options.inlineOptions : {};
|
|
19
|
-
const profile = {};
|
|
20
|
-
|
|
21
|
-
const email = normalizeLowerText(inlineOptions.email || process.env.JSKIT_PREVIEW_USER_EMAIL);
|
|
22
|
-
const authProvider = normalizeLowerText(inlineOptions["auth-provider"] || process.env.JSKIT_PREVIEW_AUTH_PROVIDER);
|
|
23
|
-
const username = normalizeLowerText(inlineOptions.username || process.env.JSKIT_PREVIEW_USER_USERNAME);
|
|
24
|
-
const displayName = normalizeText(inlineOptions["display-name"] || process.env.JSKIT_PREVIEW_USER_DISPLAY_NAME);
|
|
25
|
-
const authProviderUserSid = normalizeText(
|
|
26
|
-
inlineOptions["auth-provider-user-sid"] || process.env.JSKIT_PREVIEW_AUTH_PROVIDER_USER_SID
|
|
27
|
-
);
|
|
28
|
-
|
|
29
|
-
if (authProvider) {
|
|
30
|
-
profile.authProvider = authProvider;
|
|
31
|
-
}
|
|
32
|
-
if (authProviderUserSid) {
|
|
33
|
-
profile.authProviderUserSid = authProviderUserSid;
|
|
34
|
-
}
|
|
35
|
-
if (displayName) {
|
|
36
|
-
profile.displayName = displayName;
|
|
37
|
-
}
|
|
38
|
-
if (email) {
|
|
39
|
-
profile.email = email;
|
|
40
|
-
}
|
|
41
|
-
if (username) {
|
|
42
|
-
profile.username = username;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
return profile;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
async function fileExists(filePath = "") {
|
|
49
|
-
try {
|
|
50
|
-
await access(filePath);
|
|
51
|
-
return true;
|
|
52
|
-
} catch {
|
|
53
|
-
return false;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
function createAppRequire(appRoot = "") {
|
|
58
|
-
return createRequire(path.join(appRoot, "package.json"));
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
async function importFreshModule(filePath = "") {
|
|
62
|
-
return import(`${pathToFileURL(filePath).href}?mtime=${Date.now()}`);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
async function loadKnexConfig(appRoot = "") {
|
|
66
|
-
const knexfilePath = path.join(appRoot, "knexfile.js");
|
|
67
|
-
if (!(await fileExists(knexfilePath))) {
|
|
68
|
-
return null;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
const requireFromApp = createAppRequire(appRoot);
|
|
72
|
-
const moduleValue = requireFromApp("knex");
|
|
73
|
-
const createKnex =
|
|
74
|
-
typeof moduleValue === "function"
|
|
75
|
-
? moduleValue
|
|
76
|
-
: typeof moduleValue?.default === "function"
|
|
77
|
-
? moduleValue.default
|
|
78
|
-
: null;
|
|
79
|
-
if (!createKnex) {
|
|
80
|
-
throw new Error("App-local knex package resolved but did not expose a callable factory.");
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
const knexfileModule = await importFreshModule(knexfilePath);
|
|
84
|
-
const exported = knexfileModule.default || knexfileModule.config || knexfileModule;
|
|
85
|
-
const knexfile = typeof exported === "function" ? await exported() : exported;
|
|
86
|
-
const environment = normalizeText(process.env.NODE_ENV) || "development";
|
|
87
|
-
|
|
88
|
-
return {
|
|
89
|
-
createKnex,
|
|
90
|
-
knexConfig: knexfile?.[environment] || knexfile
|
|
91
|
-
};
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
async function importAppPackageExport(appRoot = "", specifier = "", { required = true } = {}) {
|
|
95
|
-
const requireFromApp = createAppRequire(appRoot);
|
|
96
|
-
let resolvedPath = "";
|
|
97
|
-
try {
|
|
98
|
-
resolvedPath = requireFromApp.resolve(specifier);
|
|
99
|
-
} catch {
|
|
100
|
-
if (!required) {
|
|
101
|
-
return null;
|
|
102
|
-
}
|
|
103
|
-
throw new Error(
|
|
104
|
-
`Unable to load app-local ${specifier}. Upgrade the installed JSKIT package that owns this preview provisioning contract.`
|
|
105
|
-
);
|
|
106
|
-
}
|
|
107
|
-
return importFreshModule(resolvedPath);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
async function writeProfile(profileFile = "", authProfile = {}) {
|
|
111
|
-
if (!profileFile) {
|
|
112
|
-
return false;
|
|
113
|
-
}
|
|
114
|
-
await mkdir(path.dirname(profileFile), { recursive: true });
|
|
115
|
-
await writeFile(profileFile, `${JSON.stringify(authProfile, null, 2)}\n`, "utf8");
|
|
116
|
-
return true;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
function isTrueOption(value) {
|
|
120
|
-
const normalized = normalizeLowerText(value);
|
|
121
|
-
return value === true || normalized === "true" || normalized === "1" || normalized === "yes";
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
function normalizeTenancyMode(value = "") {
|
|
125
|
-
const normalized = normalizeLowerText(value);
|
|
126
|
-
return normalized === "personal" || normalized === "workspaces" ? normalized : TENANCY_MODE_NONE;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
async function hasWorkspaceTables(db) {
|
|
130
|
-
return (
|
|
131
|
-
(await db.schema.hasTable("workspaces")) &&
|
|
132
|
-
(await db.schema.hasTable("workspace_memberships")) &&
|
|
133
|
-
(await db.schema.hasTable("workspace_settings"))
|
|
134
|
-
);
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
async function runAppPreparePreviewUserCommand(_ctx = {}, { appRoot = "", options = {}, stdout = process.stdout }) {
|
|
138
|
-
const inlineOptions = options?.inlineOptions && typeof options.inlineOptions === "object" ? options.inlineOptions : {};
|
|
139
|
-
const profileFile = normalizeText(inlineOptions["profile-file"] || process.env.VIBE64_PREVIEW_AUTH_PROFILE_FILE);
|
|
140
|
-
const ensureWorkspace = isTrueOption(inlineOptions["ensure-workspace"]);
|
|
141
|
-
const loadedKnex = await loadKnexConfig(appRoot);
|
|
142
|
-
if (!loadedKnex) {
|
|
143
|
-
stdout.write("[jskit:preview] skipped: knexfile.js was not found.\n");
|
|
144
|
-
return 0;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
const db = loadedKnex.createKnex(loadedKnex.knexConfig);
|
|
148
|
-
try {
|
|
149
|
-
if (!(await db.schema.hasTable("users"))) {
|
|
150
|
-
stdout.write("[jskit:preview] skipped: users table was not found.\n");
|
|
151
|
-
return 0;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
const usersPreviewModule = await importAppPackageExport(
|
|
155
|
-
appRoot,
|
|
156
|
-
"@jskit-ai/users-core/server/previewUserProvisioning"
|
|
157
|
-
);
|
|
158
|
-
if (typeof usersPreviewModule?.ensurePreviewUser !== "function") {
|
|
159
|
-
throw new Error("@jskit-ai/users-core preview provisioning export is missing ensurePreviewUser().");
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
const userResult = await usersPreviewModule.ensurePreviewUser(db, profileFromOptions(options));
|
|
163
|
-
if (userResult?.skipped) {
|
|
164
|
-
stdout.write(`[jskit:preview] skipped: ${userResult.skipped}.\n`);
|
|
165
|
-
return 0;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
const authProfile = userResult.profile;
|
|
169
|
-
let workspace = null;
|
|
170
|
-
|
|
171
|
-
if (ensureWorkspace) {
|
|
172
|
-
const appConfig = await loadAppConfigFromAppRoot({ appRoot });
|
|
173
|
-
const tenancyMode = normalizeTenancyMode(appConfig?.tenancyMode);
|
|
174
|
-
if (tenancyMode === TENANCY_MODE_NONE) {
|
|
175
|
-
stdout.write("[jskit:preview] skipped workspace: workspace tenancy is disabled.\n");
|
|
176
|
-
} else if (!(await hasWorkspaceTables(db))) {
|
|
177
|
-
throw new Error(
|
|
178
|
-
`Workspace tenancy is "${tenancyMode}", but workspace tables were not found. Run migrations before prepare-preview-user.`
|
|
179
|
-
);
|
|
180
|
-
} else {
|
|
181
|
-
const workspacesPreviewModule = await importAppPackageExport(
|
|
182
|
-
appRoot,
|
|
183
|
-
"@jskit-ai/workspaces-core/server/previewWorkspaceProvisioning"
|
|
184
|
-
);
|
|
185
|
-
if (typeof workspacesPreviewModule?.ensurePreviewWorkspace !== "function") {
|
|
186
|
-
throw new Error("@jskit-ai/workspaces-core preview provisioning export is missing ensurePreviewWorkspace().");
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
const workspaceResult = await workspacesPreviewModule.ensurePreviewWorkspace(db, userResult.user, authProfile, {
|
|
190
|
-
appConfig,
|
|
191
|
-
tenancyMode
|
|
192
|
-
});
|
|
193
|
-
workspace = workspaceResult?.workspace || null;
|
|
194
|
-
if (workspaceResult?.skipped) {
|
|
195
|
-
stdout.write(`[jskit:preview] skipped workspace: ${workspaceResult.skipped}.\n`);
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
const outputProfile = {
|
|
201
|
-
...authProfile,
|
|
202
|
-
...(workspace ? { workspace } : {})
|
|
203
|
-
};
|
|
204
|
-
await writeProfile(profileFile, outputProfile);
|
|
205
|
-
stdout.write(`[jskit:preview] user is ready: ${outputProfile.email} (${outputProfile.id}).\n`);
|
|
206
|
-
if (workspace) {
|
|
207
|
-
stdout.write(`[jskit:preview] workspace is ready: ${workspace.slug} (${workspace.id}).\n`);
|
|
208
|
-
}
|
|
209
|
-
return 0;
|
|
210
|
-
} finally {
|
|
211
|
-
await db.destroy();
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
export { runAppPreparePreviewUserCommand };
|