c8y-nitro 0.6.0 → 0.7.0
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/dist/{bootstrap-DUDpmXcU.mjs → bootstrap-ObaEF7rN.mjs} +6 -6
- package/dist/{c8y-api-BbRS1-Ls.mjs → c8y-api-BFjK6AYb.mjs} +36 -8
- package/dist/cli/index.mjs +4 -4
- package/dist/{credentials-9FO7rTIR.d.mts → credentials-eKnJ7m9Y.d.mts} +0 -1
- package/dist/{index-uOwpI6rD.d.mts → index-ByDSMEvU.d.mts} +20 -4
- package/dist/index.d.mts +2 -3
- package/dist/index.mjs +163 -35
- package/dist/{options-Web5UyIU.mjs → options-B7_yb_wr.mjs} +4 -4
- package/dist/{package-DsLC9Mo3.mjs → package-BhFLRPOb.mjs} +1 -1
- package/dist/{roles-D7rpbxPp.mjs → roles-B58Fguop.mjs} +4 -4
- package/dist/runtime/handlers/invalidateTenantOptions.mjs +616 -5
- package/dist/runtime/middlewares/c8y-client.mjs +2 -2
- package/dist/runtime/middlewares/dev-user.dev.mjs +2 -2
- package/dist/runtime/plugins/c8y-variables.dev.mjs +1 -1
- package/dist/types.d.mts +3 -3
- package/dist/utils.d.mts +201 -40
- package/dist/utils.mjs +380 -109
- package/package.json +11 -9
- /package/dist/{config-BRnvtthI.mjs → config-BVQHqlgV.mjs} +0 -0
- /package/dist/{env-file-B0BK-uZW.mjs → env-file-BrOTmVjA.mjs} +0 -0
- /package/dist/{logging-CVlaRS4O.mjs → logging-DYnYICoX.mjs} +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { a as findMicroserviceByName, d as updateMicroservice, l as subscribeToApplication, n as createBasicAuthHeader, o as getBootstrapCredentials, p as createC8yManifest, r as createMicroservice } from "./c8y-api-
|
|
2
|
-
import { t as writeBootstrapCredentials } from "./env-file-
|
|
3
|
-
import { n as validateBootstrapEnv, t as loadC8yConfig } from "./config-
|
|
1
|
+
import { a as findMicroserviceByName, d as updateMicroservice, l as subscribeToApplication, n as createBasicAuthHeader, o as getBootstrapCredentials, p as createC8yManifest, r as createMicroservice } from "./c8y-api-BFjK6AYb.mjs";
|
|
2
|
+
import { t as writeBootstrapCredentials } from "./env-file-BrOTmVjA.mjs";
|
|
3
|
+
import { n as validateBootstrapEnv, t as loadC8yConfig } from "./config-BVQHqlgV.mjs";
|
|
4
4
|
import { defineCommand, runCommand } from "citty";
|
|
5
5
|
import { consola } from "consola";
|
|
6
6
|
//#region src/cli/commands/bootstrap.ts
|
|
@@ -12,11 +12,11 @@ var bootstrap_default = defineCommand({
|
|
|
12
12
|
args: {},
|
|
13
13
|
async run() {
|
|
14
14
|
consola.info("Loading configuration...");
|
|
15
|
-
const { env,
|
|
15
|
+
const { env, nitroConfig, configDir } = await loadC8yConfig();
|
|
16
16
|
consola.info("Validating environment variables...");
|
|
17
17
|
const envVars = validateBootstrapEnv(env);
|
|
18
18
|
consola.info("Building manifest...");
|
|
19
|
-
const manifest = await createC8yManifest(configDir,
|
|
19
|
+
const manifest = await createC8yManifest(configDir, nitroConfig);
|
|
20
20
|
consola.success(`Manifest created for: ${manifest.name} v${manifest.version}`);
|
|
21
21
|
const authHeader = createBasicAuthHeader(envVars.C8Y_DEVELOPMENT_TENANT, envVars.C8Y_DEVELOPMENT_USER, envVars.C8Y_DEVELOPMENT_PASSWORD);
|
|
22
22
|
consola.info(`Checking if microservice "${manifest.name}" exists...`);
|
|
@@ -52,7 +52,7 @@ var bootstrap_default = defineCommand({
|
|
|
52
52
|
});
|
|
53
53
|
consola.success(`Bootstrap credentials written to ${envFileName}`);
|
|
54
54
|
if (manifest.roles && manifest.roles.length > 0) {
|
|
55
|
-
if (await consola.prompt("Do you want to manage microservice roles for your development user?", { type: "confirm" })) await runCommand(await import("./roles-
|
|
55
|
+
if (await consola.prompt("Do you want to manage microservice roles for your development user?", { type: "confirm" })) await runCommand(await import("./roles-B58Fguop.mjs").then((r) => r.default), { rawArgs: [] });
|
|
56
56
|
}
|
|
57
57
|
consola.success("Bootstrap complete!");
|
|
58
58
|
}
|
|
@@ -6,8 +6,29 @@ const GENERATED_READINESS_ROUTE = "/_c8y_nitro/readiness";
|
|
|
6
6
|
const GENERATED_INVALIDATE_TENANT_OPTIONS_ROUTE = "/_c8y_nitro/invalidate-tenant-options";
|
|
7
7
|
//#endregion
|
|
8
8
|
//#region src/module/manifest.ts
|
|
9
|
+
const DEFAULT_OPENAPI_ROUTE = "/_openapi.json";
|
|
9
10
|
const ROLE_OPTION_MANAGEMENT_READ = "ROLE_OPTION_MANAGEMENT_READ";
|
|
10
|
-
|
|
11
|
+
/**
|
|
12
|
+
* Decide whether Nitro will expose an OpenAPI spec in the deployed build,
|
|
13
|
+
* and if so return the configured route (with leading slash).
|
|
14
|
+
* Returns null when OpenAPI is not enabled for production.
|
|
15
|
+
* @param config - Nitro config (or options) to inspect
|
|
16
|
+
*/
|
|
17
|
+
function resolveNitroOpenApiRoute(config) {
|
|
18
|
+
if (!config.experimental?.openAPI) return null;
|
|
19
|
+
if (!config.openAPI?.production) return null;
|
|
20
|
+
return config.openAPI?.route || DEFAULT_OPENAPI_ROUTE;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Pull the parts of a Nitro config that feed manifest generation.
|
|
24
|
+
* @param config - Nitro config (or options) loaded from disk or from a Nitro instance
|
|
25
|
+
*/
|
|
26
|
+
function extractManifestInputs(config) {
|
|
27
|
+
return {
|
|
28
|
+
options: config.c8y?.manifest ?? {},
|
|
29
|
+
openApiSpec: resolveNitroOpenApiRoute(config) ?? void 0
|
|
30
|
+
};
|
|
31
|
+
}
|
|
11
32
|
function validateManifestSettings(options) {
|
|
12
33
|
const invalidSettings = options.settings?.filter((setting) => typeof setting.defaultValue !== "string" || setting.defaultValue.length === 0) ?? [];
|
|
13
34
|
if (invalidSettings.length === 0) return;
|
|
@@ -39,13 +60,18 @@ async function readPackageJsonFieldsForManifest(rootDir, logger) {
|
|
|
39
60
|
};
|
|
40
61
|
}
|
|
41
62
|
/**
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
63
|
+
* Create a Cumulocity manifest from a project root and its Nitro config.
|
|
64
|
+
* Reads `package.json` for identity fields, takes user manifest options from
|
|
65
|
+
* `nitroConfig.c8y.manifest`, and auto-injects `openApiSpec` when Nitro
|
|
66
|
+
* OpenAPI is enabled for production.
|
|
67
|
+
*
|
|
68
|
+
* Used by both the Nitro module and the CLI commands.
|
|
69
|
+
* @param rootDir - Directory containing the project's `package.json`
|
|
70
|
+
* @param nitroConfig - Nitro config (or options) for this project
|
|
46
71
|
* @param logger - Optional logger for debug output
|
|
47
72
|
*/
|
|
48
|
-
async function createC8yManifest(rootDir,
|
|
73
|
+
async function createC8yManifest(rootDir, nitroConfig = {}, logger) {
|
|
74
|
+
const { options, openApiSpec } = extractManifestInputs(nitroConfig);
|
|
49
75
|
validateManifestSettings(options);
|
|
50
76
|
const { name, version, provider, ...restManifestFields } = await readPackageJsonFieldsForManifest(rootDir, logger);
|
|
51
77
|
const probeFields = {};
|
|
@@ -59,7 +85,7 @@ async function createC8yManifest(rootDir, options = {}, logger) {
|
|
|
59
85
|
};
|
|
60
86
|
const key = `${name}-key`;
|
|
61
87
|
let requiredRoles = options.requiredRoles ? [...options.requiredRoles] : void 0;
|
|
62
|
-
if (options.settings && options.settings.length > 0 && !requiredRoles?.includes(ROLE_OPTION_MANAGEMENT_READ)
|
|
88
|
+
if (options.settings && options.settings.length > 0 && !requiredRoles?.includes(ROLE_OPTION_MANAGEMENT_READ)) {
|
|
63
89
|
requiredRoles = [...requiredRoles ?? [], ROLE_OPTION_MANAGEMENT_READ];
|
|
64
90
|
logger?.debug(`Auto-added ${ROLE_OPTION_MANAGEMENT_READ} to requiredRoles because manifest.settings are defined`);
|
|
65
91
|
}
|
|
@@ -68,6 +94,7 @@ async function createC8yManifest(rootDir, options = {}, logger) {
|
|
|
68
94
|
...probeFields,
|
|
69
95
|
...options,
|
|
70
96
|
...requiredRoles !== void 0 ? { requiredRoles } : {},
|
|
97
|
+
...openApiSpec ? { openApiSpec } : {},
|
|
71
98
|
provider,
|
|
72
99
|
name,
|
|
73
100
|
version,
|
|
@@ -75,6 +102,7 @@ async function createC8yManifest(rootDir, options = {}, logger) {
|
|
|
75
102
|
key,
|
|
76
103
|
type: "MICROSERVICE"
|
|
77
104
|
};
|
|
105
|
+
if (openApiSpec) logger?.debug(`Auto-added openApiSpec="${openApiSpec}" to manifest (Nitro OpenAPI is enabled)`);
|
|
78
106
|
logger?.debug(`Created Cumulocity manifest: ${JSON.stringify(manifest, null, 2)}`);
|
|
79
107
|
return manifest;
|
|
80
108
|
}
|
|
@@ -84,7 +112,7 @@ async function createC8yManifest(rootDir, options = {}, logger) {
|
|
|
84
112
|
* @param nitro - The Nitro instance
|
|
85
113
|
*/
|
|
86
114
|
async function createC8yManifestFromNitro(nitro) {
|
|
87
|
-
return createC8yManifest(nitro.options.rootDir, nitro.options
|
|
115
|
+
return createC8yManifest(nitro.options.rootDir, nitro.options, nitro.logger);
|
|
88
116
|
}
|
|
89
117
|
//#endregion
|
|
90
118
|
//#region src/cli/utils/c8y-api.ts
|
package/dist/cli/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as name, r as version, t as description } from "../package-
|
|
1
|
+
import { n as name, r as version, t as description } from "../package-BhFLRPOb.mjs";
|
|
2
2
|
import { defineCommand, runMain } from "citty";
|
|
3
3
|
//#region src/cli/index.ts
|
|
4
4
|
runMain(defineCommand({
|
|
@@ -8,9 +8,9 @@ runMain(defineCommand({
|
|
|
8
8
|
description
|
|
9
9
|
},
|
|
10
10
|
subCommands: {
|
|
11
|
-
bootstrap: () => import("../bootstrap-
|
|
12
|
-
roles: () => import("../roles-
|
|
13
|
-
options: () => import("../options-
|
|
11
|
+
bootstrap: () => import("../bootstrap-ObaEF7rN.mjs").then((r) => r.default),
|
|
12
|
+
roles: () => import("../roles-B58Fguop.mjs").then((r) => r.default),
|
|
13
|
+
options: () => import("../options-B7_yb_wr.mjs").then((r) => r.default)
|
|
14
14
|
}
|
|
15
15
|
}));
|
|
16
16
|
//#endregion
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import "./credentials-eKnJ7m9Y.mjs";
|
|
1
2
|
import { C8YTenantOptionKeysCacheConfig } from "c8y-nitro/types";
|
|
2
|
-
|
|
3
3
|
//#region src/types/apiClient.d.ts
|
|
4
4
|
interface C8YAPIClientOptions {
|
|
5
5
|
/**
|
|
@@ -16,7 +16,7 @@ interface C8YAPIClientOptions {
|
|
|
16
16
|
}
|
|
17
17
|
//#endregion
|
|
18
18
|
//#region src/types/manifest.d.ts
|
|
19
|
-
type C8YManifestOptions = Omit<C8YManifest, 'name' | 'version' | 'apiVersion' | 'key' | 'type' | 'provider'>;
|
|
19
|
+
type C8YManifestOptions = Omit<C8YManifest, 'name' | 'version' | 'apiVersion' | 'key' | 'type' | 'provider' | 'openApiSpec'>;
|
|
20
20
|
interface C8YManifest {
|
|
21
21
|
/**
|
|
22
22
|
* API version (e.g., "v2", "2"). Version 2 required for enhanced container security.
|
|
@@ -126,6 +126,16 @@ interface C8YManifest {
|
|
|
126
126
|
* Platform extensions (e.g., UI plugins).
|
|
127
127
|
*/
|
|
128
128
|
extensions?: Extension[];
|
|
129
|
+
/**
|
|
130
|
+
* OpenAPI specification path exposed by this microservice. Auto-populated
|
|
131
|
+
* by c8y-nitro when Nitro's `experimental.openAPI` is enabled and
|
|
132
|
+
* `openAPI.production` is configured. Used by clients (e.g., mc8yp) to
|
|
133
|
+
* auto-discover the API.
|
|
134
|
+
*
|
|
135
|
+
* Path is relative to the microservice context path.
|
|
136
|
+
* @example "/_openapi.json"
|
|
137
|
+
*/
|
|
138
|
+
openApiSpec?: string;
|
|
129
139
|
}
|
|
130
140
|
/**
|
|
131
141
|
* Company/organization information.
|
|
@@ -383,10 +393,16 @@ interface C8YRoles$1 {}
|
|
|
383
393
|
*/
|
|
384
394
|
type C8YTenantOptionKeysCacheConfig$1 = Partial<Record<C8YTenantOptionKey$1, number>>;
|
|
385
395
|
/**
|
|
386
|
-
*
|
|
396
|
+
* Union of tenant option keys declared in `manifest.settings`.
|
|
387
397
|
* Overwritten by generated types from manifest settings (manifest.settings[].key).
|
|
388
398
|
*/
|
|
389
399
|
type C8YTenantOptionKey$1 = string;
|
|
400
|
+
/**
|
|
401
|
+
* The microservice's own tenant option settings category, resolved from
|
|
402
|
+
* `manifest.settingsCategory` (falling back to `contextPath`, then `name`).
|
|
403
|
+
* Overwritten by generated types from the manifest.
|
|
404
|
+
*/
|
|
405
|
+
type C8YSettingsCategory$1 = string;
|
|
390
406
|
//#endregion
|
|
391
407
|
//#region src/types/index.d.ts
|
|
392
408
|
interface C8yDevOptions {
|
|
@@ -428,4 +444,4 @@ interface C8yNitroModuleOptions {
|
|
|
428
444
|
skipBootstrap?: boolean;
|
|
429
445
|
}
|
|
430
446
|
//#endregion
|
|
431
|
-
export {
|
|
447
|
+
export { C8YTenantOptionKeysCacheConfig$1 as a, C8YZipOptions as c, C8YAPIClientOptions as d, C8YTenantOptionKey$1 as i, C8YManifest as l, C8yNitroModuleOptions as n, C8YRoles$1 as o, C8YSettingsCategory$1 as r, C8yCacheOptions as s, C8yDevOptions as t, C8YManifestOptions as u };
|
package/dist/index.d.mts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { n as C8yNitroModuleOptions } from "./index-
|
|
2
|
-
import { t as TenantCredentials } from "./credentials-
|
|
1
|
+
import { n as C8yNitroModuleOptions } from "./index-ByDSMEvU.mjs";
|
|
2
|
+
import { t as TenantCredentials } from "./credentials-eKnJ7m9Y.mjs";
|
|
3
3
|
import { NitroModule } from "nitro/types";
|
|
4
|
-
|
|
5
4
|
//#region src/index.d.ts
|
|
6
5
|
declare module 'nitro/types' {
|
|
7
6
|
interface NitroOptions {
|
package/dist/index.mjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { _ as GENERATED_READINESS_ROUTE, a as findMicroserviceByName, g as GENERATED_LIVENESS_ROUTE, h as GENERATED_INVALIDATE_TENANT_OPTIONS_ROUTE, l as subscribeToApplication, m as createC8yManifestFromNitro, n as createBasicAuthHeader, o as getBootstrapCredentials, p as createC8yManifest, r as createMicroservice } from "./c8y-api-
|
|
2
|
-
import { t as writeBootstrapCredentials } from "./env-file-
|
|
3
|
-
import { n as name } from "./package-
|
|
1
|
+
import { _ as GENERATED_READINESS_ROUTE, a as findMicroserviceByName, d as updateMicroservice, g as GENERATED_LIVENESS_ROUTE, h as GENERATED_INVALIDATE_TENANT_OPTIONS_ROUTE, l as subscribeToApplication, m as createC8yManifestFromNitro, n as createBasicAuthHeader, o as getBootstrapCredentials, p as createC8yManifest, r as createMicroservice } from "./c8y-api-BFjK6AYb.mjs";
|
|
2
|
+
import { t as writeBootstrapCredentials } from "./env-file-BrOTmVjA.mjs";
|
|
3
|
+
import { n as name } from "./package-BhFLRPOb.mjs";
|
|
4
4
|
import { basename, dirname, join, relative } from "node:path";
|
|
5
|
-
import { mkdir, writeFile } from "node:fs/promises";
|
|
5
|
+
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
6
6
|
import { x } from "tinyexec";
|
|
7
7
|
import { join as join$1 } from "pathe";
|
|
8
8
|
import { mkdir as mkdir$1, readFile as readFile$1, stat, writeFile as writeFile$1 } from "fs/promises";
|
|
@@ -11,6 +11,7 @@ import Spinnies from "spinnies";
|
|
|
11
11
|
import { colors } from "consola/utils";
|
|
12
12
|
import { mkdirSync, writeFileSync } from "fs";
|
|
13
13
|
import { fileURLToPath } from "node:url";
|
|
14
|
+
import { createHash } from "node:crypto";
|
|
14
15
|
import process from "node:process";
|
|
15
16
|
import evlog from "evlog/nitro/v3";
|
|
16
17
|
//#region src/module/apiClient.ts
|
|
@@ -113,6 +114,7 @@ type C8ySerialize<T> = T extends void | undefined | null ? undefined : T extends
|
|
|
113
114
|
* @param className - The class name for the generated service (e.g., "PlaygroundAPIClient")
|
|
114
115
|
*/
|
|
115
116
|
function generateAPIClient(routes, contextPath, className) {
|
|
117
|
+
const methods = routes.map((route) => generateMethod(route)).join("\n\n");
|
|
116
118
|
return `/* eslint-disable eslint-comments/no-unlimited-disable */
|
|
117
119
|
/* eslint-disable */
|
|
118
120
|
/**
|
|
@@ -157,7 +159,7 @@ export class ${className} {
|
|
|
157
159
|
return JSON.parse(text);
|
|
158
160
|
}
|
|
159
161
|
|
|
160
|
-
${
|
|
162
|
+
${methods}
|
|
161
163
|
}`;
|
|
162
164
|
}
|
|
163
165
|
/**
|
|
@@ -262,6 +264,8 @@ async function buildDockerImage(nitro, c8yDir) {
|
|
|
262
264
|
try {
|
|
263
265
|
const result = await x("docker", [
|
|
264
266
|
"build",
|
|
267
|
+
"--platform",
|
|
268
|
+
"linux/amd64",
|
|
265
269
|
"-t",
|
|
266
270
|
imageName,
|
|
267
271
|
"-f",
|
|
@@ -370,6 +374,7 @@ function setupRuntime(nitro, manifest) {
|
|
|
370
374
|
nitro.logger.debug("Setting up C8Y nitro runtime");
|
|
371
375
|
const roles = manifest.roles ?? [];
|
|
372
376
|
const settingKeys = (manifest.settings ?? []).map((s) => s.key);
|
|
377
|
+
const settingsCategory = manifest.settingsCategory ?? manifest.contextPath ?? manifest.name;
|
|
373
378
|
const completeTypesDir = join$1(nitro.options.rootDir, nitro.options.typescript.generatedTypesDir ?? "node_modules/.nitro/types");
|
|
374
379
|
const typesFile = join$1(completeTypesDir, "c8y-nitro.d.ts");
|
|
375
380
|
const typesContent = `// generated by c8y-nitro
|
|
@@ -379,6 +384,7 @@ ${roles.map((role) => ` '${role}': '${role}';`).join("\n")}
|
|
|
379
384
|
}
|
|
380
385
|
type C8YTenantOptionKey = ${settingKeys.length > 0 ? `${settingKeys.map((k) => `'${k}'`).join(" | ")}` : "never"}
|
|
381
386
|
type C8YTenantOptionKeysCacheConfig = Partial<Record<C8YTenantOptionKey, number>>;
|
|
387
|
+
type C8YSettingsCategory = ${settingsCategory ? `'${settingsCategory}'` : "string"};
|
|
382
388
|
}
|
|
383
389
|
|
|
384
390
|
declare module 'c8y-nitro/runtime' {
|
|
@@ -413,7 +419,7 @@ async function setupRuntimeConfig(nitro, options) {
|
|
|
413
419
|
nitro.options.runtimeConfig.c8yCredentialsCacheTTL = options.cache?.credentialsTTL ?? 600;
|
|
414
420
|
nitro.options.runtimeConfig.c8yDefaultTenantOptionsTTL = options.cache?.defaultTenantOptionsTTL ?? 600;
|
|
415
421
|
nitro.options.runtimeConfig.c8yTenantOptionsPerKeyTTL = options.cache?.tenantOptions ?? {};
|
|
416
|
-
const manifest = await createC8yManifest(nitro.options.rootDir, options
|
|
422
|
+
const manifest = await createC8yManifest(nitro.options.rootDir, nitro.options, nitro.logger);
|
|
417
423
|
nitro.options.runtimeConfig.c8ySettingsCategory = options.manifest?.settingsCategory ?? manifest.contextPath ?? manifest.name;
|
|
418
424
|
}
|
|
419
425
|
//#endregion
|
|
@@ -515,39 +521,156 @@ function checkProbeEndpoint(nitro, probe, probeType) {
|
|
|
515
521
|
}
|
|
516
522
|
}
|
|
517
523
|
//#endregion
|
|
524
|
+
//#region src/module/manifestCache.ts
|
|
525
|
+
/**
|
|
526
|
+
* Directory (inside the project's `node_modules`) where c8y-nitro persists
|
|
527
|
+
* local dev state such as the last-synced manifest hash. Lives in
|
|
528
|
+
* `node_modules` so it is naturally gitignored and scoped per-checkout.
|
|
529
|
+
*/
|
|
530
|
+
const CACHE_DIR_SEGMENTS = ["node_modules", ".c8y-nitro"];
|
|
531
|
+
const CACHE_FILE_NAME = "bootstrap-state.json";
|
|
532
|
+
const CACHE_SCHEMA_VERSION = 1;
|
|
533
|
+
function getCacheDir(rootDir) {
|
|
534
|
+
return join(rootDir, ...CACHE_DIR_SEGMENTS);
|
|
535
|
+
}
|
|
536
|
+
function getCacheFile(rootDir) {
|
|
537
|
+
return join(getCacheDir(rootDir), CACHE_FILE_NAME);
|
|
538
|
+
}
|
|
539
|
+
/**
|
|
540
|
+
* Compute a hash over the manifest and the deployment target it will be synced
|
|
541
|
+
* to. Folding the target in means switching tenant/baseURL invalidates the
|
|
542
|
+
* cached hash even if the manifest itself is unchanged.
|
|
543
|
+
*
|
|
544
|
+
* @param manifest - The generated Cumulocity manifest
|
|
545
|
+
* @param target - The deployment target identity
|
|
546
|
+
*/
|
|
547
|
+
function hashManifest(manifest, target) {
|
|
548
|
+
const payload = JSON.stringify({
|
|
549
|
+
manifest,
|
|
550
|
+
target
|
|
551
|
+
});
|
|
552
|
+
return createHash("sha256").update(payload).digest("hex");
|
|
553
|
+
}
|
|
554
|
+
/**
|
|
555
|
+
* Read the persisted bootstrap state, or `null` when absent, unreadable, or
|
|
556
|
+
* from an incompatible schema version.
|
|
557
|
+
* @param rootDir - Project root directory (containing `node_modules`)
|
|
558
|
+
*/
|
|
559
|
+
async function readManifestCache(rootDir) {
|
|
560
|
+
try {
|
|
561
|
+
const content = await readFile(getCacheFile(rootDir), "utf-8");
|
|
562
|
+
const state = JSON.parse(content);
|
|
563
|
+
if (state.schemaVersion !== CACHE_SCHEMA_VERSION) return null;
|
|
564
|
+
return state;
|
|
565
|
+
} catch {
|
|
566
|
+
return null;
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
/**
|
|
570
|
+
* Persist the bootstrap state for future dev restarts.
|
|
571
|
+
* @param rootDir - Project root directory (containing `node_modules`)
|
|
572
|
+
* @param state - The state to persist (schema version is filled in automatically)
|
|
573
|
+
*/
|
|
574
|
+
async function writeManifestCache(rootDir, state) {
|
|
575
|
+
await mkdir(getCacheDir(rootDir), { recursive: true });
|
|
576
|
+
const payload = {
|
|
577
|
+
schemaVersion: CACHE_SCHEMA_VERSION,
|
|
578
|
+
...state
|
|
579
|
+
};
|
|
580
|
+
await writeFile(getCacheFile(rootDir), JSON.stringify(payload, null, 2), "utf-8");
|
|
581
|
+
}
|
|
582
|
+
//#endregion
|
|
518
583
|
//#region src/module/autoBootstrap.ts
|
|
584
|
+
const BOOTSTRAP_ENV_VARS = [
|
|
585
|
+
"C8Y_BOOTSTRAP_TENANT",
|
|
586
|
+
"C8Y_BOOTSTRAP_USER",
|
|
587
|
+
"C8Y_BOOTSTRAP_PASSWORD"
|
|
588
|
+
];
|
|
589
|
+
const DEV_ENV_VARS = [
|
|
590
|
+
"C8Y_BASEURL",
|
|
591
|
+
"C8Y_DEVELOPMENT_TENANT",
|
|
592
|
+
"C8Y_DEVELOPMENT_USER",
|
|
593
|
+
"C8Y_DEVELOPMENT_PASSWORD"
|
|
594
|
+
];
|
|
519
595
|
/**
|
|
520
|
-
*
|
|
521
|
-
*
|
|
596
|
+
* Verifies the microservice on the development tenant and keeps it in sync with
|
|
597
|
+
* the local manifest, bootstrapping (or re-bootstrapping) when needed.
|
|
598
|
+
*
|
|
599
|
+
* Unlike a purely env-var-based check, this always verifies against the tenant
|
|
600
|
+
* when development credentials are available, so it also handles:
|
|
601
|
+
* - the manifest changing between dev restarts (auto-updates a placeholder), and
|
|
602
|
+
* - the application being deleted on the tenant while stale bootstrap env vars
|
|
603
|
+
* still linger locally (re-creates it).
|
|
604
|
+
*
|
|
605
|
+
* A previously *deployed* microservice (one with an active version) is never
|
|
606
|
+
* overwritten automatically - we only warn about the drift.
|
|
607
|
+
*
|
|
608
|
+
* Runs silently unless a bootstrap/sync was performed or an error occurs.
|
|
522
609
|
* @param nitro - Nitro instance
|
|
523
610
|
*/
|
|
524
611
|
async function autoBootstrap(nitro) {
|
|
525
612
|
try {
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
"C8Y_BOOTSTRAP_USER",
|
|
529
|
-
"C8Y_BOOTSTRAP_PASSWORD"
|
|
530
|
-
].every((v) => process.env[v])) return;
|
|
531
|
-
if ([
|
|
532
|
-
"C8Y_BASEURL",
|
|
533
|
-
"C8Y_DEVELOPMENT_TENANT",
|
|
534
|
-
"C8Y_DEVELOPMENT_USER",
|
|
535
|
-
"C8Y_DEVELOPMENT_PASSWORD"
|
|
536
|
-
].filter((v) => !process.env[v]).length > 0) return;
|
|
613
|
+
const rootDir = nitro.options.rootDir;
|
|
614
|
+
if (DEV_ENV_VARS.filter((v) => !process.env[v]).length > 0) return;
|
|
537
615
|
const baseUrl = process.env.C8Y_BASEURL.endsWith("/") ? process.env.C8Y_BASEURL.slice(0, -1) : process.env.C8Y_BASEURL;
|
|
538
|
-
const
|
|
539
|
-
const
|
|
616
|
+
const developmentTenant = process.env.C8Y_DEVELOPMENT_TENANT;
|
|
617
|
+
const authHeader = createBasicAuthHeader(developmentTenant, process.env.C8Y_DEVELOPMENT_USER, process.env.C8Y_DEVELOPMENT_PASSWORD);
|
|
618
|
+
const manifest = await createC8yManifest(rootDir, nitro.options, nitro.logger);
|
|
619
|
+
const target = {
|
|
620
|
+
baseUrl,
|
|
621
|
+
developmentTenant,
|
|
622
|
+
name: manifest.name
|
|
623
|
+
};
|
|
624
|
+
const currentHash = hashManifest(manifest, target);
|
|
540
625
|
const existingApp = await findMicroserviceByName(baseUrl, manifest.name, authHeader);
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
626
|
+
if (!existingApp) {
|
|
627
|
+
const createdApp = await createMicroservice(baseUrl, manifest, authHeader);
|
|
628
|
+
nitro.logger.debug(`Microservice "${manifest.name}" created (ID: ${createdApp.id})`);
|
|
629
|
+
await finalizeBootstrap(nitro, {
|
|
630
|
+
baseUrl,
|
|
631
|
+
developmentTenant,
|
|
632
|
+
appId: createdApp.id,
|
|
633
|
+
authHeader,
|
|
634
|
+
target,
|
|
635
|
+
manifestHash: currentHash,
|
|
636
|
+
forceCredentials: true
|
|
637
|
+
});
|
|
638
|
+
nitro.logger.success(`Auto-bootstrap complete! Microservice "${manifest.name}" created on tenant.`);
|
|
639
|
+
return;
|
|
640
|
+
}
|
|
641
|
+
const appId = existingApp.id;
|
|
642
|
+
const isRealService = Boolean(existingApp.activeVersionId);
|
|
643
|
+
const cache = await readManifestCache(rootDir);
|
|
644
|
+
if (!cache || cache.manifestHash !== currentHash || cache.appId !== appId) if (isRealService) nitro.logger.warn(`Local manifest for "${manifest.name}" differs from the deployed microservice on the development tenant. The running service was left untouched. Redeploy or run \`npx c8y-nitro bootstrap\` to update it explicitly.`);
|
|
645
|
+
else {
|
|
646
|
+
await updateMicroservice(baseUrl, appId, manifest, authHeader);
|
|
647
|
+
nitro.logger.success(`Auto-bootstrap: updated microservice "${manifest.name}" with changed manifest.`);
|
|
548
648
|
}
|
|
549
|
-
|
|
550
|
-
const
|
|
649
|
+
const hasBootstrapCreds = BOOTSTRAP_ENV_VARS.every((v) => process.env[v]);
|
|
650
|
+
const appChanged = cache?.appId !== appId;
|
|
651
|
+
await finalizeBootstrap(nitro, {
|
|
652
|
+
baseUrl,
|
|
653
|
+
developmentTenant,
|
|
654
|
+
appId,
|
|
655
|
+
authHeader,
|
|
656
|
+
target,
|
|
657
|
+
manifestHash: currentHash,
|
|
658
|
+
forceCredentials: !hasBootstrapCreds || appChanged
|
|
659
|
+
});
|
|
660
|
+
} catch (error) {
|
|
661
|
+
nitro.logger.warn("Auto-bootstrap failed:", error instanceof Error ? error.message : String(error));
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
/**
|
|
665
|
+
* Subscribe the tenant, optionally (re)write bootstrap credentials, and persist
|
|
666
|
+
* the synced state to the local cache.
|
|
667
|
+
* @param nitro
|
|
668
|
+
* @param opts
|
|
669
|
+
*/
|
|
670
|
+
async function finalizeBootstrap(nitro, opts) {
|
|
671
|
+
await subscribeToApplication(opts.baseUrl, opts.developmentTenant, opts.appId, opts.authHeader);
|
|
672
|
+
if (opts.forceCredentials) {
|
|
673
|
+
const credentials = await getBootstrapCredentials(opts.baseUrl, opts.appId, opts.authHeader);
|
|
551
674
|
const envFileName = await writeBootstrapCredentials(nitro.options.rootDir, {
|
|
552
675
|
C8Y_BOOTSTRAP_TENANT: credentials.tenant,
|
|
553
676
|
C8Y_BOOTSTRAP_USER: credentials.name,
|
|
@@ -556,10 +679,13 @@ async function autoBootstrap(nitro) {
|
|
|
556
679
|
process.env.C8Y_BOOTSTRAP_TENANT = credentials.tenant;
|
|
557
680
|
process.env.C8Y_BOOTSTRAP_USER = credentials.name;
|
|
558
681
|
process.env.C8Y_BOOTSTRAP_PASSWORD = credentials.password;
|
|
559
|
-
nitro.logger.
|
|
560
|
-
} catch (error) {
|
|
561
|
-
nitro.logger.warn("Auto-bootstrap failed:", error instanceof Error ? error.message : String(error));
|
|
682
|
+
nitro.logger.debug(`Bootstrap credentials written to ${envFileName}`);
|
|
562
683
|
}
|
|
684
|
+
await writeManifestCache(nitro.options.rootDir, {
|
|
685
|
+
manifestHash: opts.manifestHash,
|
|
686
|
+
target: opts.target,
|
|
687
|
+
appId: opts.appId
|
|
688
|
+
});
|
|
563
689
|
}
|
|
564
690
|
//#endregion
|
|
565
691
|
//#region src/index.ts
|
|
@@ -575,7 +701,8 @@ function c8y() {
|
|
|
575
701
|
nitro.options.noExternals = nitro.options.noExternals === true ? true : [
|
|
576
702
|
...Array.isArray(nitro.options.noExternals) ? nitro.options.noExternals : [],
|
|
577
703
|
name,
|
|
578
|
-
"@c8y/client"
|
|
704
|
+
"@c8y/client",
|
|
705
|
+
"tslib"
|
|
579
706
|
];
|
|
580
707
|
nitro.options.rolldownConfig = {
|
|
581
708
|
...nitro.options.rolldownConfig,
|
|
@@ -597,12 +724,13 @@ function c8y() {
|
|
|
597
724
|
exclude: [manifest.livenessProbe?.httpGet?.path, manifest.readinessProbe?.httpGet?.path].filter(Boolean)
|
|
598
725
|
});
|
|
599
726
|
await setupEvlog(nitro);
|
|
600
|
-
if (!options.skipBootstrap) await autoBootstrap(nitro);
|
|
727
|
+
if (nitro.options.preset === "nitro-dev" && !options.skipBootstrap) await autoBootstrap(nitro);
|
|
601
728
|
await setupRuntimeConfig(nitro, options);
|
|
602
729
|
setupRuntime(nitro, manifest);
|
|
603
730
|
nitro.hooks.hook("dev:reload", async () => {
|
|
604
731
|
manifest = await createC8yManifestFromNitro(nitro);
|
|
605
732
|
setupRuntime(nitro, manifest);
|
|
733
|
+
if (!options.skipBootstrap) await autoBootstrap(nitro);
|
|
606
734
|
if (options.apiClient) {
|
|
607
735
|
nitro.logger.debug("Generating C8Y API client");
|
|
608
736
|
await writeAPIClient(nitro, options);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { c as getTenantOptionsByCategory, f as upsertTenantOption, i as deleteTenantOption, n as createBasicAuthHeader, p as createC8yManifest, s as getTenantOption } from "./c8y-api-
|
|
2
|
-
import { n as validateBootstrapEnv, t as loadC8yConfig } from "./config-
|
|
1
|
+
import { c as getTenantOptionsByCategory, f as upsertTenantOption, i as deleteTenantOption, n as createBasicAuthHeader, p as createC8yManifest, s as getTenantOption } from "./c8y-api-BFjK6AYb.mjs";
|
|
2
|
+
import { n as validateBootstrapEnv, t as loadC8yConfig } from "./config-BVQHqlgV.mjs";
|
|
3
3
|
import { defineCommand } from "citty";
|
|
4
4
|
import { consola } from "consola";
|
|
5
5
|
//#region src/cli/commands/options.ts
|
|
@@ -11,11 +11,11 @@ var options_default = defineCommand({
|
|
|
11
11
|
args: {},
|
|
12
12
|
async run() {
|
|
13
13
|
consola.info("Loading configuration...");
|
|
14
|
-
const { env,
|
|
14
|
+
const { env, nitroConfig, configDir } = await loadC8yConfig();
|
|
15
15
|
consola.info("Validating environment variables...");
|
|
16
16
|
const envVars = validateBootstrapEnv(env);
|
|
17
17
|
consola.info("Loading manifest...");
|
|
18
|
-
const manifest = await createC8yManifest(configDir,
|
|
18
|
+
const manifest = await createC8yManifest(configDir, nitroConfig);
|
|
19
19
|
const category = manifest.settingsCategory || manifest.contextPath || manifest.name;
|
|
20
20
|
if (!manifest.settings || manifest.settings.length === 0) throw new Error("No settings defined in manifest. Add settings to your c8y.manifest configuration.");
|
|
21
21
|
consola.success(`Using category: ${category}`);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { n as createBasicAuthHeader, p as createC8yManifest, t as assignUserRole, u as unassignUserRole } from "./c8y-api-
|
|
2
|
-
import { n as validateBootstrapEnv, t as loadC8yConfig } from "./config-
|
|
1
|
+
import { n as createBasicAuthHeader, p as createC8yManifest, t as assignUserRole, u as unassignUserRole } from "./c8y-api-BFjK6AYb.mjs";
|
|
2
|
+
import { n as validateBootstrapEnv, t as loadC8yConfig } from "./config-BVQHqlgV.mjs";
|
|
3
3
|
import { defineCommand } from "citty";
|
|
4
4
|
import { consola } from "consola";
|
|
5
5
|
//#region src/cli/commands/roles.ts
|
|
@@ -11,11 +11,11 @@ var roles_default = defineCommand({
|
|
|
11
11
|
args: {},
|
|
12
12
|
async run() {
|
|
13
13
|
consola.info("Loading configuration...");
|
|
14
|
-
const { env,
|
|
14
|
+
const { env, nitroConfig, configDir } = await loadC8yConfig();
|
|
15
15
|
consola.info("Validating environment variables...");
|
|
16
16
|
const envVars = validateBootstrapEnv(env);
|
|
17
17
|
consola.info("Building manifest...");
|
|
18
|
-
const manifest = await createC8yManifest(configDir,
|
|
18
|
+
const manifest = await createC8yManifest(configDir, nitroConfig);
|
|
19
19
|
const authHeader = createBasicAuthHeader(envVars.C8Y_DEVELOPMENT_TENANT, envVars.C8Y_DEVELOPMENT_USER, envVars.C8Y_DEVELOPMENT_PASSWORD);
|
|
20
20
|
if (manifest.roles && manifest.roles.length > 0) {
|
|
21
21
|
const rolesToAssign = await consola.prompt("Select roles to assign to your user (unselected roles will be removed):", {
|