web-dev-qa-db-fra.com

Construire automatiquement le module NPM lors de l'installation à partir de Github

Étant donné que le répertoire lib/ d'un projet ne doit pas être archivé dans Git car les fichiers qu'il contient sont des fichiers dérivés (issus du processus de construction). Lors de l'installation d'un package à partir du github du projet (pendant le développement, par exemple), le répertoire lib/ n'existera pas. Par conséquent, le champ main du package package.json pointe vers (par exemple) lib/index.js, il ne peut pas être compilé s'il est importé, car ces fichiers n'existent pas. le référentiel et donc dans le package installé dans node_modules. Cela signifie que le paquet doit être construit (comme avant la version), mais cette fois localement pour que le répertoire lib (ou tout autre fichier généré pendant le processus de construction) soit ajouté au répertoire du module.

En supposant qu'il y ait un script build dans le champ scripts du fichier package.json, le paquet peut-il être configuré pour l'exécuter automatiquement dans le cas où il est installé à partir de github uniquement? Si ce n’est pas le cas, quelle est la meilleure approche pour s’assurer qu’il est construit une fois installé à partir de github?

Il existe désormais des hooks de cycle de vie prepublish, prepublishOnly et prepare, mais aucun ne fournit de réponse à ce problème car ils ne permettent en aucun cas de différencier la source de l'installation. En bref, oui, ils vous permettent de construire sur l'installation, mais ils ne vous permettent pas de construire uniquement à partir de github. Il n'y a pas seulement aucune raison de forcer une construction lorsque les gens installent à partir de npm, mais plus important encore, les dépendances de développement ne seront pas installées (par exemple, babel qui est essentiel à la construction).

Je suis au courant d'une stratégie pour faire face à ce problème:

  • Fourchette/branche le repo
  • construire localement
  • supprimez le répertoire lib/ de .gitignore et archivez-le.
  • installer le module à partir de votre fourche/branche
  • Lorsque vous êtes prêt pour un PR/une base, ajoutez lib/ dir à .gitignore et supprimez dir de git.

Mais c'est loin d'être idéal. Je suppose que cela pourrait être automatisé avec un githook cependant. Ainsi, tous les Push pour maîtriser le projet sont également générés et transmis à une branche distincte.

Il y a un numéro fermé sur le Github de NPM sans solution - il y a beaucoup de gens qui veulent une solution. De ce problème, il est clair que l'utilisation de prepare n'est pas la solution.

Mon cas d'utilisation est que je développe un module qui est utilisé dans un certain nombre d'autres projets. Je souhaite utiliser la dernière version du module sans avoir à envoyer une publication au NPM chaque fois que je mets à jour le code - je préférerais utiliser moins de versions lorsque je serai prêt, mais je devrai toujours utiliser la dernière version de la lib est sur Github.

Remarque: j'ai également sollicité l'assistance de NPM pour résoudre ce problème et je vais ajouter leur réponse si j'en reçois une.

36
Undistraction

Edit: Détecter si le paquet est en cours d'installation à partir de git repo

Je n'ai pas bien compris la question. Vous trouverez ci-dessous des choses que j'ai écrites mais qui sont un peu hors sujet. Pour l'instant, si vous souhaitez exécuter build.js uniquement lors de l'installation à partir du référentiel:

Fichiers en repo:

 .gitignore
 .npmignore
 ThisIsNpmPackage
 build.js
 package.json

Le .gitginore:

ThisIsNpmPackage

Le .npmignore:

!ThisIsNpmPackage

Dans le package.json:

"scripts": {
    "install": "( [ ! -f ./ThisIsNpmPackage ] && [ ! -f ./AlreadyInstalled ] && echo \"\" > ./AlreadyInstalled && npm install . && node ./build.js ) || echo \"SKIP: NON GIT SOURCE\""
}

L'idée est de rendre le fichier ThisIsNpmPackage disponible sur le référentiel, mais pas dans le package npm.

Install hook C'est juste un morceau de script bashy pour vérifier si ThisIsNpmPackage existe. Si oui, alors on exécute npm install . _ (cela nous assurera que devDependencies. fichier AlreadyInstalled est généré pour éviter les boucles infinies (npm install invoquera de manière récursive le crochet d’installation)

Quand je publie, je fais git Push et npm publish
Notez que la publication par npm peut être automatisée via des outils de CI - githooks

