Repository: elastic/synthetics
Pull request #908 (2024-03-27 18:54:17)

feat: add support for --config option in push command
+ Add support for Push command that was not handling the `--config` option correctly. This PR fixed that and allows users to push the same journeys under different projects using different configuration file. 
```
> npx @elastic/synthetics push --config <synthetics.config.prod.ts>

> npx @elastic/synthetics push --config <synthetics.config.qa.ts>
```

gsantoro: I tested this and it works as expected.

I can push the same set of journeys (the same files) with two different configs for two different projects.

The two different push commands won't interfere with each other, and all parameters from the config files will be used correctly.

Patch:
diff --git a/src/cli.ts b/src/cli.ts
index 39a7f75..932b2b2 100644
--- a/src/cli.ts
+++ b/src/cli.ts
@@ -56,16 +56,13 @@ import { installTransform } from './core/transform';
 /* eslint-disable-next-line @typescript-eslint/no-var-requires */
 const { name, version } = require('../package.json');
 
-const { params, pattern, playwrightOpts, auth, authMandatory } =
+const { params, pattern, playwrightOpts, auth, authMandatory, configOpt } =
   getCommonCommandOpts();
 
 program
   .name(`npx ${name}`)
   .usage('[options] [dir] [files] file')
-  .option(
-    '-c, --config <path>',
-    'configuration path (default: synthetics.config.js)'
-  )
+  .addOption(configOpt)
   .addOption(pattern)
   .addOption(params)
   .option('--tags <name...>', 'run tests with a tag that matches the glob')
@@ -189,16 +186,17 @@ program
   .addOption(pattern)
   .addOption(params)
   .addOption(playwrightOpts)
