diff --git a/package-lock.json b/package-lock.json index 1316a7c7..fdf97442 100644 --- a/package-lock.json +++ b/package-lock.json @@ -30,6 +30,7 @@ "net": "^1.0.2", "node-cache": "^4.2.0", "node-ssdp": "^4.0.0", + "open": "^8.4.2", "postman-request": "^2.88.1-postman.32", "pretty-bytes": "^5.6.0", "roku-debug": "^0.21.6", @@ -692,6 +693,23 @@ "node": ">=0.10.0" } }, + "node_modules/@compodoc/live-server/node_modules/open": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz", + "integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==", + "dev": true, + "dependencies": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@cspotcode/source-map-support": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", @@ -3551,7 +3569,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", - "dev": true, "engines": { "node": ">=8" } @@ -5994,7 +6011,6 @@ "version": "2.2.1", "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", - "dev": true, "bin": { "is-docker": "cli.js" }, @@ -6211,7 +6227,6 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", - "dev": true, "dependencies": { "is-docker": "^2.0.0" }, @@ -7721,10 +7736,9 @@ } }, "node_modules/open": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz", - "integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==", - "dev": true, + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", + "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", "dependencies": { "define-lazy-prop": "^2.0.0", "is-docker": "^2.1.1", @@ -11533,6 +11547,19 @@ "proxy-middleware": "latest", "send": "latest", "serve-index": "^1.9.1" + }, + "dependencies": { + "open": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz", + "integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==", + "dev": true, + "requires": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + } + } } }, "@cspotcode/source-map-support": { @@ -13779,8 +13806,7 @@ "define-lazy-prop": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", - "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", - "dev": true + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==" }, "define-properties": { "version": "1.1.4", @@ -15616,8 +15642,7 @@ "is-docker": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", - "dev": true + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==" }, "is-extglob": { "version": "2.1.1", @@ -15750,7 +15775,6 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", - "dev": true, "requires": { "is-docker": "^2.0.0" } @@ -16951,10 +16975,9 @@ } }, "open": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz", - "integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==", - "dev": true, + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", + "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", "requires": { "define-lazy-prop": "^2.0.0", "is-docker": "^2.1.1", diff --git a/package.json b/package.json index 460b7ac0..713a8b42 100644 --- a/package.json +++ b/package.json @@ -72,6 +72,7 @@ "net": "^1.0.2", "node-cache": "^4.2.0", "node-ssdp": "^4.0.0", + "open": "^8.4.2", "postman-request": "^2.88.1-postman.32", "pretty-bytes": "^5.6.0", "roku-debug": "^0.21.6", @@ -175,7 +176,10 @@ "onCommand:extension.brightscript.sendRemoteText", "onCommand:brighterscript.showPreview", "onCommand:brighterscript.showPreviewToSide", - "onCommand:extension.brightscript.captureScreenshot" + "onCommand:extension.brightscript.captureScreenshot", + "onCommand:extension.brightscript.rekeyDevice", + "onCommand:extension.brightscript.createPackage", + "onCommand:extension.brightscript.rekeyAndPackage" ], "contributes": { "viewsContainers": { @@ -2986,6 +2990,21 @@ "category": "BrighterScript", "icon": "./images/icons/inspect-active.svg" }, + { + "command": "extension.brightscript.rekeyDevice", + "title": "Rekey Device", + "category": "BrightScript" + }, + { + "command": "extension.brightscript.createPackage", + "title": "Create Package", + "category": "BrightScript" + }, + { + "command": "extension.brightscript.rekeyAndPackage", + "title": "Rekey Device and Create Package", + "category": "BrightScript" + }, { "command": "extension.brightscript.captureScreenshot", "title": "Capture Screenshot", diff --git a/src/BrightScriptCommands.ts b/src/BrightScriptCommands.ts index ae071d69..2a8a67ad 100644 --- a/src/BrightScriptCommands.ts +++ b/src/BrightScriptCommands.ts @@ -4,6 +4,7 @@ import BrightScriptFileUtils from './BrightScriptFileUtils'; import { GlobalStateManager } from './GlobalStateManager'; import { brighterScriptPreviewCommand } from './commands/BrighterScriptPreviewCommand'; import { captureScreenshotCommand } from './commands/CaptureScreenshotCommand'; +import { rekeyAndPackageCommand } from './commands/RekeyAndPackageCommand'; import { languageServerInfoCommand } from './commands/LanguageServerInfoCommand'; import { util } from './util'; import { util as rokuDebugUtil } from 'roku-debug/dist/util'; @@ -37,6 +38,7 @@ export class BrightScriptCommands { brighterScriptPreviewCommand.register(this.context); languageServerInfoCommand.register(this.context); captureScreenshotCommand.register(this.context, this); + rekeyAndPackageCommand.register(this.context, this, this.userInputManager); this.registerGeneralCommands(); @@ -394,13 +396,13 @@ export class BrightScriptCommands { } } - public async getRemoteHost() { + public async getRemoteHost(showPrompt = true) { this.host = await this.context.workspaceState.get('remoteHost'); if (!this.host) { let config = vscode.workspace.getConfiguration('brightscript.remoteControl', null); this.host = config.get('host'); // eslint-disable-next-line no-template-curly-in-string - if (!this.host || this.host === '${promptForHost}') { + if ((!this.host || this.host === '${promptForHost}') && showPrompt) { this.host = await vscode.window.showInputBox({ placeHolder: 'The IP address of your Roku device', value: '' @@ -423,13 +425,13 @@ export class BrightScriptCommands { return this.host; } - public async getRemotePassword() { + public async getRemotePassword(showPrompt = true) { this.password = await this.context.workspaceState.get('remotePassword'); if (!this.password) { let config = vscode.workspace.getConfiguration('brightscript.remoteControl', null); this.password = config.get('password'); // eslint-disable-next-line no-template-curly-in-string - if (!this.password || this.password === '${promptForPassword}') { + if ((!this.password || this.password === '${promptForPassword}') && showPrompt) { this.password = await vscode.window.showInputBox({ placeHolder: 'The developer account password for your Roku device', value: '' diff --git a/src/commands/RekeyAndPackageCommand.ts b/src/commands/RekeyAndPackageCommand.ts new file mode 100644 index 00000000..cd456cd8 --- /dev/null +++ b/src/commands/RekeyAndPackageCommand.ts @@ -0,0 +1,448 @@ +import * as vscode from 'vscode'; +import * as rokuDeploy from 'roku-deploy'; +import type { BrightScriptCommands } from '../BrightScriptCommands'; +import * as path from 'path'; +import { readFileSync } from 'fs-extra'; +import type { UserInputManager } from '../managers/UserInputManager'; +import { standardizePath } from 'brighterscript'; +// eslint-disable-next-line @typescript-eslint/no-require-imports +import open = require('open'); + +export const FILE_SCHEME = 'bs-captureScreenshot'; + +export class RekeyAndPackageCommand { + + private brightScriptCommands: BrightScriptCommands; + private userInputManager: UserInputManager; + + public register(context: vscode.ExtensionContext, BrightScriptCommandsInstance: BrightScriptCommands, userInputManager: UserInputManager) { + this.brightScriptCommands = BrightScriptCommandsInstance; + this.userInputManager = userInputManager; + + context.subscriptions.push(vscode.commands.registerCommand('extension.brightscript.rekeyDevice', async (hostParam?: string) => { + await this.rekeyDevice(); + })); + + context.subscriptions.push(vscode.commands.registerCommand('extension.brightscript.createPackage', async (hostParam?: string) => { + await this.createPackage({}); + })); + + context.subscriptions.push(vscode.commands.registerCommand('extension.brightscript.rekeyAndPackage', async (hostParam?: string) => { + await this.createPackage({}, true); + })); + } + + private async rekeyDevice() { + const PICK_FROM_JSON = 'Pick from Json file'; + const MANUAL_ENTRY = 'Enter manually'; + + let rekeyConfig: RekeyConfig = { + signingPassword: '', + rekeySignedPackage: '', + host: '', + password: '' + }; + + let rekeyOptionList = [PICK_FROM_JSON, MANUAL_ENTRY]; + let rekeyOption = await vscode.window.showQuickPick(rekeyOptionList, { placeHolder: 'How would you like to select your configuration', canPickMany: false }); + if (rekeyOption) { + switch (rekeyOption) { + case PICK_FROM_JSON: + rekeyConfig = await this.getRekeyConfigFromJson(rekeyConfig); + break; + + case MANUAL_ENTRY: + rekeyConfig = await this.getRekeyManualEntries(rekeyConfig, {}); + break; + } + } + + await rokuDeploy.rekeyDevice(rekeyConfig); + void vscode.window.showInformationMessage(`Device successfully rekeyed!`); + } + + private async getRekeyConfigFromJson(rekeyConfig: RekeyConfig) { + const options: vscode.OpenDialogOptions = { + canSelectMany: false, + openLabel: 'Select', + canSelectFiles: true, + canSelectFolders: false, + filters: { + 'Json files': ['json'] + } + }; + + let fileUri = await vscode.window.showOpenDialog(options); + if (fileUri?.[0]) { + let content = JSON.parse(readFileSync(fileUri[0].fsPath).toString()); + + if (content.signingPassword) { + rekeyConfig.signingPassword = content.signingPassword; + } + + if (content.rekeySignedPackage?.includes('./')) { + await this.brightScriptCommands.getWorkspacePath(); + let workspacePath = this.brightScriptCommands.workspacePath; + rekeyConfig.rekeySignedPackage = workspacePath + content.rekeySignedPackage.replace('./', '/'); + } + + if (content.host) { + rekeyConfig.host = content.host; + } + + if (content.password) { + rekeyConfig.password = content.password; + } + } + return this.getRekeyManualEntries(rekeyConfig, rekeyConfig); + } + + private async getRekeyManualEntries(rekeyConfig: RekeyConfig, defaultValues) { + rekeyConfig.host = await this.userInputManager.promptForHost({ defaultValue: rekeyConfig?.host ?? defaultValues?.host }); + + rekeyConfig.password = await vscode.window.showInputBox({ + title: 'Enter password for the Roku device you want to rekey', + value: defaultValues?.password ?? '' + }); + if (!rekeyConfig.password) { + throw new Error('Cancelled'); + } + + rekeyConfig.signingPassword = await vscode.window.showInputBox({ + title: 'Enter signingPassword to be used to rekey the Roku', + value: defaultValues?.signingPassword ?? '' + }); + if (!rekeyConfig.signingPassword) { + throw new Error('Cancelled'); + } + + rekeyConfig.rekeySignedPackage = await this.getSignedPackage(rekeyConfig.rekeySignedPackage); + + const selection = await vscode.window.showInformationMessage('Rekey info:', { + modal: true, + detail: [ + `host: ${rekeyConfig.host}`, + `password: ${rekeyConfig.password}`, + `signing password: ${rekeyConfig.signingPassword}`, + `package: ${rekeyConfig.rekeySignedPackage}` + ].join('\n') + }, 'Rekey', 'I want to change something'); + if (selection === 'Rekey') { + return rekeyConfig; + } else if (selection === 'I want to change something') { + return this.getRekeyManualEntries(rekeyConfig, rekeyConfig); + } + } + + private async getSignedPackage(rekeySignedPackage: string) { + let response = ''; + rekeySignedPackage = standardizePath(rekeySignedPackage); + if (rekeySignedPackage?.length > 0) { + response = await vscode.window.showInformationMessage( + 'Please choose a signed package (a .pkg file) to rekey your device', + { + modal: true, + detail: `Current file: ${rekeySignedPackage}` + }, + 'Use the current file', 'Pick a different file' + ); + } else { + response = await vscode.window.showInformationMessage( + 'Please choose a signed package (a .pkg file) to rekey your device', + { modal: true }, + 'Open file picker' + ); + } + if ((response === 'Open file picker') || (response === 'Pick a different file')) { + const options: vscode.OpenDialogOptions = { + canSelectMany: false, + openLabel: 'Select signed package file', + canSelectFiles: true, + canSelectFolders: false, + filters: { + 'Pkg files': ['pkg'] + } + }; + let fileUri = await vscode.window.showOpenDialog(options); + if (fileUri?.[0]) { + return fileUri[0].fsPath; + } + } else if (response === 'Use the current file') { + return rekeySignedPackage; + } else { + throw new Error('Cancelled'); + } + } + + private async promptUserForAFolder(dialogTitle) { + let response = ''; + + response = await vscode.window.showInformationMessage( + dialogTitle, + { modal: true }, + 'Open file picker' + ); + + if (response === 'Open file picker') { + const options: vscode.OpenDialogOptions = { + canSelectMany: false, + openLabel: 'Select', + canSelectFiles: false, + canSelectFolders: true + }; + let folderUri = await vscode.window.showOpenDialog(options); + if (folderUri?.[0]) { + return folderUri[0].fsPath; + } + } else { + throw new Error('Cancelled'); + } + } + + private async createPackage(defaultValues: Partial, rekeyFlag = false) { + const workspaceFolder = await this.brightScriptCommands.getWorkspacePath(); + + let rokuDeployOptions = defaultValues as RokuDeployOptions; + + let PACKAGE_FOLDER = 'Pick a folder'; + let PACKAGE_FROM_LAUNCH_JSON = 'Pick from a launch.json'; + let PACKAGE_FROM_ROKU_DEPLOY = 'Pick a rokudeploy.json'; + let packageOptionList = []; + + if (rokuDeployOptions.packageConfig) { + packageOptionList.push({ + label: `Previous Selection:`, + detail: `${rokuDeployOptions.packageConfig}` + }); + } + packageOptionList.push(PACKAGE_FOLDER, PACKAGE_FROM_LAUNCH_JSON, PACKAGE_FROM_ROKU_DEPLOY); + + let packageOption = await vscode.window.showQuickPick(packageOptionList, { placeHolder: 'What would you like to package', canPickMany: false }); + if (packageOption) { + switch (packageOption) { + case PACKAGE_FOLDER: + rokuDeployOptions = await this.packageFromFolder(rokuDeployOptions); + break; + + case PACKAGE_FROM_LAUNCH_JSON: + rokuDeployOptions = await this.packageFromLaunchConfig(rokuDeployOptions); + break; + + case PACKAGE_FROM_ROKU_DEPLOY: + rokuDeployOptions = await this.packageFromRokuDeploy(rokuDeployOptions); + break; + } + + rokuDeployOptions.host = await this.userInputManager.promptForHost({ defaultValue: rokuDeployOptions?.host ?? '' }); + + rokuDeployOptions.password = await vscode.window.showInputBox({ + title: 'Enter password for the Roku device', + value: rokuDeployOptions.password ?? '' + }); + if (!rokuDeployOptions.password) { + throw new Error('Cancelled'); + } + + rokuDeployOptions.signingPassword = await vscode.window.showInputBox({ + title: 'Enter signingPassword for the Roku', + value: rokuDeployOptions.signingPassword ?? '' + }); + + if (!rokuDeployOptions.rootDir) { + rokuDeployOptions.rootDir = await this.promptUserForAFolder('Select rootDir to create package'); + } + if (!rokuDeployOptions.rootDir) { + throw new Error('Cancelled'); + } + + //normalize a few options + rokuDeployOptions.outFile ??= rokuDeploy.getOptions(rokuDeployOptions).outFile; + rokuDeployOptions.outDir = standardizePath(rokuDeployOptions.outDir ?? `${workspaceFolder}/out`); + rokuDeployOptions.rootDir = standardizePath(rokuDeployOptions.rootDir); + rokuDeployOptions.retainStagingDir = true; + if (rokuDeployOptions.rekeySignedPackage?.length > 0) { + rokuDeployOptions.rekeySignedPackage = standardizePath(rokuDeployOptions.rekeySignedPackage); + } + + let details = [ + `host: ${rokuDeployOptions.host}`, + `password: ${rokuDeployOptions.password}`, + `signing password: ${rokuDeployOptions.signingPassword}`, + `outDir: ${rokuDeployOptions.outDir}`, + `outFile: ${rokuDeployOptions.outFile}.pkg`, + `rootDir: ${rokuDeployOptions.rootDir}` + ]; + + if (rekeyFlag) { + rokuDeployOptions.rekeySignedPackage = await this.getSignedPackage(rokuDeployOptions.rekeySignedPackage); + details.push(`rekeySignedPackage: ${rokuDeployOptions.rekeySignedPackage}`); + } + + let confirmText = 'Create Package'; + let changeText = 'I want to change something'; + let response = await vscode.window.showInformationMessage('Create Package info:', { + modal: true, + detail: details.join('\n') + }, confirmText, changeText); + + if (response === confirmText) { + if (rekeyFlag) { + //rekey device + await rokuDeploy.rekeyDevice(rokuDeployOptions); + } + + //create a zip and pkg file of the app based on the selected launch config + await rokuDeploy.createPackage(rokuDeployOptions); + let remotePkgPath = await rokuDeploy.signExistingPackage(rokuDeployOptions); + await rokuDeploy.retrieveSignedPackage(remotePkgPath, rokuDeployOptions); + const outPath = standardizePath(`${rokuDeployOptions.outDir}/${rokuDeployOptions.outFile}`); + let successfulMessage = `Package successfully created at ${outPath}`; + void vscode.window.showInformationMessage(successfulMessage, 'View in folder').then(() => { + return open(rokuDeployOptions.outDir); + }); + + } else if (response === changeText) { + return this.createPackage(rokuDeployOptions, rekeyFlag); + } + } + } + + private async packageFromFolder(rokuDeployOptions) { + const options: vscode.OpenDialogOptions = { + canSelectMany: false, + openLabel: 'Select Folder to package', + canSelectFiles: false, + canSelectFolders: true + }; + let fileUri = await vscode.window.showOpenDialog(options); + if (fileUri?.[0]) { + let rootDir = fileUri?.[0].fsPath; + + rokuDeployOptions.rootDir = rootDir; + rokuDeployOptions.outFile = path.basename(rootDir); + rokuDeployOptions.packageConfig = 'folder: ' + rootDir; + + return rokuDeployOptions; + } + } + + + private async packageFromRokuDeploy(rokuDeployOptions) { + const options: vscode.OpenDialogOptions = { + canSelectMany: false, + openLabel: 'Select', + canSelectFiles: true, + canSelectFolders: false, + filters: { + 'Json files': ['json'] + } + }; + + let fileUri = await vscode.window.showOpenDialog(options); + if (fileUri?.[0]) { + return this.parseRokuDeployJson(fileUri[0].fsPath, rokuDeployOptions); + } + return rokuDeployOptions; + } + + private async packageFromLaunchConfig(rokuDeployOptions) { + let config = vscode.workspace.getConfiguration('launch', null); + const configurations = config.get('configurations'); + let configNames = []; + for (let config of configurations) { + configNames.push(config.name); + } + + //show user a list of available launch configs to choose from + let selectedConfig = configurations[0]; + let selectedConfigName = await vscode.window.showQuickPick(configNames, { placeHolder: 'Please select a config', canPickMany: false }); + if (selectedConfigName) { + let selectedIndex = configNames.indexOf(selectedConfigName); + selectedConfig = configurations[selectedIndex]; + } + + if (selectedConfig.rootDir?.includes('${workspaceFolder}')) { + await this.brightScriptCommands.getWorkspacePath(); + let workspacePath = this.brightScriptCommands.workspacePath; + + selectedConfig.rootDir = path.normalize(selectedConfig.rootDir.replace('${workspaceFolder}', workspacePath)); + } + rokuDeployOptions.packageConfig = 'launch.json: ' + selectedConfig.rootDir; + + if (!selectedConfig.host.includes('${')) { + rokuDeployOptions.host = selectedConfig.host; + } + + if (!selectedConfig.password.includes('${')) { + rokuDeployOptions.password = selectedConfig.password; + } + + rokuDeployOptions.rootDir = selectedConfig.rootDir; + rokuDeployOptions.files = selectedConfig.files; + rokuDeployOptions.outFile = 'roku-' + selectedConfig.name.replace(/ /g, '-'); + + return rokuDeployOptions; + } + + private async parseRokuDeployJson(filePath: string, rokuDeployOptions) { + rokuDeployOptions.packageConfig = 'rokudeploy.json: ' + filePath; + let content = JSON.parse(readFileSync(filePath).toString()); + await this.brightScriptCommands.getWorkspacePath(); + let workspacePath = this.brightScriptCommands.workspacePath; + + if (content.signingPassword) { + rokuDeployOptions.signingPassword = content.signingPassword; + } + + if (content.rekeySignedPackage?.includes('./')) { + rokuDeployOptions.rekeySignedPackage = workspacePath + content.rekeySignedPackage.replace('./', '/'); + } + + if (content.host) { + rokuDeployOptions.host = content.host; + } + + if (content.password) { + rokuDeployOptions.password = content.password; + } + + if (content.rootDir?.includes('./')) { + rokuDeployOptions.rootDir = workspacePath + content.rootDir.replace('./', '/'); + } + + if (content.outDir?.includes('./')) { + rokuDeployOptions.outDir = workspacePath + content.outDir.replace('./', '/'); + } + + if (content.outFile) { + rokuDeployOptions.outFile = content.outFile; + } + + if (content.retainStagingDir) { + rokuDeployOptions.retainStagingDir = content.retainStagingDir; + } + + return rokuDeployOptions; + } +} + +interface RekeyConfig { + signingPassword: string; + rekeySignedPackage: string; + host: string; + password: string; +} + +interface RokuDeployOptions { + rootDir: string; + outDir: string; + outFile: string; + retainStagingDir: boolean; + host: string; + password: string; + signingPassword: string; + rekeySignedPackage: string; + packageConfig: string; +} + +export const rekeyAndPackageCommand = new RekeyAndPackageCommand();