Nexus搭建npm私庫(角色管理、上傳腳本)

安裝Nexus

官網下載

https://www.sonatype.com/products/sonatype-nexus-oss-download

進入官網下載,最新下載方式需要輸入個人信息才能下載了

image-20231206140429489

選擇對應的系統進行下載

  • Windows 推薦也下載 UNIX 版本(Windows 版本配置比較難改)

image-20231206135901989

如果沒有下載,點一下 click here 重新下載,下載還是很快的

image-20231206140624901

Windows安裝

linux 安裝步驟與啟動方式跟 windows 是一樣的,解壓后如下圖

image-20231206141655045

$ tar -zxvf nexus-3.63.0-01-unix.tar.gz
$ cd nexus-3.63.0-01/bin
$ ./nexus /run

注意:nexus 必須使用 Java1.8,我現在用的是 Java17,提示信息讓咱們配置 INSTALL4J_JAVA_HOME

image-20231206145305323

$ vim nexus
INSTALL4J_JAVA_HOME_OVERRIDE=/d/Develop/Java/jdk1.8.0_161

image-20231206150123385

顯示這個之后訪問:http://localhost:8081/,點擊 Sign In

  • 賬號為 admin,密碼粘貼 sonatype-work/nexus3/admin.password 里面的密碼,登錄成功會提示你改密碼

image-20231206150205067

如果想更改啟動端口,可以修改 etc/nexus-default.properties 文件

image-20231206150804664

docker安裝nexus

nexus3 安裝參考:

  • docker-nexus3 GitHub

鏡像說明:

  • sonatype/nexus 是 nexus2 版本
  • sonatype/nexus3 是 nexus3 版本
$ docker pull sonatype/nexus3
$ docker run -d -p 8081:8081 --name nexus sonatype/nexus# 查看 nexus 日志
$ docker logs -f nexus

注意:已經有的容器,直接 docker start 即可

  • -d:在 docker 守護線程運行這個鏡像
  • -p:綁定端口,前面的端口表示宿主機端口,后面的表示容器端口
  • --restart=always:指定 docker 重啟啟動容器,當服務器或者 docker 進程重啟之后,nexus 容器會在 docker 守護進程啟動后由 docker 守護進程啟動容器
  • --name <container-name>:這里是指定了容器建立后的名稱
  • sonatype/nexus3 是鏡像名

查看是否啟動成功

docker ps

報內存不夠的話輸入這個

Memory: 4k page, physical 1006968k(877280k free), swap 0k(0k free)$ docker run -d -p 8081:8081 --name nexus3 --restart=always --platform linux/amd64 -e INSTALL4J_ADD_VM_PARAMS="-Xms256M -Xmx256M -XX:MaxDirectMemorySize=2048M" sonatype/nexus3

用戶權限不夠的話輸入如下內容,之后即可啟動成功

WARNING: ************************************************************
WARNING: Detected execution as "root" user.  This is NOT recommended!
WARNING: ************************************************************
$ cd /opt/sonatype/nexus/bin
$ vi nexus.rc
run_as_user=root
$ vi /etc/profile
export RUN_AS_USER=root
$ vi nexus
run_as_root=true -> run_as_root=false

創建倉庫

倉庫管理

Nexus 倉庫類型分為如下幾種

  1. hosted:本地倉庫,通常我們會部署自己的構件到這一類型的倉庫,如公司的第二方庫
  2. proxy:代理倉庫,它們被用來代理遠程的公共倉庫,如 Maven 中央倉庫
  3. group:倉庫組,用來合并多個 hosted/proxy 倉庫,當你的項目希望在多個 repository 使用資源時就不需要多次引用了,只需要引用一個 group 即可

在創建 repository 之前,最好再創建 Blob Stores 便于統一管理,默認的是 default

image-20231206151333577

選擇 File,名字填寫為 npm

image-20231206151428449

創建倉庫

之后即可創建倉庫

image-20231206151933100

一會兒會創建 npm(group)、npm(hosted)、npm(proxy) 這幾個倉庫

image-20231206151758355

創建npm(hosted)

hosted 宿主倉庫:主要用于部署無法從公共倉庫獲取的構件以及自己或第三方的項目構件

  • 如果是內網情況(無法訪問互聯網)即可當 npm 總倉庫
  • 是否允許重復推送可以根據自己項目情況來考慮

image-20231206153443838

創建npm(proxy)

proxy 代理倉庫:代理公共的遠程倉庫

image-20231206152954330

創建npm(group)

