Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions packages/cli/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/package.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down
2 changes: 2 additions & 0 deletions packages/cli/src/deploy/command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import * as o2 from '../projects/options';
export type DeployOptions = Required<
Pick<
Opts & POpts,
| 'endpoint'
| 'apiKey'
| 'beta'
| 'command'
| 'configPath'
Expand Down
28 changes: 16 additions & 12 deletions packages/cli/src/deploy/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<F extends (...args: any) => any>(
options: DeployOptions,
logger: Logger,
deployFn: F
deployFn: F,
betaHandler?: BetaHandlerFn
): Promise<ReturnType<typeof deployFn>>;

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 {
Expand All @@ -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) {
Expand Down Expand Up @@ -110,24 +114,24 @@ 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.'
);

// 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
);
Expand Down
55 changes: 28 additions & 27 deletions packages/cli/src/projects/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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();

Expand All @@ -321,7 +322,7 @@ export async function handler(options: DeployOptions, logger: Logger) {
config,
ws,
localProject,
tracker,
tracker!,
logger
);
if (!syncResult) {
Expand Down
1 change: 0 additions & 1 deletion packages/cli/src/projects/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: {
Expand Down
85 changes: 85 additions & 0 deletions packages/cli/test/deploy/deploy.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
Loading