Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
715af62
Clarify zip commands
MilapNaik May 20, 2025
3a294ec
Remove duplicate args
MilapNaik May 20, 2025
2e6f2ca
Clarify createSignedPackage and sideload args
MilapNaik May 20, 2025
1dc59c5
Merge branch 'v4' into new-args-for-commands
MilapNaik Jun 23, 2025
2669edb
More args changes
MilapNaik Jul 24, 2025
d27a227
Take rootdir out of RekeyDevice in tests
MilapNaik Jul 24, 2025
26aec57
Use outDir for rekeyDevice() use since we took out rootDir and getOut…
MilapNaik Jul 30, 2025
4f52123
fixes to path to pkg flow: only make CLI changes, leave internal func…
MilapNaik Aug 5, 2025
f3d4e32
Take out outdir
MilapNaik Aug 6, 2025
d4f4fe8
Take out all aliases
MilapNaik Aug 7, 2025
022e0c0
Change bundle to use out instead of outdir and outfile
MilapNaik Aug 7, 2025
c36dd30
Take out duplicate host, change outdir type to avoid confusion
MilapNaik Aug 7, 2025
7aebfaa
Change all remotePort to ecpPort, and add packagePort when necessary
MilapNaik Aug 7, 2025
d57455d
Change readme examples to use variable names that the CLI was changed to
MilapNaik Aug 7, 2025
f8e8bb4
Delete bundle, change sideload for most of what bundle was
MilapNaik Aug 7, 2025
8b15191
Use outzip instead
MilapNaik Aug 7, 2025
f738554
Get rid of aliases
MilapNaik Aug 29, 2025
26844b3
Turn createSignedPackage into package, and remove exec
MilapNaik Aug 29, 2025
f47dbad
Process app title and app version, or manifest path for creating a si…
MilapNaik Aug 29, 2025
9a3b3d7
Close app when sideloading unless noclose is specified
MilapNaik Aug 29, 2025
e1c41f7
Allow for either sideloading zip or rootDir; if it's zip, we retain t…
MilapNaik Aug 29, 2025
65fa031
resolve cwd with manifestPath
MilapNaik Sep 2, 2025
3d9827f
Take out stagingDir form createSignedPackage, use tempDir in tests
MilapNaik Sep 2, 2025
d98c0e8
Take out more stagingDir
MilapNaik Sep 3, 2025
21bfc56
Delete exec command
MilapNaik Sep 3, 2025
6936b82
Delete more exec command
MilapNaik Sep 3, 2025
c07856d
Fix tests, add a few new ones
MilapNaik Sep 4, 2025
cd41167
Fix tests and add a few more for sideload command
MilapNaik Sep 4, 2025
edf2068
Forgot to delete this alias
MilapNaik Sep 4, 2025
a184314
Make changes to readme so it aligns with cli changes
MilapNaik Sep 4, 2025
cfcc535
Make changes to getOptionsFromJson so it stays faithful to Readme
MilapNaik Sep 4, 2025
1330889
Fix test failing because of getOptionsFromJson change
MilapNaik Sep 4, 2025
0b3d408
Some cli doc wording tweaks
TwitchBronBron Nov 18, 2025
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
13 changes: 3 additions & 10 deletions src/RokuDeploy.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1393,7 +1393,6 @@ describe('index', () => {
password: 'password',
rekeySignedPackage: options.rekeySignedPackage,
signingPassword: options.signingPassword,
rootDir: options.rootDir,
devId: options.devId
});
} catch (e) {
Expand All @@ -1408,17 +1407,16 @@ describe('index', () => {
</div>`;
mockDoPostRequest(body);
try {
fsExtra.writeFileSync(s`${tempDir}/notReal.pkg`, '');
fsExtra.writeFileSync(s`notReal.pkg`, '');
await rokuDeploy.rekeyDevice({
host: '1.2.3.4',
password: 'password',
rekeySignedPackage: s`../notReal.pkg`,
rekeySignedPackage: s`notReal.pkg`,
signingPassword: options.signingPassword,
rootDir: options.rootDir,
devId: options.devId
});
} finally {
fsExtra.removeSync(s`${tempDir}/notReal.pkg`);
fsExtra.removeSync(s`notReal.pkg`);
}
});

Expand All @@ -1432,7 +1430,6 @@ describe('index', () => {
password: 'password',
rekeySignedPackage: s`${tempDir}/testSignedPackage.pkg`,
signingPassword: options.signingPassword,
rootDir: options.rootDir,
devId: options.devId
});
});
Expand All @@ -1447,7 +1444,6 @@ describe('index', () => {
password: 'password',
rekeySignedPackage: options.rekeySignedPackage,
signingPassword: options.signingPassword,
rootDir: options.rootDir,
devId: options.devId
});
});
Expand All @@ -1462,7 +1458,6 @@ describe('index', () => {
password: 'password',
rekeySignedPackage: options.rekeySignedPackage,
signingPassword: options.signingPassword,
rootDir: options.rootDir,
devId: undefined
});
});
Expand All @@ -1475,7 +1470,6 @@ describe('index', () => {
password: 'password',
rekeySignedPackage: options.rekeySignedPackage,
signingPassword: options.signingPassword,
rootDir: options.rootDir,
devId: options.devId
});
} catch (e) {
Expand Down Expand Up @@ -1516,7 +1510,6 @@ describe('index', () => {
password: 'password',
rekeySignedPackage: options.rekeySignedPackage,
signingPassword: options.signingPassword,
rootDir: options.rootDir,
devId: '45fdc2019903ac333ff624b0b2cddd2c733c3e74'
});
} catch (e) {
Expand Down
62 changes: 42 additions & 20 deletions src/cli.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#!/usr/bin/env node
import * as yargs from 'yargs';
import * as path from 'path';
import { ExecCommand } from './commands/ExecCommand';
import { SendTextCommand } from './commands/SendTextCommand';
import { StageCommand } from './commands/StageCommand';
Expand Down Expand Up @@ -45,7 +46,6 @@ void yargs
.option('remoteDebugConnectEarly', { type: 'boolean', description: 'Should the command connect to the debugger early', demandOption: false })
.option('failOnCompileError', { type: 'boolean', description: 'Should the command fail if there is a compile error', demandOption: false })
.option('retainDeploymentArchive', { type: 'boolean', description: 'Should the deployment archive be retained', demandOption: false })
.option('outDir', { type: 'string', description: 'The output directory', demandOption: false })
.option('outFile', { type: 'string', description: 'The output file', demandOption: false })
.option('deleteDevChannel', { type: 'boolean', description: 'Should the dev channel be deleted', demandOption: false })
.option('cwd', { type: 'string', description: 'The current working directory to use for relative paths', demandOption: false });
Expand All @@ -69,7 +69,6 @@ void yargs
.option('remoteDebugConnectEarly', { type: 'boolean', description: 'Should the command connect to the debugger early', demandOption: false })
.option('failOnCompileError', { type: 'boolean', description: 'Should the command fail if there is a compile error', demandOption: false })
.option('retainDeploymentArchive', { type: 'boolean', description: 'Should the deployment archive be retained', demandOption: false })
.option('outDir', { type: 'string', description: 'The output directory', demandOption: false })
.option('outFile', { type: 'string', description: 'The output file', demandOption: false })
.option('deleteDevChannel', { type: 'boolean', description: 'Should the dev channel be deleted', demandOption: false })
.option('signingPassword', { type: 'string', description: 'The password of the signing key', demandOption: false })
Expand Down Expand Up @@ -113,56 +112,61 @@ void yargs
return builder
.option('key', { type: 'string', description: 'The key to send', demandOption: true })
.option('host', { type: 'string', description: 'The IP Address of the host Roku', demandOption: false })
.option('remotePort', { type: 'number', description: 'The port to use for remote', demandOption: false })
.option('port', { type: 'number', description: 'The port to use for remote', demandOption: false })
.option('timeout', { type: 'number', description: 'The timeout for the command', demandOption: false });
}, (args: any) => {
args.remotePort = args.port;
return new KeyPressCommand().run(args);
})

.command('keyUp', 'send keyup command', (builder) => {
return builder
.option('key', { type: 'string', description: 'The key to send', demandOption: true })
.option('host', { type: 'string', description: 'The IP Address of the host Roku', demandOption: false })
.option('remotePort', { type: 'number', description: 'The port to use for remote', demandOption: false })
.option('port', { type: 'number', description: 'The port to use for remote', demandOption: false })
.option('timeout', { type: 'number', description: 'The timeout for the command', demandOption: false });
}, (args: any) => {
args.remotePort = args.port;
return new KeyUpCommand().run(args);
})

.command('keyDown', 'send keydown command', (builder) => {
return builder
.option('key', { type: 'string', description: 'The key to send', demandOption: true })
.option('host', { type: 'string', description: 'The IP Address of the host Roku', demandOption: false })
.option('remotePort', { type: 'number', description: 'The port to use for remote', demandOption: false })
.option('port', { type: 'number', description: 'The port to use for remote', demandOption: false })
.option('timeout', { type: 'number', description: 'The timeout for the command', demandOption: false });
}, (args: any) => {
args.remotePort = args.port;
return new KeyDownCommand().run(args);
})

.command(['sendText', 'text'], 'Send text command', (builder) => {
return builder
.option('text', { type: 'string', description: 'The text to send', demandOption: true })
.option('host', { type: 'string', description: 'The IP Address of the host Roku', demandOption: false })
.option('remotePort', { type: 'number', description: 'The port to use for remote', demandOption: false })
.option('port', { type: 'number', description: 'The port to use for remote', demandOption: false })
.option('timeout', { type: 'number', description: 'The timeout for the command', demandOption: false });
}, (args: any) => {
args.remotePort = args.port;
return new SendTextCommand().run(args);
})

.command(['remote-control', 'rc'], 'Provides a way to send a series of ECP key events similar to how Roku Remote Tool works but from the command line', (builder) => {
return builder
.option('host', { type: 'string', description: 'The IP Address of the host Roku', demandOption: false })
.option('remotePort', { type: 'number', description: 'The port to use for remote', demandOption: false });
.option('port', { type: 'number', description: 'The port to use for remote', demandOption: false });
}, (args: any) => {
args.remotePort = args.port;
return new RemoteControlCommand().run(args);
})

.command(['stage', 'prepublishToStaging'], 'Copies all of the referenced files to the staging folder', (builder) => {
return builder
.option('stagingDir', { type: 'string', description: 'The selected staging folder', demandOption: false })
.option('cwd', { type: 'string', description: 'The current working directory to use for relative paths', demandOption: false })
.option('rootDir', { type: 'string', description: 'The selected root folder to be copied', demandOption: false })
.option('files', { type: 'array', description: 'An array of source file paths indicating where the source files are', demandOption: false })
.option('cwd', { type: 'string', description: 'The current working directory to use for relative paths', demandOption: false });
.option('out', { type: 'string', description: 'The selected staging folder where all files will be copied to', demandOption: false });
}, (args: any) => {
return new StageCommand().run(args);
})
Expand All @@ -171,15 +175,17 @@ void yargs
return builder
.option('host', { type: 'string', description: 'The IP Address of the host Roku', demandOption: false })
.option('password', { type: 'string', description: 'The password of the host Roku', demandOption: false })
.option('zip', { type: 'string', description: 'The file to be sideloaded, relative to cwd.', demandOption: false })
.option('remoteDebug', { type: 'boolean', description: 'Should the command be run in remote debug mode', demandOption: false })
.option('remoteDebugConnectEarly', { type: 'boolean', description: 'Should the command connect to the debugger early', demandOption: false })
.option('failOnCompileError', { type: 'boolean', description: 'Should the command fail if there is a compile error', demandOption: false })
.option('retainDeploymentArchive', { type: 'boolean', description: 'Should the deployment archive be retained', demandOption: false })
.option('outDir', { type: 'string', description: 'The output directory', demandOption: false })
.option('outFile', { type: 'string', description: 'The output file', demandOption: false })
.option('deleteDevChannel', { type: 'boolean', description: 'Should the dev channel be deleted', demandOption: false })
.option('cwd', { type: 'string', description: 'The current working directory to use for relative paths', demandOption: false });
}, (args: any) => {
args.zip = path.resolve(args.cwd, args.zip);
args.outDir = path.dirname(args.zip);
args.outFile = path.basename(args.zip);
return new SideloadCommand().run(args);
})

Expand All @@ -195,12 +201,12 @@ void yargs
return builder
.option('host', { type: 'string', description: 'The IP Address of the host Roku', demandOption: false })
.option('password', { type: 'string', description: 'The password of the host Roku', demandOption: false })
.option('rekeySignedPackage', { type: 'string', description: 'The signed package to be used for rekeying', demandOption: false })
.option('pkg', { type: 'string', description: 'The path to thesigned package to be used for rekeying, relative to cwd', demandOption: false })
.option('signingPassword', { type: 'string', description: 'The password of the signing key', demandOption: false })
.option('rootDir', { type: 'string', description: 'The root directory', demandOption: false })
.option('devId', { type: 'string', description: 'The dev ID', demandOption: false })
.option('cwd', { type: 'string', description: 'The current working directory to use for relative paths', demandOption: false });
}, (args: any) => {
args.rekeySignedPackage = path.resolve(args.cwd, args.pkg);
return new RekeyDeviceCommand().run(args);
})

Expand All @@ -209,11 +215,19 @@ void yargs
.option('host', { type: 'string', description: 'The IP Address of the host Roku', demandOption: false })
.option('password', { type: 'string', description: 'The password of the host Roku', demandOption: false })
.option('signingPassword', { type: 'string', description: 'The password of the signing key', demandOption: false })
.option('stagingDir', { type: 'string', description: 'The selected staging folder', demandOption: false })
.option('outDir', { type: 'string', description: 'The output directory', demandOption: false })
.option('dir', { type: 'string', description: 'The selected staging folder, relative to cwd', demandOption: false, alias: ['stagingDir', 'stagingdir'] })
.option('out', { type: 'string', description: 'The location where the signed package will be saved, relative to cwd', demandOption: false, defaultDescription: './out/roku-deploy.pkg' })
.option('devId', { type: 'string', description: 'The dev ID', demandOption: false })
.option('cwd', { type: 'string', description: 'The current working directory to use for relative paths', demandOption: false });
}, (args: any) => {
if (args.out) {
if (!args.out.endsWith('.pkg')) {
throw new Error('Out must end with a .pkg');
}
args.out = path.resolve(args.cwd, args.out);
args.outDir = path.dirname(args.out);
args.outFile = path.basename(args.out);
}
return new CreateSignedPackageCommand().run(args);
})

Expand All @@ -229,10 +243,14 @@ void yargs
return builder
.option('host', { type: 'string', description: 'The IP Address of the host Roku', demandOption: false })
.option('password', { type: 'string', description: 'The password of the host Roku', demandOption: false })
.option('screenshotDir', { type: 'string', description: 'A full path to the folder where the screenshots should be saved.', demandOption: false })
.option('screenshotFile', { type: 'string', description: 'The base filename the image file should be given (excluding the extension). Default: screenshot-YYYY-MM-DD-HH.mm.ss.SSS.<jpg|png>', demandOption: false })
.option('out', { type: 'string', description: 'The location where the screenshot will be saved relative to cwd', demandOption: false, defaultDescription: './out/roku-deploy.jpg' })
.option('cwd', { type: 'string', description: 'The current working directory to use for relative paths', demandOption: false });
}, (args: any) => {
if (args.out) {
args.out = path.resolve(args.cwd, args.out);
args.screenshotDir = path.dirname(args.out);
args.screenshotFile = path.basename(args.out);
}
return new CaptureScreenshotCommand().run(args);
})

Expand All @@ -252,11 +270,15 @@ void yargs

.command('zip', 'Given a path to a folder, zip up that folder and all of its contents', (builder) => {
return builder
.option('stagingDir', { type: 'string', description: 'The folder that should be zipped', demandOption: false })
.option('outDir', { type: 'string', description: 'The path to the zip that will be created. Must be .zip file name', demandOption: false })
.option('outFile', { type: 'string', description: 'The output file', demandOption: false })
.option('dir', { type: 'string', description: 'The folder to be zipped', demandOption: false, alias: ['stagingDir', 'stagingdir'] })
.option('out', { type: 'string', description: 'the path to the zip file that will be created, relative to cwd', demandOption: false })
.option('cwd', { type: 'string', description: 'The current working directory to use for relative paths', demandOption: false });
}, (args: any) => {
if (args.out) {
args.out = path.resolve(args.cwd, args.out);
args.outDir = path.dirname(args.out);
args.outFile = path.basename(args.out);
}
return new ZipCommand().run(args);
})

Expand Down
Loading