Where did my NodeJS executable go?

Hello, I am trying to build a snap for Meerkat DSA (not trying to self-promote; just giving the link in case it is useful to view relevant files). This project is a part of an Nx monorepo, so the build process is a little less friendly to the Snapcraft process. I have to build the project and all its dependencies, then I have a bundled Javascript file, a package.json, an assets folder, etc. Once I have this, I have to install the production-only Meerkat-only dependencies. After I initialize the SQLite database, it should be good-to-execute. It does work locally.

I tried to get this to work in a Snapcraft snap using the npm plugin and using strict confinement. My problem is that the node executable seems to be missing in the final snap and I don’t get why.

I looked at wethr as an example, and in that snap, the node executable ends up right at $SNAP/bin along with the actual executable itself, but that does not happen in my case, and I don’t see what I am doing that would remove the file.

Any ideas of what would cause this?

My current snapcraft:

name: meerkat-dsa
title: Meerkat DSA
license: MIT
type: app
base: core22
version: '4.0.0'
summary: Meerkat X.500 Directory System Agent (DSA) / LDAP Server
description: |
  Meerkat DSA is an X.500 Directory System Agent (DSA) that speaks the
  Directory Access Protocol (DAP) and other protocols described in the
  International Telecommunications Union's X.500 series of specifications. It
  also supports Lightweight Directory Access Protocol (LDAP). It is written in
  TypeScript, runs on NodeJS, and uses SQLite as a backing data store.

  It supports almost every X.500 feature, including shadowing, chaining,
  request and response signing, service administration, hierarchical groups,
  compound entries, zonal matching, cross references, contexts, non-specific
  hierarchical operational bindings, dynamic entries, attribute certificates,
  all defined access control schemes, and almost all schema defined in the X.500
  series and in most LDAP servers.

grade: stable
confinement: strict
contact:
  - jonathan@wilbur.space
source-code: https://github.com/Wildboar-Software/directory
website: https://wildboar-software.github.io/directory/
issues: https://github.com/Wildboar-Software/directory/issues
lint:
  ignore:
    - metadata:
      - donation
icon: images/Wildboar.png
# TODO: system-usernames
apps:
  meerkat-dsa-app:
    daemon: simple
    plugs: [network, network-bind]
    command: main.mjs
    start-timeout: 20s
    stop-timeout: 20s
    restart-delay: 10s
    restart-condition: on-failure
    environment:
      DATABASE_URL: file:$SNAP_DATA/meerkat.db
      MEERKAT_IDM_PORT: "1389"

parts:
  meerkat-dsa:
    plugin: npm
    npm-include-node: true
    npm-node-version: 25.2.1
    source-type: local
    source: .
    build-environment:
      - CI: "true"
      - FORCE_COLOR: "0"
      - NODE_OPTIONS: "--trace-warnings"
      - NX_DAEMON: "false"
    override-build: |
      cp .npmrc /root
      craftctl default
      npm ci --include=dev --no-audit --no-fund --no-save
      npx -q nx --tuiAutoExit=true --outputStyle=static --parallel=1 run meerkat:build:production --skipNxCache --skipRemoteCache --skip-nx-cache --verbose
      cp .npmrc dist/apps/meerkat
      cd dist/apps/meerkat
      npm ci --only=production --no-audit --no-fund --no-save
      npm install --no-package-lock --no-save prisma
      export DATABASE_URL=file:$SNAP_DATA/meerkat.db
      npx -q --verbose prisma migrate deploy --schema ./prisma/schema.prisma --config ./prisma/prisma.config.ts
      cp main.js $SNAPCRAFT_PART_INSTALL/main.mjs
      cp -r assets $SNAPCRAFT_PART_INSTALL/assets
      cp package.json $SNAPCRAFT_PART_INSTALL/
      chmod a+x $SNAPCRAFT_PART_INSTALL/main.mjs
    stage-packages:
      - libatomic1
    stage:
      - main.mjs
      - assets
      - package.json

Also, when I run ls $SNAP, I see:

assets  main.mjs  meta  package.json

I don’t really know how meta got there either. The wethr snap doesn’t have a meta/gui like mine does.

The problem comes from your list of stage files, which is excluding lots of stuff, including bin/node.

Under the hood, the node binary gets pulled in during the build step, as part of craftctl default in your override-build script, but it is then not being carried forward to subsequent steps.

That does seem to have worked. Thank you!