diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index 5432f762f..1308fbb05 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -1,5 +1,12 @@ # @openfn/cli +## 1.36.3 + +### Patch Changes + +- 393b324: sync v2: fix an issue deploying a new project +- d405328: Fix endpoint tracing on deploy command + ## 1.36.2 ### Patch Changes diff --git a/packages/cli/package.json b/packages/cli/package.json index d588d5661..78dca3c84 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@openfn/cli", - "version": "1.36.2", + "version": "1.36.3", "description": "CLI devtools for the OpenFn toolchain", "engines": { "node": ">=18", diff --git a/packages/cli/src/deploy/command.ts b/packages/cli/src/deploy/command.ts index 6b144f8ee..f6e32d1f6 100644 --- a/packages/cli/src/deploy/command.ts +++ b/packages/cli/src/deploy/command.ts @@ -8,6 +8,8 @@ import * as o2 from '../projects/options'; export type DeployOptions = Required< Pick< Opts & POpts, + | 'endpoint' + | 'apiKey' | 'beta' | 'command' | 'configPath' diff --git a/packages/cli/src/deploy/handler.ts b/packages/cli/src/deploy/handler.ts index b448a6210..13b0d7fd7 100644 --- a/packages/cli/src/deploy/handler.ts +++ b/packages/cli/src/deploy/handler.ts @@ -14,23 +14,27 @@ import { yamlToJson } from '@openfn/project'; import fs from 'node:fs/promises'; export type DeployFn = typeof deploy; +export type BetaHandlerFn = typeof beta.handler; const actualDeploy: DeployFn = deploy; +const actualBetaHandler: BetaHandlerFn = beta.handler; -// Flexible `deployFn` interface for testing. +// Flexible `deployFn` / `betaHandler` interfaces for testing. async function deployHandler any>( options: DeployOptions, logger: Logger, - deployFn: F + deployFn: F, + betaHandler?: BetaHandlerFn ): Promise>; async function deployHandler( options: DeployOptions, logger: Logger, - deployFn = actualDeploy + deployFn = actualDeploy, + betaHandler: BetaHandlerFn = actualBetaHandler ) { if (options.beta) { - return beta.handler(options as any, logger); + return betaHandler(options as any, logger); } try { @@ -41,7 +45,7 @@ async function deployHandler( 'openfn.yaml' ); if (!process.env.PREFER_LEGACY_SYNC && (await fileExists(v2ConfigPath))) { - return redirectTov2(v2ConfigPath, options, config, logger); + return redirectTov2(v2ConfigPath, options, config, logger, betaHandler); } if (options.confirm === false) { @@ -110,7 +114,8 @@ const redirectTov2 = async ( v2ConfigPath: string, options: DeployOptions, config: DeployConfig, - logger: Logger + logger: Logger, + betaHandler: BetaHandlerFn = actualBetaHandler ) => { logger.always( 'Detected openfn.yaml file - switching to v2 deploy (openfn project deploy). Set PREFER_LEGACY_SYNC to disable this.' @@ -118,16 +123,15 @@ const redirectTov2 = async ( // default endpoint to one from openfn.yaml const v2config = yamlToJson(await fs.readFile(v2ConfigPath, 'utf-8')); - if (!config.endpoint && v2config?.project?.endpoint) { - config.endpoint = v2config.project.endpoint; - } + const endpoint = + options.endpoint ?? v2config?.project?.endpoint ?? config.endpoint; - return beta.handler( + return betaHandler( { ...options, force: true, - endpoint: config.endpoint, - apiKey: config.apiKey ?? undefined, + endpoint, + apiKey: options.apiKey ?? config.apiKey ?? undefined, }, logger ); diff --git a/packages/cli/src/projects/deploy.ts b/packages/cli/src/projects/deploy.ts index 69f17d6aa..03f4e21eb 100644 --- a/packages/cli/src/projects/deploy.ts +++ b/packages/cli/src/projects/deploy.ts @@ -148,7 +148,6 @@ const syncProjects = async ( // TODO should we prefer endpoint over alias? // maybe if it's explicitly passed? const endpoint = trackedProject.openfn?.endpoint ?? config.endpoint; - const { data } = await fetchProject( endpoint, config.apiKey, @@ -162,7 +161,7 @@ const syncProjects = async ( logger.info('Downloaded latest version of project at ', endpoint); } catch (e) { - console.log(e); + logger.error(e); throw e; // If fetch failed because of compatiblity with the local project, what do we do? // Well, actually I don't think I want this fetch to write to disk yet @@ -266,23 +265,32 @@ export async function handler(options: DeployOptions, logger: Logger) { // Track the remote we want to target // If the user passed a project alias, we need to use that // Otherwise just sync with the local project - const tracker = ws.get(options.project ?? localProject.uuid!); - - if (!tracker) { - // Is this really an error? Unlikely to happen I thuink - console.log( - `ERROR: Failed to find tracked remote project ${ - options.project ?? localProject.uuid! - } locally` - ); - console.log('To deploy a new project, add --new to the command'); - // TODO can we automate the fetch bit? - // If it's a UUID it should be ok? - console.log( - 'You may need to fetch the project before you can safely deploy' - ); + let tracker; + if (!options.new) { + tracker = ws.get(options.project ?? localProject.uuid!); + if (!tracker) { + console.log('FOUND TRACKER'); + console.log(options.project ?? localProject.uuid); + // Is this really an error? Unlikely to happen I thuink + console.log( + `ERROR: Failed to find tracked remote project ${ + options.project ?? localProject.uuid! + } locally` + ); + console.log('To deploy a new project, add --new to the command'); + // TODO can we automate the fetch bit? + // If it's a UUID it should be ok? + console.log( + 'You may need to fetch the project before you can safely deploy' + ); - throw new Error('Failed to find remote project locally'); + throw new Error('Failed to find remote project locally'); + } + } else { + // reset all metadata + localProject.openfn = { + endpoint: config.endpoint, + }; } // Choose the target endpoint we want to deploy to @@ -291,17 +299,10 @@ export async function handler(options: DeployOptions, logger: Logger) { // Otherwise fallback to to the auto-loaded config (probably coming from env) let endpoint: string = options.endpoint ?? - tracker.openfn?.endpoint ?? + tracker?.openfn?.endpoint ?? config.endpoint ?? DEFAULT_ENDPOINT; - if (options.new) { - // reset all metadata - localProject.openfn = { - endpoint: config.endpoint, - }; - } - // generate a credential map localProject.credentials = localProject.buildCredentialMap(); @@ -321,7 +322,7 @@ export async function handler(options: DeployOptions, logger: Logger) { config, ws, localProject, - tracker, + tracker!, logger ); if (!syncResult) { diff --git a/packages/cli/src/projects/util.ts b/packages/cli/src/projects/util.ts index 7a9185585..abe1f7e7b 100644 --- a/packages/cli/src/projects/util.ts +++ b/packages/cli/src/projects/util.ts @@ -121,7 +121,6 @@ export async function fetchProject( ): Promise<{ data: Provisioner.Project | null }> { const url = getLightningUrl(endpoint, projectId, snapshots); logger?.info(`Checking ${url} for existing project`); - try { const response = await fetch(url, { headers: { diff --git a/packages/cli/test/deploy/deploy.test.ts b/packages/cli/test/deploy/deploy.test.ts index b58bc25a9..0e9ea411d 100644 --- a/packages/cli/test/deploy/deploy.test.ts +++ b/packages/cli/test/deploy/deploy.test.ts @@ -88,6 +88,91 @@ test.serial('sets the exit code to 1', async (t) => { process.exitCode = origExitCode; }); +test.serial( + 'redirects to beta handler when openfn.yaml exists in cwd', + async (t) => { + t.plan(3); + mockfs({ + ['./config.json']: `{"apiKey": "123"}`, + ['./project.yaml']: `{"apiKey": "123"}`, + ['./openfn.yaml']: 'project:\n endpoint: https://from-yaml.org', + }); + + await deployHandler(options, logger, mockDeploy, async (args: any) => { + t.is(args.force, true); + t.is(args.endpoint, 'https://from-yaml.org'); + t.truthy(logger._find('always', /Detected openfn.yaml file/i)); + }); + } +); + +test.serial('does not redirect when PREFER_LEGACY_SYNC is set', async (t) => { + t.plan(1); + mockfs({ + ['./config.json']: `{"apiKey": "123", "endpoint": "https://api.example.com"}`, + ['./project.yaml']: `{"apiKey": "123"}`, + ['./openfn.yaml']: 'project:\n endpoint: https://from-yaml.org', + }); + process.env.PREFER_LEGACY_SYNC = '1'; + + await deployHandler(options, logger, mockDeploy, async (args: any) => { + t.fail('called beta handler'); + }); + + delete process.env.PREFER_LEGACY_SYNC; + t.pass(); +}); + +test.serial('CLI endpoint preferred over openfn.yaml endpoint', async (t) => { + t.plan(1); + mockfs({ + ['./config.json']: `{"apiKey": "123"}`, + ['./project.yaml']: `{"apiKey": "123"}`, + ['./openfn.yaml']: 'project:\n endpoint: https://from-yaml.org', + }); + + await deployHandler( + { ...options, endpoint: 'https://from-cli.org' } as any, + logger, + mockDeploy, + async (args: any) => { + t.is(args.endpoint, 'https://from-cli.org'); + } + ); +}); + +test.serial( + 'openfn.yaml endpoint preferred over config.json endpoint', + async (t) => { + mockfs({ + ['./config.json']: `{"apiKey": "123", "endpoint": "https://from-config.org"}`, + ['./project.yaml']: `{"apiKey": "123"}`, + ['./openfn.yaml']: 'project:\n endpoint: https://from-yaml.org', + }); + + await deployHandler(options, logger, mockDeploy, async (args: any) => { + t.is(args.endpoint, 'https://from-yaml.org'); + }); + } +); + +test.serial('CLI apiKey preferred over config.json apiKey', async (t) => { + mockfs({ + ['./config.json']: `{"apiKey": "from-config"}`, + ['./project.yaml']: `{"apiKey": "from-config"}`, + ['./openfn.yaml']: 'project:\n endpoint: https://from-yaml.org', + }); + + await deployHandler( + { ...options, apiKey: 'from-cli' } as any, + logger, + mockDeploy, + async (args: any) => { + t.is(args.apiKey, 'from-cli'); + } + ); +}); + test.serial('catches DeployErrors', async (t) => { const origExitCode = process.exitCode;