Skip to content

Commit

Permalink
Added PackagePacker
Browse files Browse the repository at this point in the history
  • Loading branch information
Joao Santos committed Jan 8, 2021
1 parent c0915b5 commit b6b5a89
Show file tree
Hide file tree
Showing 7 changed files with 826 additions and 5,865 deletions.
69 changes: 69 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,75 @@ To run the app, execute the following command under the `svg` directory:
yo @criticalmanufacturing/iot:fontgen
```



## packagePacker

It is not desirable to have Internet services (NPM, GitHub, etc) as a dependency for the packages that would run on a production environment.

To bypass such dependency, we provide a tool that will pack all the package dependencies from a development environment into a ready-to-be-used package.

The steps this tool executes in background are somehow complicated, but, in a nutshell, it will statically analyze all the dependencies of the package and subsequent dependencies and merge everything into a single `index.js` file. Some other dependencies, like configurations, certificates, node addons (*.node) are also added into the resulting package, however, to keep everything in a clean state, some post-processing steps are needed and this tool supports them up to some extent.

In a terminal window run:

```
yo @criticalmanufacturing/iot:packagePacker --help
```

The following parameters can be supplied:

| **Parameter** | Type | Default | Description |
| ------------- | --------- | ------------------------ | ------------------------------------------------------------ |
| i, input | `String` | `${cwd}` | Location of the package to pack (directory where the `package.json` is located) |
| o, output | `String` | | (optional) When defined, it is the directory where the `.tgz` package file will be placed |
| t, temp | `String` | `${cwd}\__TEMP__` | Temporary directory where the processed files will be placed |
| c, config | `String` | `${cwd}\packConfig.json` | Location where the file with the post-processing instructions is located |
| a, addons | `String` | | Location where the binary addons (`\*.node`) are located. Required to prepare a package that is cross-platform, cross-architecture and supporting multiple Node versions.<br />**Note**: Due to the complexity of this option, the usage is not described in this documentation and requires some support from our company |
| d, debug | `Boolean` | `false` | Activate the debug mode. This mode will not delete the temporary directory allowing the user to properly define the post-processing directives |
| v, version | `String` | | Flag that allows to override the version defined in the `package.json` into an user-defined value |

### Configuration file structure

The configuration is a .json file that identifies the type of package and declare post-packing actions to perform to organize, clean and possibly, fix some issues with the result structure.

```json
{
"type": "<Package Type>",
"postActions": [
{ "type": "<ActionType>", "parameter1": "value1", "parameter2": "value2", "...": "..." },
{ "type": "<ActionType>", "parameter1": "value1", "parameter2": "value2", "...": "..." }
]
}
```

Possible Package Types:

| Type | Description |
| -------------- | ------------------------------------------------------------ |
| `TasksPackage` | Represents a package used to contain `Tasks` and `Converters`. The result package will be ready for runtime (no internet dependencies) and for design-time (all `.js`, `.html`, `.css`, etc) required by the GUI but not required for the runtime. |
| `Component` | Represents a package that is only used for runtime (driver, etc) |

Possible Post Actions:

| Structure | Description | Example |
| ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ |
| `DeleteFile`(`source`) | Deletes the file `source` | ```{ "type": "DeleteFile", "source": "${Temp}/completion.sh.hbs" }``` |
| `DeleteDirectory`(`source`) | Deletes the directory `source` | ```{ "type": "DeleteDirectory", "source": "${Temp}/locales" }``` |
| `CopyDirectory`(`source`, `destination`) | Copies the entire directory structure from `source` into `destination` | ```{ "type": "CopyDirectory", "source": "font", "destination": "${Temp}/font" }``` |
| `CopyFile`(`file`, `source`, `destination`) | Copy the file `file` located in the directory `source` into the directory `destination` | ```{ "type": "CopyFile", "source": "${Source}/certificates/default.pem", "destination": "${Temp}/examples" }``` |
| `MoveFile`(`file`, `source`, `destination`) | Moves the file `file` located in the directory `source` into the directory `destination` | ```{ "type": "MoveFile", "file": "client_selfsigned_cert_2048.pem", "source": "${Temp}", "destination": "${Temp}/certificates" }`` |
| `ReplaceText`(`source`, `search`, `replace`, `isRegularExpression`) | In the file `source`, tried to find all occurrences of `search` and replaces them with `replace`. If `isRegularExpression` the search is expected to be a valid regular expression.<br />*Note: Make sure the `replaced` value is not captured again by the `search` value, otherwise, the process will enter into an infinite loop.* | ```{ "type": "ReplaceText", "source": "${Temp}/index.js", "search":"\"client_selfsigned_cert_2048.pem\"", "replace": "\"/../certificates/client_selfsigned_cert_2048.pem\"" }```<br />`{ "type": "ReplaceText", "source": "${Temp}/index.js", "search":"__webpack_require__\\(\\d*\\)\\('HID-hidraw.node'\\)", "replace": "require(__webpack_require__.ab + \"/../lib/hid-hidraw.node\")", "isRegularExpression": true }` |

Some tokens can be used in the Post Actions to be replaced according to the environment/command line arguments:

| Token | Description |
| ---------------- | --------------------------------------------- |
| `${Source}` | Source location (argument `i`, `input`) |
| `${Destination}` | Destination location (argument `o`, `output`) |
| `${Temp}` | Temporary location (argument `t`, `temp`) |
| `${Addons}` | Addons location (argument `a`, `addons`) |

# Development tips

