ADR-280: Binary Management in Creator Hub

More details about this document
Latest published version:
https://adr.decentraland.org/adr/ADR-280
Authors:
cazala
Feedback:
GitHub decentraland/adr (pull requests, new issue, open issues)
Edit this documentation:
GitHub View commits View commits on githistory.xyz

Abstract

This document describes the approach for managing Node.js binaries and their execution within the Creator Hub Electron application. It outlines the challenges of distributing Node.js-based tools within an ASAR archive and presents a solution for cross-platform binary execution.

Introduction

The Creator Hub needs to run various Node.js-based tools and binaries (like @dcl/sdk-commands) to function properly. This presents several challenges in the context of an Electron application:

  1. The app needs Node.js to run these binaries
  2. NPM is required to manage and install dependencies
  3. The app is packaged in an ASAR archive for distribution
  4. Binary execution needs to work cross-platform (Windows and macOS)

ASAR Archive Requirements

ASAR (Atom Shell Archive) is crucial for our distribution process, particularly for macOS:

The PATH Environment Variable

The $PATH environment variable is fundamental to how operating systems locate executable programs:

Decision

We've implemented a multi-layered approach to handle binary execution:

Node.js Binary Management

Instead of bundling a separate Node.js binary, we utilize Electron's built-in Node.js runtime. Electron is built on Node.js, making its binary capable of running Node.js code. To make this work:

NPM and Package Management

To handle NPM and package management:

  1. The package.json and NPM binaries are kept outside the ASAR archive
  2. NPM binaries are accessed using the Node.js symlink/cmd created above
  3. The $PATH environment variable is modified in forked processes to include:

Installation Process

The installation process follows these steps:

  1. Create Node.js symlink/cmd pointing to Electron binary
  2. Set up proper $PATH environment variable
  3. Use the Node.js symlink to run NPM
  4. Install dependencies from the unpackaged package.json
  5. Track installation versions to handle updates

Running Arbitrary Binaries

To run binaries like sdk-commands:

  1. Fork a new process using Electron's utilityProcess
  2. Inject the modified $PATH containing Node.js and NPM locations
  3. Execute the binary using the forked process
  4. Provide utilities for output handling and process management

Process Monitoring

The forked processes are augmented with monitoring capabilities through a wrapper that provides:

  1. Pattern-based Output Monitoring

  2. Process Control

  3. Logging and Debugging

Example monitoring usage:

const process = run("@dcl/sdk-commands", "sdk-commands", {
  args: ["start"]
})

// Wait for specific output
await process.waitFor(/Server is listening/)

// Monitor errors
process.on(/Error:/, (error) => {
  console.error("SDK error:", error)
})

// Graceful shutdown
await process.kill()

Consequences

Positive

Negative

Risks

Implementation Details

// Example of PATH setup
PATH = joinEnvPaths(
  path.dirname(nodeCmdPath), // Node.js location
  path.dirname(npmBinPath), // NPM location
  process.env.PATH // System PATH
)

// Example of binary execution run(package, bin, args)
const child = run("@dcl/sdk-commands", "sdk-commands", {
  args: ["start"]
})

References

License

Copyright and related rights waived via CC0-1.0. Final