使用 Nexus3 搭建 npm 私库,超详细的图文教程

2,575 阅读2分钟

使用 Nexus3 搭建 npm 私库,超详细的图文教程

安装运行 Nexus3

1. 下载
2. 安装
  • nexus-3.46.0-01-win64.zip 文件解压到 D:\Program Files\nexus3 目录下,如图所示:

image-20230614164910393

其中:

nexus-3.46.0-01 为 nexus 服务器相关文件目录 sonatype-work 为 nexus 数据存储目录

  • 新增全局变量。在 path 变量中新增 D:\Program Files\nexus3\nexus-3.46.0-01\bin

  • 注册服务。以管理员身份打开命令行窗口。运行nexus /install nexus3。 即可在系统同注册一个名为 nexus3 的服务。

  • windows + r 打开运行窗口,输入services.msc 找到 nexus3 服务,点击启动,即可启动服务,如图所示:

image-20230614165739006

  • 更多命令。
# 启动服务
$ nexus /run nexus3
# 停止服务
$ nexus /stop nexus3
# 删除服务
$ nexus /uninstall nexus3
  • 稍等片刻后,你可以通过在浏览器中输入http://localhost:8081/,进入服务,如图所示:

image-20230614170149239

  • 点击 Sign in 进行登录。其中用户名为 admin,密码在文件 D:\Program Files\nexus3\sonatype-work\nexus3\admin.password 中查看,第一次登录需要修改密码,如修改成 admin123, 修改密码后 admin.password ⽂件会⾃动删除。

  • 至此,nexus 安装运行已全部完成。

创建新仓库

  • 创建一个名为 vue-cms 的仓库,如图所示:

image-20230614171036475

image-20230614171235654

image-20230614171409432

  • 完成后,点击 Create repository 完成。

  • 以上操作完成后,现在我们就可以在浏览列表看到 vue-cms 仓库了,如图所示:

image-20230614171710784

上传 npm 依赖到自定义仓库

接下来我们测试一下,上传项目中的 npm 依赖到我们的自定义仓库

1. 创建项目
  • 创建目录 test-project
  • 执行 npm init -y

image-20230614172553873

  • 安装依赖 npm install chart.js

image-20230614172716826

2. 下载 tgz 包

接下来我们要做的就是,将package.json 中的依赖 chart.js 进行下载。

方式1:

  • 打开 package-lock.json 文件,找到 dependencies 属性,将其下的所有依赖包依赖包,进行下载。

image-20230614173255675

  • 复制每个插件 resolved 属性后的链接到浏览器进行下载。

  • 将下载的 .tgz 包放到与 package.json 同级目录的 node(需创建) 文件夹下。

方式2(推荐):

通过 python 脚本进行下载,需要有 python 环境

  • package.json 同级目录下创建 download.py 文件, 脚本如下:
# -*-coding:utf-8-*-
import json
import os
import urllib.request
from pathlib import Path


def node_modules(file_dir):
    # 通过递归遍历 node_modules 每个子包的 package.json 解析下载链接
    links = []
    for root, dirs, files in os.walk(file_dir):
        if 'package.json' in files:
            package_json_file = os.path.join(root, 'package.json')
            try:
                with open(package_json_file, 'r', encoding='UTF-8') as load_f:
                    load_dict = json.load(load_f)
                    if '_resolved' in load_dict.keys():
                        links.append(load_dict['_resolved'])
            except Exception as e:
                print(package_json_file)
                print('Error:', e)
    return links


def package_lock(package_lock_path):
    # 通过递归遍历 package-lock.json 解析下载链接
    links = []
    with open(package_lock_path, 'r', encoding='UTF-8') as load_f:
        load_dict = json.load(load_f)
        search(load_dict, "resolved", links)
    return links


def yarn_lock(package_lock_path):
    # 通过递归遍历 xxx-yarn.lock 解析下载链接
    links = []
    with open(package_lock_path, 'r', encoding='UTF-8') as load_f:
        for line in load_f:
            if line.find('resolved') >= 0:
                line = line.replace('resolved', '')
                url = line.strip().strip('"')
                links.append(url)
    return links


def search(json_object, key, links):
    # 遍历查找指定的key
    for k in json_object:
        if k == key:
            links.append(json_object[k])
        if isinstance(json_object[k], dict):
            search(json_object[k], key, links)
        if isinstance(json_object[k], list):
            for item in json_object[k]:
                if isinstance(item, dict):
                    search(item, key, links)


