roku-deploy
Advanced tools
Comparing version 2.0.0 to 2.1.0-beta1
/* eslint-disable */ | ||
var jumpToCode = (function init () { | ||
// Classes of code we would like to highlight | ||
var missingCoverageClasses = [ '.cbranch-no', '.cstat-no', '.fstat-no' ]; | ||
var jumpToCode = (function init() { | ||
// Classes of code we would like to highlight in the file view | ||
var missingCoverageClasses = ['.cbranch-no', '.cstat-no', '.fstat-no']; | ||
// We don't want to select elements that are direct descendants of another match | ||
var notSelector = ':not(' + missingCoverageClasses.join('):not(') + ') > '; // becomes `:not(a):not(b) > ` | ||
// Elements to highlight in the file listing view | ||
var fileListingElements = ['td.pct.low']; | ||
// Selecter that finds elements on the page to which we can jump | ||
var selector = notSelector + missingCoverageClasses.join(', ' + notSelector); // becomes `:not(a):not(b) > a, :not(a):not(b) > b` | ||
// We don't want to select elements that are direct descendants of another match | ||
var notSelector = ':not(' + missingCoverageClasses.join('):not(') + ') > '; // becomes `:not(a):not(b) > ` | ||
// The NodeList of matching elements | ||
var missingCoverageElements = document.querySelectorAll(selector); | ||
// Selecter that finds elements on the page to which we can jump | ||
var selector = | ||
fileListingElements.join(', ') + | ||
', ' + | ||
notSelector + | ||
missingCoverageClasses.join(', ' + notSelector); // becomes `:not(a):not(b) > a, :not(a):not(b) > b` | ||
var currentIndex; | ||
// The NodeList of matching elements | ||
var missingCoverageElements = document.querySelectorAll(selector); | ||
function toggleClass(index) { | ||
missingCoverageElements.item(currentIndex).classList.remove('highlighted'); | ||
missingCoverageElements.item(index).classList.add('highlighted'); | ||
} | ||
var currentIndex; | ||
function makeCurrent(index) { | ||
toggleClass(index); | ||
currentIndex = index; | ||
missingCoverageElements.item(index) | ||
.scrollIntoView({ behavior: 'smooth', block: 'center', inline: 'center' }); | ||
} | ||
function toggleClass(index) { | ||
missingCoverageElements | ||
.item(currentIndex) | ||
.classList.remove('highlighted'); | ||
missingCoverageElements.item(index).classList.add('highlighted'); | ||
} | ||
function goToPrevious() { | ||
var nextIndex = 0; | ||
if (typeof currentIndex !== 'number' || currentIndex === 0) { | ||
nextIndex = missingCoverageElements.length - 1; | ||
} else if (missingCoverageElements.length > 1) { | ||
nextIndex = currentIndex - 1; | ||
function makeCurrent(index) { | ||
toggleClass(index); | ||
currentIndex = index; | ||
missingCoverageElements.item(index).scrollIntoView({ | ||
behavior: 'smooth', | ||
block: 'center', | ||
inline: 'center' | ||
}); | ||
} | ||
makeCurrent(nextIndex); | ||
} | ||
function goToPrevious() { | ||
var nextIndex = 0; | ||
if (typeof currentIndex !== 'number' || currentIndex === 0) { | ||
nextIndex = missingCoverageElements.length - 1; | ||
} else if (missingCoverageElements.length > 1) { | ||
nextIndex = currentIndex - 1; | ||
} | ||
function goToNext() { | ||
var nextIndex = 0; | ||
if (typeof currentIndex === 'number' && currentIndex < (missingCoverageElements.length - 1)) { | ||
nextIndex = currentIndex + 1; | ||
makeCurrent(nextIndex); | ||
} | ||
makeCurrent(nextIndex); | ||
} | ||
function goToNext() { | ||
var nextIndex = 0; | ||
return function jump(event) { | ||
switch (event.which) { | ||
case 78: // n | ||
case 74: // j | ||
goToNext(); | ||
break; | ||
case 66: // b | ||
case 75: // k | ||
case 80: // p | ||
goToPrevious(); | ||
break; | ||
if ( | ||
typeof currentIndex === 'number' && | ||
currentIndex < missingCoverageElements.length - 1 | ||
) { | ||
nextIndex = currentIndex + 1; | ||
} | ||
makeCurrent(nextIndex); | ||
} | ||
}; | ||
}()); | ||
return function jump(event) { | ||
switch (event.which) { | ||
case 78: // n | ||
case 74: // j | ||
goToNext(); | ||
break; | ||
case 66: // b | ||
case 75: // k | ||
case 80: // p | ||
goToPrevious(); | ||
break; | ||
} | ||
}; | ||
})(); | ||
window.addEventListener('keydown', jumpToCode); |
/* eslint-disable */ | ||
var addSorting = (function () { | ||
"use strict"; | ||
var addSorting = (function() { | ||
'use strict'; | ||
var cols, | ||
@@ -11,9 +11,17 @@ currentSort = { | ||
// returns the summary table element | ||
function getTable() { return document.querySelector('.coverage-summary'); } | ||
function getTable() { | ||
return document.querySelector('.coverage-summary'); | ||
} | ||
// returns the thead element of the summary table | ||
function getTableHeader() { return getTable().querySelector('thead tr'); } | ||
function getTableHeader() { | ||
return getTable().querySelector('thead tr'); | ||
} | ||
// returns the tbody element of the summary table | ||
function getTableBody() { return getTable().querySelector('tbody'); } | ||
function getTableBody() { | ||
return getTable().querySelector('tbody'); | ||
} | ||
// returns the th element for nth column | ||
function getNthColumn(n) { return getTableHeader().querySelectorAll('th')[n]; } | ||
function getNthColumn(n) { | ||
return getTableHeader().querySelectorAll('th')[n]; | ||
} | ||
@@ -38,3 +46,4 @@ // loads all columns | ||
col.defaultDescSort = col.type === 'number'; | ||
colNode.innerHTML = colNode.innerHTML + '<span class="sorter"></span>'; | ||
colNode.innerHTML = | ||
colNode.innerHTML + '<span class="sorter"></span>'; | ||
} | ||
@@ -76,3 +85,3 @@ } | ||
var key = cols[index].key, | ||
sorter = function (a, b) { | ||
sorter = function(a, b) { | ||
a = a.data[key]; | ||
@@ -89,3 +98,3 @@ b = b.data[key]; | ||
if (desc) { | ||
finalSorter = function (a, b) { | ||
finalSorter = function(a, b) { | ||
return -1 * sorter(a, b); | ||
@@ -116,3 +125,5 @@ }; | ||
function addSortIndicators() { | ||
getNthColumn(currentSort.index).className += currentSort.desc ? ' sorted-desc' : ' sorted'; | ||
getNthColumn(currentSort.index).className += currentSort.desc | ||
? ' sorted-desc' | ||
: ' sorted'; | ||
} | ||
@@ -126,3 +137,3 @@ // adds event listeners for all sorter widgets | ||
return function () { | ||
return function() { | ||
var desc = col.defaultDescSort; | ||
@@ -140,3 +151,3 @@ | ||
}; | ||
for (i =0 ; i < cols.length; i += 1) { | ||
for (i = 0; i < cols.length; i += 1) { | ||
if (cols[i].sortable) { | ||
@@ -155,3 +166,3 @@ // add the click event handler on the th so users | ||
// adds sorting functionality to the UI | ||
return function () { | ||
return function() { | ||
if (!getTable()) { | ||
@@ -158,0 +169,0 @@ return; |
@@ -0,1 +1,2 @@ | ||
#!/usr/bin/env node | ||
export {}; |
@@ -1,154 +0,49 @@ | ||
export declare var __request: any; | ||
/** | ||
* Copies all of the referenced files to the staging folder | ||
* @param options | ||
*/ | ||
export declare function prepublishToStaging(options: RokuDeployOptions): Promise<string>; | ||
/** | ||
* Make all file references absolute | ||
* @param files | ||
* @param rootDir | ||
*/ | ||
export declare function makeFilesAbsolute(files: { | ||
export * from './RokuDeploy'; | ||
declare let createPackage: (options: import("./RokuDeploy").RokuDeployOptions, beforeZipCallback?: (info: import("./RokuDeploy").BeforeZipCallbackInfo) => void) => Promise<void>; | ||
declare let prepublishToStaging: (options: import("./RokuDeploy").RokuDeployOptions) => Promise<string>; | ||
declare let zipPackage: (options: import("./RokuDeploy").RokuDeployOptions) => Promise<void>; | ||
declare let getOutputZipFilePath: (options: import("./RokuDeploy").RokuDeployOptions) => string; | ||
declare let pressHomeButton: (host: any) => Promise<{ | ||
response: any; | ||
body: any; | ||
}>; | ||
declare let publish: (options: import("./RokuDeploy").RokuDeployOptions) => Promise<{ | ||
message: string; | ||
results: any; | ||
}>; | ||
declare let getStagingFolderPath: (options?: import("./RokuDeploy").RokuDeployOptions) => string; | ||
declare let signExistingPackage: (options: import("./RokuDeploy").RokuDeployOptions) => Promise<string>; | ||
declare let makeFilesAbsolute: (files: { | ||
src: string[]; | ||
dest: string; | ||
}[], rootDir: string): { | ||
}[], rootDir: string) => { | ||
src: string[]; | ||
dest: string; | ||
}[]; | ||
/** | ||
* Determine if a given string ends in a file system slash (\ for windows and / for everything else) | ||
* @param dirPath | ||
*/ | ||
export declare function endsWithSlash(dirPath: string): boolean; | ||
/** | ||
* Given an array of files, normalize them into a standard {src;dest} object. | ||
* This will make plain folder names into fully qualified paths, add globs to plain folders, etc. | ||
* This makes it easier to reason about later on in the process. | ||
* @param files | ||
*/ | ||
export declare function normalizeFilesOption(files: FilesType[]): { | ||
declare let normalizeFilesOption: (files: (string | string[] | { | ||
src: string | string[]; | ||
dest?: string; | ||
})[]) => { | ||
src: string[]; | ||
dest: string; | ||
}[]; | ||
/** | ||
* Given an already-populated staging folder, create a zip archive of it and copy it to the output folder | ||
* @param options | ||
*/ | ||
export declare function zipPackage(options: RokuDeployOptions): Promise<void>; | ||
/** | ||
* Create a zip folder containing all of the specified roku project files. | ||
* @param options | ||
*/ | ||
export declare function createPackage(options: RokuDeployOptions): Promise<void>; | ||
/** | ||
* Given a root directory, normalize it to a full path. | ||
* Fall back to cwd if not specified | ||
* @param rootDir | ||
*/ | ||
export declare function normalizeRootDir(rootDir: string): string; | ||
/** | ||
* Get all file paths for the specified options | ||
*/ | ||
export declare function getFilePaths(files: FilesType[], stagingPath: string, rootDir: string): Promise<{ | ||
src: string; | ||
dest: string; | ||
}[]>; | ||
/** | ||
* Determine if the given path is a directory | ||
* @param path | ||
*/ | ||
export declare function isDirectory(pathToDirectoryOrFile: string): Promise<any>; | ||
/** | ||
* Simulate pressing the home button on the remote for this roku. | ||
* This makes the roku return to the home screen | ||
* @param host | ||
*/ | ||
export declare function pressHomeButton(host: any): Promise<{}>; | ||
/** | ||
* Publish a pre-existing packaged zip file to a remote Roku. | ||
* @param options | ||
*/ | ||
export declare function publish(options: RokuDeployOptions): Promise<{ | ||
declare let deploy: (options?: import("./RokuDeploy").RokuDeployOptions, beforeZipCallback?: (info: import("./RokuDeploy").BeforeZipCallbackInfo) => void) => Promise<{ | ||
message: string; | ||
results: any; | ||
}>; | ||
/** | ||
* Create a zip of the project, and then publish to the target Roku device | ||
* @param options | ||
*/ | ||
export declare function deploy(options?: RokuDeployOptions): Promise<{ | ||
message: string; | ||
results: any; | ||
}>; | ||
/** | ||
* Get an options with all overridden vaues, and then defaults for missing values | ||
* @param options | ||
*/ | ||
export declare function getOptions(options?: RokuDeployOptions): RokuDeployOptions; | ||
/** | ||
* Given a path to a folder, zip up that folder and all of its contents | ||
* @param srcFolder | ||
* @param zipFilePath | ||
*/ | ||
export declare function zipFolder(srcFolder: string, zipFilePath: string): Promise<{}>; | ||
export interface RokuDeployOptions { | ||
/** | ||
* A full path to the folder where the zip package should be placed | ||
* @default "./out" | ||
*/ | ||
outDir?: string; | ||
/** | ||
* The name the zip file should be given. | ||
* @default "roku-deploy.zip" | ||
*/ | ||
outFile?: string; | ||
/** | ||
* The root path to the folder holding your Roku project's source files (manifest, components/, source/ should be directly under this folder) | ||
* @default './' | ||
*/ | ||
rootDir?: string; | ||
/** | ||
* An array of source file paths, source file globs, or {src,dest} objects indicating | ||
* where the source files are and where they should be placed | ||
* in the output directory | ||
* @default [ | ||
"source/**\/*.*", | ||
"components/**\/*.*", | ||
"images/**\/*.*", | ||
"manifest" | ||
], | ||
*/ | ||
files?: FilesType[]; | ||
/** | ||
* Set this to true prevent the staging folder from being deleted after creating the package | ||
* @default false | ||
*/ | ||
retainStagingFolder?: boolean; | ||
/** | ||
* The IP address or hostname of the target Roku device. | ||
* @required | ||
* @example "192.168.1.21" | ||
* | ||
*/ | ||
host?: string; | ||
/** | ||
* The username for the roku box. This will always be 'rokudev', but allows to be overridden | ||
* just in case roku adds support for custom usernames in the future | ||
* @default "rokudev" | ||
*/ | ||
username?: string; | ||
/** | ||
* The password for logging in to the developer portal on the target Roku device | ||
* @required | ||
*/ | ||
password?: string; | ||
/** | ||
* If true, the publish will fail on compile error | ||
*/ | ||
failOnCompileError?: boolean; | ||
} | ||
export declare type FilesType = (string | string[] | { | ||
declare let zipFolder: (srcFolder: string, zipFilePath: string) => Promise<{}>; | ||
declare let parseManifest: (manifestPath: string) => Promise<import("./RokuDeploy").ManifestData>; | ||
declare let endsWithSlash: (dirPath: string) => boolean; | ||
declare let getFilePaths: (files: (string | string[] | { | ||
src: string | string[]; | ||
dest?: string; | ||
}); | ||
})[], stagingPath: string, rootDir: string) => Promise<{ | ||
src: string; | ||
dest: string; | ||
}[]>; | ||
declare let normalizeRootDir: (rootDir: string) => string; | ||
declare let getOptions: (options?: import("./RokuDeploy").RokuDeployOptions) => import("./RokuDeploy").RokuDeployOptions; | ||
declare let getOutputPkgFilePath: (options?: import("./RokuDeploy").RokuDeployOptions) => string; | ||
declare let deployAndSignPackage: (options?: import("./RokuDeploy").RokuDeployOptions, beforeZipCallback?: (info: import("./RokuDeploy").BeforeZipCallbackInfo) => void) => Promise<string>; | ||
declare let retrieveSignedPackage: (pkgPath: string, options: import("./RokuDeploy").RokuDeployOptions) => Promise<string>; | ||
export { createPackage, deploy, deployAndSignPackage, endsWithSlash, getFilePaths, getOptions, makeFilesAbsolute, normalizeFilesOption, normalizeRootDir, prepublishToStaging, pressHomeButton, publish, retrieveSignedPackage, signExistingPackage, zipFolder, zipPackage, getStagingFolderPath, getOutputZipFilePath, parseManifest, getOutputPkgFilePath }; |
"use strict"; | ||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
return new (P || (P = Promise))(function (resolve, reject) { | ||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } | ||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } | ||
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } | ||
step((generator = generator.apply(thisArg, _arguments || [])).next()); | ||
}); | ||
}; | ||
var __generator = (this && this.__generator) || function (thisArg, body) { | ||
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; | ||
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; | ||
function verb(n) { return function (v) { return step([n, v]); }; } | ||
function step(op) { | ||
if (f) throw new TypeError("Generator is already executing."); | ||
while (_) try { | ||
if (f = 1, y && (t = y[op[0] & 2 ? "return" : op[0] ? "throw" : "next"]) && !(t = t.call(y, op[1])).done) return t; | ||
if (y = 0, t) op = [0, t.value]; | ||
switch (op[0]) { | ||
case 0: case 1: t = op; break; | ||
case 4: _.label++; return { value: op[1], done: false }; | ||
case 5: _.label++; y = op[1]; op = [0]; continue; | ||
case 7: op = _.ops.pop(); _.trys.pop(); continue; | ||
default: | ||
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } | ||
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } | ||
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } | ||
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } | ||
if (t[2]) _.ops.pop(); | ||
_.trys.pop(); continue; | ||
} | ||
op = body.call(thisArg, _); | ||
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } | ||
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; | ||
} | ||
}; | ||
function __export(m) { | ||
for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p]; | ||
} | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var path = require("path"); | ||
var fsExtra = require("fs-extra"); | ||
var Q = require("q"); | ||
var globAll = require("glob-all"); | ||
var request = require("request"); | ||
var fs = require("fs"); | ||
var archiver = require("archiver"); | ||
// tslint:disable-next-line | ||
exports.__request = request; | ||
/** | ||
* Copies all of the referenced files to the staging folder | ||
* @param options | ||
*/ | ||
function prepublishToStaging(options) { | ||
return __awaiter(this, void 0, void 0, function () { | ||
var files, stagingFolderPath; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
options = getOptions(options); | ||
//cast some of the options as not null so we don't have to cast them below | ||
options.rootDir = options.rootDir; | ||
options.outDir = options.outDir; | ||
files = normalizeFilesOption(options.files); | ||
//make all path references absolute | ||
makeFilesAbsolute(files, options.rootDir); | ||
stagingFolderPath = path.join(options.outDir, '.roku-deploy-staging'); | ||
stagingFolderPath = path.resolve(stagingFolderPath); | ||
//clean the staging directory | ||
return [4 /*yield*/, fsExtra.remove(stagingFolderPath)]; | ||
case 1: | ||
//clean the staging directory | ||
_a.sent(); | ||
//make sure the staging folder exists | ||
return [4 /*yield*/, fsExtra.ensureDir(stagingFolderPath)]; | ||
case 2: | ||
//make sure the staging folder exists | ||
_a.sent(); | ||
return [4 /*yield*/, copyToStaging(files, stagingFolderPath, options.rootDir)]; | ||
case 3: | ||
_a.sent(); | ||
return [2 /*return*/, stagingFolderPath]; | ||
} | ||
}); | ||
}); | ||
} | ||
var RokuDeploy_1 = require("./RokuDeploy"); | ||
//export everything from the RokuDeploy file | ||
__export(require("./RokuDeploy")); | ||
//create a new static instance of RokuDeploy, and export those functions for backwards compatibility | ||
var rokuDeploy = new RokuDeploy_1.RokuDeploy(); | ||
var createPackage = RokuDeploy_1.RokuDeploy.prototype.createPackage.bind(rokuDeploy); | ||
exports.createPackage = createPackage; | ||
var prepublishToStaging = RokuDeploy_1.RokuDeploy.prototype.prepublishToStaging.bind(rokuDeploy); | ||
exports.prepublishToStaging = prepublishToStaging; | ||
/** | ||
* Make all file references absolute | ||
* @param files | ||
* @param rootDir | ||
*/ | ||
function makeFilesAbsolute(files, rootDir) { | ||
//append the rootDir to every relative glob and string file entry | ||
for (var _i = 0, files_1 = files; _i < files_1.length; _i++) { | ||
var fileEntry = files_1[_i]; | ||
for (var i = 0; i < fileEntry.src.length; i++) { | ||
var src = fileEntry.src[i]; | ||
var isNegated = src.indexOf('!') === 0; | ||
if (isNegated) { | ||
src = src.substring(1); | ||
} | ||
if (path.isAbsolute(src) === false) { | ||
var absoluteSource = path.join(rootDir, src); | ||
if (isNegated) { | ||
absoluteSource = '!' + absoluteSource; | ||
} | ||
fileEntry.src[i] = absoluteSource; | ||
} | ||
} | ||
} | ||
return files; | ||
} | ||
exports.makeFilesAbsolute = makeFilesAbsolute; | ||
/** | ||
* Determine if a given string ends in a file system slash (\ for windows and / for everything else) | ||
* @param dirPath | ||
*/ | ||
function endsWithSlash(dirPath) { | ||
if ('string' === typeof dirPath && dirPath.length > 0 && | ||
(dirPath.lastIndexOf('/') === dirPath.length - 1 || dirPath.lastIndexOf('\\') === dirPath.length - 1)) { | ||
return true; | ||
} | ||
else { | ||
return false; | ||
} | ||
} | ||
exports.endsWithSlash = endsWithSlash; | ||
/** | ||
* Given an array of files, normalize them into a standard {src;dest} object. | ||
* This will make plain folder names into fully qualified paths, add globs to plain folders, etc. | ||
* This makes it easier to reason about later on in the process. | ||
* @param files | ||
*/ | ||
function normalizeFilesOption(files) { | ||
var result = []; | ||
var topLevelGlobs = []; | ||
//standardize the files object | ||
for (var _i = 0, _a = files; _i < _a.length; _i++) { | ||
var fileEntry = _a[_i]; | ||
//handle single string top-level globs | ||
if (typeof fileEntry === 'string') { | ||
topLevelGlobs.push(fileEntry); | ||
continue; | ||
//handle src;dest; object with single string for src | ||
} | ||
else if (typeof fileEntry.src === 'string') { | ||
fileEntry.src = [fileEntry.src]; | ||
} | ||
fileEntry.dest = fileEntry.dest ? fileEntry.dest : ''; | ||
//hard-fail if dest is anything other than a string at this point | ||
if ('string' !== typeof fileEntry.dest) { | ||
throw new Error('dest must be a string'); | ||
} | ||
//standardize the dest path separator | ||
fileEntry.dest = path.normalize(fileEntry.dest).trim(); | ||
if (fileEntry.dest === '' || fileEntry.dest === '.' || fileEntry.dest === '.\\' || fileEntry.dest === './') { | ||
fileEntry.dest = ''; | ||
} | ||
//force all slashes to the current platform's version | ||
fileEntry.dest = fileEntry.dest.replace('\\', path.sep).replace('/', path.sep); | ||
if (typeof fileEntry !== 'string' && (!fileEntry || fileEntry.src === null || fileEntry.src === undefined || fileEntry.dest === null || fileEntry.dest === undefined)) { | ||
throw new Error('Entry must be a string or a {src;dest;} object'); | ||
} | ||
//if there is a wildcard in any src, ensure a slash in dest | ||
{ | ||
var srcContainsWildcard = fileEntry.src.findIndex(function (src) { | ||
return src.indexOf('*') > -1; | ||
}) > -1; | ||
if (fileEntry.dest.length > 0 && !endsWithSlash(fileEntry.dest) && srcContainsWildcard) { | ||
fileEntry.dest += path.sep; | ||
} | ||
} | ||
result.push(fileEntry); | ||
} | ||
//if there are any top level globs, add that entry to the beginning | ||
if (topLevelGlobs.length > 0) { | ||
result.splice(0, 0, { | ||
src: topLevelGlobs, | ||
dest: '' | ||
}); | ||
} | ||
return result; | ||
} | ||
exports.normalizeFilesOption = normalizeFilesOption; | ||
/** | ||
* Given an already-populated staging folder, create a zip archive of it and copy it to the output folder | ||
* @param options | ||
*/ | ||
function zipPackage(options) { | ||
return __awaiter(this, void 0, void 0, function () { | ||
var stagingFolderPath, outFolderPath, outFilePath; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
options = getOptions(options); | ||
stagingFolderPath = path.join(options.outDir, '.roku-deploy-staging'); | ||
stagingFolderPath = path.resolve(stagingFolderPath); | ||
outFolderPath = path.resolve(options.outDir); | ||
//make sure the output folder exists | ||
return [4 /*yield*/, fsExtra.ensureDir(outFolderPath)]; | ||
case 1: | ||
//make sure the output folder exists | ||
_a.sent(); | ||
outFilePath = path.join(outFolderPath, options.outFile); | ||
//create a zip of the staging folder | ||
return [4 /*yield*/, zipFolder(stagingFolderPath, outFilePath)]; | ||
case 2: | ||
//create a zip of the staging folder | ||
_a.sent(); | ||
if (!(options.retainStagingFolder !== true)) return [3 /*break*/, 4]; | ||
return [4 /*yield*/, fsExtra.remove(stagingFolderPath)]; | ||
case 3: | ||
_a.sent(); | ||
_a.label = 4; | ||
case 4: return [2 /*return*/]; | ||
} | ||
}); | ||
}); | ||
} | ||
var zipPackage = RokuDeploy_1.RokuDeploy.prototype.zipPackage.bind(rokuDeploy); | ||
exports.zipPackage = zipPackage; | ||
/** | ||
* Create a zip folder containing all of the specified roku project files. | ||
* @param options | ||
*/ | ||
function createPackage(options) { | ||
return __awaiter(this, void 0, void 0, function () { | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: return [4 /*yield*/, prepublishToStaging(options)]; | ||
case 1: | ||
_a.sent(); | ||
return [4 /*yield*/, zipPackage(options)]; | ||
case 2: | ||
_a.sent(); | ||
return [2 /*return*/]; | ||
} | ||
}); | ||
}); | ||
} | ||
exports.createPackage = createPackage; | ||
/** | ||
* Given a root directory, normalize it to a full path. | ||
* Fall back to cwd if not specified | ||
* @param rootDir | ||
*/ | ||
function normalizeRootDir(rootDir) { | ||
if (!rootDir || (typeof rootDir === 'string' && rootDir.trim().length === 0)) { | ||
return process.cwd(); | ||
} | ||
else { | ||
return path.resolve(rootDir); | ||
} | ||
} | ||
exports.normalizeRootDir = normalizeRootDir; | ||
/** | ||
* Get all file paths for the specified options | ||
*/ | ||
function getFilePaths(files, stagingPath, rootDir) { | ||
return __awaiter(this, void 0, void 0, function () { | ||
var _this = this; | ||
var normalizedFiles, filePathObjects, fileObjects, _i, filePathObjects_1, filePathObject, _a, fileObjects_1, fileObject, result; | ||
return __generator(this, function (_b) { | ||
switch (_b.label) { | ||
case 0: | ||
stagingPath = path.normalize(stagingPath); | ||
normalizedFiles = normalizeFilesOption(files); | ||
rootDir = normalizeRootDir(rootDir); | ||
return [4 /*yield*/, Promise.all(normalizedFiles.map(function (file) { return __awaiter(_this, void 0, void 0, function () { | ||
var pathRemoveFromDest, fileAsDirPath, _a, originalSrc, filePathArray, output, _i, filePathArray_1, filePath, dest, normalizedFilePath; | ||
return __generator(this, function (_b) { | ||
switch (_b.label) { | ||
case 0: | ||
if (!(file.src.length === 1)) return [3 /*break*/, 4]; | ||
fileAsDirPath = void 0; | ||
return [4 /*yield*/, isDirectory(file.src[0])]; | ||
case 1: | ||
//try the path as is | ||
if (_b.sent()) { | ||
fileAsDirPath = file.src[0]; | ||
} | ||
_a = !fileAsDirPath; | ||
if (!_a) return [3 /*break*/, 3]; | ||
return [4 /*yield*/, isDirectory(path.join(rootDir, file.src[0]))]; | ||
case 2: | ||
_a = (_b.sent()); | ||
_b.label = 3; | ||
case 3: | ||
/* istanbul ignore next */ | ||
//assume path is relative, append root dir and try that way | ||
if (_a) { | ||
fileAsDirPath = path.normalize(path.join(rootDir, file.src[0])); | ||
} | ||
if (fileAsDirPath) { | ||
pathRemoveFromDest = fileAsDirPath; | ||
//add the wildcard glob | ||
file.src[0] = path.join(fileAsDirPath, '**', '*'); | ||
} | ||
_b.label = 4; | ||
case 4: | ||
originalSrc = file.src; | ||
return [4 /*yield*/, Q.nfcall(globAll, file.src, { cwd: rootDir })]; | ||
case 5: | ||
filePathArray = _b.sent(); | ||
output = []; | ||
_i = 0, filePathArray_1 = filePathArray; | ||
_b.label = 6; | ||
case 6: | ||
if (!(_i < filePathArray_1.length)) return [3 /*break*/, 10]; | ||
filePath = filePathArray_1[_i]; | ||
dest = file.dest; | ||
if (!pathRemoveFromDest) return [3 /*break*/, 8]; | ||
normalizedFilePath = path.normalize(filePath); | ||
//remove the specified source path | ||
dest = normalizedFilePath.replace(pathRemoveFromDest, ''); | ||
return [4 /*yield*/, isDirectory(filePath)]; | ||
case 7: | ||
//remove the filename if it's a file | ||
if ((_b.sent()) === false) { | ||
dest = path.dirname(dest); | ||
} | ||
//prepend the specified dest | ||
dest = path.join(file.dest, dest, path.basename(normalizedFilePath)); | ||
//blank out originalSrc since we already handled the dest | ||
originalSrc = []; | ||
_b.label = 8; | ||
case 8: | ||
//create a src;dest; object for every file or directory that was found | ||
output.push({ | ||
src: filePath, | ||
dest: dest, | ||
srcOriginal: originalSrc.length === 1 ? originalSrc[0] : undefined | ||
}); | ||
_b.label = 9; | ||
case 9: | ||
_i++; | ||
return [3 /*break*/, 6]; | ||
case 10: return [2 /*return*/, output]; | ||
} | ||
}); | ||
}); }))]; | ||
case 1: | ||
filePathObjects = _b.sent(); | ||
fileObjects = []; | ||
//create a single array of all paths | ||
for (_i = 0, filePathObjects_1 = filePathObjects; _i < filePathObjects_1.length; _i++) { | ||
filePathObject = filePathObjects_1[_i]; | ||
fileObjects = fileObjects.concat(filePathObject); | ||
} | ||
//make all file paths absolute | ||
for (_a = 0, fileObjects_1 = fileObjects; _a < fileObjects_1.length; _a++) { | ||
fileObject = fileObjects_1[_a]; | ||
//only normalize non-absolute paths | ||
if (path.isAbsolute(fileObject.src) === false) { | ||
fileObject.src = path.resolve(path.join(rootDir, fileObject.src)); | ||
} | ||
//normalize the path | ||
fileObject.src = path.normalize(fileObject.src); | ||
} | ||
result = []; | ||
//copy each file, retaining their folder structure relative to the rootDir | ||
return [4 /*yield*/, Promise.all(fileObjects.map(function (fileObject) { return __awaiter(_this, void 0, void 0, function () { | ||
var src, dest, sourceIsDirectory, relativeSrc, globDoubleStarIndex, pathToDoubleStar, rootDirWithTrailingSlash, destinationPath; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
src = path.normalize(fileObject.src); | ||
dest = fileObject.dest; | ||
return [4 /*yield*/, isDirectory(src)]; | ||
case 1: | ||
sourceIsDirectory = _a.sent(); | ||
globDoubleStarIndex = fileObject.srcOriginal ? fileObject.srcOriginal.indexOf('**') : -1; | ||
if (fileObject.srcOriginal && globDoubleStarIndex > -1 && sourceIsDirectory === false) { | ||
pathToDoubleStar = fileObject.srcOriginal.substring(0, globDoubleStarIndex); | ||
relativeSrc = src.replace(pathToDoubleStar, ''); | ||
dest = path.join(dest, relativeSrc); | ||
} | ||
else { | ||
rootDirWithTrailingSlash = path.normalize(rootDir + path.sep); | ||
relativeSrc = src.replace(rootDirWithTrailingSlash, ''); | ||
} | ||
//if item is a directory | ||
if (sourceIsDirectory) { | ||
//source is a directory (which is only possible when glob resolves it as such) | ||
//do nothing, because we don't want to copy empty directories to output | ||
} | ||
else { | ||
destinationPath = void 0; | ||
//if the relativeSrc is stil absolute, then this file exists outside of the rootDir. Copy to dest, and only retain filename from src | ||
if (path.isAbsolute(relativeSrc)) { | ||
destinationPath = path.join(stagingPath, dest, path.basename(relativeSrc)); | ||
} | ||
else { | ||
//the relativeSrc is actually relative | ||
//if the dest ends in a slash, use the filename from src, but the folder structure from dest | ||
if (dest.endsWith(path.sep)) { | ||
destinationPath = path.join(stagingPath, dest, path.basename(src)); | ||
//dest is empty, so use the relative path | ||
} | ||
else if (dest === '') { | ||
destinationPath = path.join(stagingPath, relativeSrc); | ||
//use all of dest | ||
} | ||
else { | ||
destinationPath = path.join(stagingPath, dest); | ||
} | ||
} | ||
fileObject.dest = destinationPath; | ||
delete fileObject.srcOriginal; | ||
//add the file object to the results | ||
result.push(fileObject); | ||
} | ||
return [2 /*return*/]; | ||
} | ||
}); | ||
}); }))]; | ||
case 2: | ||
//copy each file, retaining their folder structure relative to the rootDir | ||
_b.sent(); | ||
return [2 /*return*/, result]; | ||
} | ||
}); | ||
}); | ||
} | ||
exports.getFilePaths = getFilePaths; | ||
/** | ||
* Copy all of the files to the staging directory | ||
* @param fileGlobs | ||
* @param stagingPath | ||
*/ | ||
function copyToStaging(files, stagingPath, rootDir) { | ||
return __awaiter(this, void 0, void 0, function () { | ||
var fileObjects, _i, fileObjects_2, fileObject, i, e_1; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: return [4 /*yield*/, getFilePaths(files, stagingPath, rootDir)]; | ||
case 1: | ||
fileObjects = _a.sent(); | ||
_i = 0, fileObjects_2 = fileObjects; | ||
_a.label = 2; | ||
case 2: | ||
if (!(_i < fileObjects_2.length)) return [3 /*break*/, 11]; | ||
fileObject = fileObjects_2[_i]; | ||
//make sure the containing folder exists | ||
return [4 /*yield*/, fsExtra.ensureDir(path.dirname(fileObject.dest))]; | ||
case 3: | ||
//make sure the containing folder exists | ||
_a.sent(); | ||
i = 0; | ||
_a.label = 4; | ||
case 4: | ||
if (!(i < 10)) return [3 /*break*/, 10]; | ||
_a.label = 5; | ||
case 5: | ||
_a.trys.push([5, 7, , 9]); | ||
//copy the src item (file or directory full of files) | ||
return [4 /*yield*/, fsExtra.copy(fileObject.src, fileObject.dest, { | ||
//copy the actual files that symlinks point to, not the symlinks themselves | ||
dereference: true | ||
})]; | ||
case 6: | ||
//copy the src item (file or directory full of files) | ||
_a.sent(); | ||
//copy succeeded, | ||
i = 10; //break out of the loop and still achieve coverage for i++ | ||
return [3 /*break*/, 9]; | ||
case 7: | ||
e_1 = _a.sent(); | ||
//wait a small amount of time and try again | ||
/* istanbul ignore next */ | ||
return [4 /*yield*/, new Promise(function (resolve) { | ||
setTimeout(resolve, 50); | ||
})]; | ||
case 8: | ||
//wait a small amount of time and try again | ||
/* istanbul ignore next */ | ||
_a.sent(); | ||
return [3 /*break*/, 9]; | ||
case 9: | ||
i++; | ||
return [3 /*break*/, 4]; | ||
case 10: | ||
_i++; | ||
return [3 /*break*/, 2]; | ||
case 11: return [2 /*return*/]; | ||
} | ||
}); | ||
}); | ||
} | ||
/** | ||
* Determine if the given path is a directory | ||
* @param path | ||
*/ | ||
function isDirectory(pathToDirectoryOrFile) { | ||
return __awaiter(this, void 0, void 0, function () { | ||
var stat, e_2; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
_a.trys.push([0, 2, , 3]); | ||
return [4 /*yield*/, Q.nfcall(fs.lstat, pathToDirectoryOrFile)]; | ||
case 1: | ||
stat = _a.sent(); | ||
return [2 /*return*/, stat.isDirectory()]; | ||
case 2: | ||
e_2 = _a.sent(); | ||
// lstatSync throws an error if path doesn't exist | ||
return [2 /*return*/, false]; | ||
case 3: return [2 /*return*/]; | ||
} | ||
}); | ||
}); | ||
} | ||
exports.isDirectory = isDirectory; | ||
/** | ||
* Simulate pressing the home button on the remote for this roku. | ||
* This makes the roku return to the home screen | ||
* @param host | ||
*/ | ||
function pressHomeButton(host) { | ||
return __awaiter(this, void 0, void 0, function () { | ||
var homeClickUrl; | ||
return __generator(this, function (_a) { | ||
homeClickUrl = "http://" + host + ":8060/keypress/Home"; | ||
// press the home button to return to the main screen | ||
return [2 /*return*/, new Promise(function (resolve, reject) { | ||
request.post(homeClickUrl, function (err, response) { | ||
if (err) { | ||
return reject(err); | ||
} | ||
return resolve(response); | ||
}); | ||
})]; | ||
}); | ||
}); | ||
} | ||
var getOutputZipFilePath = RokuDeploy_1.RokuDeploy.prototype.getOutputZipFilePath.bind(rokuDeploy); | ||
exports.getOutputZipFilePath = getOutputZipFilePath; | ||
var pressHomeButton = RokuDeploy_1.RokuDeploy.prototype.pressHomeButton.bind(rokuDeploy); | ||
exports.pressHomeButton = pressHomeButton; | ||
/** | ||
* Publish a pre-existing packaged zip file to a remote Roku. | ||
* @param options | ||
*/ | ||
function publish(options) { | ||
return __awaiter(this, void 0, void 0, function () { | ||
var packageFolderPath, packagePath, hostUrl, packageUploadUrl; | ||
return __generator(this, function (_a) { | ||
options = getOptions(options); | ||
if (!options.host) { | ||
throw new Error('must specify the host for the Roku device'); | ||
} | ||
packageFolderPath = path.resolve(options.outDir); | ||
packagePath = path.join(packageFolderPath, options.outFile); | ||
hostUrl = "http://" + options.host; | ||
packageUploadUrl = hostUrl + "/plugin_install"; | ||
return [2 /*return*/, Promise.all([]).then(function () { | ||
return pressHomeButton(options.host).then(function (response) { | ||
// upload the package to the Roku | ||
return new Promise(function (resolve, reject) { | ||
request.post({ | ||
url: packageUploadUrl, | ||
formData: { | ||
mysubmit: 'Replace', | ||
archive: fs.createReadStream(packagePath) | ||
} | ||
}, function (err, resp, body) { | ||
if (err) { | ||
return reject(err); | ||
} | ||
return resolve({ response: resp, body: body }); | ||
}).auth(options.username, options.password, false); | ||
}); | ||
}).then(function (results) { | ||
var error; | ||
if (options.failOnCompileError) { | ||
if (results && results.body && results.body.indexOf('Install Failure: Compilation Failed.') > -1) { | ||
error = new Error('Compile error'); | ||
error.results = results; | ||
return Q.reject(error); | ||
} | ||
} | ||
if (results && results.response && results.response.statusCode === 200) { | ||
if (results.body.indexOf('Identical to previous version -- not replacing.') > -1) { | ||
return { message: 'Identical to previous version -- not replacing', results: results }; | ||
} | ||
return { message: 'Successful deploy', results: results }; | ||
} | ||
else if (results && results.response) { | ||
if (results.response.statusCode === 401) { | ||
error = new Error('Unauthorized. Please verify username and password for target Roku.'); | ||
} | ||
else { | ||
error = new Error('Error, statusCode other than 200: ' + results.response.statusCode); | ||
} | ||
error.results = results; | ||
return Q.reject(error); | ||
} | ||
else { | ||
error = new Error('Invalid response'); | ||
error.results = results; | ||
return Q.reject(error); | ||
} | ||
}); | ||
})]; | ||
}); | ||
}); | ||
} | ||
var publish = RokuDeploy_1.RokuDeploy.prototype.publish.bind(rokuDeploy); | ||
exports.publish = publish; | ||
/** | ||
* Create a zip of the project, and then publish to the target Roku device | ||
* @param options | ||
*/ | ||
function deploy(options) { | ||
return __awaiter(this, void 0, void 0, function () { | ||
var result; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
options = getOptions(options); | ||
return [4 /*yield*/, createPackage(options)]; | ||
case 1: | ||
_a.sent(); | ||
return [4 /*yield*/, publish(options)]; | ||
case 2: | ||
result = _a.sent(); | ||
return [2 /*return*/, result]; | ||
} | ||
}); | ||
}); | ||
} | ||
var getStagingFolderPath = RokuDeploy_1.RokuDeploy.prototype.getStagingFolderPath.bind(rokuDeploy); | ||
exports.getStagingFolderPath = getStagingFolderPath; | ||
var signExistingPackage = RokuDeploy_1.RokuDeploy.prototype.signExistingPackage.bind(rokuDeploy); | ||
exports.signExistingPackage = signExistingPackage; | ||
var makeFilesAbsolute = RokuDeploy_1.RokuDeploy.prototype.makeFilesAbsolute.bind(rokuDeploy); | ||
exports.makeFilesAbsolute = makeFilesAbsolute; | ||
var normalizeFilesOption = RokuDeploy_1.RokuDeploy.prototype.normalizeFilesOption.bind(rokuDeploy); | ||
exports.normalizeFilesOption = normalizeFilesOption; | ||
var deploy = RokuDeploy_1.RokuDeploy.prototype.deploy.bind(rokuDeploy); | ||
exports.deploy = deploy; | ||
/** | ||
* Get an options with all overridden vaues, and then defaults for missing values | ||
* @param options | ||
*/ | ||
function getOptions(options) { | ||
if (options === void 0) { options = {}; } | ||
var fileOptions = {}; | ||
//load a rokudeploy.json file if it exists | ||
if (fsExtra.existsSync('rokudeploy.json')) { | ||
var configFileText = fsExtra.readFileSync('rokudeploy.json').toString(); | ||
fileOptions = JSON.parse(configFileText); | ||
} | ||
var defaultOptions = { | ||
outDir: './out', | ||
outFile: 'roku-deploy.zip', | ||
retainStagingFolder: false, | ||
failOnCompileError: true, | ||
rootDir: './', | ||
files: [ | ||
'source/**/*.*', | ||
'components/**/*.*', | ||
'images/**/*.*', | ||
'manifest' | ||
], | ||
username: 'rokudev' | ||
}; | ||
//override the defaults with any found or provided options | ||
var finalOptions = Object.assign({}, defaultOptions, fileOptions, options); | ||
//fully resolve the folder paths | ||
finalOptions.rootDir = path.resolve(finalOptions.rootDir); | ||
finalOptions.outDir = path.resolve(finalOptions.outDir); | ||
return finalOptions; | ||
} | ||
var zipFolder = RokuDeploy_1.RokuDeploy.prototype.zipFolder.bind(rokuDeploy); | ||
exports.zipFolder = zipFolder; | ||
var parseManifest = RokuDeploy_1.RokuDeploy.prototype.parseManifest.bind(rokuDeploy); | ||
exports.parseManifest = parseManifest; | ||
var endsWithSlash = RokuDeploy_1.RokuDeploy.prototype.endsWithSlash.bind(rokuDeploy); | ||
exports.endsWithSlash = endsWithSlash; | ||
var getFilePaths = RokuDeploy_1.RokuDeploy.prototype.getFilePaths.bind(rokuDeploy); | ||
exports.getFilePaths = getFilePaths; | ||
var normalizeRootDir = RokuDeploy_1.RokuDeploy.prototype.normalizeRootDir.bind(rokuDeploy); | ||
exports.normalizeRootDir = normalizeRootDir; | ||
var getOptions = RokuDeploy_1.RokuDeploy.prototype.getOptions.bind(rokuDeploy); | ||
exports.getOptions = getOptions; | ||
/** | ||
* Given a path to a folder, zip up that folder and all of its contents | ||
* @param srcFolder | ||
* @param zipFilePath | ||
*/ | ||
function zipFolder(srcFolder, zipFilePath) { | ||
return new Promise(function (resolve, reject) { | ||
var output = fs.createWriteStream(zipFilePath); | ||
var archive = archiver('zip'); | ||
output.on('close', function () { | ||
resolve(); | ||
}); | ||
output.on('error', function (err) { | ||
reject(err); | ||
}); | ||
/* istanbul ignore next */ | ||
archive.on('warning', function (err) { | ||
if (err.code === 'ENOENT') { | ||
console.warn(err); | ||
} | ||
else { | ||
reject(err); | ||
} | ||
}); | ||
/* istanbul ignore next */ | ||
archive.on('error', function (err) { | ||
reject(err); | ||
}); | ||
archive.pipe(output); | ||
//add every file in the source folder | ||
archive.directory(srcFolder, false); | ||
//finalize the archive | ||
archive.finalize(); | ||
}); | ||
} | ||
exports.zipFolder = zipFolder; | ||
var getOutputPkgFilePath = RokuDeploy_1.RokuDeploy.prototype.getOutputPkgFilePath.bind(rokuDeploy); | ||
exports.getOutputPkgFilePath = getOutputPkgFilePath; | ||
var deployAndSignPackage = RokuDeploy_1.RokuDeploy.prototype.deployAndSignPackage.bind(rokuDeploy); | ||
exports.deployAndSignPackage = deployAndSignPackage; | ||
var retrieveSignedPackage = RokuDeploy_1.RokuDeploy.prototype.retrieveSignedPackage.bind(rokuDeploy); | ||
exports.retrieveSignedPackage = retrieveSignedPackage; |
{ | ||
"name": "roku-deploy", | ||
"version": "2.0.0", | ||
"version": "2.1.0-beta1", | ||
"description": "Package and publish a Roku application using Node.js", | ||
@@ -10,3 +10,5 @@ "main": "dist/index.js", | ||
"tslint": "tslint './src/**/*.ts' --outputAbsolutePaths", | ||
"test": "nyc mocha src/**/*.spec.ts --full-trace", | ||
"test": "nyc mocha src/RokuDeploy.spec.ts --full-trace", | ||
"test:device": "nyc mocha src/device.spec.ts --full-trace", | ||
"test:all": "nyc mocha src/**/*.spec.ts --full-trace", | ||
"coverage": "nyc report --reporter=text-lcov | coveralls" | ||
@@ -26,3 +28,4 @@ }, | ||
"include": [ | ||
"src/index.ts" | ||
"src/index.ts", | ||
"src/RokuDeploy.ts" | ||
], | ||
@@ -52,5 +55,6 @@ "extension": [ | ||
"@types/fs-extra": "^5.0.1", | ||
"@types/mocha": "^2.2.48", | ||
"@types/node": "^9.4.7", | ||
"@types/mocha": "^5.2.5", | ||
"@types/node": "^11.9.0", | ||
"@types/request": "^2.47.0", | ||
"@types/sinon": "^7.0.6", | ||
"adm-zip": "^0.4.11", | ||
@@ -64,12 +68,15 @@ "chai": "^4.2.0", | ||
"rimraf": "^2.6.2", | ||
"sinon": "^7.2.3", | ||
"source-map-support": "^0.5.3", | ||
"testdouble": "^3.5.2", | ||
"ts-node": "^7.0.1", | ||
"ts-node": "^8.0.2", | ||
"tslint": "^5.9.1", | ||
"typescript": "^2.7.2" | ||
"typescript": "^3.3.3" | ||
}, | ||
"dependencies": { | ||
"archiver": "^3.0.0", | ||
"fs-extra": "^5.0.0", | ||
"dateformat": "^3.0.3", | ||
"fs-extra": "^7.0.1", | ||
"glob-all": "^3.1.0", | ||
"ini": "^1.3.5", | ||
"path": "^0.12.7", | ||
@@ -76,0 +83,0 @@ "q": "^1.5.1", |
@@ -8,3 +8,3 @@ # roku-deploy | ||
[![Coverage Status](https://coveralls.io/repos/github/TwitchBronBron/roku-deploy/badge.svg?branch=master)](https://coveralls.io/github/TwitchBronBron/roku-deploy?branch=master) | ||
[![NPM Version](https://badge.fury.io/js/roku-deploy.svg?style=flat)](https://npmjs.org/package/roku-deploy) | ||
[![NPM Version](https://badge.fury.io/js/roku-deploy.svg?style=flat)](https://npmjs.org/package/roku-deploy) [![Greenkeeper badge](https://badges.greenkeeper.io/TwitchBronBron/roku-deploy.svg)](https://greenkeeper.io/) | ||
## Installation | ||
@@ -16,10 +16,8 @@ | ||
1. Your project must be structured the way that Roku expects. The source files can be in a subdirectory (using the `rootDir` config option), but whever your roku files exist, they must align with the following folder structure: | ||
components/ | ||
images/ | ||
source/ | ||
manifest | ||
1. Your project must be structured the way that Roku expects. The source files can be in a subdirectory (using the `rootDir` config option), but whever your roku files exist, they must align with the following folder structure: | ||
components/ | ||
images/ | ||
source/ | ||
manifest | ||
@@ -42,2 +40,3 @@ 2. You should create a rokudeploy.json file at the root of your project that contains all of the overrides to the default options. roku-deploy will auto-detect this file and use it when possible. | ||
//deploy a .zip package of your project to a roku device | ||
rokuDeploy.deploy({ | ||
@@ -53,4 +52,18 @@ host: 'ip-of-roku', | ||
``` | ||
Or | ||
```javascript | ||
//create a signed package of your project | ||
rokuDeploy.deployAndSignPackage({ | ||
host: 'ip-of-roku', | ||
password: 'password for roku dev admin portal', | ||
signingPassword: 'signing password' | ||
//other options if necessary | ||
}).then(function(pathToSignedPackage){ | ||
console.log('Signed package created at ', pathToSignedPackage); | ||
}, function(){ | ||
//it failed | ||
}); | ||
``` | ||
From an npm script in package.json. (Requires rokudeploy.json to exist at the root level where this is being run) | ||
From an npm script in `package.json`. (Requires `rokudeploy.json` to exist at the root level where this is being run) | ||
@@ -63,2 +76,22 @@ { | ||
You can provide a callback in any of the higher level methods, which allows you to modify the copied contents before the package is zipped. An info object is passed in with the following attributes | ||
- **manifestData:** [key: string]: string | ||
Contains all the parsed values from the manifest file | ||
- **stagingFolderPath:** string | ||
Path to staging folder to make it so you only need to know the relative path to what you're trying to modify | ||
let options = { | ||
host: 'ip-of-roku', | ||
password: 'password for roku dev admin portal' | ||
//other options if necessary | ||
}; | ||
rokuDeploy.deploy(options, (info) => { | ||
//modify staging dir before it's zipped | ||
}).then(function(){ | ||
//it worked | ||
}, function(){ | ||
//it failed | ||
}); | ||
## Options | ||
@@ -73,7 +106,10 @@ Here are the available options. The defaults are shown to the right of the option name, but all can be overridden: | ||
- **signingPassword:** string (*required for signing*) | ||
The password used for creating signed packages | ||
- **outDir?:** string = `"./out"` | ||
A full path to the folder where the zip package should be placed | ||
A full path to the folder where the zip/pkg package should be placed | ||
- **outFile?:** string = `"roku-deploy.zip"` | ||
The name the zip file should be given. | ||
- **outFile?:** string = `"roku-deploy"` | ||
The base filename the zip/pkg file should be given (excluding the extension) | ||
@@ -100,4 +136,4 @@ - **rootDir?:** string = `'./'` | ||
{ | ||
"src": "configs/dev.config.json", | ||
"dest": "config.json" | ||
"src": "configs/dev.config.json", | ||
"dest": "config.json" | ||
} | ||
@@ -128,10 +164,12 @@ ``` | ||
- **retainStagingFolder?:** boolean = `false` | ||
Set this to true prevent the staging folder from being deleted after creating the package. This is helpful for troubleshooting why your package isn't being created the way you expected. | ||
Set this to true to prevent the staging folder from being deleted after creating the package. This is helpful for troubleshooting why your package isn't being created the way you expected. | ||
- **incrementBuildNumber?:** boolean = `false` | ||
If true we increment the build number to be a timestamp in the format yymmddHHMM | ||
- **username?:** string = `"rokudev"` | ||
The username for the roku box. This will always be 'rokudev', but allow to be passed in | ||
just in case roku adds support for custom usernames in the future | ||
Click [here](https://github.com/TwitchBronBron/roku-deploy/blob/2648069de1f3e889c58b8119b5f852f126e60042/src/index.ts#L288) to see the typescript interface for these options | ||
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
308892
22
1781
168
1
8
21
2
1
+ Addeddateformat@^3.0.3
+ Addedini@^1.3.5
+ Addeddateformat@3.0.3(transitive)
+ Addedfs-extra@7.0.1(transitive)
+ Addedini@1.3.8(transitive)
- Removedfs-extra@5.0.0(transitive)
Updatedfs-extra@^7.0.1