Aplicación de paquetes de electrones en nix.

Resuelto tobiasBora asked hace 7 meses • 0 respuestas

Históricamente, las aplicaciones electrónicas eran difíciles de empaquetar en Nix (ver este interesante meme ), pero aparentemente ya no es así .

¿Cómo puedo empaquetar una aplicación electrónica en nix y desarrollar rápidamente elementos relacionados con npm en NixOs?

tobiasBora avatar Feb 16 '24 08:02 tobiasBora
Aceptado

(Descargo de responsabilidad: no soy un experto en JS en absoluto, solo intento entender el empaquetado de electrones)

TL;DR: Si solo desea empaquetar y ya tiene un proyecto existente, puede ir directamente a la sección "hacer la parte del empaque" y copiar/pegar el .nixarchivo (bastante simple, nada demasiado mágico). Sin embargo, antes explico cómo desarrollar eficientemente en NixOs, etc. También hice un proyecto mínimo aquí con el que puedes probar:

$ nix run github:tobiasBora/basic-nix-packaging

No hay nada más que hacer cuando se usa electron-forge o una biblioteca externa, pero hice una demostración aquí:

$ nix run github:tobiasBora/basic-nix-packaging/electron-forge
$ nix run github:tobiasBora/basic-nix-packaging/using-library

Cómo desarrollarse rápidamente en NixOs

Npm/electron tiene la cultura de empaquetar archivos binarios prediseñados (lo cual no es muy bueno, por ejemplo en términos de seguridad, pero es otra cuestión), que espera un cargador en, por ejemplo, /lib64/ld-linux-x86-64.so.2. Esto puede ser un problema si utiliza NixOs, ya que NixOs elimina de forma predeterminada los cargadores para una máxima reproducibilidad (consulte más detalles y solución aquí ). Lo que recomiendo si no quieres molestarte más y simplemente seguir los tutoriales habituales utilizables en cualquier distribución estándar de Linux, es habilitar en tu configuration.nix:

programs.nix-ld.enable = true;
## If needed, you can add missing libraries here. nix-index-database is your friend to
## find the name of the package from the error message, like:
## $ nix run github:mic92/nix-index-database missinglib.so
## More details: https://github.com/nix-community/nix-index-database, you might like 
programs.nix-ld.libraries = options.programs.nix-ld.libraries.default ++ (with pkgs; [
  # put here missing libraries
]);

(Es posible que deba reiniciar para propagar correctamente las variables de entorno)

¡Con eso, puedes usarlo npmcomo en cualquier otro sistema!

Ahora, es posible que desees empaquetar tus programas de una manera limpia y pura, que puedas instalar con tu flake favorito, etc. Veamos cómo hacerlo ahora.

Crea un proyecto electrónico (nada interesante aquí)

Para este tutorial, crearemos un proyecto electrónico básico como se explica en el tutorial (como lo he nix-ldhabilitado):

$ mkdir my-electron-app && cd my-electron-app
$ nix-shell -p nodejs_latest
$ npm init # do set an author & description, and entrypoint = main.js
$ npm install --save-dev electron

luego agregue la scriptsección de package.jsonun startcomando como en:

{
  "scripts": {
    "start": "electron ."
  }
}

Luego, cree los archivos enumerados en https://www.electronjs.org/docs/latest/tutorial/quick-start , en particular:

main.html:

// main.js

// Modules to control application life and create native browser window
const { app, BrowserWindow } = require('electron')
const path = require('node:path')

const createWindow = () => {
  // Create the browser window.
  const mainWindow = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      preload: path.join(__dirname, 'preload.js')
    }
  })

  mainWindow.webContents.openDevTools();
  // and load the index.html of the app.
  mainWindow.loadFile('index.html')

  // Open the DevTools.
  // mainWindow.webContents.openDevTools()
}

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.whenReady().then(() => {
  createWindow()

  app.on('activate', () => {
    // On macOS it's common to re-create a window in the app when the
    // dock icon is clicked and there are no other windows open.
    if (BrowserWindow.getAllWindows().length === 0) createWindow()
  })
})

