Skip to content

Commit f86df4e

Browse files
ensure directory is required for multiple directories
1 parent 2f6af9f commit f86df4e

File tree

12 files changed

+46
-24
lines changed

12 files changed

+46
-24
lines changed

cli/src/commands/deploy/index.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,11 +63,12 @@ export default class DeployAll extends CloudInstanceCommand {
6363
default: DEFAULT_DEPLOY_TIMEOUT_MS / 1000,
6464
description:
6565
'Seconds to wait after scheduling a deploy before timing out while polling status (default 300 seconds).',
66-
parse: async (input) => {
66+
async parse(input) {
6767
const value = Number(input);
6868
if (!Number.isFinite(value) || value <= 0) {
6969
throw new Error('deploy-timeout must be a positive number of seconds');
7070
}
71+
7172
return value;
7273
}
7374
}),
@@ -255,11 +256,13 @@ export default class DeployAll extends CloudInstanceCommand {
255256

256257
protected async withDeploy(timeoutMs: number, fn: () => Promise<routes.DeployInstanceResponse>): Promise<void> {
257258
const { client, project } = this;
259+
258260
const spinner = ora({
259261
prefixText: '\nDeploying instance.\n',
260262
spinner: 'moon',
261263
suffixText: '\nThis may take a few minutes.\n'
262264
});
265+
263266
spinner.start();
264267

265268
try {

cli/src/commands/deploy/sync-config.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,11 @@ export class DeploySyncConfig extends DeployAll {
1818
* Deploys only the sync config.
1919
* Uses existing cloud config state for other fields.
2020
*/
21-
protected async deploySyncConfig(params: { cloudConfigState: routes.InstanceConfigResponse }): Promise<void> {
22-
const { cloudConfigState } = params;
21+
protected async deploySyncConfig(params: {
22+
cloudConfigState: routes.InstanceConfigResponse;
23+
timeout: number;
24+
}): Promise<void> {
25+
const { cloudConfigState, timeout } = params;
2326
const { project } = this;
2427
const { linked } = project;
2528

@@ -37,7 +40,7 @@ export class DeploySyncConfig extends DeployAll {
3740
});
3841
}
3942

40-
return this.withDeploy(async () =>
43+
return this.withDeploy(timeout, async () =>
4144
this.client.deployInstance(
4245
routes.DeployInstanceRequest.encode({
4346
...cloudConfigState,
@@ -71,6 +74,6 @@ export class DeploySyncConfig extends DeployAll {
7174

7275
this.log('Validations completed successfully.\n');
7376

74-
await this.deploySyncConfig({ cloudConfigState });
77+
await this.deploySyncConfig({ cloudConfigState, timeout: flags.timeout });
7578
}
7679
}

cli/src/commands/init/cloud.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import { Flags, ux } from '@oclif/core';
22
import {
33
CLI_FILENAME,
44
InstanceCommand,
5-
PowerSyncCommand,
65
SERVICE_FILENAME,
76
SYNC_FILENAME,
87
YAML_CLI_SCHEMA,
@@ -18,7 +17,7 @@ import { writeVscodeSettingsForYamlEnv } from '../../api/write-vscode-settings-f
1817
const __dirname = dirname(fileURLToPath(import.meta.url));
1918
const TEMPLATES_DIR = join(__dirname, '..', '..', '..', 'templates');
2019

21-
export default class InitCloud extends PowerSyncCommand {
20+
export default class InitCloud extends InstanceCommand {
2221
static description =
2322
'Copy a Cloud template into a config directory (default powersync/). Edit service.yaml then run link cloud and deploy.';
2423
static examples = [

cli/src/commands/init/self-hosted.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import { Flags, ux } from '@oclif/core';
22
import {
33
CLI_FILENAME,
44
InstanceCommand,
5-
PowerSyncCommand,
65
SERVICE_FILENAME,
76
SYNC_FILENAME,
87
YAML_CLI_SCHEMA,
@@ -18,7 +17,7 @@ import { writeVscodeSettingsForYamlEnv } from '../../api/write-vscode-settings-f
1817
const __dirname = dirname(fileURLToPath(import.meta.url));
1918
const TEMPLATES_DIR = join(__dirname, '..', '..', '..', 'templates');
2019

21-
export default class InitSelfHosted extends PowerSyncCommand {
20+
export default class InitSelfHosted extends InstanceCommand {
2221
static description =
2322
'Copy a self-hosted template into a config directory (default powersync/). Configure service.yaml with your self-hosted instance details.';
2423
static examples = [

cli/src/commands/link/cloud.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ export default class LinkCloud extends CloudInstanceCommand {
7878
this.styledError({ error, message: 'Failed to create Cloud instance' });
7979
}
8080

81-
const projectDir = this.ensureProjectDirExists({ directory });
81+
const projectDir = this.ensureProjectDirectory({ directory });
8282
ensureServiceTypeMatches({
8383
command: this,
8484
configRequired: false,

cli/src/commands/link/self-hosted.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ export default class LinkSelfHosted extends SelfHostedInstanceCommand {
3131
const { flags } = await this.parse(LinkSelfHosted);
3232
const { 'api-url': apiUrl, directory } = flags;
3333

34-
const projectDir = this.ensureProjectDirExists(flags);
34+
const projectDir = this.ensureProjectDirectory(flags);
3535
ensureServiceTypeMatches({
3636
command: this,
3737
configRequired: false,

cli/src/commands/migrate/sync-rules.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ export default class MigrateSyncRules extends InstanceCommand {
2323
async run(): Promise<void> {
2424
const { flags } = await this.parse(MigrateSyncRules);
2525

26-
const syncInputPath = flags['input-file'] ?? join(this.ensureProjectDirExists(flags), SYNC_FILENAME);
26+
const syncInputPath = flags['input-file'] ?? join(this.ensureProjectDirectory(flags), SYNC_FILENAME);
2727
const syncOutputPath = flags['output-file'] ?? syncInputPath;
2828

2929
if (

packages/cli-core/src/command-types/CloudInstanceCommand.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ export abstract class CloudInstanceCommand extends InstanceCommand {
103103
...options,
104104
...DEFAULT_ENSURE_CONFIG_OPTIONS
105105
};
106-
const projectDir = this.ensureProjectDirExists(flags);
106+
const projectDir = this.ensureProjectDirectory(flags);
107107

108108
// Check if the service.yaml file is present and has _type: cloud
109109
ensureServiceTypeMatches({

packages/cli-core/src/command-types/InstanceCommand.ts

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { Flags, ux } from '@oclif/core';
2-
import { existsSync } from 'node:fs';
2+
import fs from 'node:fs';
3+
import { CLI_FILENAME } from '../utils/project-config.js';
34
import { HelpGroup } from './HelpGroup.js';
45
import { PowerSyncCommand } from './PowerSyncCommand.js';
56

@@ -14,23 +15,40 @@ export const DEFAULT_ENSURE_CONFIG_OPTIONS: Required<EnsureConfigOptions> = {
1415
configFileRequired: false
1516
};
1617

18+
export const DEFAULT_INSTANCE_DIRECTORY = 'powersync';
19+
1720
/** Base command for operations that target a PowerSync project directory (e.g. link, init). */
1821
export abstract class InstanceCommand extends PowerSyncCommand {
1922
static flags = {
2023
...PowerSyncCommand.flags,
2124
directory: Flags.string({
22-
default: 'powersync',
23-
description: 'Directory containing PowerSync config.',
24-
helpGroup: HelpGroup.PROJECT
25+
description:
26+
'Directory containing PowerSync config. Defaults to "powersync". This is required if multiple powersync config files are present in subdirectories of the current working directory.',
27+
helpGroup: HelpGroup.PROJECT,
28+
default: async () => {
29+
// Before we default, we need to ensure only 1 linked project is present.
30+
const directories = fs.readdirSync(process.cwd()).filter((dir) => fs.existsSync(`${dir}/${CLI_FILENAME}`));
31+
if (directories.length > 1) {
32+
throw new Error(
33+
[
34+
`Multiple directories containing ${CLI_FILENAME} found. Please specify the target directory with --directory.`,
35+
...directories.map(
36+
(dir) => `Use ${ux.colorize('blue', `--directory=${dir}`)} to target the ${dir} config.`
37+
)
38+
].join('\n')
39+
);
40+
}
41+
42+
return DEFAULT_INSTANCE_DIRECTORY;
43+
}
2544
})
2645
};
2746

28-
ensureProjectDirExists(flags: { directory: string }): string {
29-
const { directory } = flags;
47+
ensureProjectDirectory(flags: { directory: string }): string {
3048
const projectDir = this.resolveProjectDir(flags);
31-
if (!existsSync(projectDir)) {
49+
if (!fs.existsSync(projectDir)) {
3250
this.styledError({
33-
message: `Directory "${directory}" not found. Run ${ux.colorize('blue', 'powersync init cloud')} or ${ux.colorize('blue', 'powersync init self-hosted')} first to create the project.`
51+
message: `Directory "${flags.directory}" not found. Run ${ux.colorize('blue', 'powersync init cloud')} or ${ux.colorize('blue', 'powersync init self-hosted')} first to create the project.`
3452
});
3553
}
3654
return projectDir;

packages/cli-core/src/command-types/SelfHostedInstanceCommand.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ export abstract class SelfHostedInstanceCommand extends InstanceCommand {
5858
...options
5959
};
6060

61-
const projectDir = this.ensureProjectDirExists(flags);
61+
const projectDir = this.ensureProjectDirectory(flags);
6262

6363
ensureServiceTypeMatches({
6464
command: this,

0 commit comments

Comments
 (0)