You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

无网络环境下如何将本地node_modules上传至Nexus搭建私有npm仓库?

Great question! Since your Jenkins server doesn't have network access and you can't use an npm proxy, uploading packages from your local node_modules to a Nexus private npm repository is a perfect workaround. Let's walk through exactly how to do this, including the required package structure and step-by-step workflows.

1. First: Set Up Your Nexus npm Hosted Repository

Before uploading anything, make sure you have a Hosted-type npm repository configured in Nexus:

  • Log into your Nexus UI, go to Repositories → Create repository → Select npm (hosted).
  • Configure basic settings (name, storage location, etc.), and ensure the repository allows deployments (set Deployment policy to "Allow redeploy" if you might need to update packages later).
  • Note down the repository's URL (e.g., http://your-nexus-instance:8081/repository/npm-private/).
  • Make sure you have a Nexus user account with deploy permissions for this repository.

2. Required Package Structure: Don't Upload node_modules Directly

Nexus's npm repository only recognizes standard npm package archives (.tgz files) — you can't just upload the raw node_modules folder. Each package must:

  • Be a valid npm package (contains a package.json with required fields like name, version, main, etc.).
  • Be packaged into a .tgz archive using npm pack (this ensures the package follows npm's structural rules, including proper file inclusion/exclusion based on package.json's files field or .npmignore).

3. Batch Package All Valid Packages from node_modules

Your node_modules folder has all the installed packages, but we need to turn each one into a .tgz file. You can automate this with a simple script:

Shell Script (Linux/macOS)

Create a script named pack-packages.sh in the same directory as your node_modules folder:

#!/bin/bash

# Create a folder to store all packaged .tgz files
mkdir -p npm-packages

# Process regular (non-scoped) packages
for dir in node_modules/*/; do
    if [ -f "$dir/package.json" ]; then
        echo "Packing $(basename "$dir")..."
        cd "$dir"
        npm pack > /dev/null 2>&1
        mv *.tgz ../../npm-packages/
        cd ../..
    fi
done

# Process scoped packages (e.g., @babel/core)
for scope_dir in node_modules/@*/*/; do
    if [ -f "$scope_dir/package.json" ]; then
        pkg_name=$(basename "$(dirname "$scope_dir")")/$(basename "$scope_dir")
        echo "Packing $pkg_name..."
        cd "$scope_dir"
        npm pack > /dev/null 2>&1
        mv *.tgz ../../../npm-packages/
        cd ../../..
    fi
done

echo "Done! All packages are in the npm-packages folder."

Run it with:

chmod +x pack-packages.sh && ./pack-packages.sh

Node.js Script (Cross-Platform)

If you prefer a cross-platform option, create pack-packages.js:

const fs = require('fs').promises;
const path = require('path');
const { execSync } = require('child_process');

const rootDir = process.cwd();
const outputDir = path.join(rootDir, 'npm-packages');

async function packPackage(packagePath) {
    try {
        const pkgJson = require(path.join(packagePath, 'package.json'));
        console.log(`Packing ${pkgJson.name}@${pkgJson.version}...`);
        
        process.chdir(packagePath);
        execSync('npm pack', { stdio: 'ignore' });
        
        const tgzFile = `${pkgJson.name.replace('/', '-')}-${pkgJson.version}.tgz`;
        await fs.copyFile(tgzFile, path.join(outputDir, tgzFile));
        await fs.unlink(tgzFile);
        
        process.chdir(rootDir);
    } catch (err) {
        console.error(`Failed to pack ${packagePath}:`, err.message);
    }
}

async function main() {
    await fs.mkdir(outputDir, { recursive: true });
    
    // Process regular packages
    const regularPackages = await fs.readdir(path.join(rootDir, 'node_modules'));
    for (const pkg of regularPackages) {
        const pkgPath = path.join(rootDir, 'node_modules', pkg);
        const stats = await fs.stat(pkgPath);
        if (stats.isDirectory() && await fs.access(path.join(pkgPath, 'package.json')).then(() => true).catch(() => false)) {
            await packPackage(pkgPath);
        }
    }
    
    // Process scoped packages
    const scopes = await fs.readdir(path.join(rootDir, 'node_modules'));
    for (const scope of scopes) {
        if (scope.startsWith('@')) {
            const scopePath = path.join(rootDir, 'node_modules', scope);
            const scopedPackages = await fs.readdir(scopePath);
            for (const pkg of scopedPackages) {
                const pkgPath = path.join(scopePath, pkg);
                const stats = await fs.stat(pkgPath);
                if (stats.isDirectory() && await fs.access(path.join(pkgPath, 'package.json')).then(() => true).catch(() => false)) {
                    await packPackage(pkgPath);
                }
            }
        }
    }
    
    console.log('All packages packed successfully!');
}

main();

Run it with:

node pack-packages.js

4. Batch Upload to Nexus

Once you have all .tgz files in the npm-packages folder, upload them to Nexus:

Step 1: Authenticate with Nexus

First, log npm into your Nexus repository (you only need to do this once):

npm login --registry=http://your-nexus-url/repository/npm-private/

Enter your Nexus username, password, and email when prompted.

Step 2: Batch Upload Script

Create upload-packages.sh (Linux/macOS):

#!/bin/bash

REGISTRY="http://your-nexus-url/repository/npm-private/"

for tgz in npm-packages/*.tgz; do
    echo "Uploading $tgz..."
    npm publish "$tgz" --registry="$REGISTRY"
done

echo "All packages uploaded (or skipped if already present)!"

Run it with:

chmod +x upload-packages.sh && ./upload-packages.sh

Or a Node.js cross-platform script upload-packages.js:

const fs = require('fs').promises;
const path = require('path');
const { execSync } = require('child_process');

const registry = 'http://your-nexus-url/repository/npm-private/';
const packagesDir = path.join(process.cwd(), 'npm-packages');

async function main() {
    const files = await fs.readdir(packagesDir);
    for (const file of files) {
        if (file.endsWith('.tgz')) {
            const tgzPath = path.join(packagesDir, file);
            console.log(`Uploading ${file}...`);
            try {
                execSync(`npm publish "${tgzPath}" --registry=${registry}`, { stdio: 'inherit' });
            } catch (err) {
                console.error(`Failed to upload ${file}:`, err.message);
            }
        }
    }
    console.log('Upload process complete!');
}

main();

Run with:

node upload-packages.js

5. Key Notes & Troubleshooting

  • Duplicate Packages: If a package with the same name and version already exists in Nexus, npm publish will fail. Either delete the existing package in Nexus or set the repository's Deployment policy to "Allow redeploy" (in Nexus repo settings) to overwrite.
  • Invalid Packages: Some packages in node_modules might not be valid npm packages (e.g., missing package.json). The scripts above will skip these, so you can review the error logs to handle them manually if needed.
  • Testing: After uploading, verify you can install packages from Nexus by running npm install <package-name> --registry=http://your-nexus-url/repository/npm-private/ on a machine with access to Nexus.

内容的提问来源于stack exchange,提问作者Benny Afriat

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 03:54:39