Datenintelligenz von Platon.
Vertikale Suche & KI.

Erstellen Sie mit Node.js eine JavaScript-Befehlszeilenschnittstelle (CLI)

Datum:

So großartig Node.js für „traditionelle“ Webanwendungen ist, seine Einsatzmöglichkeiten sind weitaus breiter. Microservices, REST-APIs, Tools, die Arbeit mit dem Internet der Dinge und sogar Desktop-Anwendungen: Es hält Ihnen den Rücken frei.

Ein weiterer Bereich, in dem Node.js wirklich nützlich ist, ist das Erstellen von Befehlszeilenanwendungen – und genau das werden wir in diesem Artikel tun. Wir beginnen damit, uns eine Reihe von Paketen von Drittanbietern anzusehen, die entwickelt wurden, um die Arbeit mit der Befehlszeile zu unterstützen, und bauen dann ein Beispiel aus der realen Welt von Grund auf neu.

Was wir bauen werden, ist ein Tool zum Initialisieren eines Git-Repositorys. Klar, es läuft git init unter der Haube, aber es wird mehr als nur das tun. Es wird auch direkt von der Befehlszeile aus ein Remote-Repository auf GitHub erstellen, mit dem der Benutzer interaktiv eine .gitignore Datei, und führen Sie schließlich ein anfängliches Commit und Push durch.

Den Code zu diesem Tutorial finden Sie wie immer auf unserer GitHub Repo.

Erstellen Sie eine Knoten-CLI

Dieser Artikel wurde im Jahr 2020 aktualisiert. Weitere Informationen zu JavaScript finden Sie in unserem Buch. JavaScript: Novice to Ninja, 2. Auflage.

Bevor wir eintauchen und mit dem Erstellen beginnen, sollten Sie sich überlegen, warum wir Node.js zum Erstellen einer Befehlszeilenanwendung auswählen sollten.

Der offensichtlichste Vorteil ist, dass Sie, wenn Sie dies lesen, wahrscheinlich bereits damit vertraut sind – und tatsächlich mit JavaScript.

Ein weiterer wichtiger Vorteil, wie wir im weiteren Verlauf sehen werden, besteht darin, dass das starke Node.js-Ökosystem bedeutet, dass es unter den Hunderttausenden von Paketen, die für alle möglichen Zwecke verfügbar sind, eine Reihe gibt, die speziell entwickelt wurden, um beim Aufbau leistungsstarker Pakete zu helfen Kommandozeilen-Tools.

Schließlich können wir verwenden npm um Abhängigkeiten zu verwalten, anstatt sich um betriebssystemspezifische Paketmanager wie Aptitude, Yum oder Homebrew kümmern zu müssen.

Tipp: Das ist nicht unbedingt wahr, da Ihr Befehlszeilentool andere externe Abhängigkeiten haben kann.

Was wir bauen werden: ginit

Ginit, unsere Node-CLI in Aktion

Für dieses Tutorial erstellen wir ein Befehlszeilendienstprogramm, das ich aufrufe gint. Es ist git init, aber auf Steroiden.

Sie fragen sich wahrscheinlich, was um alles in der Welt das bedeutet.

Wie Sie sicherlich bereits wissen, git init initialisiert ein Git-Repository im aktuellen Ordner. Dies ist jedoch normalerweise nur einer von mehreren sich wiederholenden Schritten, um ein neues oder vorhandenes Projekt mit Git zu verbinden. Als Teil eines typischen Arbeitsablaufs können Sie beispielsweise Folgendes tun:

  1. Initialisieren Sie das lokale Repository, indem Sie es ausführen git init
  2. Erstellen Sie ein Remote-Repository, beispielsweise auf GitHub oder Bitbucket – normalerweise, indem Sie die Befehlszeile verlassen und einen Webbrowser starten
  3. fügen Sie die Fernbedienung hinzu
  4. erstellen .gitignore Datei
  5. Fügen Sie Ihre Projektdateien hinzu
  6. Übertragen Sie den anfänglichen Satz von Dateien
  7. in das Remote-Repository hochschieben.

Es sind oft mehr Schritte erforderlich, aber wir bleiben für die Zwecke unserer App bei diesen. Trotzdem sind diese Schritte ziemlich repetitiv. Wäre es nicht besser, wenn wir all dies über die Befehlszeile tun könnten, ohne das Kopieren und Einfügen von Git-URLs und dergleichen?

