@geekmidas/cli 10.0.0-alpha.51 → 10.0.0-alpha.52
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/{backends-B9V2XD2M.cjs → backends-BXcwhb7p.cjs} +2 -2
- package/dist/{backends-B9V2XD2M.cjs.map → backends-BXcwhb7p.cjs.map} +1 -1
- package/dist/{backends-CSuLl0YP.mjs → backends-CLq1wlQf.mjs} +2 -2
- package/dist/{backends-CSuLl0YP.mjs.map → backends-CLq1wlQf.mjs.map} +1 -1
- package/dist/{encryption-DRCXp-U4.mjs → encryption-Cv-Ej9vR.mjs} +2 -2
- package/dist/{encryption-DRCXp-U4.mjs.map → encryption-Cv-Ej9vR.mjs.map} +1 -1
- package/dist/{encryption-Bt8SHRI6.cjs → encryption-DCsdx4Po.cjs} +2 -2
- package/dist/{encryption-Bt8SHRI6.cjs.map → encryption-DCsdx4Po.cjs.map} +1 -1
- package/dist/index.cjs +54 -40
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +54 -40
- package/dist/index.mjs.map +1 -1
- package/dist/{migrate-dx4I0epp.cjs → migrate-DCxK1NRd.cjs} +2 -2
- package/dist/{migrate-dx4I0epp.cjs.map → migrate-DCxK1NRd.cjs.map} +1 -1
- package/dist/{migrate-soUQtku3.mjs → migrate-DmabhO_N.mjs} +2 -2
- package/dist/{migrate-soUQtku3.mjs.map → migrate-DmabhO_N.mjs.map} +1 -1
- package/dist/{ready-lr2Fm-ep.cjs → ready-ClUpTxoL.cjs} +14 -2
- package/dist/ready-ClUpTxoL.cjs.map +1 -0
- package/dist/{ready-WfT-eWlm.mjs → ready-tQLNHNya.mjs} +15 -3
- package/dist/ready-tQLNHNya.mjs.map +1 -0
- package/dist/vitest.cjs +1 -1
- package/dist/vitest.mjs +1 -1
- package/dist/{workspace-GBzR_hID.cjs → workspace-BEGUDNwK.cjs} +3 -3
- package/dist/{workspace-GBzR_hID.cjs.map → workspace-BEGUDNwK.cjs.map} +1 -1
- package/dist/{workspace-Dp2zK_Nk.mjs → workspace-fa5H6l7D.mjs} +3 -3
- package/dist/{workspace-Dp2zK_Nk.mjs.map → workspace-fa5H6l7D.mjs.map} +1 -1
- package/package.json +14 -14
- package/dist/ready-WfT-eWlm.mjs.map +0 -1
- package/dist/ready-lr2Fm-ep.cjs.map +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const require_ready = require("./ready-
|
|
1
|
+
const require_ready = require("./ready-ClUpTxoL.cjs");
|
|
2
2
|
//#region src/workspace/backends.ts
|
|
3
3
|
/**
|
|
4
4
|
* Which backend each declared kind resolves to — decided by where the project
|
|
@@ -79,4 +79,4 @@ Object.defineProperty(exports, "storageBackendFor", {
|
|
|
79
79
|
}
|
|
80
80
|
});
|
|
81
81
|
|
|
82
|
-
//# sourceMappingURL=backends-
|
|
82
|
+
//# sourceMappingURL=backends-BXcwhb7p.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"backends-
|
|
1
|
+
{"version":3,"file":"backends-BXcwhb7p.cjs","names":["DEFAULT_CACHE","DEFAULT_STORAGE","DEFAULT_EVENTS"],"sources":["../src/workspace/backends.ts"],"sourcesContent":["/**\n * Which backend each declared kind resolves to — decided by where the project\n * deploys, and by nothing else.\n *\n * These used to be read out of a `services:` block, where one key answered two\n * questions: *does this exist* and *what backs it*. The first is the manifest's\n * — a declared `Cache` is why there is a cache — and the second is the deploy\n * target's, because the right cache for a Lambda is not the right cache for a\n * box already running Postgres. With both answered elsewhere there was nothing\n * left for the block to say, so it is gone and these read the target.\n *\n * Mail has no answer here at all: every provider speaks SMTP, so the provider\n * is whichever one the stage's mail URL points at.\n */\n\nimport {\n\ttype CacheBackend,\n\tDEFAULT_CACHE,\n\tDEFAULT_EVENTS,\n\tDEFAULT_STORAGE,\n\ttype EventsBackend,\n\ttype MainProvider,\n\ttype StorageBackend,\n} from '../types.js';\n\n/**\n * Which family of defaults a workspace's deploy target belongs to.\n *\n * One question, asked once: **does this target run containers the project\n * controls?** Dokploy and a bare server do, so a bucket or a cache can live\n * beside the app; AWS, Vercel and Cloudflare do not, so the default has to be\n * something managed. That is the whole of the distinction, and it is why\n * `MainProvider` is the right axis rather than the deploy target's own name —\n * three targets share one answer.\n *\n * An AWS deploy names `deploy.default: 'sst'`, because it goes through SST.\n */\nexport function providerOf(workspace: {\n\tdeploy?: { default?: string } | undefined;\n}): MainProvider {\n\tconst target = workspace.deploy?.default;\n\n\treturn target === 'dokploy' || target === 'server' ? 'server' : 'aws';\n}\n\n/**\n * The cache backend a target implies.\n *\n * The same answer locally and deployed: a local `gkm dev` for an AWS app still\n * uses the Upstash protocol, because a backend that differed between the two\n * would be worse than a slower one.\n */\nexport function cacheBackendFor(on: MainProvider): CacheBackend {\n\treturn DEFAULT_CACHE[on];\n}\n\n/** Where a declared bucket lives on a target. */\nexport function storageBackendFor(on: MainProvider): StorageBackend {\n\treturn DEFAULT_STORAGE[on];\n}\n\n/**\n * Which broker carries declared queues and topics on a target.\n *\n * Only consulted when something declares one; a project with no `Topic` or\n * `Queue` has no events at all, whatever the target.\n */\nexport function eventsBackendFor(on: MainProvider): EventsBackend {\n\treturn DEFAULT_EVENTS[on];\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqCA,SAAgB,WAAW,WAEV;CAChB,MAAM,SAAS,UAAU,QAAQ;CAEjC,OAAO,WAAW,aAAa,WAAW,WAAW,WAAW;AACjE;;;;;;;;AASA,SAAgB,gBAAgB,IAAgC;CAC/D,OAAOA,cAAAA,cAAc;AACtB;;AAGA,SAAgB,kBAAkB,IAAkC;CACnE,OAAOC,cAAAA,gBAAgB;AACxB;;;;;;;AAQA,SAAgB,iBAAiB,IAAiC;CACjE,OAAOC,cAAAA,eAAe;AACvB"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { d as DEFAULT_EVENTS, f as DEFAULT_STORAGE, u as DEFAULT_CACHE } from "./ready-
|
|
1
|
+
import { d as DEFAULT_EVENTS, f as DEFAULT_STORAGE, u as DEFAULT_CACHE } from "./ready-tQLNHNya.mjs";
|
|
2
2
|
//#region src/workspace/backends.ts
|
|
3
3
|
/**
|
|
4
4
|
* Which backend each declared kind resolves to — decided by where the project
|
|
@@ -56,4 +56,4 @@ function eventsBackendFor(on) {
|
|
|
56
56
|
//#endregion
|
|
57
57
|
export { storageBackendFor as i, eventsBackendFor as n, providerOf as r, cacheBackendFor as t };
|
|
58
58
|
|
|
59
|
-
//# sourceMappingURL=backends-
|
|
59
|
+
//# sourceMappingURL=backends-CLq1wlQf.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"backends-
|
|
1
|
+
{"version":3,"file":"backends-CLq1wlQf.mjs","names":[],"sources":["../src/workspace/backends.ts"],"sourcesContent":["/**\n * Which backend each declared kind resolves to — decided by where the project\n * deploys, and by nothing else.\n *\n * These used to be read out of a `services:` block, where one key answered two\n * questions: *does this exist* and *what backs it*. The first is the manifest's\n * — a declared `Cache` is why there is a cache — and the second is the deploy\n * target's, because the right cache for a Lambda is not the right cache for a\n * box already running Postgres. With both answered elsewhere there was nothing\n * left for the block to say, so it is gone and these read the target.\n *\n * Mail has no answer here at all: every provider speaks SMTP, so the provider\n * is whichever one the stage's mail URL points at.\n */\n\nimport {\n\ttype CacheBackend,\n\tDEFAULT_CACHE,\n\tDEFAULT_EVENTS,\n\tDEFAULT_STORAGE,\n\ttype EventsBackend,\n\ttype MainProvider,\n\ttype StorageBackend,\n} from '../types.js';\n\n/**\n * Which family of defaults a workspace's deploy target belongs to.\n *\n * One question, asked once: **does this target run containers the project\n * controls?** Dokploy and a bare server do, so a bucket or a cache can live\n * beside the app; AWS, Vercel and Cloudflare do not, so the default has to be\n * something managed. That is the whole of the distinction, and it is why\n * `MainProvider` is the right axis rather than the deploy target's own name —\n * three targets share one answer.\n *\n * An AWS deploy names `deploy.default: 'sst'`, because it goes through SST.\n */\nexport function providerOf(workspace: {\n\tdeploy?: { default?: string } | undefined;\n}): MainProvider {\n\tconst target = workspace.deploy?.default;\n\n\treturn target === 'dokploy' || target === 'server' ? 'server' : 'aws';\n}\n\n/**\n * The cache backend a target implies.\n *\n * The same answer locally and deployed: a local `gkm dev` for an AWS app still\n * uses the Upstash protocol, because a backend that differed between the two\n * would be worse than a slower one.\n */\nexport function cacheBackendFor(on: MainProvider): CacheBackend {\n\treturn DEFAULT_CACHE[on];\n}\n\n/** Where a declared bucket lives on a target. */\nexport function storageBackendFor(on: MainProvider): StorageBackend {\n\treturn DEFAULT_STORAGE[on];\n}\n\n/**\n * Which broker carries declared queues and topics on a target.\n *\n * Only consulted when something declares one; a project with no `Topic` or\n * `Queue` has no events at all, whatever the target.\n */\nexport function eventsBackendFor(on: MainProvider): EventsBackend {\n\treturn DEFAULT_EVENTS[on];\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqCA,SAAgB,WAAW,WAEV;CAChB,MAAM,SAAS,UAAU,QAAQ;CAEjC,OAAO,WAAW,aAAa,WAAW,WAAW,WAAW;AACjE;;;;;;;;AASA,SAAgB,gBAAgB,IAAgC;CAC/D,OAAO,cAAc;AACtB;;AAGA,SAAgB,kBAAkB,IAAkC;CACnE,OAAO,gBAAgB;AACxB;;;;;;;AAQA,SAAgB,iBAAiB,IAAiC;CACjE,OAAO,eAAe;AACvB"}
|
|
@@ -2,7 +2,7 @@ import { u as loadWorkspaceSettings } from "./config-SOkDH-n1.mjs";
|
|
|
2
2
|
import { D as assertDeployedStage } from "./workspace-bOsks1uk.mjs";
|
|
3
3
|
import { a as withCustomSecret } from "./storage-BzaUxmqe.mjs";
|
|
4
4
|
import { n as secretsStoreFor } from "./store-DeGATCS-.mjs";
|
|
5
|
-
import { r as providerOf } from "./backends-
|
|
5
|
+
import { r as providerOf } from "./backends-CLq1wlQf.mjs";
|
|
6
6
|
import { canonicalId, provideKey } from "@geekmidas/manifest";
|
|
7
7
|
import { parseKeyring, retireKey, rotateKeyring } from "@geekmidas/constructs/encryption";
|
|
8
8
|
//#region src/encryption/index.ts
|
|
@@ -87,4 +87,4 @@ async function updateKeyring(construct, { stage }, context, update) {
|
|
|
87
87
|
//#endregion
|
|
88
88
|
export { encryptionRetireCommand, encryptionRotateCommand };
|
|
89
89
|
|
|
90
|
-
//# sourceMappingURL=encryption-
|
|
90
|
+
//# sourceMappingURL=encryption-Cv-Ej9vR.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"encryption-
|
|
1
|
+
{"version":3,"file":"encryption-Cv-Ej9vR.mjs","names":[],"sources":["../src/encryption/index.ts"],"sourcesContent":["/**\n * `gkm encryption:rotate` and `gkm encryption:retire` — the keyring of an\n * `Encryption` construct on a server stage.\n *\n * Rotating adds a key in front and keeps every older one, so what they wrote\n * still opens. Retiring removes one, and is the half that makes rotating worth\n * doing: until a key is gone, it still opens everything it ever wrote. It is\n * never automatic, because the only thing that knows nothing is left under a\n * key is the sweep that re-encrypted the column — so it is done after one,\n * deliberately, by name.\n *\n * Neither applies everywhere, and each says so rather than doing something\n * that looks right: a local stage's keyring is derived and would be replaced on\n * the next `gkm dev`, and on AWS the key is KMS's, which rotates it itself and\n * never strands a value.\n */\n\nimport {\n\tparseKeyring,\n\tretireKey,\n\trotateKeyring,\n} from '@geekmidas/constructs/encryption';\nimport { canonicalId, provideKey } from '@geekmidas/manifest';\nimport { loadWorkspaceSettings } from '../config';\nimport { withCustomSecret } from '../secrets/storage.js';\nimport { type SecretsStore, secretsStoreFor } from '../secrets/store.js';\nimport { providerOf } from '../workspace/backends.js';\nimport { assertDeployedStage } from '../workspace/stages.js';\nimport type { NormalizedWorkspace } from '../workspace/types.js';\n\nconst logger = console;\n\nexport class LocalKeyringIsDerived extends Error {\n\tconstructor(\n\t\treadonly construct: string,\n\t\treadonly stage: string,\n\t) {\n\t\tsuper(\n\t\t\t`'${stage}' is the local stage, where ${construct}'s keyring is derived from the project and stage — nothing stored there would be read. Rotate a deployed stage: gkm encryption:rotate ${construct} --stage <stage>.`,\n\t\t);\n\t\tthis.name = 'LocalKeyringIsDerived';\n\t}\n}\n\nexport class KmsRotatesItself extends Error {\n\tconstructor(readonly construct: string) {\n\t\tsuper(\n\t\t\t`${construct} is a KMS key on AWS: KMS rotates it yearly and keeps every version, so no value is ever stranded and there is no key to retire. Nothing to do here.`,\n\t\t);\n\t\tthis.name = 'KmsRotatesItself';\n\t}\n}\n\nexport class NoKeyringYet extends Error {\n\tconstructor(\n\t\treadonly construct: string,\n\t\treadonly stage: string,\n\t\treadonly key: string,\n\t) {\n\t\tsuper(\n\t\t\t`'${stage}' has no ${key} yet. The first deploy generates ${construct}'s keyring: gkm deploy --stage ${stage}.`,\n\t\t);\n\t\tthis.name = 'NoKeyringYet';\n\t}\n}\n\nexport interface EncryptionCommandOptions {\n\tstage: string;\n}\n\n/** Where a command reads and writes; the real ones unless a test says. */\nexport interface EncryptionCommandContext {\n\tworkspace?: NormalizedWorkspace;\n\tstore?: SecretsStore;\n}\n\nexport async function encryptionRotateCommand(\n\tconstruct: string,\n\toptions: EncryptionCommandOptions,\n\tcontext: EncryptionCommandContext = {},\n): Promise<string> {\n\treturn updateKeyring(construct, options, context, (id, url) => {\n\t\tconst next = rotateKeyring(id, url);\n\t\tconst [current, ...older] = parseKeyring(id, next).keys.map(({ id }) => id);\n\n\t\tlogger.log(`\\n✓ ${id} on '${options.stage}' now writes with '${current}'.`);\n\t\tlogger.log(\n\t\t\t` Still opening: ${older.join(', ')}. Redeploy to start writing with '${current}', reencrypt what is stored, then: gkm encryption:retire ${id} <key> --stage ${options.stage}`,\n\t\t);\n\t\treturn next;\n\t});\n}\n\nexport async function encryptionRetireCommand(\n\tconstruct: string,\n\tkeyId: string,\n\toptions: EncryptionCommandOptions,\n\tcontext: EncryptionCommandContext = {},\n): Promise<string> {\n\treturn updateKeyring(construct, options, context, (id, url) => {\n\t\tconst next = retireKey(id, url, keyId);\n\n\t\tlogger.log(\n\t\t\t`\\n✓ ${id} on '${options.stage}' no longer holds '${keyId}'. Anything still under it cannot be read after the next deploy.`,\n\t\t);\n\t\treturn next;\n\t});\n}\n\nasync function updateKeyring(\n\tconstruct: string,\n\t{ stage }: EncryptionCommandOptions,\n\tcontext: EncryptionCommandContext,\n\tupdate: (id: string, url: string) => string,\n): Promise<string> {\n\tconst workspace = context.workspace ?? (await loadWorkspaceSettings());\n\tconst id = canonicalId(construct);\n\n\tif (stage === workspace.stages.local) {\n\t\tthrow new LocalKeyringIsDerived(id, stage);\n\t}\n\tassertDeployedStage(workspace.stages, stage);\n\tif (providerOf(workspace) === 'aws') throw new KmsRotatesItself(id);\n\n\tconst store = context.store ?? (await secretsStoreFor(workspace, stage));\n\tconst key = provideKey(id, 'url');\n\tconst secrets = await store.read(stage);\n\tconst url = secrets?.custom[key];\n\tif (!secrets || !url) throw new NoKeyringYet(id, stage, key);\n\n\tconst next = update(id, url);\n\tawait store.write(stage, withCustomSecret(secrets, key, next));\n\treturn next;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AA8BA,MAAM,SAAS;AAEf,IAAa,wBAAb,cAA2C,MAAM;CAEtC;CACA;CAFV,YACC,WACA,OACC;EACD,MACC,IAAI,MAAM,8BAA8B,UAAU,wIAAwI,UAAU,kBACrM;EALS,KAAA,YAAA;EACA,KAAA,QAAA;EAKT,KAAK,OAAO;CACb;AACD;AAEA,IAAa,mBAAb,cAAsC,MAAM;CACtB;CAArB,YAAY,WAA4B;EACvC,MACC,GAAG,UAAU,qJACd;EAHoB,KAAA,YAAA;EAIpB,KAAK,OAAO;CACb;AACD;AAEA,IAAa,eAAb,cAAkC,MAAM;CAE7B;CACA;CACA;CAHV,YACC,WACA,OACA,KACC;EACD,MACC,IAAI,MAAM,WAAW,IAAI,mCAAmC,UAAU,iCAAiC,MAAM,EAC9G;EANS,KAAA,YAAA;EACA,KAAA,QAAA;EACA,KAAA,MAAA;EAKT,KAAK,OAAO;CACb;AACD;AAYA,eAAsB,wBACrB,WACA,SACA,UAAoC,CAAC,GACnB;CAClB,OAAO,cAAc,WAAW,SAAS,UAAU,IAAI,QAAQ;EAC9D,MAAM,OAAO,cAAc,IAAI,GAAG;EAClC,MAAM,CAAC,SAAS,GAAG,SAAS,aAAa,IAAI,IAAI,CAAC,CAAC,KAAK,KAAK,EAAE,SAAS,EAAE;EAE1E,OAAO,IAAI,OAAO,GAAG,OAAO,QAAQ,MAAM,qBAAqB,QAAQ,GAAG;EAC1E,OAAO,IACN,oBAAoB,MAAM,KAAK,IAAI,EAAE,oCAAoC,QAAQ,2DAA2D,GAAG,iBAAiB,QAAQ,OACzK;EACA,OAAO;CACR,CAAC;AACF;AAEA,eAAsB,wBACrB,WACA,OACA,SACA,UAAoC,CAAC,GACnB;CAClB,OAAO,cAAc,WAAW,SAAS,UAAU,IAAI,QAAQ;EAC9D,MAAM,OAAO,UAAU,IAAI,KAAK,KAAK;EAErC,OAAO,IACN,OAAO,GAAG,OAAO,QAAQ,MAAM,qBAAqB,MAAM,iEAC3D;EACA,OAAO;CACR,CAAC;AACF;AAEA,eAAe,cACd,WACA,EAAE,SACF,SACA,QACkB;CAClB,MAAM,YAAY,QAAQ,aAAc,MAAM,sBAAsB;CACpE,MAAM,KAAK,YAAY,SAAS;CAEhC,IAAI,UAAU,UAAU,OAAO,OAC9B,MAAM,IAAI,sBAAsB,IAAI,KAAK;CAE1C,oBAAoB,UAAU,QAAQ,KAAK;CAC3C,IAAI,WAAW,SAAS,MAAM,OAAO,MAAM,IAAI,iBAAiB,EAAE;CAElE,MAAM,QAAQ,QAAQ,SAAU,MAAM,gBAAgB,WAAW,KAAK;CACtE,MAAM,MAAM,WAAW,IAAI,KAAK;CAChC,MAAM,UAAU,MAAM,MAAM,KAAK,KAAK;CACtC,MAAM,MAAM,SAAS,OAAO;CAC5B,IAAI,CAAC,WAAW,CAAC,KAAK,MAAM,IAAI,aAAa,IAAI,OAAO,GAAG;CAE3D,MAAM,OAAO,OAAO,IAAI,GAAG;CAC3B,MAAM,MAAM,MAAM,OAAO,iBAAiB,SAAS,KAAK,IAAI,CAAC;CAC7D,OAAO;AACR"}
|
|
@@ -2,7 +2,7 @@ const require_config = require("./config-Cvr5fCbn.cjs");
|
|
|
2
2
|
const require_workspace = require("./workspace-BcCiLzTO.cjs");
|
|
3
3
|
const require_storage = require("./storage-CSGoC3bK.cjs");
|
|
4
4
|
const require_store = require("./store-DQgxT_WR.cjs");
|
|
5
|
-
const require_backends = require("./backends-
|
|
5
|
+
const require_backends = require("./backends-BXcwhb7p.cjs");
|
|
6
6
|
let _geekmidas_manifest = require("@geekmidas/manifest");
|
|
7
7
|
let _geekmidas_constructs_encryption = require("@geekmidas/constructs/encryption");
|
|
8
8
|
//#region src/encryption/index.ts
|
|
@@ -88,4 +88,4 @@ async function updateKeyring(construct, { stage }, context, update) {
|
|
|
88
88
|
exports.encryptionRetireCommand = encryptionRetireCommand;
|
|
89
89
|
exports.encryptionRotateCommand = encryptionRotateCommand;
|
|
90
90
|
|
|
91
|
-
//# sourceMappingURL=encryption-
|
|
91
|
+
//# sourceMappingURL=encryption-DCsdx4Po.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"encryption-
|
|
1
|
+
{"version":3,"file":"encryption-DCsdx4Po.cjs","names":["rotateKeyring","parseKeyring","retireKey","loadWorkspaceSettings","canonicalId","providerOf","secretsStoreFor","provideKey","withCustomSecret"],"sources":["../src/encryption/index.ts"],"sourcesContent":["/**\n * `gkm encryption:rotate` and `gkm encryption:retire` — the keyring of an\n * `Encryption` construct on a server stage.\n *\n * Rotating adds a key in front and keeps every older one, so what they wrote\n * still opens. Retiring removes one, and is the half that makes rotating worth\n * doing: until a key is gone, it still opens everything it ever wrote. It is\n * never automatic, because the only thing that knows nothing is left under a\n * key is the sweep that re-encrypted the column — so it is done after one,\n * deliberately, by name.\n *\n * Neither applies everywhere, and each says so rather than doing something\n * that looks right: a local stage's keyring is derived and would be replaced on\n * the next `gkm dev`, and on AWS the key is KMS's, which rotates it itself and\n * never strands a value.\n */\n\nimport {\n\tparseKeyring,\n\tretireKey,\n\trotateKeyring,\n} from '@geekmidas/constructs/encryption';\nimport { canonicalId, provideKey } from '@geekmidas/manifest';\nimport { loadWorkspaceSettings } from '../config';\nimport { withCustomSecret } from '../secrets/storage.js';\nimport { type SecretsStore, secretsStoreFor } from '../secrets/store.js';\nimport { providerOf } from '../workspace/backends.js';\nimport { assertDeployedStage } from '../workspace/stages.js';\nimport type { NormalizedWorkspace } from '../workspace/types.js';\n\nconst logger = console;\n\nexport class LocalKeyringIsDerived extends Error {\n\tconstructor(\n\t\treadonly construct: string,\n\t\treadonly stage: string,\n\t) {\n\t\tsuper(\n\t\t\t`'${stage}' is the local stage, where ${construct}'s keyring is derived from the project and stage — nothing stored there would be read. Rotate a deployed stage: gkm encryption:rotate ${construct} --stage <stage>.`,\n\t\t);\n\t\tthis.name = 'LocalKeyringIsDerived';\n\t}\n}\n\nexport class KmsRotatesItself extends Error {\n\tconstructor(readonly construct: string) {\n\t\tsuper(\n\t\t\t`${construct} is a KMS key on AWS: KMS rotates it yearly and keeps every version, so no value is ever stranded and there is no key to retire. Nothing to do here.`,\n\t\t);\n\t\tthis.name = 'KmsRotatesItself';\n\t}\n}\n\nexport class NoKeyringYet extends Error {\n\tconstructor(\n\t\treadonly construct: string,\n\t\treadonly stage: string,\n\t\treadonly key: string,\n\t) {\n\t\tsuper(\n\t\t\t`'${stage}' has no ${key} yet. The first deploy generates ${construct}'s keyring: gkm deploy --stage ${stage}.`,\n\t\t);\n\t\tthis.name = 'NoKeyringYet';\n\t}\n}\n\nexport interface EncryptionCommandOptions {\n\tstage: string;\n}\n\n/** Where a command reads and writes; the real ones unless a test says. */\nexport interface EncryptionCommandContext {\n\tworkspace?: NormalizedWorkspace;\n\tstore?: SecretsStore;\n}\n\nexport async function encryptionRotateCommand(\n\tconstruct: string,\n\toptions: EncryptionCommandOptions,\n\tcontext: EncryptionCommandContext = {},\n): Promise<string> {\n\treturn updateKeyring(construct, options, context, (id, url) => {\n\t\tconst next = rotateKeyring(id, url);\n\t\tconst [current, ...older] = parseKeyring(id, next).keys.map(({ id }) => id);\n\n\t\tlogger.log(`\\n✓ ${id} on '${options.stage}' now writes with '${current}'.`);\n\t\tlogger.log(\n\t\t\t` Still opening: ${older.join(', ')}. Redeploy to start writing with '${current}', reencrypt what is stored, then: gkm encryption:retire ${id} <key> --stage ${options.stage}`,\n\t\t);\n\t\treturn next;\n\t});\n}\n\nexport async function encryptionRetireCommand(\n\tconstruct: string,\n\tkeyId: string,\n\toptions: EncryptionCommandOptions,\n\tcontext: EncryptionCommandContext = {},\n): Promise<string> {\n\treturn updateKeyring(construct, options, context, (id, url) => {\n\t\tconst next = retireKey(id, url, keyId);\n\n\t\tlogger.log(\n\t\t\t`\\n✓ ${id} on '${options.stage}' no longer holds '${keyId}'. Anything still under it cannot be read after the next deploy.`,\n\t\t);\n\t\treturn next;\n\t});\n}\n\nasync function updateKeyring(\n\tconstruct: string,\n\t{ stage }: EncryptionCommandOptions,\n\tcontext: EncryptionCommandContext,\n\tupdate: (id: string, url: string) => string,\n): Promise<string> {\n\tconst workspace = context.workspace ?? (await loadWorkspaceSettings());\n\tconst id = canonicalId(construct);\n\n\tif (stage === workspace.stages.local) {\n\t\tthrow new LocalKeyringIsDerived(id, stage);\n\t}\n\tassertDeployedStage(workspace.stages, stage);\n\tif (providerOf(workspace) === 'aws') throw new KmsRotatesItself(id);\n\n\tconst store = context.store ?? (await secretsStoreFor(workspace, stage));\n\tconst key = provideKey(id, 'url');\n\tconst secrets = await store.read(stage);\n\tconst url = secrets?.custom[key];\n\tif (!secrets || !url) throw new NoKeyringYet(id, stage, key);\n\n\tconst next = update(id, url);\n\tawait store.write(stage, withCustomSecret(secrets, key, next));\n\treturn next;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AA8BA,MAAM,SAAS;AAEf,IAAa,wBAAb,cAA2C,MAAM;CAEtC;CACA;CAFV,YACC,WACA,OACC;EACD,MACC,IAAI,MAAM,8BAA8B,UAAU,wIAAwI,UAAU,kBACrM;EALS,KAAA,YAAA;EACA,KAAA,QAAA;EAKT,KAAK,OAAO;CACb;AACD;AAEA,IAAa,mBAAb,cAAsC,MAAM;CACtB;CAArB,YAAY,WAA4B;EACvC,MACC,GAAG,UAAU,qJACd;EAHoB,KAAA,YAAA;EAIpB,KAAK,OAAO;CACb;AACD;AAEA,IAAa,eAAb,cAAkC,MAAM;CAE7B;CACA;CACA;CAHV,YACC,WACA,OACA,KACC;EACD,MACC,IAAI,MAAM,WAAW,IAAI,mCAAmC,UAAU,iCAAiC,MAAM,EAC9G;EANS,KAAA,YAAA;EACA,KAAA,QAAA;EACA,KAAA,MAAA;EAKT,KAAK,OAAO;CACb;AACD;AAYA,eAAsB,wBACrB,WACA,SACA,UAAoC,CAAC,GACnB;CAClB,OAAO,cAAc,WAAW,SAAS,UAAU,IAAI,QAAQ;EAC9D,MAAM,QAAA,GAAOA,iCAAAA,cAAAA,CAAc,IAAI,GAAG;EAClC,MAAM,CAAC,SAAS,GAAG,UAAA,GAASC,iCAAAA,aAAAA,CAAa,IAAI,IAAI,CAAC,CAAC,KAAK,KAAK,EAAE,SAAS,EAAE;EAE1E,OAAO,IAAI,OAAO,GAAG,OAAO,QAAQ,MAAM,qBAAqB,QAAQ,GAAG;EAC1E,OAAO,IACN,oBAAoB,MAAM,KAAK,IAAI,EAAE,oCAAoC,QAAQ,2DAA2D,GAAG,iBAAiB,QAAQ,OACzK;EACA,OAAO;CACR,CAAC;AACF;AAEA,eAAsB,wBACrB,WACA,OACA,SACA,UAAoC,CAAC,GACnB;CAClB,OAAO,cAAc,WAAW,SAAS,UAAU,IAAI,QAAQ;EAC9D,MAAM,QAAA,GAAOC,iCAAAA,UAAAA,CAAU,IAAI,KAAK,KAAK;EAErC,OAAO,IACN,OAAO,GAAG,OAAO,QAAQ,MAAM,qBAAqB,MAAM,iEAC3D;EACA,OAAO;CACR,CAAC;AACF;AAEA,eAAe,cACd,WACA,EAAE,SACF,SACA,QACkB;CAClB,MAAM,YAAY,QAAQ,aAAc,MAAMC,eAAAA,sBAAsB;CACpE,MAAM,MAAA,GAAKC,oBAAAA,YAAAA,CAAY,SAAS;CAEhC,IAAI,UAAU,UAAU,OAAO,OAC9B,MAAM,IAAI,sBAAsB,IAAI,KAAK;CAE1C,kBAAA,oBAAoB,UAAU,QAAQ,KAAK;CAC3C,IAAIC,iBAAAA,WAAW,SAAS,MAAM,OAAO,MAAM,IAAI,iBAAiB,EAAE;CAElE,MAAM,QAAQ,QAAQ,SAAU,MAAMC,cAAAA,gBAAgB,WAAW,KAAK;CACtE,MAAM,OAAA,GAAMC,oBAAAA,WAAAA,CAAW,IAAI,KAAK;CAChC,MAAM,UAAU,MAAM,MAAM,KAAK,KAAK;CACtC,MAAM,MAAM,SAAS,OAAO;CAC5B,IAAI,CAAC,WAAW,CAAC,KAAK,MAAM,IAAI,aAAa,IAAI,OAAO,GAAG;CAE3D,MAAM,OAAO,OAAO,IAAI,GAAG;CAC3B,MAAM,MAAM,MAAM,OAAOC,gBAAAA,iBAAiB,SAAS,KAAK,IAAI,CAAC;CAC7D,OAAO;AACR"}
|
package/dist/index.cjs
CHANGED
|
@@ -6,11 +6,11 @@ const require_config = require("./config-Cvr5fCbn.cjs");
|
|
|
6
6
|
const require_workspace = require("./workspace-BcCiLzTO.cjs");
|
|
7
7
|
const require_storage = require("./storage-CSGoC3bK.cjs");
|
|
8
8
|
const require_store = require("./store-DQgxT_WR.cjs");
|
|
9
|
-
const require_workspace$1 = require("./workspace-
|
|
9
|
+
const require_workspace$1 = require("./workspace-BEGUDNwK.cjs");
|
|
10
10
|
const require_OpenApiTsGenerator = require("./OpenApiTsGenerator-BN0PIZxd.cjs");
|
|
11
11
|
const require_openapi = require("./openapi.cjs");
|
|
12
|
-
const require_ready = require("./ready-
|
|
13
|
-
const require_backends = require("./backends-
|
|
12
|
+
const require_ready = require("./ready-ClUpTxoL.cjs");
|
|
13
|
+
const require_backends = require("./backends-BXcwhb7p.cjs");
|
|
14
14
|
const require_emit = require("./emit-Dmc1nvdS.cjs");
|
|
15
15
|
let commander = require("commander");
|
|
16
16
|
let node_process = require("node:process");
|
|
@@ -48,7 +48,7 @@ let _geekmidas_constructs_queue = require("@geekmidas/constructs/queue");
|
|
|
48
48
|
let _geekmidas_constructs_topic = require("@geekmidas/constructs/topic");
|
|
49
49
|
let node_dns_promises = require("node:dns/promises");
|
|
50
50
|
//#region package.json
|
|
51
|
-
var version = "10.0.0-alpha.
|
|
51
|
+
var version = "10.0.0-alpha.52";
|
|
52
52
|
//#endregion
|
|
53
53
|
//#region src/auth/credentials.ts
|
|
54
54
|
/**
|
|
@@ -1731,9 +1731,9 @@ async function workspaceDevCommand(configured, options) {
|
|
|
1731
1731
|
for (const { id, port } of fakes) logger$9.log(`🎭 ${id} fake: http://localhost:${port}`);
|
|
1732
1732
|
logger$9.log(options.migrate || options.seed ? "🗄️ Migrating the databases" : "🗄️ Checking for pending migrations");
|
|
1733
1733
|
if (options.migrate || options.seed) {
|
|
1734
|
-
const { prepareDevDatabases } = await Promise.resolve().then(() => require("./migrate-
|
|
1734
|
+
const { prepareDevDatabases } = await Promise.resolve().then(() => require("./migrate-DCxK1NRd.cjs"));
|
|
1735
1735
|
await prepareDevDatabases(workspace, reconciled.env, { seed: !!options.seed });
|
|
1736
|
-
} else await Promise.resolve().then(() => require("./migrate-
|
|
1736
|
+
} else await Promise.resolve().then(() => require("./migrate-DCxK1NRd.cjs")).then(({ reportPendingMigrations }) => reportPendingMigrations(workspace, reconciled.env)).catch((error) => {
|
|
1737
1737
|
logger$9.log(`⚠️ Could not check for pending migrations: ${error instanceof Error ? error.message : String(error)}`);
|
|
1738
1738
|
});
|
|
1739
1739
|
const appUrls = {};
|
|
@@ -2316,7 +2316,7 @@ var CronGenerator = class extends require_discover.ConstructGenerator {
|
|
|
2316
2316
|
*/
|
|
2317
2317
|
import type { EnvironmentParser } from '@geekmidas/envkit';
|
|
2318
2318
|
import type { Logger } from '@geekmidas/logger';
|
|
2319
|
-
import { toCronExpression } from '@geekmidas/constructs/crons';
|
|
2319
|
+
import { runCron, scheduleInProcess, toCronExpression } from '@geekmidas/constructs/crons';
|
|
2320
2320
|
import { ServiceDiscovery } from '@geekmidas/services';
|
|
2321
2321
|
${Array.from(importsByFile.entries()).map(([importPath, exports]) => `import { ${exports.join(", ")} } from '${importPath}';`).join("\n")}
|
|
2322
2322
|
|
|
@@ -2357,11 +2357,10 @@ export async function setupCrons(
|
|
|
2357
2357
|
|
|
2358
2358
|
const run = async (entry: (typeof prepared)[number]) => {
|
|
2359
2359
|
try {
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
});
|
|
2360
|
+
// The cron's own function, the way its Lambda would run it — services,
|
|
2361
|
+
// the worker's database as \`db\`, its events. It used to call a
|
|
2362
|
+
// \`handler\` a Cron does not have, so every firing logged a failure.
|
|
2363
|
+
await runCron(entry.cron, serviceDiscovery);
|
|
2365
2364
|
} catch (error) {
|
|
2366
2365
|
logger.error({ error, cron: entry.name }, 'Cron failed');
|
|
2367
2366
|
}
|
|
@@ -2372,13 +2371,28 @@ export async function setupCrons(
|
|
|
2372
2371
|
// memory fires each job once per replica — so the schedule lives in
|
|
2373
2372
|
// Postgres. It used to be a second pg-boss on the worker's database, as the
|
|
2374
2373
|
// runtime role, which can neither create a schema nor use the broker's.
|
|
2375
|
-
const { url } = envParser
|
|
2374
|
+
const { url, devPid } = envParser
|
|
2376
2375
|
.create((get) => ({
|
|
2377
2376
|
url: get('EVENT_PUBLISHER_CONNECTION_STRING').string().optional(),
|
|
2377
|
+
// Set by \`gkm dev\` on the server it starts, and by nothing deployed.
|
|
2378
|
+
devPid: get('GKM_DEV_PID').string().optional(),
|
|
2378
2379
|
}))
|
|
2379
2380
|
.parse();
|
|
2380
2381
|
|
|
2381
2382
|
if (!url?.startsWith('pgboss://')) {
|
|
2383
|
+
// \`gkm dev\` on a target whose crons are infrastructure once deployed —
|
|
2384
|
+
// EventBridge rules on AWS — has no broker to keep a schedule in. It is one
|
|
2385
|
+
// process, so a timer fires each job once, which is all pg-boss is for
|
|
2386
|
+
// here. Only there: a deployed server firing by timer fires once per
|
|
2387
|
+
// replica, so outside dev this stays the error below.
|
|
2388
|
+
if (devPid) {
|
|
2389
|
+
for (const entry of prepared) {
|
|
2390
|
+
running.push(scheduleInProcess(entry.schedule, () => run(entry)));
|
|
2391
|
+
logger.info({ cron: entry.name, schedule: entry.schedule }, 'Cron scheduled in this process');
|
|
2392
|
+
}
|
|
2393
|
+
return;
|
|
2394
|
+
}
|
|
2395
|
+
|
|
2382
2396
|
logger.error(
|
|
2383
2397
|
{ crons: prepared.map(({ name }) => name) },
|
|
2384
2398
|
'These crons have nowhere to keep their schedule. On a server they are ' +
|
|
@@ -9326,26 +9340,26 @@ function loadPackageJson() {
|
|
|
9326
9340
|
* Run: pnpm --filter @geekmidas/cli sync-versions
|
|
9327
9341
|
*/
|
|
9328
9342
|
const GEEKMIDAS_VERSIONS = {
|
|
9329
|
-
"@geekmidas/audit": "~10.0.0-alpha.
|
|
9330
|
-
"@geekmidas/auth": "~10.0.0-alpha.
|
|
9331
|
-
"@geekmidas/cache": "~10.0.0-alpha.
|
|
9332
|
-
"@geekmidas/client": "~10.0.0-alpha.
|
|
9333
|
-
"@geekmidas/cloud": "~10.0.0-alpha.
|
|
9334
|
-
"@geekmidas/constructs": "~10.0.0-alpha.
|
|
9335
|
-
"@geekmidas/db": "~10.0.0-alpha.
|
|
9336
|
-
"@geekmidas/emailkit": "~10.0.0-alpha.
|
|
9337
|
-
"@geekmidas/envkit": "~10.0.0-alpha.
|
|
9338
|
-
"@geekmidas/errors": "~10.0.0-alpha.
|
|
9339
|
-
"@geekmidas/events": "~10.0.0-alpha.
|
|
9340
|
-
"@geekmidas/logger": "~10.0.0-alpha.
|
|
9341
|
-
"@geekmidas/manifest": "~10.0.0-alpha.
|
|
9342
|
-
"@geekmidas/rate-limit": "~10.0.0-alpha.
|
|
9343
|
-
"@geekmidas/schema": "~10.0.0-alpha.
|
|
9344
|
-
"@geekmidas/services": "~10.0.0-alpha.
|
|
9345
|
-
"@geekmidas/storage": "~10.0.0-alpha.
|
|
9346
|
-
"@geekmidas/studio": "~10.0.0-alpha.
|
|
9347
|
-
"@geekmidas/telescope": "~10.0.0-alpha.
|
|
9348
|
-
"@geekmidas/testkit": "~10.0.0-alpha.
|
|
9343
|
+
"@geekmidas/audit": "~10.0.0-alpha.52",
|
|
9344
|
+
"@geekmidas/auth": "~10.0.0-alpha.52",
|
|
9345
|
+
"@geekmidas/cache": "~10.0.0-alpha.52",
|
|
9346
|
+
"@geekmidas/client": "~10.0.0-alpha.52",
|
|
9347
|
+
"@geekmidas/cloud": "~10.0.0-alpha.52",
|
|
9348
|
+
"@geekmidas/constructs": "~10.0.0-alpha.52",
|
|
9349
|
+
"@geekmidas/db": "~10.0.0-alpha.52",
|
|
9350
|
+
"@geekmidas/emailkit": "~10.0.0-alpha.52",
|
|
9351
|
+
"@geekmidas/envkit": "~10.0.0-alpha.52",
|
|
9352
|
+
"@geekmidas/errors": "~10.0.0-alpha.52",
|
|
9353
|
+
"@geekmidas/events": "~10.0.0-alpha.52",
|
|
9354
|
+
"@geekmidas/logger": "~10.0.0-alpha.52",
|
|
9355
|
+
"@geekmidas/manifest": "~10.0.0-alpha.52",
|
|
9356
|
+
"@geekmidas/rate-limit": "~10.0.0-alpha.52",
|
|
9357
|
+
"@geekmidas/schema": "~10.0.0-alpha.52",
|
|
9358
|
+
"@geekmidas/services": "~10.0.0-alpha.52",
|
|
9359
|
+
"@geekmidas/storage": "~10.0.0-alpha.52",
|
|
9360
|
+
"@geekmidas/studio": "~10.0.0-alpha.52",
|
|
9361
|
+
"@geekmidas/telescope": "~10.0.0-alpha.52",
|
|
9362
|
+
"@geekmidas/testkit": "~10.0.0-alpha.52",
|
|
9349
9363
|
"@geekmidas/cli": `~${loadPackageJson().version}`
|
|
9350
9364
|
};
|
|
9351
9365
|
//#endregion
|
|
@@ -16088,7 +16102,7 @@ function testDatabases(reconciled) {
|
|
|
16088
16102
|
* starts without the reference data the app needs.
|
|
16089
16103
|
*/
|
|
16090
16104
|
async function migrateTestStage(workspace, env) {
|
|
16091
|
-
const { migrateAndSeed } = await Promise.resolve().then(() => require("./migrate-
|
|
16105
|
+
const { migrateAndSeed } = await Promise.resolve().then(() => require("./migrate-DCxK1NRd.cjs"));
|
|
16092
16106
|
const { discover } = await Promise.resolve().then(() => require("./discover-DXNviAh-.cjs")).then((n) => n.discover_exports);
|
|
16093
16107
|
const sources = {};
|
|
16094
16108
|
const manifest = await discover({
|
|
@@ -16519,7 +16533,7 @@ program.command("migrate").description("Apply each database construct's migratio
|
|
|
16519
16533
|
try {
|
|
16520
16534
|
const globalOptions = program.opts();
|
|
16521
16535
|
if (globalOptions.cwd) process.chdir(globalOptions.cwd);
|
|
16522
|
-
const { migrateCommand } = await Promise.resolve().then(() => require("./migrate-
|
|
16536
|
+
const { migrateCommand } = await Promise.resolve().then(() => require("./migrate-DCxK1NRd.cjs"));
|
|
16523
16537
|
await migrateCommand({
|
|
16524
16538
|
...options,
|
|
16525
16539
|
...construct ? { construct } : {}
|
|
@@ -16533,7 +16547,7 @@ program.command("seed").description("Migrate, then run each database construct's
|
|
|
16533
16547
|
try {
|
|
16534
16548
|
const globalOptions = program.opts();
|
|
16535
16549
|
if (globalOptions.cwd) process.chdir(globalOptions.cwd);
|
|
16536
|
-
const { seedCommand } = await Promise.resolve().then(() => require("./migrate-
|
|
16550
|
+
const { seedCommand } = await Promise.resolve().then(() => require("./migrate-DCxK1NRd.cjs"));
|
|
16537
16551
|
await seedCommand({
|
|
16538
16552
|
...options,
|
|
16539
16553
|
...construct ? { construct } : {}
|
|
@@ -16547,7 +16561,7 @@ program.command("migration").description("Write a construct's next migration: an
|
|
|
16547
16561
|
try {
|
|
16548
16562
|
const globalOptions = program.opts();
|
|
16549
16563
|
if (globalOptions.cwd) process.chdir(globalOptions.cwd);
|
|
16550
|
-
const { migrationCommand } = await Promise.resolve().then(() => require("./migrate-
|
|
16564
|
+
const { migrationCommand } = await Promise.resolve().then(() => require("./migrate-DCxK1NRd.cjs"));
|
|
16551
16565
|
await migrationCommand({
|
|
16552
16566
|
construct,
|
|
16553
16567
|
...name ? { name } : {}
|
|
@@ -16645,7 +16659,7 @@ program.command("encryption:rotate").description("Add a new current key to an En
|
|
|
16645
16659
|
try {
|
|
16646
16660
|
const globalOptions = program.opts();
|
|
16647
16661
|
if (globalOptions.cwd) process.chdir(globalOptions.cwd);
|
|
16648
|
-
const { encryptionRotateCommand } = await Promise.resolve().then(() => require("./encryption-
|
|
16662
|
+
const { encryptionRotateCommand } = await Promise.resolve().then(() => require("./encryption-DCsdx4Po.cjs"));
|
|
16649
16663
|
await encryptionRotateCommand(construct, options);
|
|
16650
16664
|
} catch (error) {
|
|
16651
16665
|
console.error(formatError(error));
|
|
@@ -16656,7 +16670,7 @@ program.command("encryption:retire").description("Remove an old key from an Encr
|
|
|
16656
16670
|
try {
|
|
16657
16671
|
const globalOptions = program.opts();
|
|
16658
16672
|
if (globalOptions.cwd) process.chdir(globalOptions.cwd);
|
|
16659
|
-
const { encryptionRetireCommand } = await Promise.resolve().then(() => require("./encryption-
|
|
16673
|
+
const { encryptionRetireCommand } = await Promise.resolve().then(() => require("./encryption-DCsdx4Po.cjs"));
|
|
16660
16674
|
await encryptionRetireCommand(construct, key, options);
|
|
16661
16675
|
} catch (error) {
|
|
16662
16676
|
console.error(formatError(error));
|
|
@@ -16708,7 +16722,7 @@ program.command("secrets:reconcile").description("Backfill missing custom secret
|
|
|
16708
16722
|
console.error(`No secrets found for stage "${stage}". Run "gkm secrets:init --stage ${stage}" first.`);
|
|
16709
16723
|
process.exit(1);
|
|
16710
16724
|
}
|
|
16711
|
-
const { derivedContainers } = await Promise.resolve().then(() => require("./workspace-
|
|
16725
|
+
const { derivedContainers } = await Promise.resolve().then(() => require("./workspace-BEGUDNwK.cjs")).then((n) => n.workspace_exports);
|
|
16712
16726
|
const result = reconcileMissingSecrets(secrets, workspace, await derivedContainers(workspace, stage));
|
|
16713
16727
|
if (!result) {
|
|
16714
16728
|
console.log(`\n✓ Secrets for stage "${stage}" are up-to-date`);
|