// Quit when all windows are closed, except on macOS. There, it's common
// for applications and their menu bar to stay active until the user quits
// explicitly with Cmd + Q.
app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') app.quit()
})

// In this file you can include the rest of your app's specific main process
// code. You can also put them in separate files and require them here.

index.html:

<!--index.html-->

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
    <meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'">
    <title>Hello World!</title>
  </head>
  <body>
    <h1>Hello World!</h1>
    We are using Node.js <span id="node-version"></span>,
    Chromium <span id="chrome-version"></span>,
    and Electron <span id="electron-version"></span>.
  </body>
</html>

y preload.js:

// preload.js

// All the Node.js APIs are available in the preload process.
// It has the same sandbox as a Chrome extension.
window.addEventListener('DOMContentLoaded', () => {
  const replaceText = (selector, text) => {
    const element = document.getElementById(selector)
    if (element) element.innerText = text
  }

  for (const dependency of ['chrome', 'node', 'electron']) {
    replaceText(`${dependency}-version`, process.versions[dependency])
    /* replaceText(`${dependency}-version`, process.versions[dependency]) */
  }
})

Ejecute el proyecto (opcional)

Si sólo desea empaquetar sin probar "interactivamente" su software, puede omitir esta sección.

Método 1 : si no lo tiene nix-ldinstalado o desea maximizar la pureza, es posible que desee utilizar electronel paquete NixOs. Simplemente ingrese a un shell con:

$ nix-shell -p electron
$ electron .

y comenzará su proyecto. Más tarde, una vez que creamos un default.nix, puedes simplemente ejecutar:

$ nix-shell
$ electron .

y automáticamente instalará electron (también funciona si estás usando bibliotecas externas)

Método 2 : Por otro lado, si ejecutas:

$ npm start

Intentará ejecutar el electrón instalado en npm. Si ha nix-ldinstalado todas las bibliotecas, debería funcionar. Si recibe un error como:

XXX: no such file or directory

