Skip to content

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

2 watching

Forks

Latest commit

 

History

38 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 

Repository files navigation

nodejs-mobile-bare-prebuilds

This repository contains shared workflows for building native modules maintained by the Holepunch team. These modules are built using bare-make.

Table of Contents

Introduction

The nodejs-mobile-bare-prebuilds repository provides a set of workflows and tools to streamline the process of building native modules for Node.js mobile applications. These workflows are designed to be used with the bare-make build system.

The build input is always the npm tarball (npm pack <module>@<version>), so the published prebuild corresponds exactly to what users get from npm install. The test input is the upstream git repo at the commit the tarball was published from — needed because the test/ folder is almost always excluded from npm tarballs.

Usage

The most common entry point is prebuild-all.yml, which builds the standard target set (three Android ABIs + iOS device + the arm64 iOS simulator), optionally runs the module's own test suite on an emulator/simulator, and publishes a GitHub Release with the artifacts.

name: Build, test, and release prebuilds

on:
  workflow_dispatch:
    inputs:
      module_version:
        description: "Exact version or dist-tag"
        required: false
        default: "latest"
        type: string

jobs:
  build:
    uses: digidem/nodejs-mobile-bare-prebuilds/.github/workflows/prebuild-all.yml@v3
    with:
      module_name: sodium-native
      module_version: ${{ inputs.module_version }}

  test-android:
    needs: build
    uses: digidem/nodejs-mobile-bare-prebuilds/.github/workflows/test-android.yml@v3
    with:
      module_spec: ${{ needs.build.outputs.module_spec }}
      test_runner: module
      git_repo_slug: ${{ needs.build.outputs.git_repo_slug }}
      git_ref: ${{ needs.build.outputs.git_ref }}
      # Skip any tests that can't run in the emulator sandbox.
      test_exclude: |
        test-spawn.js

  test-ios:
    needs: build
    uses: digidem/nodejs-mobile-bare-prebuilds/.github/workflows/test-ios.yml@v3
    with:
      module_spec: ${{ needs.build.outputs.module_spec }}
      test_runner: module
      git_repo_slug: ${{ needs.build.outputs.git_repo_slug }}
      git_ref: ${{ needs.build.outputs.git_ref }}

  release:
    needs: [ build, test-android, test-ios ]
    permissions:
      contents: write
    uses: digidem/nodejs-mobile-bare-prebuilds/.github/workflows/release.yml@v3
    with:
      module_spec: ${{ needs.build.outputs.module_spec }}

Inputs

prebuild-all.yml / prebuild.yml

Input Required Default Description
module_name yes — npm module to build
module_version no latest Exact version or dist-tag. Resolved against npm before the matrix runs.
patches_dir no patches Directory in the caller repo holding <module>+<version>.patch files (see below)
nodejs_mobile_version no 24.19.0-0 digidem/nodejs-mobile release to link against, without the leading v

test-android.yml / test-ios.yml

Input Required Default Description
module_spec yes — <module>@<version>. Pass needs.build.outputs.module_spec.
target no android-x64 / ios-arm64 Runtime prebuilds/<target>/ subdir. Android also accepts android-arm64.
artifact_name no derived from module_spec + target Override the default artifact name.
test_runner no smoke smoke / module / custom — see Testing prebuilds below.
test_script no — Caller-repo path to a test script. Only used when test_runner: custom.
test_exclude no — Newline-separated list of test file basenames to skip when test_runner: module.
git_repo_slug no — owner/repo of the module's upstream GitHub repo. Pass needs.build.outputs.git_repo_slug. Required when test_runner: module.
git_ref no — Commit SHA or tag to check out the upstream repo at. Pass needs.build.outputs.git_ref. Required when test_runner: module.
nodejs_mobile_version no v24.19.0-0 digidem/nodejs-mobile release tag the test app runs on.
harness_version no 1.0.0 Exact version of nodejs-mobile-test-harness to build and run the test app with.

release.yml

Input Required Default Description
module_spec yes — <module>@<version>. Pass needs.build.outputs.module_spec. The release tag is the version.

Outputs

prebuild-all.yml

Output Description
module_version The exact version resolved and used for all builds
module_spec <module>@<version> — pass to test-*.yml / release.yml
git_repo_slug owner/repo of the upstream GitHub repo, or empty if not on GitHub / no repository.url
git_ref Commit SHA (from gitHead) or v<version> tag, or empty if neither resolves

Manual dispatch

prebuild.yml is a thin wrapper around the composite action for single-target builds, triggerable from the Actions tab for debugging. test.yml chains prebuild → test-* for end-to-end debugging on a branch.

Testing prebuilds on an emulator / simulator

Two reusable workflows run the built .node file inside nodejs-mobile on a real emulator / simulator to catch runtime issues the prebuild step cannot (wrong page size, missing symbols, require-addon not finding the binary, etc.). The workflow writes a test.js next to the installed module and hands the directory to nodejs-mobile-test-harness, which builds it into a minimal native app, runs it on the emulator / simulator, streams the app's stdout into the workflow log, and passes/fails on the Node exit code. Use that package directly to run your own tests on nodejs-mobile outside these workflows.