Ce petit hack avec le fichier ThisIsNpmPackage rend la détection de source disponible.

Résultats de l'appel de npm install dumb-package:

"SKIP: SOURCE NON GIT"

Et en exécutant npm install https://github.com/styczynski/dumb-package

Les fichiers seront construits

Les problèmes

Les principaux problèmes auxquels nous sommes confrontés sont les suivants:

  • Devoir faire npm publish ... à chaque fois

    Parfois, il est trop pénible de corriger un petit bogue, puis de cliquer sur Push pour le dépôt et oublier de publier sur npm. Lorsque je travaillais avec un projet basé sur microservices comprenant environ 5 sous-projets autonomes divisés en modules, le problème qui se posait était de trouver un problème, de le réparer et d’oublier de publier dans tous les endroits où je devais être vraiment ennuyeux.

  • Vous ne voulez pas insérer lib dans le dépôt, car il provient de sources

  • Rebaser et fusionner est encore plus ennuyant.

  • Pas de dégâts avec .gitgnore

    Zut, je connais ce problème quand vous avez des fichiers gênants que vous devez inclure dans le repo mais ne jamais les modifier, ou parfois les supprimer? C'est juste malade.

Edit: npm hooks

Comme @Roy Tinker l'a mentionné, un package peut exécuter une commande lorsqu'il est installé.
Cela peut être réalisé avec des hooks npm.

"install": "npm run build"

Et nous exécutons le:

npm install https://github.com/<user>/<package>

Modifier:
OP question de commentaires:

Mais cela lancera une installation pour tous ceux qui téléchargeront le module à partir de npm, n'est-ce pas? Cela pose énormément de problèmes étant donné que les dépendances dev ne seront pas installées pour les personnes téléchargeant le module à partir de npm. Les bibliothèques utilisées pour créer l'application - babel, etc. ne seront pas installées.

Remarque: Mais si vous souhaitez une version spécifique du package (production/dev) avec ou sans dépendances dev vous pouvez l’installer via:

npm install --only=dev

L'argument --only = {prod [uction] | dev [elopment]} ne provoque l'installation que de devDependencies ou que de non-devDependencies quel que soit le NODE_ENV.

Une meilleure solution, à mon avis, consiste à utiliser:

npm install <git remote url>

Et puis à l'intérieur de package.json, spécifiez:

"scripts": {
    "prepare": "npm run build"
}

Si le paquet en cours d'installation contient un script de préparation, ses dépendances et devDependencies seront installés et le script de préparation sera exécuté avant que le paquet ne soit empaqueté et installé.

Exemple:

npm install git+https://[email protected]/npm/npm.git

Lisez les documents npm à cet endroit: npm install

Edit: module proxy (technique avancée)

C'est un peu une mauvaise pratique, mais bon à savoir.