group 倉庫組:通過倉庫組統一管理多個倉庫,這樣我們在項目中直接請求倉庫組即可請求到倉庫組管理的多個倉庫

  • hosted 和 proxy 這兩個都弄好了之后,在通過 group 聚合提供統一的訪問地址

image-20231206153604374

測試

指定 npm 的 registry 為自己的 group 地址,安裝 express 依賴

image-20231206155638015

proxy 地址由于沒有對應依賴會去鏡像源地址下載,之后 proxy 倉庫就有對應依賴了

image-20231206155807939

創建推送包角色

添加角色用戶

點擊 Roles,添加角色權限,搜索 npm- 之后點擊 Transfer All 把所有權限都附上即可

  • 可以根據對應需求賦對應權限,比如只讓上傳不讓修改只賦 edit 即可

image-20231206161750869

之后創建對應用戶,并設置對應權限

image-20231206161935295

點擊 Realms,添加 npm Bearer Token Realm,不然 npm publish 會報 401

image-20231206164136875

添加推送賬號

https://blog.sonatype.com/using-sonatype-nexus-repository-3-part-2-npm-packages

添加推送賬號,常用如下兩種方法

npm adduser

示例:使用 npm adduser 方法

$ npm adduser
Username: admin
Password: admin123
Email: (this IS public): any@email.com

推薦:加 --scope 限制作用域,加 --registry 限制倉庫地址

$ npm adduser --registry=http://localhost:8081/repository/npm_hosted/ --scope=@mynpm
npm notice Log in on http://localhost:8081/repository/npm_hosted/
Username: npm
Email: (this IS public) ll@123.com
Logged in to scope @mynpm on http://localhost:8081/repository/npm_hosted/.
$ npm publish --scope=@mynpm

image-20231206170241797

使用.npmrc

不添加推送賬號也可以使用 .npmrc,創建 .npmrc 文件,將私庫地址粘貼過來

  • 如果想免密登陸推送可以加上 _auth,加密規則:user:password -> base64
registry=http://localhost:8081/repository/npm_hosted/
# 只是舉例,不推薦使用 admin 用戶
_auth=YWRtaW46YWRtaW4xMjM=
email=any@email.com

_auth 加密方式:

  • 可以使用瀏覽器自帶的方法 window.btoawindow.atob

    image-20230613101343312

  • 還可以使用 linux base64 命令

    image-20230613101349194

registry 也可以通過配置 package.json 來實現

{"publishConfig": {"registry": "http://localhost:8081/repository/npm_hosted/"}
}

下載依賴包

python下載

  • 根據 resolved 字段進行下載
# -*-coding:utf-8-*-
import json
import os
import urllib.request
from pathlib import Pathdef 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 linksdef 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 linksdef search(json_object, key, links):# 遍歷查找指定的keyfor 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)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(), 'nodes')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')# 下載文件是否存放到一個新的路徑里,默認存放到 nodes 里download_flag = Truedownload_file(download_link, download_path, download_flag)print("ok")

node下載

  • 使用 npm pack 命令,下載 .tgz 文件
const shell = require('shelljs')
const fs = require('fs')function download(fileNames = []) {shell.cd('download')let count = 0fileNames.forEach(fileName => {const fileExec = shell.exec(`npm pack ${fileName}`, { async: true, silent: true })fileExec.stdout.on('data', () => {++countshell.echo(`>>> ${fileName} 下載完成...`)if (count === fileNames.length) {shell.cd('..')shell.exit(0)}}).on('err', () => {++countshell.echo(`>>> ${fileName} 下載失敗!!!...`)if (count === fileNames.length) {shell.cd('..')shell.exit(0)}})})
}function downloadByPackageJsonLockFile(depLockJsonFile = {}) {const nMap = new Map()const NotMap = new Map()// 總的nodes文件夾,方便下次避免重復下載const downloadedDir = './nodes'const downloadedArr = fs.readdirSync(downloadedDir)function getAllList(depJson) {if (depJson) {Object.keys(depJson).forEach(dep => {const depWithVersion = `${dep}@${depJson[dep].version}`let tgzFormat = `${dep}-${depJson[dep].version}.tgz`// eg: @babel/code-frame-7.14.5.tgz -> babel-code-frame-7.14.5.tgztgzFormat = dep.startsWith('@')? tgzFormat.split('/').join('-').slice(1): tgzFormatif (!nMap.has(depWithVersion) && !downloadedArr.includes(tgzFormat)) {nMap.set(depWithVersion, true)getAllList(depJson[dep].dependencies)} else if (downloadedArr.includes(tgzFormat) && !NotMap.has(tgzFormat)) {NotMap.set(tgzFormat, true)}})}}getAllList(depLockJsonFile.dependencies)shell.echo(`一共${Array.from(NotMap.keys()).length}個依賴包已在${downloadedDir}目錄下存在,不需要重復下載:\n`)shell.echo(`>>> 無需下載列表: \n - ${Array.from(NotMap.keys()).join('\n - ')}...\n`)shell.echo(`一共${Array.from(nMap.keys()).length}個依賴包待下載\n`)shell.echo(`>>> 待下載列表: \n - ${Array.from(nMap.keys()).join('\n - ')}...`)download(Array.from(nMap.keys()))
}const pkgLock = require('./package-lock')
downloadByPackageJsonLockFile(pkgLock)