If you are extending this package, it is easier to have it linked locally. Run the following command from the root directory of the package:
Expand Down
16 changes: 8 additions & 8 deletions generators/app/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,15 @@ class GeneratorApp extends ConnectIoTGenerator {

this.log(this.usage()); // .help() show all command line parameters
this.log("");
// this.log(chalk.cyan("Setup apps:"));
// this.log(" " + chalk.yellow("config") + " -> Create a configuration file to use with Automation Manager");
// this.log("");
this.log(chalk.cyan("Deployment apps:"));
this.log(" " + chalk.yellow("packagePacker") + " -> Create a self-packaged package for deployment");
this.log("");
this.log(chalk.cyan("Development apps:"));
this.log(" " + chalk.yellow("tasksPackage") + " -> Create a new basic package code for custom Tasks/Converters");
this.log(" " + chalk.yellow("task") + " -> Create the skeleton for a Task");
this.log(" " + chalk.yellow("converter") + " -> Create the skeleton for a Converter");
this.log(" " + chalk.yellow("driver") + " -> Create a basic package code for a new Protocol Driver");
this.log(" " + chalk.yellow("fontgen") + " -> Create a font based on SVG icons to use with Tasks Packages");
this.log(" " + chalk.yellow("tasksPackage") + " -> Create a new basic package code for custom Tasks/Converters");
this.log(" " + chalk.yellow("task") + " -> Create the skeleton for a Task");
this.log(" " + chalk.yellow("converter") + " -> Create the skeleton for a Converter");
this.log(" " + chalk.yellow("driver") + " -> Create a basic package code for a new Protocol Driver");
this.log(" " + chalk.yellow("fontgen") + " -> Create a font based on SVG icons to use with Tasks Packages");
this.log("");
}

Expand Down
74 changes: 74 additions & 0 deletions generators/packagePacker/configuration.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@

/** Type of package to process */
export enum ComponentType {
/** Manager, Monitor, Controller, Driver */
Component = "Component",
/** Tasks package (must process the metadata file) */
TasksPackage = "TasksPackage",
}

/** Possible action to perform */
export enum ActionType {
/** Delete file */
DeleteFile = "DeleteFile",
/** Delete directory */
DeleteDirectory = "DeleteDirectory",
/** Copy entire directory contents */
CopyDirectory = "CopyDirectory",
/** Copy single file */
CopyFile = "CopyFile",
/** Move single file */
MoveFile = "MoveFile",
/** Replace text with another one */
ReplaceText = "ReplaceText",
}

/** Configuration structure */
export interface Configuration {
/** Component Type */
type: ComponentType;
/** List of files to fully pack (will use src/index.js as default) */
packs?: Pack[];
/** Addons required for the package */
addons?: Addon[];
/** List of actions to post perform */
postActions?: Action[];
}

/** Action structure */
export interface Action {
/** Type of action */
type: ActionType;
/** Action Source */
source: string;
/** Action Destination */
destination?: string;
/** Text to search */
search?: string;
/** Text to replace with */
replace?: string;
/** File to work */
file?: string;
/** Search is regular expression */
isRegularExpression?: boolean;
}

/** Compiled version/platform specific node addon */
export interface Addon {
/** Name of the addon (also directory where it is) */
name: string;
/** Version of the addon (also the subdirectory where it is) */
version: string;
/** Extension expected (*.node) */
fileMask: string;
}

/** Files to pack (will use src/index.js is this section is missing) */
export interface Pack {
/** Relative directory where the source file is */
directory: string;
/** Source file to pack (defaults to index.js) */
source?: string;
/** Destination name to use (defaults to index.js) */
destination?: string;
}
61 changes: 61 additions & 0 deletions generators/packagePacker/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@

// this.argument('packageName', { type: String, required: true });
// console.log(args);
// console.log(opts);
// this.values.fileName = this.camelCaseValue(this.options.packageName);
// // Prepend the package suffix if not present
// if (!this.options.packageName.startsWith(`${this.ctx.packagePrefix}.`)) {
// this.options.packageName = `${this.ctx.packagePrefix}.${this.options.packageName}`;
// }

// // If this command was not executed from the root, exit
// if (this.config.get("isRoot") !== true) {
// this.env.error(new Error("Please execute this command outside a package. Hint: use the root of the repository."));
// }


import { ConnectIoTGenerator, ValueType, IoTValueType } from "../base";
import { PackagePacker } from "./packagePacker";

class GeneratorPackagePacker extends ConnectIoTGenerator {

constructor(args: any, opts: any) {
super(args, opts);

this.option("i", { alias: "input", type: String, default: process.cwd(), description: "Location of the package to pack" });
this.option("o", { alias: "output", type: String, default: "", description: "Location of the generated package will be stored" });
this.option("t", { alias: "temp", type: String, default: `${process.cwd()}\\__TEMP__`, description: "Temporary location to use" });
this.option("c", { alias: "config", type: String, default: `${process.cwd()}\\packConfig.json`, description: "Location of the Configuration to use" });
this.option("a", { alias: "addons", type: String, default: "", description: "Location of the compiled addons" });
this.option("d", { alias: "debug", type: Boolean, default: false, description: "Debug Mode (doesn't delete temporary directory after processing)" });
this.option("v", { alias: "version", type: String, default: "", description: "Version to use to generate the package" });

}

/**
* Will prompt the user for converter details
*/
// https://www.npmjs.com/package/inquirer
public async prompting() {
}

/**
* Will copy the templates for the framework tailoring all the files with the base framework it's extending from.
*/
public async copyTemplates(): Promise<void> {
const generator = new PackagePacker();
await generator.go(this.options);
}

/**
* Will install the framework's package as well as the web app.
*/
public async install() {
}

public end() {
}
}

declare var module: any;
(module).exports = GeneratorPackagePacker;
Loading

0 comments on commit b6b5a89

Please sign in to comment.