Ginit erstellt also ein Git-Repository im aktuellen Ordner, erstellt ein Remote-Repository – wir verwenden dafür GitHub – und fügt es dann als Remote hinzu. Dann wird es einen einfachen interaktiven „Assistenten“ zum Erstellen einer .gitignore Datei, fügen Sie den Inhalt des Ordners hinzu und verschieben Sie ihn in das Remote-Repository. Es spart Ihnen vielleicht keine Stunden, aber es beseitigt einige der anfänglichen Reibungen, wenn Sie ein neues Projekt starten.

In diesem Sinne fangen wir an.

Die Anwendungsabhängigkeiten

Eines steht fest: Optisch wird die Konsole nie die Raffinesse einer grafischen Benutzeroberfläche erreichen. Das bedeutet jedoch nicht, dass es sich um einfachen, hässlichen, monochromen Text handeln muss. Sie werden vielleicht überrascht sein, wie viel Sie visuell tun können, während Sie es gleichzeitig funktional halten. Wir werden uns ein paar Bibliotheken ansehen, um die Anzeige zu verbessern: Kreide zum Einfärben der Ausgabe und clui um einige zusätzliche visuelle Komponenten hinzuzufügen. Just for fun, werden wir verwenden Feige um ein schickes ASCII-basiertes Banner zu erstellen, und wir werden auch verwenden klar um die Konsole zu löschen.

In Bezug auf Ein- und Ausgang ist das Low-Level Readline Das Node.js-Modul könnte verwendet werden, um den Benutzer aufzufordern und Eingaben anzufordern, und ist in einfachen Fällen mehr als ausreichend. Aber wir werden die Vorteile eines Pakets von Drittanbietern nutzen, das ein höheres Maß an Raffinesse hinzufügt – Fragende. Es bietet nicht nur einen Mechanismus zum Stellen von Fragen, sondern implementiert auch einfache Eingabesteuerelemente: Denken Sie an Optionsfelder und Kontrollkästchen, aber in der Konsole.

Wir werden auch verwenden Minimist um Befehlszeilenargumente zu parsen.

Hier ist eine vollständige Liste der Pakete, die wir speziell für die Entwicklung auf der Befehlszeile verwenden werden:

  • Kreide — koloriert die Ausgabe
  • klar — löscht den Terminalbildschirm
  • clui — zeichnet Befehlszeilentabellen, Messgeräte und Spinner
  • Feige — Erstellt ASCII-Kunst aus Text
  • Fragesteller — Erstellt eine interaktive Befehlszeilen-Benutzeroberfläche
  • Minimist — parst Argumentoptionen
  • Konfigurationsspeicher — Einfaches Laden und Speichern der Konfiguration, ohne dass Sie darüber nachdenken müssen, wo und wie.

Darüber hinaus verwenden wir Folgendes:

Erste Schritte

Obwohl wir die Anwendung von Grund auf neu erstellen werden, vergessen Sie nicht, dass Sie auch eine Kopie des Codes aus der herunterladen können Repository, das diesen Artikel begleitet.

Erstellen Sie ein neues Verzeichnis für das Projekt. Du musst es nicht anrufen ginit, natürlich:

mkdir ginit
cd ginit

Erstelle eine neue package.json Datei:

npm init -y

Und bearbeiten Sie es so, dass es so aussieht:

{ "name": "ginit", "version": "1.0.0", "description": "'git init' on steroids", "main": "index.js", "scripts": { "test": "echo "Error: no test specified" && exit 1" }, "keywords": [ "Git", "CLI" ], "author": "<YOUR NAME>", "license": "ISC"
}

Installieren Sie nun die Abhängigkeiten:

npm install chalk clear clui figlet inquirer minimist configstore @octokit/rest @octokit/auth-basic lodash simple-git touch

Erstellen Sie nun eine index.js Datei im selben Ordner und require folgende Abhängigkeiten:

const chalk = require('chalk');
const clear = require('clear');
const figlet = require('figlet');

Hinzufügen einiger Hilfsmethoden

Wir werden eine erstellen lib Ordner, in dem wir unseren Hilfscode in Module aufteilen:

  • Dateien.js — Grundlegende Dateiverwaltung
  • Inquirer.js — Benutzerinteraktion über die Befehlszeile
  • github.js — Zugriffstokenverwaltung
  • repo.js — Git-Repository-Verwaltung.

