mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-16 17:46:45 +00:00
refactor(core): Overhaul community packages installation code (#15104)
This commit is contained in:
committed by
GitHub
parent
b3205c25c7
commit
7d05cb2a37
@@ -3,7 +3,7 @@ import { InstalledNodes } from '@n8n/db';
|
||||
import { InstalledPackages } from '@n8n/db';
|
||||
import axios from 'axios';
|
||||
import { exec } from 'child_process';
|
||||
import { access as fsAccess, mkdir as fsMkdir } from 'fs/promises';
|
||||
import { mkdir as fsMkdir } from 'fs/promises';
|
||||
import { mocked } from 'jest-mock';
|
||||
import { mock } from 'jest-mock-extended';
|
||||
import type { PackageDirectoryLoader } from 'n8n-core';
|
||||
@@ -128,7 +128,6 @@ describe('CommunityPackagesService', () => {
|
||||
|
||||
describe('executeCommand()', () => {
|
||||
beforeEach(() => {
|
||||
mocked(fsAccess).mockReset();
|
||||
mocked(fsMkdir).mockReset();
|
||||
mocked(exec).mockReset();
|
||||
});
|
||||
@@ -147,31 +146,17 @@ describe('CommunityPackagesService', () => {
|
||||
|
||||
await communityPackagesService.executeNpmCommand('ls');
|
||||
|
||||
expect(fsAccess).toHaveBeenCalled();
|
||||
expect(fsMkdir).toHaveBeenCalled();
|
||||
expect(exec).toHaveBeenCalled();
|
||||
expect(fsMkdir).toBeCalledTimes(0);
|
||||
});
|
||||
|
||||
test('should make sure folder exists', async () => {
|
||||
mocked(exec).mockImplementation(execMock);
|
||||
|
||||
await communityPackagesService.executeNpmCommand('ls');
|
||||
expect(fsAccess).toHaveBeenCalled();
|
||||
expect(exec).toHaveBeenCalled();
|
||||
expect(fsMkdir).toBeCalledTimes(0);
|
||||
});
|
||||
|
||||
test('should try to create folder if it does not exist', async () => {
|
||||
mocked(exec).mockImplementation(execMock);
|
||||
mocked(fsAccess).mockImplementation(() => {
|
||||
throw new Error('Folder does not exist.');
|
||||
});
|
||||
|
||||
await communityPackagesService.executeNpmCommand('ls');
|
||||
|
||||
expect(fsAccess).toHaveBeenCalled();
|
||||
expect(exec).toHaveBeenCalled();
|
||||
expect(fsMkdir).toHaveBeenCalled();
|
||||
expect(exec).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('should throw especial error when package is not found', async () => {
|
||||
@@ -187,9 +172,8 @@ describe('CommunityPackagesService', () => {
|
||||
|
||||
await expect(call).rejects.toThrowError(RESPONSE_ERROR_MESSAGES.PACKAGE_NOT_FOUND);
|
||||
|
||||
expect(fsAccess).toHaveBeenCalled();
|
||||
expect(fsMkdir).toHaveBeenCalled();
|
||||
expect(exec).toHaveBeenCalled();
|
||||
expect(fsMkdir).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -406,7 +390,7 @@ describe('CommunityPackagesService', () => {
|
||||
expect(exec).toHaveBeenCalledTimes(1);
|
||||
expect(exec).toHaveBeenNthCalledWith(
|
||||
1,
|
||||
`npm install ${installedPackage.packageName}@latest --registry=some.random.host`,
|
||||
`npm install ${installedPackage.packageName}@latest --audit=false --fund=false --bin-links=false --install-strategy=shallow --omit=dev --omit=optional --omit=peer --registry=some.random.host`,
|
||||
expect.any(Object),
|
||||
expect.any(Function),
|
||||
);
|
||||
|
||||
@@ -4,7 +4,7 @@ import type { InstalledPackages } from '@n8n/db';
|
||||
import { Service } from '@n8n/di';
|
||||
import axios from 'axios';
|
||||
import { exec } from 'child_process';
|
||||
import { access as fsAccess, mkdir as fsMkdir } from 'fs/promises';
|
||||
import { mkdir as fsMkdir } from 'fs/promises';
|
||||
import type { PackageDirectoryLoader } from 'n8n-core';
|
||||
import { InstanceSettings, Logger } from 'n8n-core';
|
||||
import { UnexpectedError, UserError, type PublicInstalledPackage } from 'n8n-workflow';
|
||||
@@ -26,6 +26,14 @@ import { Publisher } from '@/scaling/pubsub/publisher.service';
|
||||
import { toError } from '@/utils';
|
||||
|
||||
const DEFAULT_REGISTRY = 'https://registry.npmjs.org';
|
||||
const NPM_COMMON_ARGS = ['--audit=false', '--fund=false'];
|
||||
const NPM_INSTALL_ARGS = [
|
||||
'--bin-links=false',
|
||||
'--install-strategy=shallow',
|
||||
'--omit=dev',
|
||||
'--omit=optional',
|
||||
'--omit=peer',
|
||||
];
|
||||
|
||||
const {
|
||||
PACKAGE_NAME_NOT_PROVIDED,
|
||||
@@ -134,17 +142,11 @@ export class CommunityPackagesService {
|
||||
NODE_PATH: process.env.NODE_PATH,
|
||||
PATH: process.env.PATH,
|
||||
APPDATA: process.env.APPDATA,
|
||||
NODE_ENV: 'production',
|
||||
},
|
||||
};
|
||||
|
||||
try {
|
||||
await fsAccess(downloadFolder);
|
||||
} catch {
|
||||
await fsMkdir(downloadFolder);
|
||||
// Also init the folder since some versions
|
||||
// of npm complain if the folder is empty
|
||||
await asyncExec('npm init -y', execOptions);
|
||||
}
|
||||
await fsMkdir(downloadFolder, { recursive: true });
|
||||
|
||||
try {
|
||||
const commandResult = await asyncExec(command, execOptions);
|
||||
@@ -326,12 +328,12 @@ export class CommunityPackagesService {
|
||||
});
|
||||
}
|
||||
|
||||
private getNpmRegistry() {
|
||||
private getNpmInstallArgs() {
|
||||
const { registry } = this.globalConfig.nodes.communityPackages;
|
||||
if (registry !== DEFAULT_REGISTRY && !this.license.isCustomNpmRegistryEnabled()) {
|
||||
throw new FeatureNotLicensedError(LICENSE_FEATURES.COMMUNITY_NODES_CUSTOM_REGISTRY);
|
||||
}
|
||||
return registry;
|
||||
return [...NPM_COMMON_ARGS, ...NPM_INSTALL_ARGS, `--registry=${registry}`].join(' ');
|
||||
}
|
||||
|
||||
private async installOrUpdatePackage(
|
||||
@@ -340,7 +342,7 @@ export class CommunityPackagesService {
|
||||
) {
|
||||
const isUpdate = 'installedPackage' in options;
|
||||
const packageVersion = isUpdate || !options.version ? 'latest' : options.version;
|
||||
const command = `npm install ${packageName}@${packageVersion} --registry=${this.getNpmRegistry()}`;
|
||||
const command = `npm install ${packageName}@${packageVersion} ${this.getNpmInstallArgs()}`;
|
||||
|
||||
try {
|
||||
await this.executeNpmCommand(command);
|
||||
@@ -394,7 +396,7 @@ export class CommunityPackagesService {
|
||||
|
||||
async installOrUpdateNpmPackage(packageName: string, packageVersion: string) {
|
||||
await this.executeNpmCommand(
|
||||
`npm install ${packageName}@${packageVersion} --registry=${this.getNpmRegistry()}`,
|
||||
`npm install ${packageName}@${packageVersion} ${this.getNpmInstallArgs()}`,
|
||||
);
|
||||
await this.loadNodesAndCredentials.loadPackage(packageName);
|
||||
await this.loadNodesAndCredentials.postProcessLoaders();
|
||||
|
||||
Reference in New Issue
Block a user