Three test_runner modes, selected per test workflow:

  • smoke (default): generated test that just require()s the module. Enough to catch linker / load-time breakage without caller JS.
  • module: runs the module's own test suite inside nodejs-mobile. See below.
  • custom: uses the file at test_script (caller-repo path).

Running the module's own test suite

The module's tests are usually excluded from npm tarballs (via .npmignore or the package's files field), so the test workflows check out the upstream GitHub repo separately and overlay the tests into node_modules/<module>/ before running. Two layouts are supported:

  • directory style — test/*.{js,mjs,cjs} (each file import()ed in sorted order)
  • single-file style — test.{js,mjs,cjs} at the module root

The module's devDependencies are installed from the tarball's own package.json into node_modules/<module>/node_modules/, so whatever test runner the module declares (brittle, tape, tap, node:test, …) is available when the test files require() / import it. npm ci is used when the upstream ships a package-lock.json, npm install otherwise.

The git ref is resolved in prebuild-all.yml's resolve job:

  1. gitHead from npm metadata — recorded by npm publish and usually reliable for modern publishes. This is a commit SHA; actions/checkout accepts bare SHAs even when the commit isn't reachable from a branch (e.g. after a force-push).
  2. v<version> tag — checked via the GitHub API (authenticated with GITHUB_TOKEN). Fallback for older tarballs with missing gitHead.
  3. Empty — no git source available. test_runner: module will fail loudly at test time; use smoke instead.

Pass both git_repo_slug and git_ref from prebuild-all.yml's outputs into the test workflows when using test_runner: module. Non-GitHub upstreams are not supported.

Skipping individual tests. Some suites include tests that can't run in the emulator / simulator sandbox (process spawning, host FS paths, native OS features). Pass a newline-separated list of basenames in test_exclude to skip them:

test_exclude: |
  test-spawn.js
  test-ipc.js

Excluded files are listed as a TAP # excluded: … comment in the workflow log for visibility.

Note: the build always uses the npm tarball as its source. The git checkout is only used to populate the test files for the module test runner.

Patching the module before build

Some modules need small patches to build for nodejs-mobile — e.g. fixing an include path in CMakeLists.txt. Patches use the patch-package filename convention:

<caller-repo>/
└── patches/
    ├── quickbit-native+2.4.1.patch
    └── quickbit-native+2.4.2.patch

The prebuild step picks patches/<module_name>+<resolved_version>.patch and applies it with patch -p1 --forward --no-backup-if-mismatch after npm pack unpack and before npm install.

Failure modes:

  • Patch applies cleanly → build proceeds.
  • No <module>+*.patch files exist → no-op, build proceeds.
  • A matching file is missing but siblings for other versions exist → fail loudly. This catches the usual error of forgetting to rename the patch after bumping module_version.
  • Patch applies but with fuzz/rejects → fail loudly (--forward suppresses the interactive prompt and surfaces rejects as a non-zero exit).

To use a different directory name, pass patches_dir: to prebuild-all.yml / prebuild.yml.

Versioning and releasing

Callers pin the reusable workflows to a floating major tag:

uses: digidem/nodejs-mobile-bare-prebuilds/.github/workflows/prebuild-all.yml@v3

Internally, each reusable workflow checks this repo out at its own ref (job.workflow_sha) and runs the composite actions from that checkout, so workflow and actions always run at the same commit — whatever v3 (or a branch, for dispatches) resolves to. Never reference a composite action with an @<ref> from inside this repo; use the local path from that checkout. The test harness is the one exception: it is an npm package, pinned to an exact version by the test workflows' harness_version default.

Which major tag to pin

Tag nodejs-mobile line
v3 v24 only, from digidem/nodejs-mobile (tags vX.Y.Z-R)
v2 last line supporting upstream nodejs-mobile v18.20.4

v3 also drops the ios-x64-simulator target, which the v24 line doesn't ship.

Cutting a release

  1. Merge to main. PRs touching .github/** run the real pipeline (ci.yml: prebuild → emulator test, including the floor-API emulator), at the PR's own commit — in-flight changes are what execute, so no manual validation dispatch is needed.

  2. Tag a semver release and push it:

    git tag v3.1.0 && git push origin v3.1.0
    # or, with release notes:
    gh release create v3.1.0 --generate-notes
  3. The move-major-tag.yml workflow force-moves v3 to the new tag automatically. Callers on @v3 pick the new version up on their next run — no changes needed in the calling repos.

Breaking changes (renamed/removed inputs, changed artifact layout, new required permissions) get a new major: tag v4.0.0, which creates/moves v4; existing @v3 callers are untouched until they opt in by editing their uses: references.

To test unreleased changes end-to-end, dispatch test.yml from your branch in the Actions tab — the workflows and actions run at the branch's commit.

Contributing

We welcome contributions to this repository. If you have an idea for a new feature or have found a bug, please open an issue or submit a pull request.

License

This project is licensed under the MIT License. See the LICENSE file for more details.

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

2 watching

Forks

Releases

Packages

Used by

Contributors