Lass uns beginnen mit lib/files.js. Hier müssen wir:

  • Holen Sie sich das aktuelle Verzeichnis (um einen Standard-Repo-Namen zu erhalten)
  • prüfen, ob ein Verzeichnis existiert (um festzustellen, ob der aktuelle Ordner bereits ein Git-Repository ist, indem Sie nach einem Ordner mit dem Namen .git).

Das klingt einfach, aber es gibt ein paar Fallstricke zu berücksichtigen.

Erstens könnten Sie versucht sein, die zu verwenden fs Moduls realpathSync Methode zum Abrufen des aktuellen Verzeichnisses:

path.basename(path.dirname(fs.realpathSync(__filename)));

Dies funktioniert, wenn wir die Anwendung aus demselben Verzeichnis aufrufen (z. B. mit node index.js), aber denken Sie daran, dass wir unsere Konsolenanwendung weltweit verfügbar machen werden. Das heißt, wir wollen den Namen des Verzeichnisses, in dem wir arbeiten, und nicht das Verzeichnis, in dem sich die Anwendung befindet. Zu diesem Zweck ist es besser zu verwenden prozess.cwd:

path.basename(process.cwd());

Zweitens die bevorzugte Methode, um zu prüfen, ob eine Datei oder ein Verzeichnis existiert ändert sich ständig. Der aktuelle Weg ist zu verwenden existsSync. Dies kehrt zurück true wenn der Pfad existiert, false Andernfalls.

Schließlich ist es erwähnenswert, dass beim Schreiben einer Befehlszeilenanwendung die Verwendung der synchronen Version dieser Art von Methoden völlig in Ordnung ist.

Lassen Sie uns das alles zusammenfassen und ein Dienstprogrammpaket erstellen lib/files.js:

const fs = require('fs');
const path = require('path'); module.exports = { getCurrentDirectoryBase: () => { return path.basename(process.cwd()); }, directoryExists: (filePath) => { return fs.existsSync(filePath); }
};

Gehen Sie zurück zu index.js und dir versichern require die neue Datei:

const files = require('./lib/files');

Wenn dies vorhanden ist, können wir mit der Entwicklung der Anwendung beginnen.

Initialisieren der Knoten-CLI

Lassen Sie uns nun die Startphase unserer Konsolenanwendung implementieren.

Um einige der Pakete zu demonstrieren, die wir installiert haben, um die Konsolenausgabe zu verbessern, lassen Sie uns den Bildschirm löschen und dann ein Banner anzeigen:

// index.js clear(); console.log( chalk.yellow( figlet.textSync('Ginit', { horizontalLayout: 'full' }) )
);

Sie können die Anwendung mit ausführen node index.js. Die Ausgabe davon ist unten gezeigt.

Das Willkommensbanner auf unserer Node-CLI, erstellt mit Chalk und Figlet

Als nächstes führen wir eine einfache Überprüfung durch, um sicherzustellen, dass der aktuelle Ordner nicht bereits ein Git-Repository ist. Das ist einfach: Wir prüfen einfach, ob a existiert .git Ordner mit der gerade erstellten Utility-Methode:

//index.js if (files.directoryExists('.git')) { console.log(chalk.red('Already a Git repository!')); process.exit();
}

Tipp: Beachten Sie, dass wir die verwenden Kreidemodul um eine rot gefärbte Nachricht anzuzeigen.

Aufforderung des Benutzers zur Eingabe

Als nächstes müssen wir eine Funktion erstellen, die den Benutzer zur Eingabe seiner GitHub-Anmeldeinformationen auffordert.

Wir verwenden Fragende dafür. Das Modul enthält eine Reihe von Methoden für verschiedene Arten von Eingabeaufforderungen, die ungefähr analog zu HTML-Formularsteuerelementen sind. Um den GitHub-Benutzernamen und das Passwort des Benutzers zu erfassen, verwenden wir die input und password Typen bzw.

Zuerst erstellen lib/inquirer.js und fügen Sie diesen Code ein:

const inquirer = require('inquirer'); module.exports = { askGithubCredentials: () => { const questions = [ { name: 'username', type: 'input', message: 'Enter your GitHub username or e-mail address:', validate: function( value ) { if (value.length) { return true; } else { return 'Please enter your username or e-mail address.'; } } }, { name: 'password', type: 'password', message: 'Enter your password:', validate: function(value) { if (value.length) { return true; } else { return 'Please enter your password.'; } } } ]; return inquirer.prompt(questions); },
};