def download_file(path, store_path, flag):
    # 根据下载链接下载
    if not Path(store_path).exists():
        os.makedirs(store_path, int('0755'))

    links = []
    if path.endswith("package-lock.json"):
        links = package_lock(path)
    elif path.endswith("yarn.lock"):
        links = yarn_lock(path)
    else:
        links = node_modules(path)
    print("link resolved number:" + str(len(links)))

    for url in links:
        try:
            filename = url.split('/')[-1]
            index = filename.find('?')
            # 去掉 ? 参数和 # 哈希
            if index > 0:
                filename = filename[:index]
            index = filename.find('#')
            if index > 0:
                filename = filename[:index]
            filepath = os.path.join(store_path, filename)
            if not Path(filepath).exists():
                print("download:" + url)
                # 以防以后对请求头做限制
                opener = urllib.request.build_opener()
                opener.addheaders = [('User-agent', 'Mozilla/5.0')]
                urllib.request.install_opener(opener)
                if flag:
                    new_path = os.path.join(os.getcwd(), 'tgz')
                    if not Path(new_path).exists():
                        os.makedirs(new_path, int('0755'))
                    filepath = os.path.join(new_path, filename)
                urllib.request.urlretrieve(url, filepath)
            # else:
                # print("file already exists:", filename)
        except Exception as e:
            print('Error Url:' + url)
            print('Error:', e)


if __name__ == '__main__':
    # 通过 xxx 文件解析对应依赖树
    download_link = os.path.join(os.getcwd(), 'package-lock.json')
    # 下载文件存放的路径
    download_path = os.path.join(os.getcwd(), 'node')
    # 下载文件是否存放到一个新的路径里,默认存放到 node 目录下, download_flag 为 True 时 存放到 tgz 目录下
    download_flag = False
    download_file(download_link, download_path, download_flag)
    print("ok")
  • 运行脚本,执行 python download.py,也可以修改 scripts 命令来执行。 执行后我们可以看到在 node 目录下有了两个 .tgz 包。

image-20230614174518201

  • 如有报错,请将当前目录下的 nodetgz 文件夹删除后重新执行脚本。
3. 上传依赖
  • package.json 同级目录下创建 publish.sh
#!/bin/bash
PACKAGE_PATH=./node
REPOSITORY=http://localhost:8081/repository/vue-cms/
npm login --registry=$REPOSITORY
for file in $PACKAGE_PATH/*.tgz; do
 npm publish --registry=$REPOSITORY $file
done

如图所示:

image-20230614175634242

  • 在当前项目下,打开 Git Bash,运行 ./publish.sh。如图所示:

image-20230614175813158

  • 输入用户名 admin 和 密码 admin123 以及邮箱 123@qq.com(随意),回车。

image-20230614180059071

  • 靠,报错了401!,如图:

image-20230614180307080

  • 在 nexus 平台上添加 npm Bearer Token Realm

image-20230614180512129

  • 再次执行 ./publish.sh, 成功!

  • 我们可以去 nexus 平台上找到 vue-cms 仓库点进去看到我们已经上传的依赖。如图所示:

image-20230614180747868

测试 npm install

既然 npm 依赖已经上传到了私库上了接下来我们测试一下是否可以成功下载。

  • 删除 vue-cms 项目的 node_modules
  • 创建 .npmrc 文件,指定下载路径。
registry=http://localhost:8081/repository/vue-cms/

如图所示:

image-20230614181219095

  • 打开终端,执行 npm install,成功!

如图所示:

image-20230614181344230

常遇问题

1. 内网上传的 tgz 包无法下载成功

原因:

个别包(这里以 nw@0.36.4-sdk 为例),下载时会自动去它的官网下载安装,内网无法访问导致下载失败。

解决方案:

将下载的 nw-0.36.4-sdk-tgz 解压,其内是 package 文件夹。 阅读其 package.json 文件,发现 "scripts.postinstall": "node scripts/install.js" 走的是 scripts 下的install.js 文件。 阅读 install.js 文件, 发现其 urlBase 为 官网源,将其修改为 我们 nginx 文件下载的服务器,如 http://IP:Port/nwjs/nwjs-sdk-v0.36.4-win-x64.zip, 这里的压缩包是 nw-0.36.4-sdk 的可执行包,点击下载,并将 url = urlBase,注释其平台类型判断代码。 随后在 package 文件夹下新增 .npmrc 文件, 设置 registry 源为内网的 nexus 库, 设置完毕后执行 npm pulish进行推送。 推送成功后即可测试下载。