roku-pkg-cli
Version:
A comprehensive CLI tool for managing multiple Roku projects with automated device discovery, build integration, and package generation. Perfect for CI/CD pipelines with full automation support.
228 lines (227 loc) • 8.38 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.RokuDeployManager = void 0;
const rokuDeploy = __importStar(require("roku-deploy"));
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
class RokuDeployManager {
/**
* Build and deploy an app to Roku device
*/
async deploy(options) {
const deployOptions = {
host: options.host,
password: options.password,
username: options.username || 'rokudev',
rootDir: options.rootDir,
files: options.files || [
'source/**/*',
'components/**/*',
'images/**/*',
'manifest'
],
retainStagingFolder: options.retainStagingFolder || false,
outFile: 'app.zip'
};
console.log('Deploying with options:', {
host: deployOptions.host,
rootDir: deployOptions.rootDir,
files: deployOptions.files.length + ' patterns'
});
await rokuDeploy.deploy(deployOptions);
}
/**
* Build, deploy, and create a signed package
*/
async deployAndSignPackage(options) {
const deployOptions = {
host: options.host,
password: options.password,
username: options.username || 'rokudev',
rootDir: options.rootDir,
files: options.files || [
'source/**/*',
'components/**/*',
'images/**/*',
'manifest'
],
signingPassword: options.signingPassword,
rekeySignedPackage: options.rekeySignedPackage,
devId: options.devId,
outFile: options.outFile || 'app',
retainStagingFolder: options.retainStagingFolder || false
};
try {
console.log('Calling rokuDeploy.deployAndSignPackage...');
console.log('Deploy options:', JSON.stringify({
host: deployOptions.host,
rootDir: deployOptions.rootDir,
files: deployOptions.files ? deployOptions.files.length + ' patterns' : 'default',
retainStagingFolder: deployOptions.retainStagingFolder,
outFile: deployOptions.outFile
}, null, 2));
// Add progress tracking
const startTime = Date.now();
console.log('Starting deployment at:', new Date().toISOString());
const result = await rokuDeploy.deployAndSignPackage(deployOptions);
const endTime = Date.now();
console.log('Deployment completed in:', (endTime - startTime) / 1000, 'seconds');
return result;
}
catch (error) {
console.error('deployAndSignPackage error:', error);
console.error('Error occurred at:', new Date().toISOString());
throw error;
}
}
/**
* Rekey a Roku device
*/
async rekeyDevice(options) {
const rekeyOptions = {
host: options.host,
password: options.password,
rekeySignedPackage: options.rekeySignedPackage,
signingPassword: options.signingPassword,
devId: options.devId
};
try {
await rokuDeploy.rekeyDevice(rekeyOptions);
}
catch (error) {
throw error;
}
}
/**
* Create a signed package without deploying
*/
async createPackage(options) {
// First create a zip package
const zipPath = await this.zipPackage({
rootDir: options.rootDir,
files: options.files,
outFile: options.outFile,
outDir: options.outDir || './out' // Use ./out instead of ./build
});
console.log('Zip created, now creating signed package from it...');
// Then sign the package
const packageOptions = {
host: options.host,
password: options.password,
username: options.username || 'rokudev',
signingPassword: options.signingPassword,
outFile: options.outFile || 'app',
retainStagingFolder: options.retainStagingFolder || false,
stagingDir: path.dirname(zipPath),
// Provide the rootDir to ensure roku-deploy knows where the files are
rootDir: options.rootDir
};
try {
// Use publish method which signs the already deployed app
const result = await rokuDeploy.publish(packageOptions);
// Look for the created package
const pkgPath = path.join(packageOptions.stagingDir || './out', packageOptions.outFile + '.pkg');
if (fs.existsSync(pkgPath)) {
return pkgPath;
}
// Try alternate location
const altPath = path.join('./out', packageOptions.outFile + '.pkg');
if (fs.existsSync(altPath)) {
return altPath;
}
throw new Error('Package was created but could not be found');
}
catch (error) {
console.error('Error creating package:', error);
throw error;
}
}
/**
* Build a zip file without deploying
*/
async zipPackage(options) {
const zipOptions = {
rootDir: options.rootDir,
files: options.files || [
'source/**/*',
'components/**/*',
'images/**/*',
'manifest'
],
outFile: options.outFile || 'app.zip',
outDir: options.outDir || './build'
};
const result = await rokuDeploy.zipPackage(zipOptions);
const zipPath = result.packagePath || path.join(result.outDir, result.outFile);
// Ensure the zip was created
if (!fs.existsSync(zipPath)) {
throw new Error(`Failed to create zip at: ${zipPath}`);
}
return zipPath;
}
/**
* Validate project structure
*/
validateProject(rootDir) {
const errors = [];
// Check for manifest
const manifestPath = path.join(rootDir, 'manifest');
if (!fs.existsSync(manifestPath)) {
errors.push('Missing required file: manifest');
}
else {
}
// Check for source directory
const sourcePath = path.join(rootDir, 'source');
if (!fs.existsSync(sourcePath)) {
errors.push('Missing required directory: source');
}
else {
// Check for main.brs
const mainPath = path.join(sourcePath, 'main.brs');
if (!fs.existsSync(mainPath)) {
errors.push('Missing required file: source/main.brs');
}
else {
}
}
return {
valid: errors.length === 0,
errors
};
}
}
exports.RokuDeployManager = RokuDeployManager;