Wie Sie sehen können, inquirer.prompt() stellt dem Benutzer eine Reihe von Fragen, die in Form eines Arrays als erstes Argument bereitgestellt werden. Jede Frage besteht aus einem Objekt, das die definiert name des Feldes, die type (Wir verwenden nur input und password hier, aber später sehen wir uns ein fortgeschritteneres Beispiel an) und die Eingabeaufforderung (message) anzuzeigen.

Die Eingabe des Benutzers wird an die aufrufende Funktion als übergeben Promise. Bei Erfolg erhalten wir ein einfaches Objekt mit zwei Eigenschaften – username und password.

Sie können dies alles testen, indem Sie Folgendes zu hinzufügen index.js:

const inquirer = require('./lib/inquirer'); const run = async () => { const credentials = await inquirer.askGithubCredentials(); console.log(credentials);
}; run();

Führen Sie dann das Skript mit aus node index.js.

Abrufen von Benutzereingaben mit Inquirer

Tipp: Wenn Sie mit dem Testen fertig sind, vergessen Sie nicht, die Linie zu entfernen const inquirer = require('./lib/inquirer'); für index.js, da wir es in dieser Datei eigentlich nicht brauchen werden.

Umgang mit der GitHub-Authentifizierung

Der nächste Schritt besteht darin, eine Funktion zum Abrufen eines OAuth-Tokens für die GitHub-API zu erstellen. Im Wesentlichen „tauschen“ wir den Benutzernamen und das Passwort gegen ein Token aus.

Natürlich möchten wir nicht, dass Benutzer jedes Mal ihre Anmeldeinformationen eingeben müssen, wenn sie das Tool verwenden. Stattdessen speichern wir das OAuth-Token für nachfolgende Anfragen. Hier ist die Konfigurationsspeicher Paket kommt.

Konfig speichern

Das Speichern der Konfiguration ist äußerlich recht einfach: Sie können einfach in eine/aus einer JSON-Datei lesen und schreiben, ohne dass ein Paket eines Drittanbieters erforderlich ist. Das configstore-Paket bietet jedoch einige entscheidende Vorteile:

  1. Es bestimmt unter Berücksichtigung Ihres Betriebssystems und des aktuellen Benutzers den für Sie am besten geeigneten Speicherort für die Datei.
  2. Es ist nicht erforderlich, explizit in die Datei zu lesen oder zu schreiben. Sie ändern einfach ein Configstore-Objekt und das wird im Hintergrund für Sie erledigt.

Um es zu verwenden, erstellen Sie einfach eine Instanz und übergeben ihr eine Anwendungskennung. Zum Beispiel:

const Configstore = require('configstore');
const conf = new Configstore('ginit');

Besitzt das configstore Datei nicht existiert, wird ein leeres Objekt zurückgegeben und die Datei im Hintergrund erstellt. Wenn es schon eine gibt configstore Datei wird der Inhalt Ihrer Anwendung zur Verfügung gestellt. Sie können jetzt verwenden conf als einfaches Objekt, das je nach Bedarf Eigenschaften erhält oder festlegt. Wie oben erwähnt, müssen Sie sich danach keine Gedanken über das Speichern machen. Das wird für Sie erledigt.

Tipp: Unter macOS finden Sie die Datei in /Users/[YOUR-USERNME]/.config/configstore/ginit.json. Unter Linux ist es in /home/[YOUR-USERNME]/.config/configstore/ginit.json.

Kommunikation mit der GitHub-API

Lassen Sie uns eine Bibliothek zum Umgang mit dem GitHub-Token erstellen. Erstellen Sie die Datei lib/github.js und fügen Sie den folgenden Code darin ein:

const CLI = require('clui');
const Configstore = require('configstore');
const Octokit = require('@octokit/rest');
const Spinner = CLI.Spinner;
const { createBasicAuth } = require("@octokit/auth-basic"); const inquirer = require('./inquirer');
const pkg = require('../package.json'); const conf = new Configstore(pkg.name);

Fügen wir nun die Funktion hinzu, die prüft, ob wir bereits ein Zugriffstoken haben. Wir werden auch eine Funktion hinzufügen, die anderen Bibliotheken den Zugriff ermöglicht octokit(GitHub) Funktionen:

let octokit; module.exports = { getInstance: () => { return octokit; }, getStoredGithubToken: () => { return conf.get('github.token'); },
};