Parfois (comme dans le cas de Electron framework vous devez installer d'autres packages externes ou ressources/modules en fonction de diverses conditions).

Dans ces cas, l'idée de proxy est utilisée:

  • Vous créez un module qui se comporte comme un programme d'installation et installe tout ce dont vous avez besoin

Dans votre cas , préparer un script sera suffisant, mais je laisse cette option, car elle peut parfois être utile.

L'idée est que vous écrivez un module et écrivez un installez kook pour cela:

"scripts": {
    "install": "<do the install>"
}

Dans ce scénario, vous pouvez y placer:

npm install . && npm run build

De toute façon, qui installe toutes les devDependencies (comme nous l’avons déjà préparé,), mais c’est un peu de piratage informatique.

Si vous voulez faire le réel piratage là:

 "scripts": {
    "install": "curl -L -J -O \"<some_url>\""
 }

qui télécharge manuellement des fichiers en utilisant la commande -nix curl

Cela devrait être évité, mais c'est une option dans le cas où le module contient d'énormes fichiers binaires pour chaque plate-forme et que vous ne voulez pas tous les installer.

Comme dans le cas de Electron où vous avez compilé des fichiers binaires (chacun pour la plate-forme séparée)

Donc, vous voulez que les gens fassent install package ne pas install package-linux ou package-window etc.

Vous fournissez donc un script personnalisé install dans le fichier package.json

{
  ...
  "scripts": {
     "install": "node ./install_platform_dep.js"
  }
}

Ensuite, lors de l’installation de module, le install_platform_dep.js le script sera exécuté. À l'intérieur install_platform_dep.js vous placez:

// For Windows...
if(process.platform === 'win32') {
    // Trigger Windows module installation
    exec('npm install fancy-module-windows', (err, stdout, stderr) => {
         // Some error handling...
    }
} else if ... // Process other OS'es

Et cela de manière purement manuelle installe tout.

Remarque: Une fois encore, cette approche est utilisable avec des modules dépendant de la plate-forme. Si vous l'utilisez, il s'agit probablement d'un problème de conception lié à votre code.

Construire sur CI

Ce qui me vient à l’esprit, c’est la solution que j’ai vraiment utilisée pendant longtemps (construction automatique avec services CI).

Le but principal des services de CI est de tester/construire/publier votre code lorsque vous appuyez sur la branche ou effectuez d'autres actions avec le référentiel.

L'idée est que vous fournissiez un fichier de paramètres (comme travis.yml ou . Gitlab-ci.yml) et les outils s'occupent du reste.

Si vous vraiment ne voulez pas inclure la bibliothèque dans le projet, faites simplement confiance à CI pour tout faire:

  • Githook déclenchera la construction sur commit (sur une branche ou une autre - c'est juste une question de configuration)
  • CI construira vos fichiers puis les passera en phase de test et les publiera

Maintenant, je travaille sur Gitlab sur mon propre projet en faisant (en tant que passe-temps) une page Web. La configuration Gitlab qui construit le projet se présente comme suit:

image: tetraweb/php

cache:
  untracked: true
  paths:
    - public_html/
    - node_modules/

before_script:
  - apt-get update

stages:
  - build
  - test
  - deploy

build_product:
  stage: build
  script:
    - npm run test

build_product:
  stage: build
  script:
    - npm run build

deploy_product:
  stage: deploy
  script:
    - npm run deploy

Lorsque je fusionne dans la branche principale, les événements suivants se produisent:

  • CI exécute build stage
  • Si la construction réussit, alors l'étape test est lancée
  • Si test phase est ok, alors finalement l'étape deploy est déclenchée

Le script est la liste des commandes unix à exécuter.

Vous pouvez spécifier n'importe quelle image Docker dans la configuration, utilisez donc toute version Unix de votre choix avec certains outils préinstallés (ou non).

Il existe un package deploy-to-git qui déploie des artefacts dans la branche de référentiel souhaitée.

Ou ici (pour Travis CI) le morceau de config qui publie des artefacts dans le repo:

travis-publish-to-git

(Je l'ai utilisé par moi-même)

Ensuite, bien sûr, vous pouvez laisser CI s'exécuter:

npm publish .

Parce que CI exécute des commandes Unix, il peut alors (au moins un groupe de fournisseurs de CI):

  • Publier des tags (release tag peut-être?)
  • Script de déclenchement pour mettre à jour la version du projet dans tous les fichiers README et partout
  • Vous envoyer une notification si toutes les phases ont réussi

Donc ce que je fais:
Je commets, poussez et laissez les outils faire tout ce que je veux.
Entre-temps, j'effectue d'autres modifications et, après une à dix minutes, je reçois le rapport de mise à jour par courrier.

Il y a beaucoup de fournisseur de CI là-bas:

Je joins ici un autre exemple de mon autre projet (. Travis.yml):

language: generic
install:
    - npm install
script:
    - chmod u+x ./utest.sh 
    - chmod u+x ./self_test/autodetection_cli/someprogram.sh
    - cd self_test && bash ../utest.sh --ttools stime --tno-spinner

Si vous configurez CI pour Push et publiez votre package, vous pouvez toujours être sûr d'utiliser la dernière version de votre code sans vous soucier de eh. Je dois aussi exécuter cette commande maintenant. . problème.

Je vous recommande de choisir l’un des prestataires de services de CI sur le marché.
Les meilleurs vous offrent des centaines de capacités!

Lorsque vous vous habituerez à effectuer automatiquement les phases de publication, de test et de construction, vous verrez à quel point il est utile de profiter de la vie!
Ensuite, pour démarrer un autre projet avec des scripts automatiques, copiez simplement les configurations!

Sommaire

À mon avis , le script de préparation de npm est une option.
Vous pouvez aussi vouloir essayer d’autres.

Chacune des méthodes décrites présente des inconvénients et peut être utilisée en fonction de vos objectifs.
Je veux juste proposer quelques alternatives en espérant que certaines d’entre elles conviendront à votre problème!

16
Piotr Styczyński

En supposant qu'il y ait un script build dans le champ des scripts du fichier package.json, le package peut-il être configuré pour l'exécuter automatiquement dans cette situation?

Oui. Il y a 2 choses que vous devez faire:

  1. Assurez-vous que votre système utilise npm ou yarn pour installer le paquet à partir de GitHub. Si ce paquet est une dépendance d'un autre paquet, vous pouvez utiliser l'URL GitHub à la place du numéro de version dans package.json. Sinon, la commande suivante fonctionnera:

    npm install https://github.com/YourUser/your-package
    

    Vous pouvez ajouter /tags/v1.0.0 Ou autre chose à la fin de l'URL si vous recherchez une balise ou une branche spécifique.

  2. Ajoutez ce qui suit au scripts dans le module package.json De votre module:

    "install": "npm run build"
    

install est un crochet que le gestionnaire de paquets exécute après l'installation du module. (preinstall et postinstall également - voir la documentation).

Documentation: https://docs.npmjs.com/misc/scripts

3
Roy Tinker

Édité 2

C'est une bonne question. Dommage qu'il n'y ait pas de solution fiable reconnue, mais ce qui suit semble fonctionner.

Créez un fichier marqueur .buildme Et validez-le avec git.

Dans package.json:

  "files": ["lib"],
  "scripts": {
    "build": "echo DO WHAT YOU NEED TO BUILD",
    "prepack": "[ ! -f .buildme ] || npm run build",
    "preinstall": "[ ! -f .buildme ] || npm run build"
  },

Voici les choses à noter.

  1. Le fichier marqueur spécial .buildme Doit être exclu du paquet npm avec la touche "files" Ou via .npmignore.

  2. Le hook prepack s'exécute lorsque vous publiez (prepublishOnly pourrait également fonctionner, mais c'est bien qu'avec prepack, npm pack Produise une archive correcte).

  3. Lors de l'installation à partir de npm, preinstall s'exécute, mais ne fait rien car .buildme Est manquant (grâce à la clause [ ! -f .buildme ]).

  4. Lors de l'installation à partir de github, .buildme Existe. Sur npm6, prepack hook exécute la construction (et produit un paquet sans .buildme), Et preinstall ne fait rien. Sur le fil 1.12, preinstall fait le build.

  5. Si vous installez une version mise à jour à partir de github, preinstall sera exécuté à nouveau et compilé à nouveau.

NOTE: Lors de l’installation à partir de github, il appartient à la personne qui s’installe d’avoir assez de devDependencies de votre paquet déjà installé pour que la compilation fonctionne. (Cette solution ne tente pas d'installer automatiquement devDependencies.)

C'est ça. Il semble fonctionner avec différentes combinaisons de npm 6 et du fil 1.12.

2
DS.

prepare est la bonne façon

Si vous avez un référentiel avec des fichiers source mais qu'une étape de "compilation" est nécessaire pour l'utiliser, prepare fait exactement ce que vous voulez dans tous les cas (à partir de npm 4).

prepare: Exécutez les deux AVANT que le paquet ne soit compressé et publié, sur le répertoire local npm install sans aucun argument et lors de l'installation de dépendances git.

Vous pouvez même mettre vos dépendances de construction dans devDependencies et elles seront installées avant que prepare ne soit exécuté.

Voici n exemple d'un paquet de la mienne qui utilise cette méthode.


Problèmes avec .gitignore

Il y a un problème avec cette option qui attire beaucoup de gens. Lors de la préparation d'une dépendance, Npm et Yarn uniquement conservent les fichiers répertoriés dans la section files de package.json. .

On pourrait voir que files par défaut pour tous les fichiers inclus et penser qu'ils sont terminés. Ce qui est facilement oublié, c’est que .npmignore généralement annule la directive files et , si .npmignore n’existe pas, .gitignore est utilisé à la place.

Donc, si vos fichiers construits sont listés dans .gitignore Comme une personne sensée, ne les listez pas dans files, et n’utilisez pas de fichier .npmignore, prepare sera semble cassé .

Si vous corrigez files pour inclure uniquement les fichiers construits ou ajoutez un .npmignore Vide, vous êtes tous ensemble.

2
Cameron Tacklind