Node. js checks for updates using the command line tool, and node. js command line
With the popularity of Node. js, it becomes easier to develop command line tools using Node. js. A mature command line tool should give users an "elegant" idea of how to update later versions from the very beginning. The best way is to prompt the user with relevant information when the user executes commands on the terminal.
This article provides an easy-to-use, efficient, and customizable method. The source code is here: GITHUB. You are welcome to like it. Next, I will explain how it works.
Use
Let's take a brief look at the usage of this npm package:
Const updater = require ('pkg-updater '); const pkg = require ('. /package. json'); // The package information of the command line tool ({'pkg ': pkg }). then () = >{/ * Start the command line tool here */}); updater ({'pkg ': pkg, // custom registry 'registry ': 'http: // xxx.registry.com ', // The dist-tag of the custom request. The default value is latest 'tag': 'Next'. // The custom check interval, the default value is 1 h 'checkinterval': 24*60*60*1000. // The Custom update prompt message 'updatemessage ': 'package update from <% = current %> to <% = latest %>. ', // The version update level of the custom forced update. The default value is major 'level': 'minor '}). then () => {/* Start the command line tool */}); updater ({'pkg ': pkg, // The logic 'onversionchange' for fully custom version update: function * (opts ){}}). then () => {/* Start the command line tool here */});
Effect
Implementation
The usage method is very simple. Let's take a look at its implementation method.
Requirement
Let's sort out the requirements first. A command line check Updater should provide at least the following functions:
The latest version can be obtained remotely.
Prompt Based on check results
You can exit directly when the version is incompatible and force the user to upgrade the program.
Get version
To get the latest version, you can send a request to obtain information from "somewhere. However, we need to consider the following issues:
Where can I obtain the version information?
What is the policy for obtaining version information? (When to obtain it? How to handle the obtained information ?)
Where can I obtain the version information?
Generally, our command line tools use npm for distribution. The easiest way is to directly obtain them through registry. Requesthttps://registry.npmjs.org/{name}/{dist-tag}
You can get the version information of the tag corresponding to the package. The result is similar to the following:
// https://registry.npmjs.org/co/latest{ "name": "co", "version": "4.5.0"}
In actual implementation, we should allow callers to customize the registry address and the requested dist-tag so that more customization can be made.
Policy for obtaining version information
The first method is to obtain the version information every time you execute the command. Such a retrieval policy should be the simplest and real-time.
However, this policy is not suitable:
Each time you run a command, you must send a request for check. If the network delay occurs, the command execution will be blocked, affecting the user experience.
In fact, tool version updates are not frequent and there is no need to perform real-time checks.
There are many factors that affect network requests, and they cannot be guaranteed to succeed each time. Therefore, a local cache mechanism should be provided to store the results of successful requests to avoid version information unavailability.
Based on the above points, we design the following acquisition policies:
Put the logic for sending network requests to get version information in an independent background process for execution, to ensure that the main command execution is not blocked
After the request is successful, the version information and check time are cached on the user's machine.
Each time a command is executed, only the version information cached locally is read, and no network request is sent.
Based on the cache check time and current time, no background check process is created at one interval.
Translating the above policy into code is probably as follows:
// Read the local cache check result const checkInfo = yield updater. readCheckInfo (opts); const lastCheck = checkInfo. lastCheck; const lastVersion = checkInfo. lastVersion; // the user is prompted based on the version information //... // return if (Date. now ()-lastCheck <opts. checkInterval) {return;} // create a background check process try {require ('child _ Process '). spawn (process.exe cPath, [require ('path '). join (_ dirname, '_ check. js'), JSON. stringify ({'pkg ': opts. pkg, // package information 'tag': opts. tag, // check the dist-tag 'logfile': opts. logFile, // cache file path 'registry ': opts. registry // registry address})], {'stdio ': ['ignore', 'ignore', 'ignore'], 'detached': true }). unref ();} catch (e ){}
Background Process Execution_check.js
The file is also very simple, as shown below:
Const opts = JSON. parse (process. argv [2]); let lastVersion = ''; try {// send a request to obtain the latest const url = normalizeUrl (opts. registry + '/' + opts. pkg. name + '/' + (opts. tag | 'latest '); const res = yield got. get (url, {'json': true, 'timeout': 60*1000}); if (res & res. body & res. body. version) {lastVersion = res. body. version ;}} catch (e) {}// if the retrieval fails, the latest version is the current version (package. version) if (! LastVersion) {lastVersion = opts. pkg. version;} let data = yield util. readJson (opts. logFile); if (! Data [opts. pkg. name]) {data [opts. pkg. name] ={};} data [opts. pkg. name]. lastVersion = lastVersion; // the latest data [opts. pkg. name]. lastCheck = Date. now (); // check the time // write to the cache yield util. writeJson (opts. logFile, data );
Prompt
When the version is updated, we should prompt the user on the terminal. There are two problems:
Prompt text Problems
Prompt text display interval (always show? Display at intervals ?)
Here we adopt the following strategy:
Provides the default prompt text, which clearly describes the current version, the latest version, and the update method, and allows the caller to customize the prompt text.
As long as there is an update, the prompt text is always displayed, because we want the user to update it frequently.
The implementation code is as follows:
// Compare the version const type = updater. diffType (opts. pkg. version, lastVersion, opts. level); if (type) {// according to the template rendering prompt information const str = updater. template (opts. updateMessage | updater. defaultOpts. updateMessage) ({'colors ': updater. colors, 'name': opts. pkg. name, 'current': opts. pkg. version, 'latest ': opts. lastVersion, 'command': 'npm I-G' + opts. pkg. name}); // prompts console. log (updater. boxen (str, {'padding': 1, 'margin ': 1, 'borderstyle': 'classic '}));}
Force update
For the npm module, update of Version a. B. c generally involves three situations:
Patch: c-bit, minor version update, usually bug fixing
Minor: B-bit. It is a medium version update. New Features and bug fixes are generally added.
Major, a-bit, major version update, usually incompatible upgrade
We hope that when the remote version is updated in the form of major, the command line tool will exit and force the user to upgrade before using it. This ensures that after a major version is pushed, all users can immediately update it, instead of continuing to use the old version, resulting in version fragmentation issues.
The implementation code is roughly as follows:
// Compare the version const type = updater. diffType (opts. pkg. version, lastVersion, opts. level); if (type) {// according to the template rendering prompt information const str = updater. template (opts. updateMessage | updater. defaultOpts. updateMessage) ({'colors ': updater. colors, 'name': opts. pkg. name, 'current': opts. pkg. version, 'latest ': opts. lastVersion, 'command': 'npm I-G' + opts. pkg. name}); // prompts console. log (updater. boxen (str, {'padding': 1, 'margin ': 1, 'borderstyle': 'classic'}); // incompatible update, directly let the process exit if (type = 'compatible ') {process. exit (1 );}}
Summary
The command line check for updates seems simple. In fact, there are still many details for careful consideration. I hope this article will inspire you.