Sollten Sie jetzt aufgefordert werden, ein conf Objekt existiert und es hat github.token -Eigenschaft bedeutet dies, dass bereits ein Token gespeichert ist. In diesem Fall geben wir den Tokenwert an die aufrufende Funktion zurück. Dazu kommen wir später.

Wenn kein Token erkannt wird, müssen wir einen abrufen. Das Erhalten eines OAuth-Tokens beinhaltet natürlich eine Netzwerkanfrage, was eine kurze Wartezeit für den Benutzer bedeutet. Dies gibt uns die Möglichkeit, uns das anzusehen clui Paket, das einige Verbesserungen für konsolenbasierte Anwendungen bietet, darunter einen animierten Spinner.

Einen Spinner zu erstellen ist einfach:

const status = new Spinner('Authenticating you, please wait...');
status.start();

Wenn Sie fertig sind, stoppen Sie es einfach und es verschwindet vom Bildschirm:

status.stop();

Tipp: Sie können die Beschriftung auch dynamisch mit setzen update Methode. Dies kann nützlich sein, wenn Sie einen Hinweis auf den Fortschritt haben, z. B. die Anzeige des abgeschlossenen Prozentsatzes.

Hier ist der Code zur Authentifizierung bei GitHub:

module.exports = { getInstance: () => { ... }, getStoredGithubToken: () => { ... }, getPersonalAccesToken: async () => { const credentials = await inquirer.askGithubCredentials(); const status = new Spinner('Authenticating you, please wait...'); status.start(); const auth = createBasicAuth({ username: credentials.username, password: credentials.password, async on2Fa() { // TBD }, token: { scopes: ['user', 'public_repo', 'repo', 'repo:status'], note: 'ginit, the command-line tool for initalizing Git repos' } }); try { const res = await auth(); if(res.token) { conf.set('github.token', res.token); return res.token; } else { throw new Error("GitHub token was not found in the response"); } } finally { status.stop(); } },
};

Lassen Sie uns das durchgehen:

  1. Wir fordern den Benutzer mithilfe von zur Eingabe seiner Anmeldeinformationen auf askGithubCredentials Methode, die wir zuvor definiert haben.
  2. Wir nutzen die createBasicAuth Methode zum Erstellen eines auth Funktion, die wir im nächsten Schritt aufrufen werden. Wir übergeben den Benutzernamen und das Passwort des Benutzers an diese Methode sowie ein Token-Objekt mit zwei Eigenschaften:
    • note — eine Notiz, die uns daran erinnert, wofür das OAuth-Token ist.
    • scopes — eine Liste der Bereiche, in denen sich diese Autorisierung befindet. Sie können lesen mehr über verfügbare Bereiche in der GitHub-Dokumentation.
  3. Wir haben dann await das Ergebnis des Aufrufs der auth Funktion innerhalb eines try blockieren.
  4. Wenn die Authentifizierung erfolgreich ist und ein Token in der Antwort vorhanden ist, setzen wir es in die configstore für das nächste Mal und geben Sie den Token zurück.
  5. Wenn das Token fehlt oder die Authentifizierung aus irgendeinem Grund nicht erfolgreich ist, wird der Fehler im Stapel angezeigt, sodass wir ihn erfassen können index.js. Wir werden diese Funktionalität später implementieren.

Alle Zugriffstoken, die Sie erstellen, ob manuell oder über die API, wie wir es hier tun, können Sie erstellen sieh sie hier. Im Laufe der Entwicklung stellen Sie möglicherweise fest, dass Sie das Zugriffstoken von ginit löschen müssen – erkennbar an der note oben angegebenen Parameter — damit Sie ihn neu generieren können.

Wenn Sie mitverfolgt haben und ausprobieren möchten, was wir bisher haben, können Sie aktualisieren index.js wie folgt:

const github = require('./lib/github'); ... const run = async () => { let token = github.getStoredGithubToken(); if(!token) { token = await github.getPersonalAccesToken(); } console.log(token);
};

Wenn Sie es zum ersten Mal ausführen, sollten Sie nach Ihrem Benutzernamen und GitHub-Passwort gefragt werden. Die App sollte dann ein persönliches Zugriffstoken auf GitHub erstellen und das Token in der configstore, bevor Sie es an der Konsole anmelden. Jedes Mal, wenn Sie die App danach ausführen, zieht die App das Token direkt aus der configstore und protokolliere das auf dem Bildschirm.

Umgang mit Zwei-Faktor-Authentifizierung

Hoffentlich ist Ihnen das aufgefallen on2Fa Methode im obigen Code. Dies wird aufgerufen, wenn ein Benutzer die Zwei-Faktor-Authentifizierung auf seinem GitHub-Konto aktiviert hat. Füllen wir das jetzt aus:

// inquirer.js const inquirer = require('inquirer'); module.exports = { askGithubCredentials: () => { ... }, getTwoFactorAuthenticationCode: () => { return inquirer.prompt({ name: 'twoFactorAuthenticationCode', type: 'input', message: 'Enter your two-factor authentication code:', validate: function(value) { if (value.length) { return true; } else { return 'Please enter your two-factor authentication code.'; } } }); },
};

Wir können das anrufen getTwoFactorAuthenticationCode Methode aus dem on2Fa Methode, wie so:

// github.js async on2Fa() { status.stop(); const res = await inquirer.getTwoFactorAuthenticationCode(); status.start(); return res.twoFactorAuthenticationCode;
},

Und jetzt kann unsere App GitHub-Konten mit aktivierter Zwei-Faktor-Authentifizierung verarbeiten.

Erstellen eines Repositorys

Sobald wir ein OAuth-Token haben, können wir damit ein Remote-Repository mit GitHub erstellen.

Auch hier können wir Inquirer verwenden, um eine Reihe von Fragen zu stellen. Wir brauchen einen Namen für das Repo, wir fragen nach einer optionalen Beschreibung und wir müssen auch wissen, ob es öffentlich oder privat sein soll.

Wir werden verwenden Minimist um Standardwerte für den Namen und die Beschreibung aus optionalen Befehlszeilenargumenten abzurufen. Zum Beispiel:

ginit my-repo "just a test repository"

Dadurch wird der Standardname auf festgelegt my-repo und die Beschreibung dazu just a test repository.

Die folgende Zeile platziert die Argumente in einem Array, das durch einen Unterstrich indiziert ist:

const argv = require('minimist')(process.argv.slice(2));
// { _: [ 'my-repo', 'just a test repository' ] }

Tipp: Das kratzt nur an der Oberfläche des Mini-Pakets. Sie können damit auch Flags, Schalter und Name/Wert-Paare interpretieren. Weitere Informationen finden Sie in der Dokumentation.

Wir schreiben Code, um die Befehlszeilenargumente zu parsen, und stellen eine Reihe von Fragen. Zuerst aktualisieren lib/inquirer.js wie folgt:

const inquirer = require('inquirer');
const files = require('./files'); module.exports = { askGithubCredentials: () => { ... }, getTwoFactorAuthenticationCode: () => { ... }, askRepoDetails: () => { const argv = require('minimist')(process.argv.slice(2)); const questions = [ { type: 'input', name: 'name', message: 'Enter a name for the repository:', default: argv._[0] || files.getCurrentDirectoryBase(), validate: function( value ) { if (value.length) { return true; } else { return 'Please enter a name for the repository.'; } } }, { type: 'input', name: 'description', default: argv._[1] || null, message: 'Optionally enter a description of the repository:' }, { type: 'list', name: 'visibility', message: 'Public or private:', choices: [ 'public', 'private' ], default: 'public' } ]; return inquirer.prompt(questions); },
};

Als nächstes erstellen Sie die Datei lib/repo.js und fügen Sie diesen Code hinzu:

const CLI = require('clui');
const fs = require('fs');
const git = require('simple-git/promise')();
const Spinner = CLI.Spinner;
const touch = require("touch");
const _ = require('lodash'); const inquirer = require('./inquirer');
const gh = require('./github'); module.exports = { createRemoteRepo: async () => { const github = gh.getInstance(); const answers = await inquirer.askRepoDetails(); const data = { name: answers.name, description: answers.description, private: (answers.visibility === 'private') }; const status = new Spinner('Creating remote repository...'); status.start(); try { const response = await github.repos.createForAuthenticatedUser(data); return response.data.ssh_url; } finally { status.stop(); } },
};

Sobald wir diese Informationen haben, können wir einfach das GitHub-Paket dazu verwenden Repos erstellen, wodurch wir eine URL für das neu erstellte Repository erhalten. Wir können das dann als Remote in unserem lokalen Git-Repository einrichten. Lassen Sie uns zunächst jedoch interaktiv a erstellen .gitignore Datei.

Erstellen einer .gitignore-Datei

Im nächsten Schritt erstellen wir einen einfachen Befehlszeilen-"Assistenten", um eine .gitignore Datei. Wenn der Benutzer unsere Anwendung in einem vorhandenen Projektverzeichnis ausführt, zeigen wir ihm eine Liste der Dateien und Verzeichnisse, die sich bereits im aktuellen Arbeitsverzeichnis befinden, und lassen ihn auswählen, welche ignoriert werden sollen.

Das Inquirer-Paket bietet a checkbox Eingabetyp für genau das.

Checkboxen des Fragestellers in Aktion

Als erstes müssen wir das aktuelle Verzeichnis scannen und ignorieren .git Ordner und alle vorhandenen .gitignore Datei (wir tun dies, indem wir lodash's verwenden ohne Methode):

const filelist = _.without(fs.readdirSync('.'), '.git', '.gitignore');

Wenn es nichts hinzuzufügen gibt, macht es keinen Sinn weiterzumachen, also lassen Sie uns einfach touch die jetzige .gitignore file und bail out aus der Funktion:

if (filelist.length) { ...
} else { touch('.gitignore');
}

Lassen Sie uns schließlich das Kontrollkästchen „Widget“ des Inquirers verwenden, um die Dateien aufzulisten. Fügen Sie den folgenden Code ein lib/inquirer.js:

askIgnoreFiles: (filelist) => { const questions = [ { type: 'checkbox', name: 'ignore', message: 'Select the files and/or folders you wish to ignore:', choices: filelist, default: ['node_modules', 'bower_components'] } ]; return inquirer.prompt(questions);
},

Beachten Sie, dass wir auch eine Liste mit Standardwerten bereitstellen können. In diesem Fall treffen wir eine Vorauswahl node_modules und bower_components, sollten sie vorhanden sein.

Wenn der Inquirer-Code vorhanden ist, können wir jetzt die erstellen createGitignore() Funktion. Fügen Sie diesen Code ein lib/repo.js:

createGitignore: async () => { const filelist = _.without(fs.readdirSync('.'), '.git', '.gitignore'); if (filelist.length) { const answers = await inquirer.askIgnoreFiles(filelist); if (answers.ignore.length) { fs.writeFileSync( '.gitignore', answers.ignore.join( 'n' ) ); } else { touch( '.gitignore' ); } } else { touch('.gitignore'); }
},

Nach dem „Einreichen“ generieren wir dann eine .gitignore durch Zusammenfügen der ausgewählten Dateiliste, getrennt durch einen Zeilenumbruch. Unsere Funktion garantiert jetzt ziemlich genau, dass wir a haben .gitignore Datei, damit wir mit der Initialisierung eines Git-Repositorys fortfahren können.

Interaktion mit Git aus der App heraus

Es gibt eine Reihe von Möglichkeiten, mit Git zu interagieren, aber die einfachste ist vielleicht die Verwendung von simple-git Paket. Dies stellt eine Reihe von verkettbaren Methoden bereit, die hinter den Kulissen die ausführbare Git-Datei ausführen.

Dies sind die sich wiederholenden Aufgaben, die wir damit automatisieren:

  1. Lauf git init
  2. Ergänzen Sie die .gitignore Datei
  3. fügen Sie den restlichen Inhalt des Arbeitsverzeichnisses hinzu
  4. Führen Sie eine anfängliche Festschreibung durch
  5. fügen Sie das neu erstellte Remote-Repository hinzu
  6. Pushen Sie das Arbeitsverzeichnis auf die Fernbedienung.

Geben Sie den folgenden Code ein lib/repo.js:

setupRepo: async (url) => { const status = new Spinner('Initializing local repository and pushing to remote...'); status.start(); try { git.init() .then(git.add('.gitignore')) .then(git.add('./*')) .then(git.commit('Initial commit')) .then(git.addRemote('origin', url)) .then(git.push('origin', 'master')); } finally { status.stop(); }
},

Putting It All Together

Lassen Sie uns zunächst eine Hilfsfunktion einrichten lib/github.js zum Einrichten eines oauth Authentifizierung:

githubAuth: (token) => { octokit = new Octokit({ auth: token });
},

Als nächstes erstellen wir eine Funktion in index.js zum Handhaben der Logik des Erwerbs des Tokens. Platzieren Sie diesen Code vor dem run() Funktion:

const getGithubToken = async () => { // Fetch token from config store let token = github.getStoredGithubToken(); if(token) { return token; } // No token found, use credentials to access GitHub account token = await github.getPersonalAccesToken(); return token;
};

Abschließend aktualisieren wir die run() Funktion, indem Sie Code schreiben, der die Hauptlogik der App handhabt:

const repo = require('./lib/repo'); ... const run = async () => { try { // Retrieve & Set Authentication Token const token = await getGithubToken(); github.githubAuth(token); // Create remote repository const url = await repo.createRemoteRepo(); // Create .gitignore file await repo.createGitignore(); // Set up local repository and push to remote await repo.setupRepo(url); console.log(chalk.green('All done!')); } catch(err) { if (err) { switch (err.status) { case 401: console.log(chalk.red('Couldn't log you in. Please provide correct credentials/token.')); break; case 422: console.log(chalk.red('There is already a remote repository or token with the same name')); break; default: console.log(chalk.red(err)); } } }
};

Wie Sie sehen können, stellen wir sicher, dass der Benutzer authentifiziert wird, bevor alle unsere anderen Funktionen aufgerufen werden (createRemoteRepo(), createGitignore(), setupRepo()) der Reihe nach. Der Code behandelt auch etwaige Fehler und bietet dem Benutzer ein entsprechendes Feedback.

Sie können die abgeschlossenen überprüfen index.js Datei in unserem GitHub-Repo.

An diesem Punkt sollten Sie eine funktionierende App haben. Probieren Sie es aus und vergewissern Sie sich, dass es wie erwartet funktioniert.

Den ginit-Befehl global verfügbar machen

Die einzige verbleibende Sache ist, unser Kommando global verfügbar zu machen. Dazu müssen wir a hinzufügen Shebang Linie nach oben index.js:

#!/usr/bin/env node

Als nächstes müssen wir a hinzufügen bin Eigentum zu unserem package.json Datei. Dies ordnet den Befehlsnamen (ginit) auf den Namen der auszuführenden Datei (relativ zu package.json):

"bin": { "ginit": "./index.js"
}

Danach installieren Sie das Modul global und Sie haben einen funktionierenden Shell-Befehl:

npm install -g

Tipp: Dies funktioniert auch unter Windows, da npm installiert hilfreicherweise einen cmd-Wrapper neben Ihrem Skript.

Wenn Sie bestätigen möchten, dass die Installation funktioniert hat, können Sie Ihre global installierten Node-Module folgendermaßen auflisten:

npm ls -g --depth=0

Weiter gehen

Wir haben eine ziemlich raffinierte, wenn auch einfache Befehlszeilen-App zum Initialisieren von Git-Repositories. Aber es gibt noch viel mehr, was Sie tun könnten, um es weiter zu verbessern.

Wenn Sie ein Bitbucket-Benutzer sind, können Sie das Programm so anpassen, dass es die Bitbucket-API verwendet, um ein Repository zu erstellen. Da ist ein Node.js-API-Wrapper verfügbar um Ihnen den Einstieg zu erleichtern. Möglicherweise möchten Sie eine zusätzliche Befehlszeilenoption oder Aufforderung hinzufügen, um den Benutzer zu fragen, ob er GitHub oder Bitbucket verwenden möchte (Inquirer wäre genau dafür perfekt) oder einfach den GitHub-spezifischen Code durch eine Bitbucket-Alternative ersetzen.

Sie könnten auch die Möglichkeit bereitstellen, Ihre eigenen Standardeinstellungen für die festzulegen .gitgnore Datei anstelle einer fest codierten Liste. Das Einstellungspaket könnte hier geeignet sein, oder Sie könnten eine Reihe von „Vorlagen“ bereitstellen – vielleicht um den Benutzer nach der Art des Projekts zu fragen. Vielleicht möchten Sie auch die Integration mit dem betrachten .gitignore.io Befehlszeilentool/API.

Darüber hinaus möchten Sie vielleicht auch eine zusätzliche Validierung hinzufügen, die Möglichkeit bieten, bestimmte Abschnitte zu überspringen und vieles mehr.

Quelle: https://www.sitepoint.com/javascript-command-line-interface-cli-node-js/?utm_source=rss

spot_img

Neueste Intelligenz

spot_img

Chat mit uns

Hallo! Wie kann ich dir helfen?