How to reinstall a Node.js application with Python

How to reinstall a Node.js application with Python

In this article we'll see how to perform a clean reinstallation of a Node.js application using Python.

In this article we'll see how to perform a clean reinstallation of a Node.js application using Python.

We can start from reading the dependency list from the original package.json file and return the packages names as a list.

import json
import sys
import subprocess
import os
import shutil


def get_packages_names():
    names = []
    with open('./package.json', 'r') as f:
        data = json.load(f)
        deps = data.get('dependencies')
        if deps:
            names = list(deps.keys())
    return names

By getting only the key name, we also make sure that we're going to install the latest version of each package. This is useful when we need to update our application to the latest version of its dependencies.

To reinstall our app, we first need to rename our original package.json file in order to execute a clean npm init -y command. If this first step succeeds, we move to the actual installation process, otherwise we rename our package.json file to its original name.

Now we can execute npm install [...] --save, where [...] is the list of package names we retrieved from the dependencies property of our package.json file. We also need to create a backup copy of our node_modules directory, if present.

If the installation fails, we restore both the original package.json file and node_modules directory. Otherwise, we remove the backup copies of these resources.

def install_packages():
    packages_list = get_packages_names()
    orig_pkg_file = './package.json'
    old_pkg_filename = './package-old.json'
    modules_dir = './node_modules'
    old_modules_dir_name = './node_modules_old'

    if len(packages_list) == 0:
        print('No NPM packages to install')
        sys.exit(1)
        
    npm_init_cmd = 'npm init -y'.split(' ')
    print('Renaming old package.json')
    os.rename(orig_pkg_file, old_pkg_filename)
    print('Initializing new package.json')
    init_cmd = subprocess.run(npm_init_cmd, stdout=subprocess.DEVNULL)
    
    if init_cmd.returncode == 1:
        print('Failed to initialize, restoring the old package.json')
        os.rename(old_pkg_filename, orig_pkg_file)
        sys.exit(1)
        
    install_cmd = ['npm', 'install']
    
    for pkg in packages_list:
        install_cmd.append(pkg)
    install_cmd.append('--save')
    
    print('Installing packages')
    
    if os.path.isdir(modules_dir):
        os.rename(modules_dir, old_modules_dir_name)
        
    install = subprocess.run(install_cmd, stdout=subprocess.DEVNULL)
    if install.returncode == 1:
        print('Installation failed, cleaning up')
        if os.path.isdir(modules_dir):
            shutil.rmtree(modules_dir)
            os.rename(old_modules_dir_name, modules_dir)
        os.unlink(orig_pkg_file)
        os.rename(old_pkg_filename, orig_pkg_file)
        sys.exit(1)
        
    os.unlink(old_pkg_filename)
    shutil.rmtree(old_modules_dir_name)
    print('Done')
    
    sys.exit(0)

subprocess.run() gives us the ability to know whether the executed shell command succeeded or not by exposing the returncode property, where 1 indicates failure and 0 means that the command has been successfully executed.

This method accepts a command as a list, where the first element represents the command name (here npm) and the subsequent elements are the command's parameters.

We can run our code as follows:

def main():
    install_packages()


if __name__ == '__main__':
    main()

This solution is useful when you need to rebuild your application or you just want to get the latest package versions installed without having to manually change their version number within the package.json file.