推送依賴包

shell單線程推送

  • 只使用一個線程推送,我比較常用,一般推送的包不會很多
#!/bin/bash
# 待publish文件夾地址
PACKAGE_PATH=./download
# 前端私庫地址
REPOSITORY=http://localhost:8081/repository/npm_hosted/
npm login --registry=$REPOSITORY
for file in $PACKAGE_PATH/*.tgz; donpm publish --registry=$REPOSITORY $file
done

node多線程推送

多線程推送,上傳包較多可以使用,如果包過多可能會導致電腦卡死

let fs = require('fs')
let path = require('path')
const { exec } = require('child_process')// 前端私庫地址
const registry = 'http://localhost:8081/repository/npm_hosted/'
const publishPosition = `npm publish --registry=${registry}`
// 待publish文件夾地址
const filesDir = './download'fs.readdir(filesDir, (errs, files) => {files.forEach(file => {fs.stat(filesDir + file, function (err, stats) {if (stats.isFile()) {const fullFilePath = path.resolve(__dirname, filesDir + file)console.log(fullFilePath + ' publish 開始')exec(publishPosition + ' ' + fullFilePath, function (error, stdout, stderr) {if (error) {console.error(fullFilePath + ' publish 失敗')} else {console.error(fullFilePath + ' publish 成功')}})}})})
})

nexus API上傳

官網信息見: https://help.sonatype.com/repomanager3/rest-and-integration-api/components-api

#!/bin/bash
# 待publish文件夾地址
PACKAGE_PATH=./download
# 前端私庫服務地址
PUBLISH_RESTFUL=http://localhost:8081/service/rest/v1/components?repository=npm_hostedecho ">>> 文件所在目錄:$PACKAGE_PATH <<<"
dir=$(ls -l $PACKAGE_PATH | awk '/.tgz$/ {print $NF}')
cd $PACKAGE_PATHfor file in $dir
doecho ">>> $PACKAGE_PATH/$file 上傳開始 \n"ret=`curl -u admin:admin123 -X POST "$PUBLISH_RESTFUL" -H "Accept: application/json" -H "Content-Type: multipart/form-data" -F "npm.asset=@$file;type=application/x-compressed"`echo $retecho ">>> $PACKAGE_PATH/$file 上傳完成 \n"
done

問題

內網上傳問題

  1. 分析依賴鎖時,有些包命名重復,導致下載錯誤

    比如:下載 parse-json@4.0.0 鏈接,既有可能下載的是 @types/parse-json 也有可能下載就是 parse-json,如果按文件名進行覆蓋則會導致少傳包

  2. 上傳包時,有些包的 package.json 里面配置 publishConfig

    image-20230823160618665

    • 比如:archiver-5.0.0 里面配置了如上 publishConfig。在內網情況下,使用 npm i --registry=xx,是會報錯的,內網無法訪問到 https://registry.npmjs.org/
      • 目前我想到的解決方案是:把 archiver-5.0.0.tgz 解壓,之后解壓 archiver-5.0.0.tar,修改 package.json 里面的 publishConfig,之后執行 npm publish
    • 比如:builtins@1.0.3 里面也配置了 publishConfig
    • 比如:ahooks@3.7.8ahooks-v3-count-1.0.0hoist-non-react-statics@3.3.2(react-redux@8 的依賴)

    特殊

    • 比如:simple-update-notifier@2.0.0 也是里面配置了 publishConfig,這個包里 scripts 命令里還會有 prepare 鉤子,需要先把它去掉
    • 比如:@ant-design/icons@4.8.1 這個比較有特殊性,它依賴了 @ant-design/icons-svg@4.3.1,這兩個包都配置了 publishConfig,所以需要單獨推送,但是這個包里 scripts 命令里配置了 prepublishOnly 鉤子,需要先把它去掉
    • 比如:antd@5.8.4antd@4.24.13 同時配置了 prepare、prepublishOnly、postpublish 鉤子,需要先把它們去掉
  3. 分析依賴鎖時,包下載不下來,這個就只能用笨方法(缺什么依賴,npm i 之后把對應包 tgz 包下載下來)

    比如:

    • @vue/cli 需要 @types/inquirer@8.1.3@types/accepts@1.3.5body-parser@1.19.2qs@6.9.7@vitejs/plugin-react@4.0.3@types/html-minifier-terser@6.0.0serve-index@1.9.1
    • 其實這個是和第一個原因是一樣的都是名重復了,導致下載不下來

其他問題

E400

倉庫不允許重復推送會報 400,改為 allow redeploy

image-20231206165353153

E401

沒有權限涉及情況較多:

  1. 賬號密碼不對
  2. 檢查對應角色是否有對應權限
  3. 比如 npm 推送以什么方式校驗,npm Bearer Token Realm
  4. token 是否過期,去根目錄修改 .npmrc 文件,將過期 token 刪除

image-20231206162906061

E403

禁止上傳,group 倉庫禁止上傳,上傳到 hosted 即可

image-20231206164947825

E503

倉庫離線,改為在線即可

image-20231206165252356

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/209828.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/209828.shtml
英文地址,請注明出處:http://en.pswp.cn/news/209828.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

chmod 在Linux原生應用開發過程中的簡單應用

chmod命令實質上是用來修改文件或目錄的訪問權限的命令。它通過修改文件或目錄的訪問控制列表&#xff08;Access Control List&#xff0c;ACL&#xff09;來實現權限的更改。 在Linux系統中&#xff0c;每個文件或目錄都有一個表示其權限的數字值&#xff0c;即用三個八進制…

Qt 中的窗口類

目錄 QWidget 設置父對象 窗口位置 測試代碼 窗口尺寸 窗口標題和圖標 信號 槽函數 QDialog 常用API QDialog的子類 QMessageBox QFileDialog QFontDialog QFontDialog類的靜態API QColorDialog 顏色類 QColor 靜態API函數 測試代碼 QInputDialog 靜態函數…

谷歌AI新篇章:Gemini引領股價飆升,挑戰OpenAI霸主地位

導語&#xff1a; “ 在人工智能領域的一場激烈角逐中&#xff0c;谷歌母公司Alphabet以其全新AI大模型Gemini&#xff0c;于周四收盤時引爆股市&#xff0c;股價激增5.3%&#xff0c;市值一日增長超800億美元。這一躍升不僅展示了谷歌在技術創新上的決心&#xff0c;也是對微軟…

GPT4停止訂閱付費了怎么辦? 怎么升級ChatGPT plus?提供解決方案

11月中旬日OpenAI 暫時關閉所有的升級入口之后&#xff0c;很多小伙伴就真的在排隊等待哦。其實有方法可以繞開排隊&#xff0c;直接付費訂閱升級GPT的。趕緊用起來立馬“插隊”成功&#xff01;親測~~~ 一、登錄ChatGPT賬號 1、沒有賬號可以直接注冊一個&#xff0c;流程超級…

前端使用視頻作為背景圖的方法

實現思路 通過 video source 引入視頻&#xff0c;并對視頻播放屬性進行設置&#xff0c;再通過 css 使視頻覆蓋背景即可。 代碼 <!DOCTYPE html> <html> <head> <meta charset"utf-8"> <title>有開發問題可聯系作者</title>…

學習git后,真正在項目中如何使用?

文章目錄 前言下載和安裝Git克隆遠程倉庫PyCharm鏈接本地Git創建分支修改項目工程并提交到本地倉庫推送到遠程倉庫小結 前言 網上學習git的教程&#xff0c;甚至還有很多可視化很好的git教程&#xff0c;入門git也不是什么難事。但我發現&#xff0c;當我真的要從網上克隆一個…

ubuntu18.04安裝pcl1.11.1

一、安裝pcl1.11.1依賴 sudo apt-get update sudo apt-get install git build-essential linux-libc-dev sudo apt-get install cmake cmake-gui sudo apt-get install libusb-1.0-0-dev libusb-dev libudev-dev sudo apt-get install mpi-default-dev openmpi-bin openmpi-…

SQL事務的開啟,提交和回滾

在處理數據庫數據的時候會出現一種情況就是我們刪除兩個關聯的表其中一個表的信息&#xff0c;另一個表也需要改動&#xff0c;但是我們SQL語句在同時更改兩個表的同時&#xff0c;難免會出現一個表修改成功&#xff0c;另一個出現錯誤&#xff0c;這時候表與表之間就會出現矛盾…

webrtc網之sip轉webrtc

OpenSIP是一個開源的SIP&#xff08;Session Initiation Protocol&#xff09;服務器&#xff0c;它提供了一個可擴展的基礎架構&#xff0c;用于建立、終止和管理VoIP&#xff08;Voice over IP&#xff09;通信會話。SIP是一種通信協議&#xff0c;用于建立、修改和終止多媒體…

geolife 筆記:將所有軌跡放入一個DataFrame

單條軌跡的處理&#xff1a;geolife筆記&#xff1a;整理處理單條軌跡-CSDN博客 1 加載數據 import pandas as pd import numpy as np import datetime as dt import osdata_dir Geolife Trajectories 1.3/Data/ 1.1 列出所有文件夾 dirlist os.listdir(data_dir) dirlist…

Esxi登錄超時:“由于不活動超時,您已被注銷“,修改UserVars.HostClientSessionTimeout為0永不超時

Esxi登錄超時:“由于不活動超時&#xff0c;您已被注銷”,修改UserVars.HostClientSessionTimeout為0永不超時 UserVars.HostClientSessionTimeout0永不超時 Esxi網頁登錄后,一段時間不操作就會被注銷 提示: 由于不活動超時&#xff0c;您已被注銷 主機→管理→系統→高級設…

【linux】查看CPU和內存信息

之前咱們一起學習了查看內存的和CPU的命令。 ?mpstat &#xff1a; 【linux】 mpstat 使用 uptime&#xff1a;【Linux】 uptime命令使用 CPU的使用率&#xff1a;【linux】查看CPU的使用率 nmon &#xff1a;【linux】nmon 工具使用 htop &#xff1a;【linux】htop 命令…

文件格式對齊、自定義快捷鍵、idea

文件格式對齊 Shift Alt F 自動格式化代碼的快捷鍵&#xff08;如何配置自動格式化&#xff09; 日常編碼必備idea快捷鍵 [VS Code] 入門-自定鍵盤快捷鍵 文件格式對齊 文件格式對齊通常是通過編輯器或IDE提供的快捷鍵或命令完成的。以下是一些常見編輯器和IDE中進行文件…

四、C#筆記

/// <summary> /// 第七章&#xff1a;創建并管理類和對象 /// </summary> namespace Chapter7 { class Program { public static int Num 0;//7.6.1創建共享字段 public const double PI 3.1415926;//7.6.2使用const關鍵字創建靜態字段…

快速認識什么是:Docker

Docker&#xff0c;一種可以將軟件打包到容器中并在任何環境中可靠運行的工具。但什么是容器以及為什么需要容器呢&#xff1f;今天就來一起學快速入門一下Docker吧&#xff01;希望本文對您有所幫助。 假設您使用 Cobol 構建了一個在某種奇怪風格的 Linux 上運行的應用程序。您…

Linux C語言 41-進程間通信IPC之共享內存

Linux C語言 41-進程間通信IPC之共享內存 本節關鍵字&#xff1a;C語言 進程間通信 共享內存 shared memory 相關庫函數&#xff1a;shmget、shmat、shmdt、shmctl 什么是共享內存&#xff1f; 共享內存&#xff08;Shared Memory&#xff09;指兩個或多個進程共享一個給定的…

InnoDB Architecture MySQL 5.7 vs 8.0

innodb-architecture-5-7 innodb-architecture-8-0 圖片均來源于MySQL官網

【Vue】props與$emit的簡單理解

Vue組件 組件是Vue中不可或缺的一個功能&#xff0c;它可以將一個頁面劃分為多個獨立的內部組件&#xff0c;方便代碼的管理。 定義組件 <body><div id"App"><bcomp></bcomp></div><script>const app Vue.createApp({})cons…

【2023傳智杯-新增場次】第六屆傳智杯程序設計挑戰賽AB組-ABC題復盤解題分析詳解【JavaPythonC++解題筆記】

本文僅為【2023傳智杯-第二場】第六屆傳智杯程序設計挑戰賽-題目解題分析詳解的解題個人筆記,個人解題分析記錄。 本文包含:第六屆傳智杯程序設計挑戰賽題目、解題思路分析、解題代碼、解題代碼詳解 文章目錄 一.前言二.賽題題目A題題目-B題題目-C題題目-二.賽題題解A題題解-…

Servlet should have a mapping

第一種可能&#xff1a; 你就是沒寫Servlet <servlet><servlet-name>SpringMVC</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><!-- 配置springMVC需要加載的配置文件--><init-par…