rokucommunity / roku-deploy

An npm module for zipping and deploying to Roku devices.
MIT License
41 stars 17 forks source link

Integration with BrighterScript? #111

Open taschmidt opened 1 year ago

taschmidt commented 1 year ago

I'm working on a project where I'm using BrighterScript. Maybe I'm missing something here but I'm trying to use roku-deploy to create my signed production package. During my development workflow, I use BrighterScript either by using a vscode build task to call bsc directly to build and deploy it or, if I need to debug, I use the vscode extension process here.

When it comes time to create a signed package, I'd like to simply use deployAndSignPackage but that doesn't do the bsc transpiling. I know I can manually do a bsc build and THEN call zipPackage, publish, and then signExistingPackage but is there a better way?

EDIT: I think even the method I laid out above wouldn't work for me. As part of my release I would like to increment the manifest build number but it looks like that's only called from createPackage but that will do a full build with BS untranspiled code. :(

TwitchBronBron commented 1 year ago

Unfortunately, there's not a nice way to integrate these. Currently you need to run bsc to put the code into a staging folder, then use roku-deploy to build the signed package, just like you described. I don't want to build dependencies on brighterscript into roku-deploy directly. However, I'd definitely be open to finding a way to make this flow easier.

Option 1: granular roku-deploy cli

For a while now, we've wanted to improve the roku-deploy cli. Breaking out all of its various operations into individual cli commands. Something like this:

roku-deploy zip --outFile app.zip --rootDir ./src
roku-deploy deploy --file app.zip
roku-deploy sign --file app.zip --outFile app.pkg

With something like this, you could do the following:

bsc --rootDir ./src --stagingDir ./dist 
roku-deploy zip --outFile app.zip --rootDir ./src
roku-deploy sign --file app.zip --outFile app.pkg

Option 2: brighterscript plugin

BrighterScript has a plugin system, so you could write a plugin to run roku-deploy as part of the bsc flow. Perhaps something like this: (untested, just to get the general point across).

import { Program, CompilerPlugin } from 'brighterscript';
import { rokuDeploy } from 'roku-deploy';
export default function () {
    return {
        name: 'sign-package',
        // post-parsing validation
        afterProgramTranspile: async (program: Program) => {
            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 (error) {
                //it failed
                console.error(error);
            });
        }
    } as CompilerPlugin;
};
taschmidt commented 1 year ago

Thanks for the quick response! It seemed like deployAndSignPackage wouldn't work for me since it also wants to copy everything to the staging dir (thus overwriting my transpiled and version-incremented code).

This seems to be working for me for now. I created a couple gulp tasks like this:

export async function setVersionNumber() {
    const options = rokuDeploy.getOptions();
    const manifestPath = `${options.stagingDir}/manifest`
    const manifest = await rokuDeploy.parseManifest(manifestPath);
    manifest.build_version = dateFormat(new Date(), "yymmddHHMM");
    await fsPromises.writeFile(manifestPath, rokuDeploy.stringifyManifest(manifest));
}

export async function signPackage() {
    const options = rokuDeploy.getOptions();

    console.log('Zipping...');
    await rokuDeploy.zipPackage(options);
    console.log('Publishing to Roku...');
    await rokuDeploy.publish(options);
    console.log('Signing package...');
    const pkgPath = await rokuDeploy.signExistingPackage({
        ...options,
        signingPassword: process.env.ROKU_SIGNING_PASSWORD
    });

    console.log('Retrieving package...');
    const localPath = await rokuDeploy.retrieveSignedPackage(pkgPath, options);
    console.log(`Package created at ${localPath}`);
}

Then I have these scripts defined in my package.json:

    "build": "bsc --create-package false",
    "clean": "rimraf out dist",
    "deploy": "bsc --deploy --host roku.lan",
    "lint": "npm run build -- --copy-to-staging false",
    "release": "run-s rebuild sign",
    "sign": "gulp --series setVersionNumber signPackage",
    "rebuild": "run-s clean build"

So now I can just run npm run release when I want to build a production package. Might not be the most elegant but it seems to work 🤷

On a side note: a huge thank you for your work on this and BrighterScript! Really, tools like these are the only things that make doing Roku development bearable!

taschmidt commented 1 year ago

The more I think of it, the more I like your idea of a bsc plugin. I could have one for setting the version number and one for signing. In theory I could have a separate bsconfig.prod.json which adds those plugins only when doing a prod build.

taschmidt commented 1 year ago

@TwitchBronBron it's not immediately clear to me how to change the contents of the manifest. Is this possible or do I just have to do a brute force file rewrite after transpiling is done?

TwitchBronBron commented 1 year ago

We don't have a way to modify the manifest in BrighterScript at the moment. That should be possible once we land the file API PR in BrighterScript, but that might still be a month or two out. In the mean time, you're correct yet again: change it after it's been written to.the stagingDir