-  .action(async (cmdOpts: PushOptions) => {
+  .addOption(configOpt)
+  .action(async cmdOpts => {
+    cmdOpts = { ...cmdOpts, ...program.opts() };
     const workDir = cwd();
-    const tearDown = await globalSetup({ inline: false, ...program.opts() }, [
+    const tearDown = await globalSetup({ inline: false, ...cmdOpts }, [
       workDir,
     ]);
     try {
-      const settings = await loadSettings();
+      const settings = await loadSettings(cmdOpts.config);
       const options = (await normalizeOptions(
         {
-          ...program.opts(),
           ...settings,
           ...cmdOpts,
         },
@@ -244,8 +242,7 @@ program
   .addOption(auth)
   .action(async (cmdOpts: LocationCmdOptions) => {
     const revert = installTransform();
-    const url = cmdOpts.url ?? (await loadSettings(true))?.url;
-
+    const url = cmdOpts.url ?? (await loadSettings(null, true))?.url;
     try {
       if (url && cmdOpts.auth) {
         const allLocations = await getLocations({
diff --git a/src/common_types.ts b/src/common_types.ts
index 210d36e..d904cf6 100644
--- a/src/common_types.ts
+++ b/src/common_types.ts
@@ -204,6 +204,7 @@ type BaseArgs = {
   params?: Params;
   screenshots?: ScreenshotOptions;
   dryRun?: boolean;
+  config?: string;
   pattern?: string;
   match?: string;
   tags?: Array<string>;
@@ -219,7 +220,6 @@ type BaseArgs = {
 };
 
 export type CliArgs = BaseArgs & {
-  config?: string;
   reporter?: BuiltInReporterName;
   inline?: boolean;
   require?: Array<string>;
diff --git a/src/options.ts b/src/options.ts
index 83fe0dc..c472e8b 100644
--- a/src/options.ts
+++ b/src/options.ts
@@ -208,11 +208,17 @@ export function getCommonCommandOpts() {
     .env('SYNTHETICS_API_KEY')
     .makeOptionMandatory(true);
 
+  const configOpt = createOption(
+    '-c, --config <path>',
+    'configuration path (default: synthetics.config.js)'
+  );
+
   return {
     auth,
     authMandatory,
     params,
     playwrightOpts,
     pattern,
+    configOpt,
   };
 }
diff --git a/src/push/index.ts b/src/push/index.ts
index 0341205..f3f5782 100644
--- a/src/push/index.ts
+++ b/src/push/index.ts
@@ -64,7 +64,9 @@ export async function push(monitors: Monitor[], options: PushOptions) {
   if (duplicates.size > 0) {
     throw error(formatDuplicateError(duplicates));
   }
-  progress(`Pushing monitors for project: ${options.id}`);
+  progress(
+    `Pushing monitors for '${options.id}' project in kibana '${options.space}' space`
+  );
 
   /**
    * Legacy API for kibana which does not support bulk operations
@@ -160,9 +162,12 @@ export function formatDuplicateError(monitors: Set<Monitor>) {
 
 const INSTALLATION_HELP = `Run 'npx @elastic/synthetics init' to create project with default settings.`;
 
-export async function loadSettings(ignoreMissing = false) {
+export async function loadSettings(configPath, ignoreMissing = false) {
   try {
-    const config = await readConfig(process.env['NODE_ENV'] || 'development');
+    const config = await readConfig(
+      process.env['NODE_ENV'] || 'development',
+      configPath
+    );
     // Missing config file, fake throw to capture as missing file
     if (Object.keys(config).length === 0) {
       throw '';
@@ -216,9 +221,13 @@ ${reason}
 ${INSTALLATION_HELP}`);
 }
 
-async function overrideSettings(oldValue: string, newValue: string) {
+async function overrideSettings(
+  configPath,
+  oldValue: string,
+  newValue: string
+) {
   const cwd = process.cwd();
-  const configPath = await findSyntheticsConfig(cwd, cwd);
+  configPath = configPath ?? (await findSyntheticsConfig(cwd, cwd));
   if (!configPath) {
     throw warn(`Unable to find synthetics config file: ${configPath}`);
   }
@@ -256,7 +265,7 @@ export async function catchIncorrectSettings(
     }
   }
   if (override) {
-    await overrideSettings(settings.id, options.id);
+    await overrideSettings(options.config, settings.id, options.id);
   }
 }
 


Test patch:
diff --git a/__tests__/push/index.test.ts b/__tests__/push/index.test.ts
index d8a3b12..2748b2d 100644
--- a/__tests__/push/index.test.ts
+++ b/__tests__/push/index.test.ts
@@ -46,9 +46,13 @@ describe('Push', () => {
     return cli.stderr();
   }
 
-  async function fakeProjectSetup(settings, monitor) {
+  async function fakeProjectSetup(
+    settings,
+    monitor,
+    filename = 'synthetics.config.ts'
+  ) {
     await writeFile(
-      join(PROJECT_DIR, 'synthetics.config.ts'),
+      join(PROJECT_DIR, filename),
       `export default { monitor: ${JSON.stringify(
         monitor
       )}, project: ${JSON.stringify(settings)} }`
@@ -266,13 +270,38 @@ heartbeat.monitors:
         journey('journey 2', () => monitor.use({ id: 'j2' }));`
         );
         const output = await runPush();
-        expect(output).toContain('Pushing monitors for project: test-project');
+        expect(output).toContain(
+          "Pushing monitors for 'test-project' project in kibana 'dummy' space"
+        );
         expect(output).toContain('bundling 2 monitors');
         expect(output).toContain('creating or updating 2 monitors');
         expect(output).toContain(deleteProgress);
         expect(output).toContain('✓ Pushed:');
         await rm(testJourney, { force: true });
       });
+
+      it('push journeys with --config', async () => {
+        const testJourney = join(PROJECT_DIR, 'test.journey.ts');
+        await writeFile(
+          testJourney,
+          `import {journey, monitor} from '../../../';
+        journey('journey 1', () => monitor.use({ id: 'j1' }));`
+        );
+        await fakeProjectSetup(
+          { id: 'bar', space: 'dummy', url: server.PREFIX },
+          { locations: ['test-loc'], schedule: 3 },
+          'synthetics.config.test.ts'
+        );
+        const output = await runPush([
+          ...DEFAULT_ARGS,
+          '--config',
+          join(PROJECT_DIR, 'synthetics.config.test.ts'),
+        ]);
+        expect(output).toContain(
+          "Pushing monitors for 'bar' project in kibana 'dummy' space"
+        );
+        await rm(testJourney, { force: true });
+      });
     });
   });
 });