Skip to content

Migrate to prebuildify? #9

@markandrus

Description

@markandrus

The README.md for prebuild-install suggest migrating to prebuildify where, instead of publishing native binaries to the GitHub release, they are instead added to the prebuilds/ directory and published inside the package itself.

I have done this on my private fork, because it was easier to get working than figuring out how to access a private repo's GitHub releases. Benefits include (1) less dependence on GitHub releases, (2) potentially faster install times, and (3) no need for install scripts. However, it would mean slightly larger NPM packages and a different release process.

Steps I took…
  1. Change the ci:prebuild script in package.json:

    "ci:prebuild": "prebuildify --runtime napi --target 16.0.0 --strip --verbose",
    
  2. Remove the install script from package.json. Alternatively, it could be kept and set to node-gyp-build, per the prebuildify docs; however, that would require publishing the binding.gyp and other files that are not currently published in order for it to work. In my case, the machine that installs @pg-nano/pg-parser isn't going to have the tools to build it from source anyway, so I skip this step.

  3. Update dependencies in package.json. bindings, prebuild, and prebuild-install can be removed. node-gyp-build can be added as a dependency, and prebuildify can be added as a dev dependency.

  4. Update files in package.json to include the prebuilds/ directory.

  5. Change src/lib/binding.ts. The README.md for prebuildify suggests using node-gyp-build to load the addon. Depending on whether feat: dual-publish ESM and CJS #8 is accepted, the "isomorphic __dirname" trick can be used to get the correct path in both CJS and ESM:

    import { dirname, join } from 'node:path'
    import { fileURLToPath } from 'node:url'
    
    import type { Node } from './ast'
    
    const _dirname = typeof __dirname !== 'undefined'
      ? __dirname
      : dirname(fileURLToPath(import.meta.url))
    
    const loadAddon = () => require('node-gyp-build')(join(_dirname, '..'))

Additionally, when building on macOS, I use the following script and Dockerfiles to build for Linux before publishing:

set -e

rm -rf prebuilds

docker rm pg-parser-builder-amd64
docker rm pg-parser-builder-arm64

cat >amd64.Dockerfile <<EOF
FROM --platform=linux/amd64 node:lts-bullseye

WORKDIR /app

COPY . .

RUN corepack enable pnpm

RUN cd libpg_query && make clean && cd ..

RUN pnpm build

RUN npx prebuildify --runtime napi --target 16.0.0 --strip --verbose

RUN node -e "import('./lib/index.js').then(({ parseIntervalSync }) => console.log(parseIntervalSync('1 year')))"
EOF

cat >arm64.Dockerfile <<EOF
FROM --platform=linux/arm64 node:lts-bullseye

WORKDIR /app

COPY . .

RUN corepack enable pnpm

RUN cd libpg_query && make clean && cd ..

RUN pnpm build

RUN npx prebuildify --runtime napi --target 16.0.0 --strip --verbose
EOF

docker build -t pg-parser-builder-amd64 -f amd64.Dockerfile .
docker build -t pg-parser-builder-arm64 -f arm64.Dockerfile .

docker create --name pg-parser-builder-amd64 pg-parser-builder-amd64
docker create --name pg-parser-builder-arm64 pg-parser-builder-arm64

docker cp pg-parser-builder-amd64:/app/prebuilds .
docker cp pg-parser-builder-arm64:/app/prebuilds .

pnpm run ci:prebuild

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions