A package builder for the Roku platform.
The packager consists of the following features:
- generate environment-specific manifest
- copy files of external modules ("Kopytko Modules") installed via NPM
- import internal and external brightscript dependencies
- pack & zip the files
- deploy a package to the Roku device
- rekey the Roku device with given signed package
- generate a package ready to upload to the Roku channel
- prepare and build an app for the Visual Studio Code extension
- take a screenshot of the dev app
- node version 16+
- npm version 8+
- Install the Packager
npm install @dazn/kopytko-packager --save-dev
- Create
.kopytkorc
in the root folder with minimal configuration
{
"baseManifest": "manifest/base.js"
}
- If your app code is not located under default
/app
, configure it in asourceDir
attribute - Path to base manifest is only example
- Create a base manifest file
const { baseManifest } = require('@dazn/kopytko-packager');
module.exports = {
...baseManifest,
title: 'Your app title',
mm_icon_focus_hd: 'pkg:/images/icon.png', // it is only an example path
mm_icon_focus_sd: 'pkg:/images/icon.png',
splash_screen_hd: 'pkg:/images/splash.png',
splash_screen_sd: 'pkg:/images/splash.png',
}
- Configure start script in your package.json
"scripts": {
"start": "node node_modules/@dazn/kopytko-packager/scripts/start.js"
}
- Start the app
npm start
It is recommended to use kopytko-cli package and add it to package.json scripts:
npm install @dazn/kopytko-packager --save-dev
"scripts": {
"start": "kopytko start",
"build": "kopytko build",
"test": "kopytko test"
}
The main configuration file .kopytkorc
should be placed in the root folder of the project. The example file looks like this:
{
"archivePath": "/dist/kopytko_archive.zip",
"generatedPackagePath": "/dist/kopytko_package.pkg",
"signedPackagePath": "/previous/signed.pkg",
"baseManifest": "/manifest/base.js",
"localManifestOverride": "/manifest/local.${args.env}.js",
"screenshotDir": "/dist",
"pluginDefinitions": {},
"plugins": [],
"sourceDir": "/app",
"environments": {
"production": {
"manifest": "/env/production.js",
"plugins": []
},
"staging": {
"manifest": "/env/staging.js"
},
"test": {
"manifest": "/env/test.js"
}
}
}
Available fields:
archivePath [@type string @optional]
- path to the app archive to generate; default value as in the example file abovegeneratedPackagePath [@type string @optional]
- path to the app package to generate; default value as in the example file abovebaseManifest [@type string @required]
- base manifest file path. Having the file is sufficient to run the Roku applocalManifestOverride [@type string @optional]
- path to the configuration file that overrides all other settings. Usually the file is on the git ignore listscreenshotDir [@type string @optional]
- the directory where screenshots will be saved; default value as in the example file abovepluginDefinitions [@type [name: string]:object @optional]
- plugin definitions (see plugins)plugins @optional
- global plugins list (see plugins)environments [@type [name: string]:object @optional]
- list of environments. The name should correspond to theENV
value. Each environment entry consists of:manifest [@type string @required]
- path to configuration fileplugins
environment's plugins list (see plugins)
sourceDir [@type string @optional]
- directory of app's source code; default value as in the example file abovetempDir [@type string @optional]
- absolute path of a temporary folder used during a building process as a project directory. After build, it is removed; default value is operating system directory for temporary files
archivePath and generatedPackagePath also could be defined as string templates to dynamically generate name of the created files. Example of generatedPackagePath: 'MyApp-v${manifest.major_version}.${manifest.minor_version}.${manifest.build_version}-${args.env}.pkg'. Resolves into: 'MyApp-v1.0.0-production.pkg'. Above example will take values from given manifest file and arguments passed to the build or generate-package script.
The configuration files work in waterfall scheme meaning that baseManifest
is loaded as the first file. Each environment entry overrides base configuration. The localManifestOverride
is loaded as the last one overriding all others.
The packager contains the following scripts:
scripts/build.js
- builds the appscripts/start.js
- builds and deploys the app to the devicescripts/generate-package.js
- rekey device if needed, builds, deploys the app to the device and finally signs and download the packagescripts/prepare-for-vsc.js
- helpful when using debugging protocol in the VSC extensionscripts/screenshot.js
- takes a screenshot of the current dev application (works only with dev channel), screenshot file name -Screenshot_<Date as ISOString>.jpg
Example usage:
"scripts": {
"build-prod": "node node_modules/@dazn/kopytko-packager/scripts/build.js --env=production",
"start": "node node_modules/@dazn/kopytko-packager/scripts/start.js"
}
Available parameters:
env
- your environment value that matches entry in the .kopytkorc file. Default value (if not passed) is "dev"rokuDevPassword
- dev passwordrokuDevUser
- dev userrokuDevId
- dev id, needed to rekey the devicerokuDevSigningPassword
- dev signing password needed to rekey the device and sign packagesrokuIP
- IP of your roku devicesignedPackagePath
- path to the package of the existing app (signed package), needed to rekey the devicetelnet
- true/false, when true the packager will open the telnet session after deploying app to the deviceforceHttp
- true/false, when true all the urls in the manifest file that start with https will be overwritten to http
If an unnamed parameter is passed, it is treated as env
parameter:
npm start -- production
# is a shortcut for:
npm start -- --env=production
To avoid the necessity of passing parameter with every script run, a .env file could be used. Documentation can be found here.
Available fields: ENV
, ROKU_DEV_ID
, ROKU_DEV_PASSWORD
, ROKU_DEV_SIGNING_PASSWORD
, ROKU_DEV_USER
, ROKU_IP
, SIGNED_PACKAGE_PATH
, TELNET
, FORCE_HTTP
By running this kopytko-packager will create a signed package. This script automates Packaging Roku channels
It contains 4 steps:
- rekey device (if needed)
- build an app for a given environment
- deploy an app to the Roku device
- sign deployed app and download the generated package from the Roku device
Package will be saved under generatedPackagePath if it was provided in .kopytkorc
file,
or in /dist/kopytko_package.pkg
if generatedPackagePath was not provided.
To be able to generate the package following script arguments are required (or env variables):
- ROKU_DEV_ID
- ROKU_DEV_PASSWORD
- ROKU_DEV_SIGNING_PASSWORD
- ROKU_DEV_USER
- ROKU_IP
- SIGNED_PACKAGE_PATH
If you already have an app on your Roku channel, you will need to provide a previously signed package, to rekey the Roku device. If this is your first version of the app and you don't have ROKU_DEV_ID and ROKU_DEV_SIGNING_PASSWORD yet:
Plugin is a code triggered during the build process of a project. There are few built-in plugins, one of them is executed regardless .kopytkorc config (kopytko-generate-manifest), but you can use and create custom plugins. To get information how to configure or write your own plugin check /docs/PLUGINS.md.
Built-in plugins:
Kopytko packager dynamically generates the manifest file by transpiling JavaScript configuration files, so you don't have to keep it in the source code anymore. It is done by the kopytko-generate-manifest plugin, configured out-of-the-box.
The example default manifest file:
module.exports = {
title: 'Some Title',
bs_const: {
debug: true,
},
};
will be transpiled to a manifest
file:
title=Some Title
bs_const=debug=true
You can override @dazn/kopytko-packager/base-manifest.js
which contains all required by Roku fields and dynamically
sets app version based on your package.json.
kopytko-copy-external-dependencies
plugin lets you use external Kopytko Modules. A Kopytko Module is an NPM module
with the kopytko-module
keyword.
The plugin creates a dependency list based on your package prod dependencies (and their prod dependencies, recursively)
and direct dev dependencies (without their any dependencies). If your project in overall has dependencies
on the same module but in different versions, the plugin will automatically use only the highest version of every
major version dependency.
Once a dependency list is generated, the plugin automatically copies their /components
and /source
directories
(taking into account kopytkoModuleDir
value configured in dependencies package.json file). So far the plugin doesn't
copies neither images nor fonts directories.
You can get the list of available public Kopytko Modules here. If you want to share your Roku library with the community and create your own Kopytko Module, check the Creating Kopytko Module paragraph.
The plugin creates
Import dependencies from internal files and external Kopytko Modules using the @import
annotation.
Each of dependency will be automatically added as a </script>
entry into the scope of XML file.
Nested dependencies and copying external Kopytko Modules files to the package supported.
Add predefined, built-in kopytko-import-dependencies
and, if you use external Kopytko Modules, kopytko-copy-external-dependencies
plugins into your .kopytkorc config file. We recommend configuring kopytko-copy-external-dependencies
to be run before environment plugins
so every next plugin can manipulate dependencies code if needed:
"plugins": [
{ "name": "kopytko-copy-external-dependencies", "preEnvironmentPlugin": true },
"kopytko-import-dependencies"
],
kopytko-copy-external-dependencies
copies all (even unused in the project) files from dependency moduleskopytko-import-depenencies
translates@import
annotations and imports related dependencies in XML files
Import other internal .brs file via ' @import /<path>
at the beginning of the file.
The pkg:
prefix will be automatically added into the XML file.
Example:
' @import /components/utils.brs
' @import /source/superFunction.brs
function init()
end function
Import a .brs file from an external Kopytko Module via ' @import /<path> from <package-name>
.
Every external dependency entry will be automatically added into the XML file with a proper path to the copied file.
All functions and components definitions and usages will be prefixed with a standardized module name and version
(normalized to Brightscript's requirements). E.g. function superFunction()
from @kopytko/utils
v1 will be renamed into kopytko_utils_v1_superFunction
.
This mechanism prevents naming collisions (e.g. when an imported external module has defined a function with the same name as
one of your internal functions).
Example:
' @import /components/externalUtil.brs from @kopytko/utils
will be changed into pkg:/components/kopytko_modules/kopytko_utils_v1/externalUtils.brs
XML script entry.
Kopytko-packager imports nested external dependencies out-of-the-box.
- add
"kopytko-module"
to "keywords" field in your module's package.json file - if your
components
andsource
directories are not placed in the root directory, configure it in the"kopytkoModuleDir"
field in package.json, e.g."kopytkoModuleDir": "app/"
- be aware so far Kopytko Module supports copying only the two mentioned above directories
- if your module is designed to be used as a direct dev dependency (e.g. a module for debugging the non-production app),
and it uses some other Kopytko Modules, define them as
peerDependency
because Kopytko Packager doesn't install any dependencies of direct dev dependencies. Thanks to this, it will notify the end user to install your peer dependencies as dev dependencies too.