Skip to content

Commit ab6364f

Browse files
FadhlanRclaude
andcommitted
Move push command under realm/ subcommand with ProfileManager auth and integration tests
- Move push command from src/commands/push.ts to src/commands/realm/push.ts - Use ProfileManager for auth instead of validateMatrixEnvVars - Register push as realm subcommand (boxel realm push) - Replace mock-based unit tests with real integration tests using fileSystem seeding - Test cases: basic push, incremental, force, delete, dry-run, manifest ignore Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 08aedb6 commit ab6364f

4 files changed

Lines changed: 248 additions & 288 deletions

File tree

packages/boxel-cli/src/commands/realm/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type { Command } from 'commander';
22
import { registerCreateCommand } from './create';
33
import { registerPullCommand } from './pull';
4+
import { registerPushCommand } from './push';
45

56
export function registerRealmCommand(program: Command): void {
67
let realm = program
@@ -9,4 +10,5 @@ export function registerRealmCommand(program: Command): void {
910

1011
registerCreateCommand(realm);
1112
registerPullCommand(realm);
13+
registerPushCommand(realm);
1214
}

packages/boxel-cli/src/commands/push.ts renamed to packages/boxel-cli/src/commands/realm/push.ts

Lines changed: 43 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
1-
import {
2-
RealmSyncBase,
3-
validateMatrixEnvVars,
4-
isProtectedFile,
5-
type SyncOptions,
6-
} from '../lib/realm-sync-base.js';
1+
import type { Command } from 'commander';
2+
import { RealmSyncBase, isProtectedFile, type SyncOptions } from '../../lib/realm-sync-base';
73
import {
84
CheckpointManager,
95
type CheckpointChange,
10-
} from '../lib/checkpoint-manager.js';
6+
} from '../../lib/checkpoint-manager';
7+
import {
8+
getProfileManager,
9+
type ProfileManager,
10+
} from '../../lib/profile-manager';
1111
import * as fs from 'fs';
1212
import * as path from 'path';
1313
import * as crypto from 'crypto';
@@ -49,11 +49,9 @@ class RealmPusher extends RealmSyncBase {
4949

5050
constructor(
5151
private pushOptions: PushOptions,
52-
matrixUrl: string,
53-
username: string,
54-
password: string,
52+
profileManager: ProfileManager,
5553
) {
56-
super(pushOptions, matrixUrl, username, password);
54+
super(pushOptions, profileManager);
5755
}
5856

5957
async sync(): Promise<void> {
@@ -68,7 +66,7 @@ class RealmPusher extends RealmSyncBase {
6866
console.error('Failed to access workspace:', error);
6967
throw new Error(
7068
'Cannot proceed with push: Authentication or access failed. ' +
71-
'Please check your Matrix credentials and workspace permissions.',
69+
'Please check your credentials and workspace permissions.',
7270
);
7371
}
7472
console.log('Workspace access verified');
@@ -203,15 +201,45 @@ export interface PushCommandOptions {
203201
delete?: boolean;
204202
dryRun?: boolean;
205203
force?: boolean;
204+
profileManager?: ProfileManager;
205+
}
206+
207+
export function registerPushCommand(realm: Command): void {
208+
realm
209+
.command('push')
210+
.description('Push local files to a Boxel realm')
211+
.argument('<local-dir>', 'The local directory containing files to sync')
212+
.argument(
213+
'<workspace-url>',
214+
'The URL of the target workspace (e.g., https://app.boxel.ai/demo/)',
215+
)
216+
.option('--delete', 'Delete remote files that do not exist locally')
217+
.option('--dry-run', 'Show what would be done without making changes')
218+
.option('--force', 'Upload all files, even if unchanged')
219+
.action(
220+
async (
221+
localDir: string,
222+
workspaceUrl: string,
223+
options: { delete?: boolean; dryRun?: boolean; force?: boolean },
224+
) => {
225+
await pushCommand(localDir, workspaceUrl, options);
226+
},
227+
);
206228
}
207229

208230
export async function pushCommand(
209231
localDir: string,
210232
workspaceUrl: string,
211233
options: PushCommandOptions,
212234
): Promise<void> {
213-
const { matrixUrl, username, password } =
214-
await validateMatrixEnvVars(workspaceUrl);
235+
let pm = options.profileManager ?? getProfileManager();
236+
let active = pm.getActiveProfile();
237+
if (!active) {
238+
console.error(
239+
'Error: no active profile. Run `boxel profile add` to create one.',
240+
);
241+
process.exit(1);
242+
}
215243

216244
if (!fs.existsSync(localDir)) {
217245
console.error(`Local directory does not exist: ${localDir}`);
@@ -227,12 +255,9 @@ export async function pushCommand(
227255
dryRun: options.dryRun,
228256
force: options.force,
229257
},
230-
matrixUrl,
231-
username,
232-
password,
258+
pm,
233259
);
234260

235-
await pusher.initialize();
236261
await pusher.sync();
237262

238263
if (pusher.hasError) {

packages/boxel-cli/tests/commands/push.test.ts

Lines changed: 0 additions & 270 deletions
This file was deleted.

0 commit comments

Comments
 (0)