o un error que dice que no puede ejecutar binarios en NixO, entonces probablemente significa que ejecuta NixO y no lo tiene nix-ldhabilitado (consulte la sección `Cómo desarrollar rápidamente en NixO, asegúrese de reiniciar después de eso). Si obtienes en su lugar:

/tmp/electrontest/my-electron-app/node_modules/electron/dist/electron: error while loading shared libraries: libdrm.so.2: cannot open shared object file: No such file or directory

entonces significa que necesita instalar en nix-ldel paquete que proporciona libdrm.so.2:

$ nix run github:mic92/nix-index-database -- libdrm.so.2 --top-level
xorg_sys_opengl.out                                   0 s /nix/store/br48s8nkd9d2y2qxzd52v9rsqhh5zrl1-xorg-sys-opengl-3/lib/libdrm.so.2
xorg_sys_opengl.out                                   0 s /nix/store/br48s8nkd9d2y2qxzd52v9rsqhh5zrl1-xorg-sys-opengl-3/lib/libdrm.so.2.4.0
libdrm.out                                            0 s /nix/store/lmqz8wx07avf4c5d0qqf0h5hwjni9yrj-libdrm-2.4.120/lib/libdrm.so.2
libdrm.out                                      110,352 x /nix/store/lmqz8wx07avf4c5d0qqf0h5hwjni9yrj-libdrm-2.4.120/lib/libdrm.so.2.4.0

Aquí ves que quieres la biblioteca libdrm. Simplemente agréguelo programs.nix-ld.enablecomo se muestra arriba y continúe con otras bibliotecas (en mi prueba no necesité reiniciar en KDE Plasma, pero mi configuración es un poco diferente (versión anterior), pero si ve que sus cambios no se tienen en cuenta , es posible que necesites reiniciar). Puede que sea un poco molesto la primera vez, pero una vez que la lista sea lo suficientemente larga, debería funcionar para la mayoría de los programas (tal vez podríamos proponer un conjunto más completo de opciones en nixpkgs). En mi caso, particularmente necesitaba agregar:

      libdrm
      mesa
      libxkbcommon

(pero ya hice una lista bastante larga que describo aquí )

Hacer el embalaje (parte interesante)

Para empaquetar esto, simplemente cree este archivo llamado, por ejemplo package.nix:

# Inspired by pkgs/applications/editors/uivonim/default.nix
# and pkgs/by-name/in/indiepass-desktop/package.nix
{ lib, buildNpmPackage, fetchFromGitHub, electron }:

buildNpmPackage rec {
  pname = "my-electron-app";
  version = "0.1";

  src = ./.;

  npmDepsHash = ""; # you will get an error about mismatching hash the first time. Just copy the hash here

  # Useful for debugging, just run "nix-shell" and then "electron ."
  nativeBuildInputs = [
    electron
  ];

  # Otherwise it will try to run a build phase (via npm build) that we don't have or need, with an error:
  # Missing script: "build"
  # This method is used in pkgs/by-name/in/indiepass-desktop/package.nix
  dontNpmBuild = true;

  # Needed, otherwise you will get an error:
  # RequestError: getaddrinfo EAI_AGAIN github.com
  env = {
    ELECTRON_SKIP_BINARY_DOWNLOAD = 1;
  };
  
  # The node_modules/XXX is such that XXX is the "name" in package.json
  # The path might differ, for instance in electron-forge you need build/main/main.js
  postInstall = ''
    makeWrapper ${electron}/bin/electron $out/bin/${pname} \
      --add-flags $out/lib/node_modules/${pname}/main.js
  '';

}

y un archivo default.nixque contiene:

{ pkgs ? import <nixpkgs> {} }:
pkgs.callPackage ./package.nix {}

Construya y ejecute con:

$ nix-build
$ ./result/bin/my-electron-app

(Necesitará actualizar el hash en el código anterior, ya que obtendrá un error la primera vez con el hash válido)

¡Disfrutar!

ingrese la descripción de la imagen aquí

También puedes escribir:

$ nix-shell

y obtendrás un shell con electron instalado para que puedas depurar con:

$ electron .

Alternativa: usar forja electrónica

electron-forge es la solución recomendada oficialmente para empaquetar aplicaciones electrónicas. Puedes preparar tu proyecto de la siguiente manera:

$ npm install --save-dev @electron-forge/cli
$ npx electron-forge import

En teoría, si desea empaquetar un .debo .rpmy tenerlo nix-ldhabilitado, debería poder hacer:

$ nix-shell -p dpkg fakeroot rpm
$ npm run make

pero no sé por qué se queja con un error: file "../../../../../../../nix/store/pryizz83lb9hvjknaqyl5f54d5bai3xd-my-electron-app-0.1" links out of the package.

No importa, el objetivo es empaquetarlo para nada ahora. Y buena suerte: no tienes nada más que hacer, el script anterior también sirve para este (avísame si para casos más complejos falla).

Puedes probar esta versión también en el mismo repositorio en la electron-forgesucursal:

$ nix run github:tobiasBora/basic-nix-packaging/electron-forge

Hilo

Si usa hilo, también puede usar mkYarnPackage, no lo he probado pero espero que sea similar (incluso si escuché que el hilo no era tan bueno como npm para empaquetar en nix).

Lecturas adicionales

Aquí hay algunos otros tutoriales relacionados que escribí:

  • Más detalles sobre cómo crear y probar su derivación https://unix.stackexchange.com/questions/717168/how-to-package-my-software-in-nix-or-write-my-own-package-derivation- para-nixpkgs

Otros recursos relacionados:

  • https://github.com/NixOS/nixpkgs/issues/46382
  • En nixpkgs, instale ripgrep, ejecútelo rg "buildNpmPackage" $(rg "electron" -l)y verá todos los archivos que contienen buildNpmPackagey electron: útil para obtener algunos ejemplos. Si solo desea leer los paquetes más simples, puede ver la longitud de los archivos usando for x in $(rg "buildNpmPackage" $(rg "electron" -l) -l); do echo "$x"; cat "$x" | wc -l; done. También tienes algunos ejemplos usando hilo (usa for x in $(rg "mkYarnPackage" $(rg "electron" -l) -l); do echo "$x"; cat "$x" | wc -l; done)
tobiasBora avatar Feb 16 '2024 01:02 tobiasBora