From 015e8848b57aa71651373cfe421e648697d37fb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mih=C3=A1ly=20T=C3=B3th?= Date: Thu, 27 Nov 2025 20:18:35 +0100 Subject: [PATCH 1/5] fix: skip context validation if using local yaml file --- .../commands/generate/implementation/repo.ts | 48 +++++++++++++++---- 1 file changed, 38 insertions(+), 10 deletions(-) diff --git a/packages/cli/src/commands/generate/implementation/repo.ts b/packages/cli/src/commands/generate/implementation/repo.ts index 82dd651a..b03e5e41 100644 --- a/packages/cli/src/commands/generate/implementation/repo.ts +++ b/packages/cli/src/commands/generate/implementation/repo.ts @@ -56,11 +56,22 @@ async function generateRepo({ printOutput, }); - //const resolvedContext = await resolveEffectiveContext({ instance, workspace, branch }, core); - const { instanceConfig, workspaceConfig, branchConfig } = await resolveConfigs({ - cliContext: { instance, workspace, branch }, - core, - }); + let instanceConfig, workspaceConfig, branchConfig; + if (input && !fetch) { + // Skip context validation, provide dummy configs or minimal required fields + instanceConfig = { + name: instance || 'defaultInstance', + process: { output: output || './output' }, + }; + workspaceConfig = { name: workspace || 'defaultWorkspace', id: 'dummyId' }; + branchConfig = { label: branch || 'main' }; + } else { + // Perform normal context resolution and validation + ({ instanceConfig, workspaceConfig, branchConfig } = await resolveConfigs({ + cliContext: { instance, workspace, branch }, + core, + })); + } // Resolve output dir const outputDir = output @@ -96,25 +107,42 @@ async function generateRepo({ log.step(`Reading and parsing YAML file -> ${inputFile}`); const fileContents = await core.storage.readFile(inputFile, 'utf8'); const jsonData = load(fileContents); - const plannedWrites: { path: string; content: string }[] = await core.generateRepo({ jsonData, instance: instanceConfig.name, workspace: workspaceConfig.name, branch: branchConfig.label, }); + log.step(`Writing Repository to the output directory -> ${outputDir}`); - await Promise.all( + + // Track results for logging + const writeResults = await Promise.all( plannedWrites.map(async ({ path, content }) => { const outputPath = joinPath(outputDir, path); const writeDir = dirname(outputPath); - if (!(await core.storage.exists(writeDir))) { - await core.storage.mkdir(writeDir, { recursive: true }); + + try { + if (!(await core.storage.exists(writeDir))) { + await core.storage.mkdir(writeDir, { recursive: true }); + } + await core.storage.writeFile(outputPath, content); + return { path: outputPath, success: true }; + } catch (err) { + return { path: outputPath, success: false, error: err }; } - await core.storage.writeFile(outputPath, content); }) ); + // Summary log + const failedWrites = writeResults.filter((r) => !r.success); + if (failedWrites.length) { + log.warn(`Some files failed to write (${failedWrites.length}):`); + failedWrites.forEach((r) => log.warn(` - ${r.path}: ${r.error}`)); + } else { + log.info('All files written successfully.'); + } + printOutputDir(printOutput, outputDir); outro('Directory structure rebuilt successfully!'); } From 6c69aba1245472e3a0854521d86957b5e8664dc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mih=C3=A1ly=20T=C3=B3th?= Date: Thu, 27 Nov 2025 20:19:28 +0100 Subject: [PATCH 2/5] feat: added initial runtime context setting for the test runner to allow 'safe' credential passing --- .../src/commands/test/implementation/test.ts | 29 +++++++++++++++++++ packages/cli/src/commands/test/index.ts | 20 ++++++++++++- packages/core/src/features/testing/index.ts | 4 ++- .../core/src/implementations/run-tests.ts | 4 ++- packages/core/src/index.ts | 3 ++ 5 files changed, 57 insertions(+), 3 deletions(-) diff --git a/packages/cli/src/commands/test/implementation/test.ts b/packages/cli/src/commands/test/implementation/test.ts index f2263019..a380194b 100644 --- a/packages/cli/src/commands/test/implementation/test.ts +++ b/packages/cli/src/commands/test/implementation/test.ts @@ -9,6 +9,27 @@ import { resolveConfigs, } from '../../../utils/index'; +/** + * + * @param cliOptions + * @returns + */ +function collectInitialRuntimeValues(cliEnvVars = {}) { + // 1. Collect process.env XANO_* vars (Node only) + const envVars = {}; + for (const [k, v] of Object.entries(process.env)) { + if (k.startsWith('XANO_')) envVars[k] = v; + } + + // 2. Merge CLI over ENV, namespaced + return { + ENVIRONMENT: { + ...envVars, + ...cliEnvVars, + }, + }; +} + /** * Prints a formatted summary table of test outcomes to the log. * @@ -148,6 +169,7 @@ async function runTest({ isAll = false, printOutput = false, core, + cliTestEnvVars, }: { instance: string; workspace: string; @@ -157,6 +179,7 @@ async function runTest({ isAll: boolean; printOutput: boolean; core: any; + cliTestEnvVars: any; }) { intro('☣️ Starting up the testing...'); @@ -182,6 +205,11 @@ async function runTest({ const testConfig = await loadTestConfig(testConfigPath); const s = spinner(); s.start('Running tests based on the provided spec'); + + // Collect env vars to set up + const initialRuntimeValues = collectInitialRuntimeValues(cliTestEnvVars); + + // Run tests const testResults = await core.runTests({ context: { instance: instanceConfig.name, @@ -190,6 +218,7 @@ async function runTest({ }, groups: groups, testConfig, + initialRuntimeValues }); s.stop(); diff --git a/packages/cli/src/commands/test/index.ts b/packages/cli/src/commands/test/index.ts index 9d666b81..1254e2a2 100644 --- a/packages/cli/src/commands/test/index.ts +++ b/packages/cli/src/commands/test/index.ts @@ -1,4 +1,9 @@ -import { addApiGroupOptions, addFullContextOptions, addPrintOutputFlag, withErrorHandler } from '../../utils'; +import { + addApiGroupOptions, + addFullContextOptions, + addPrintOutputFlag, + withErrorHandler, +} from '../../utils'; import { runTest } from './implementation/test'; function registerTestCommands(program, core) { @@ -21,13 +26,26 @@ function registerTestCommands(program, core) { runTestsCommand .option('--test-config-path ', 'Local path to the test configuration file.') + .option( + '--test-env ', + 'Inject environment variables (KEY=VALUE) for tests. Can be repeated to set multiple.' + ) .action( withErrorHandler(async (options) => { + const cliTestEnvVars = {}; + for (const arg of options.testEnv) { + const [key, ...rest] = arg.split('='); + if (key && rest.length > 0) { + cliTestEnvVars[key] = rest.join('='); + } + } + await runTest({ ...options, isAll: options.all, printOutput: options.printOutputDir, core, + cliTestEnvVars, }); }) ); diff --git a/packages/core/src/features/testing/index.ts b/packages/core/src/features/testing/index.ts index 349456e1..51237936 100644 --- a/packages/core/src/features/testing/index.ts +++ b/packages/core/src/features/testing/index.ts @@ -52,6 +52,7 @@ async function testRunner({ testConfig, core, storage, + initialRuntimeValues, }: { context: CoreContext; groups: ApiGroupConfig[]; @@ -66,6 +67,7 @@ async function testRunner({ }[]; core: Caly; storage: Caly['storage']; + initialRuntimeValues: Record; }): Promise< { group: ApiGroupConfig; @@ -94,7 +96,7 @@ async function testRunner({ 'X-Data-Source': 'test', 'X-Branch': branchConfig.label, }; - let runtimeValues = {}; + let runtimeValues = initialRuntimeValues ?? {}; let finalOutput = []; diff --git a/packages/core/src/implementations/run-tests.ts b/packages/core/src/implementations/run-tests.ts index 5197a28b..eb8cea11 100644 --- a/packages/core/src/implementations/run-tests.ts +++ b/packages/core/src/implementations/run-tests.ts @@ -8,6 +8,7 @@ async function runTestsImplementation({ testConfig, core, storage, + initialRuntimeValues, }: { context: CoreContext; groups: ApiGroupConfig[]; @@ -22,6 +23,7 @@ async function runTestsImplementation({ }[]; core: Caly; storage: Caly['storage']; + initialRuntimeValues: Record; }): Promise< { group: ApiGroupConfig; @@ -35,7 +37,7 @@ async function runTestsImplementation({ }[]; }[] > { - return await testRunner({ context, groups, testConfig, core, storage }); + return await testRunner({ context, groups, testConfig, core, storage, initialRuntimeValues }); } export { runTestsImplementation }; diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 706fa5f2..e038fa94 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -336,10 +336,12 @@ export class Caly extends TypedEmitter { context, groups, testConfig, + initialRuntimeValues, }: { context: Context; groups: ApiGroupConfig[]; testConfig: any; + initialRuntimeValues: Record; }): Promise< { group: ApiGroupConfig; @@ -359,6 +361,7 @@ export class Caly extends TypedEmitter { testConfig, core: this, storage: this.storage, + initialRuntimeValues, }); } From d7a4c268a77c4dc985b3a4e02aa40e06e4ec2c35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mih=C3=A1ly=20T=C3=B3th?= Date: Thu, 27 Nov 2025 20:34:09 +0100 Subject: [PATCH 3/5] chore: wrap up the dynamic initial runtime values for the test runner --- .../src/commands/test/implementation/test.ts | 23 ++++++++++--------- packages/cli/src/commands/test/index.ts | 11 +++++---- packages/core/src/features/testing/index.ts | 2 +- 3 files changed, 19 insertions(+), 17 deletions(-) diff --git a/packages/cli/src/commands/test/implementation/test.ts b/packages/cli/src/commands/test/implementation/test.ts index 6b50972f..e90f59a9 100644 --- a/packages/cli/src/commands/test/implementation/test.ts +++ b/packages/cli/src/commands/test/implementation/test.ts @@ -11,8 +11,8 @@ import { /** * - * @param cliOptions - * @returns + * @param cliEnvVars - object of CLI provided env vars (e.g., {DEMO_ADMIN_PWD: 'xyz'}) + * @returns flat object with ENVIRONMENT.* keys */ function collectInitialRuntimeValues(cliEnvVars = {}) { // 1. Collect process.env XANO_* vars (Node only) @@ -21,13 +21,14 @@ function collectInitialRuntimeValues(cliEnvVars = {}) { if (k.startsWith('XANO_')) envVars[k] = v; } - // 2. Merge CLI over ENV, namespaced - return { - ENVIRONMENT: { - ...envVars, - ...cliEnvVars, - }, - }; + // 2. Merge CLI over ENV, CLI wins + const merged = { ...envVars, ...cliEnvVars }; + + const result = {}; + for (const [k, v] of Object.entries(merged)) { + result[`${k}`] = v; + } + return result; } /** @@ -224,7 +225,7 @@ async function runTest({ }, groups: groups, testConfig, - initialRuntimeValues + initialRuntimeValues, }); s.stop(); @@ -256,4 +257,4 @@ async function runTest({ } } -export { runTest }; \ No newline at end of file +export { runTest }; diff --git a/packages/cli/src/commands/test/index.ts b/packages/cli/src/commands/test/index.ts index 1254e2a2..749f5533 100644 --- a/packages/cli/src/commands/test/index.ts +++ b/packages/cli/src/commands/test/index.ts @@ -33,13 +33,14 @@ function registerTestCommands(program, core) { .action( withErrorHandler(async (options) => { const cliTestEnvVars = {}; - for (const arg of options.testEnv) { - const [key, ...rest] = arg.split('='); - if (key && rest.length > 0) { - cliTestEnvVars[key] = rest.join('='); + if (options.testEnv) { + for (const arg of options.testEnv) { + const [key, ...rest] = arg.split('='); + if (key && rest.length > 0) { + cliTestEnvVars[key] = rest.join('='); + } } } - await runTest({ ...options, isAll: options.all, diff --git a/packages/core/src/features/testing/index.ts b/packages/core/src/features/testing/index.ts index c4216f2b..d4ccd119 100644 --- a/packages/core/src/features/testing/index.ts +++ b/packages/core/src/features/testing/index.ts @@ -261,4 +261,4 @@ async function testRunner({ return finalOutput; } -export { testRunner }; \ No newline at end of file +export { testRunner }; From f55bdfdf7e7db2a7b0cb95e42d8a1d39c31d5412 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mih=C3=A1ly=20T=C3=B3th?= Date: Fri, 28 Nov 2025 06:46:50 +0100 Subject: [PATCH 4/5] fix: allowing write for the changeset cli on release --- .github/workflows/release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ee0e2129..157a2b07 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -7,9 +7,9 @@ on: - main # Set up for Trusted Publishing by NPM -permissions: +permissions: id-token: write - contents: read + contents: write jobs: release: From 049cdb8a085e412cb2106e3f2f786e16508d5697 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mih=C3=A1ly=20T=C3=B3th?= Date: Fri, 28 Nov 2025 06:58:55 +0100 Subject: [PATCH 5/5] chore: adjustments --- packages/cli/src/commands/test/implementation/test.ts | 8 ++------ packages/core/src/features/testing/index.ts | 4 ++-- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/packages/cli/src/commands/test/implementation/test.ts b/packages/cli/src/commands/test/implementation/test.ts index e90f59a9..81b3a55b 100644 --- a/packages/cli/src/commands/test/implementation/test.ts +++ b/packages/cli/src/commands/test/implementation/test.ts @@ -12,7 +12,7 @@ import { /** * * @param cliEnvVars - object of CLI provided env vars (e.g., {DEMO_ADMIN_PWD: 'xyz'}) - * @returns flat object with ENVIRONMENT.* keys + * @returns flat object with keys as-is, to be used with {{ENVIRONMENT.KEY}} template pattern */ function collectInitialRuntimeValues(cliEnvVars = {}) { // 1. Collect process.env XANO_* vars (Node only) @@ -24,11 +24,7 @@ function collectInitialRuntimeValues(cliEnvVars = {}) { // 2. Merge CLI over ENV, CLI wins const merged = { ...envVars, ...cliEnvVars }; - const result = {}; - for (const [k, v] of Object.entries(merged)) { - result[`${k}`] = v; - } - return result; + return merged; } /** diff --git a/packages/core/src/features/testing/index.ts b/packages/core/src/features/testing/index.ts index d4ccd119..cea5e5db 100644 --- a/packages/core/src/features/testing/index.ts +++ b/packages/core/src/features/testing/index.ts @@ -67,7 +67,7 @@ async function testRunner({ testConfig, core, storage, - initialRuntimeValues, + initialRuntimeValues = {}, }: { context: CoreContext; groups: ApiGroupConfig[]; @@ -82,7 +82,7 @@ async function testRunner({ }[]; core: Caly; storage: Caly['storage']; - initialRuntimeValues: Record; + initialRuntimeValues?: Record; }): Promise< { group: ApiGroupConfig;