From 5ed13065f1d9b7c84a2f97a915c754563a04b6c3 Mon Sep 17 00:00:00 2001 From: Michael Kret <88898367+michael-radency@users.noreply.github.com> Date: Mon, 26 May 2025 15:41:13 +0300 Subject: [PATCH 1/3] fix: Community packages update check (#15684) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: कारतोफ्फेलस्क्रिप्ट™ --- packages/cli/src/commands/base-command.ts | 2 +- .../community-packages.service.test.ts | 235 +++++++++++++++--- .../services/community-packages.service.ts | 71 ++++-- .../integration/commands/worker.cmd.test.ts | 3 + 4 files changed, 256 insertions(+), 55 deletions(-) diff --git a/packages/cli/src/commands/base-command.ts b/packages/cli/src/commands/base-command.ts index 0c1db86cbf63c..2709dba98b9be 100644 --- a/packages/cli/src/commands/base-command.ts +++ b/packages/cli/src/commands/base-command.ts @@ -160,7 +160,7 @@ export abstract class BaseCommand extends Command { const { communityPackages } = this.globalConfig.nodes; if (communityPackages.enabled && this.needsCommunityPackages) { const { CommunityPackagesService } = await import('@/services/community-packages.service'); - await Container.get(CommunityPackagesService).checkForMissingPackages(); + await Container.get(CommunityPackagesService).init(); } if (this.needsTaskRunner && this.globalConfig.taskRunners.enabled) { diff --git a/packages/cli/src/services/__tests__/community-packages.service.test.ts b/packages/cli/src/services/__tests__/community-packages.service.test.ts index ac17070e5588e..0ed51c4506c95 100644 --- a/packages/cli/src/services/__tests__/community-packages.service.test.ts +++ b/packages/cli/src/services/__tests__/community-packages.service.test.ts @@ -6,11 +6,12 @@ import { InstalledNodesRepository } from '@n8n/db'; import { InstalledPackagesRepository } from '@n8n/db'; import axios from 'axios'; import { exec } from 'child_process'; -import { mkdir as fsMkdir, readFile, writeFile, rm } from 'fs/promises'; +import { mkdir, readFile, writeFile, rm, access, constants } from 'fs/promises'; import { mocked } from 'jest-mock'; import { mock } from 'jest-mock-extended'; import type { Logger, InstanceSettings, PackageDirectoryLoader } from 'n8n-core'; import type { PublicInstalledPackage } from 'n8n-workflow'; +import { join } from 'node:path'; import { NODE_PACKAGE_PREFIX, @@ -53,29 +54,11 @@ describe('CommunityPackagesService', () => { }, }); const loadNodesAndCredentials = mock(); - - const nodeName = randomName(); const installedNodesRepository = mockInstance(InstalledNodesRepository); - installedNodesRepository.create.mockImplementation(() => { - return Object.assign(new InstalledNodes(), { - name: nodeName, - type: nodeName, - latestVersion: COMMUNITY_NODE_VERSION.CURRENT.toString(), - packageName: 'test', - }); - }); - const installedPackageRepository = mockInstance(InstalledPackagesRepository); - installedPackageRepository.create.mockImplementation(() => { - return Object.assign(new InstalledPackages(), { - packageName: mockPackageName(), - installedVersion: COMMUNITY_PACKAGE_VERSION.CURRENT, - }); - }); - const instanceSettings = mock({ - nodesDownloadDir: '/tmp/n8n-jest-global-downloads', - }); + const nodesDownloadDir = '/tmp/n8n-jest-global-downloads'; + const instanceSettings = mock({ nodesDownloadDir }); const logger = mock(); const publisher = mock(); @@ -90,6 +73,26 @@ describe('CommunityPackagesService', () => { globalConfig, ); + beforeEach(() => { + jest.resetAllMocks(); + loadNodesAndCredentials.postProcessLoaders.mockResolvedValue(undefined); + + const nodeName = randomName(); + installedNodesRepository.create.mockImplementation(() => { + return Object.assign(new InstalledNodes(), { + name: nodeName, + type: nodeName, + latestVersion: COMMUNITY_NODE_VERSION.CURRENT, + }); + }); + installedPackageRepository.create.mockImplementation(() => { + return Object.assign(new InstalledPackages(), { + packageName: mockPackageName(), + installedVersion: COMMUNITY_PACKAGE_VERSION.CURRENT, + }); + }); + }); + describe('parseNpmPackageName()', () => { test('should fail with empty package name', () => { expect(() => communityPackagesService.parseNpmPackageName('')).toThrowError(); @@ -139,9 +142,6 @@ describe('CommunityPackagesService', () => { describe('executeCommand()', () => { beforeEach(() => { - mocked(fsMkdir).mockReset(); - mocked(fsMkdir).mockResolvedValue(undefined); - mocked(exec).mockReset(); mocked(exec).mockImplementation(execMock); }); @@ -159,7 +159,6 @@ describe('CommunityPackagesService', () => { await communityPackagesService.executeNpmCommand('ls'); - expect(fsMkdir).toHaveBeenCalled(); expect(exec).toHaveBeenCalled(); }); @@ -168,7 +167,6 @@ describe('CommunityPackagesService', () => { await communityPackagesService.executeNpmCommand('ls'); - expect(fsMkdir).toHaveBeenCalled(); expect(exec).toHaveBeenCalled(); }); @@ -185,7 +183,6 @@ describe('CommunityPackagesService', () => { await expect(call).rejects.toThrowError(RESPONSE_ERROR_MESSAGES.PACKAGE_NOT_FOUND); - expect(fsMkdir).toHaveBeenCalled(); expect(exec).toHaveBeenCalled(); }); }); @@ -410,7 +407,6 @@ describe('CommunityPackagesService', () => { mocked(exec).mockImplementation(execMockForThisBlock as typeof exec); - mocked(fsMkdir).mockResolvedValue(undefined); mocked(readFile).mockResolvedValue( JSON.stringify({ name: PACKAGE_NAME, @@ -446,10 +442,11 @@ describe('CommunityPackagesService', () => { ); // ASSERT: - expect(exec).toHaveBeenCalledTimes(4); - - expect(rm).toHaveBeenCalledWith(testBlockPackageDir, { recursive: true, force: true }); + expect(rm).toHaveBeenCalledTimes(2); + expect(rm).toHaveBeenNthCalledWith(1, testBlockPackageDir, { recursive: true, force: true }); + expect(rm).toHaveBeenNthCalledWith(2, `${nodesDownloadDir}/n8n-nodes-test-latest.tgz`); + expect(exec).toHaveBeenCalledTimes(3); expect(exec).toHaveBeenNthCalledWith( 1, `npm pack ${PACKAGE_NAME}@latest --registry=${testBlockRegistry} --quiet`, @@ -471,14 +468,7 @@ describe('CommunityPackagesService', () => { expect.any(Function), ); - expect(exec).toHaveBeenNthCalledWith( - 4, - `rm ${testBlockTarballName}`, - { cwd: testBlockDownloadDir }, - expect.any(Function), - ); - - expect(fsMkdir).toHaveBeenCalledWith(testBlockPackageDir, { recursive: true }); + expect(mkdir).toHaveBeenCalledWith(testBlockPackageDir, { recursive: true }); expect(readFile).toHaveBeenCalledWith(`${testBlockPackageDir}/package.json`, 'utf-8'); expect(writeFile).toHaveBeenCalledWith( `${testBlockPackageDir}/package.json`, @@ -534,4 +524,171 @@ describe('CommunityPackagesService', () => { ); }); }); + + describe('ensurePackageJson', () => { + const packageJsonPath = join(nodesDownloadDir, 'package.json'); + + test('should not create package.json if it already exists', async () => { + mocked(access).mockResolvedValue(undefined); + + await communityPackagesService.ensurePackageJson(); + + expect(access).toHaveBeenCalledWith(packageJsonPath, constants.F_OK); + expect(mkdir).not.toHaveBeenCalled(); + expect(writeFile).not.toHaveBeenCalled(); + }); + + test('should create package.json if it does not exist', async () => { + mocked(access).mockRejectedValue(new Error('ENOENT')); + + await communityPackagesService.ensurePackageJson(); + + expect(access).toHaveBeenCalledWith(packageJsonPath, constants.F_OK); + expect(mkdir).toHaveBeenCalledWith(nodesDownloadDir, { recursive: true }); + expect(writeFile).toHaveBeenCalledWith( + packageJsonPath, + JSON.stringify( + { + name: 'installed-nodes', + private: true, + dependencies: {}, + }, + null, + 2, + ), + 'utf-8', + ); + }); + }); + + describe('checkForMissingPackages', () => { + const installedPackage1 = mock({ + packageName: 'package-1', + installedVersion: '1.0.0', + installedNodes: [{ type: 'node-type-1' }], + }); + const installedPackage2 = mock({ + packageName: 'package-2', + installedVersion: '2.0.0', + installedNodes: [{ type: 'node-type-2' }], + }); + + beforeEach(() => { + jest + .spyOn(communityPackagesService, 'installPackage') + .mockResolvedValue({} as InstalledPackages); + }); + + test('should set missingPackages to empty array when no packages are missing', async () => { + const installedPackages = [installedPackage1]; + + installedPackageRepository.find.mockResolvedValue(installedPackages); + loadNodesAndCredentials.isKnownNode.mockReturnValue(true); + + await communityPackagesService.checkForMissingPackages(); + + expect(communityPackagesService.missingPackages).toEqual([]); + expect(communityPackagesService.installPackage).not.toHaveBeenCalled(); + expect(loadNodesAndCredentials.postProcessLoaders).not.toHaveBeenCalled(); + }); + + test('should identify missing packages without reinstalling when reinstallMissing is false', async () => { + const installedPackages = [installedPackage1, installedPackage2]; + + installedPackageRepository.find.mockResolvedValue(installedPackages); + loadNodesAndCredentials.isKnownNode.mockImplementation( + (nodeType) => nodeType === 'node-type-2', + ); + globalConfig.nodes.communityPackages.reinstallMissing = false; + + await communityPackagesService.checkForMissingPackages(); + + expect(communityPackagesService.missingPackages).toEqual(['package-1@1.0.0']); + expect(communityPackagesService.installPackage).not.toHaveBeenCalled(); + expect(loadNodesAndCredentials.postProcessLoaders).not.toHaveBeenCalled(); + expect(logger.warn).toHaveBeenCalled(); + }); + + test('should reinstall missing packages when reinstallMissing is true', async () => { + const installedPackages = [installedPackage1]; + + installedPackageRepository.find.mockResolvedValue(installedPackages); + loadNodesAndCredentials.isKnownNode.mockReturnValue(false); + globalConfig.nodes.communityPackages.reinstallMissing = true; + + await communityPackagesService.checkForMissingPackages(); + + expect(communityPackagesService.installPackage).toHaveBeenCalledWith('package-1', '1.0.0'); + expect(loadNodesAndCredentials.postProcessLoaders).toHaveBeenCalled(); + expect(communityPackagesService.missingPackages).toEqual([]); + expect(logger.info).toHaveBeenCalledWith( + 'Packages reinstalled successfully. Resuming regular initialization.', + ); + }); + + test('should handle failed reinstallations and record missing packages', async () => { + const installedPackages = [installedPackage1]; + + installedPackageRepository.find.mockResolvedValue(installedPackages); + loadNodesAndCredentials.isKnownNode.mockReturnValue(false); + globalConfig.nodes.communityPackages.reinstallMissing = true; + communityPackagesService.installPackage = jest + .fn() + .mockRejectedValue(new Error('Installation failed')); + + await communityPackagesService.checkForMissingPackages(); + + expect(communityPackagesService.installPackage).toHaveBeenCalledWith('package-1', '1.0.0'); + expect(logger.error).toHaveBeenCalledWith('n8n was unable to install the missing packages.'); + expect(communityPackagesService.missingPackages).toEqual(['package-1@1.0.0']); + }); + + test('should handle multiple missing packages and stop reinstalling after first failure', async () => { + const installedPackages = [installedPackage1, installedPackage2]; + + installedPackageRepository.find.mockResolvedValue(installedPackages); + loadNodesAndCredentials.isKnownNode.mockReturnValue(false); + globalConfig.nodes.communityPackages.reinstallMissing = true; + + // First installation succeeds, second fails + communityPackagesService.installPackage = jest + .fn() + .mockResolvedValueOnce({} as InstalledPackages) + .mockRejectedValueOnce(new Error('Installation failed')); + + await communityPackagesService.checkForMissingPackages(); + + expect(communityPackagesService.installPackage).toHaveBeenCalledWith('package-1', '1.0.0'); + expect(communityPackagesService.installPackage).toHaveBeenCalledWith('package-2', '2.0.0'); + expect(logger.error).toHaveBeenCalledWith('n8n was unable to install the missing packages.'); + expect(communityPackagesService.missingPackages).toEqual(['package-2@2.0.0']); + }); + }); + + describe('updatePackageJsonDependency', () => { + beforeEach(() => { + jest.clearAllMocks(); + mocked(readFile).mockResolvedValue(JSON.stringify({ dependencies: {} })); + }); + + test('should update package dependencies', async () => { + await communityPackagesService.updatePackageJsonDependency('test-package', '1.0.0'); + + expect(writeFile).toHaveBeenCalledWith( + `${nodesDownloadDir}/package.json`, + JSON.stringify({ dependencies: { 'test-package': '1.0.0' } }, null, 2), + 'utf-8', + ); + }); + + test('should create file and update package dependencies', async () => { + await communityPackagesService.updatePackageJsonDependency('test-package', '1.0.0'); + + expect(writeFile).toHaveBeenCalledWith( + `${nodesDownloadDir}/package.json`, + JSON.stringify({ dependencies: { 'test-package': '1.0.0' } }, null, 2), + 'utf-8', + ); + }); + }); }); diff --git a/packages/cli/src/services/community-packages.service.ts b/packages/cli/src/services/community-packages.service.ts index ad38d936d82c3..86cb2d3387b8c 100644 --- a/packages/cli/src/services/community-packages.service.ts +++ b/packages/cli/src/services/community-packages.service.ts @@ -5,10 +5,11 @@ import { InstalledPackagesRepository } from '@n8n/db'; import { Service } from '@n8n/di'; import axios from 'axios'; import { exec } from 'child_process'; -import { mkdir, readFile, writeFile, rm } from 'fs/promises'; +import { access, constants, mkdir, readFile, rm, writeFile } from 'fs/promises'; import type { PackageDirectoryLoader } from 'n8n-core'; import { InstanceSettings, Logger } from 'n8n-core'; -import { UnexpectedError, UserError, type PublicInstalledPackage } from 'n8n-workflow'; +import { jsonParse, UnexpectedError, UserError, type PublicInstalledPackage } from 'n8n-workflow'; +import { join } from 'path'; import { promisify } from 'util'; import { @@ -56,12 +57,22 @@ const asyncExec = promisify(exec); const INVALID_OR_SUSPICIOUS_PACKAGE_NAME = /[^0-9a-z@\-./]/; +type PackageJson = { + name: 'installed-nodes'; + private: true; + dependencies: Record; +}; + @Service() export class CommunityPackagesService { reinstallMissingPackages = false; missingPackages: string[] = []; + private readonly downloadFolder = this.instanceSettings.nodesDownloadDir; + + private readonly packageJsonPath = join(this.downloadFolder, 'package.json'); + constructor( private readonly instanceSettings: InstanceSettings, private readonly logger: Logger, @@ -72,6 +83,11 @@ export class CommunityPackagesService { private readonly globalConfig: GlobalConfig, ) {} + async init() { + await this.ensurePackageJson(); + await this.checkForMissingPackages(); + } + get hasMissingPackages() { return this.missingPackages.length > 0; } @@ -136,10 +152,8 @@ export class CommunityPackagesService { /** @deprecated */ async executeNpmCommand(command: string, options?: { doNotHandleError?: boolean }) { - const downloadFolder = this.instanceSettings.nodesDownloadDir; - const execOptions = { - cwd: downloadFolder, + cwd: this.downloadFolder, env: { NODE_PATH: process.env.NODE_PATH, PATH: process.env.PATH, @@ -148,8 +162,6 @@ export class CommunityPackagesService { }, }; - await mkdir(downloadFolder, { recursive: true }); - try { const commandResult = await asyncExec(command, execOptions); @@ -264,6 +276,20 @@ export class CommunityPackagesService { } } + async ensurePackageJson() { + try { + await access(this.packageJsonPath, constants.F_OK); + } catch { + await mkdir(this.downloadFolder, { recursive: true }); + const packageJson: PackageJson = { + name: 'installed-nodes', + private: true, + dependencies: {}, + }; + await writeFile(this.packageJsonPath, JSON.stringify(packageJson, null, 2), 'utf-8'); + } + } + async checkForMissingPackages() { const installedPackages = await this.getAllInstalledPackages(); const missingPackages = new Set<{ packageName: string; version: string }>(); @@ -435,13 +461,11 @@ export class CommunityPackagesService { } private resolvePackageDirectory(packageName: string) { - const downloadFolder = this.instanceSettings.nodesDownloadDir; - return `${downloadFolder}/node_modules/${packageName}`; + return `${this.downloadFolder}/node_modules/${packageName}`; } private async downloadPackage(packageName: string, packageVersion: string): Promise { const registry = this.getNpmRegistry(); - const downloadFolder = this.instanceSettings.nodesDownloadDir; const packageDirectory = this.resolvePackageDirectory(packageName); // (Re)create the packageDir @@ -453,27 +477,37 @@ export class CommunityPackagesService { const { stdout: tarOutput } = await asyncExec( `npm pack ${packageName}@${packageVersion} --registry=${registry} --quiet`, - { cwd: downloadFolder }, + { cwd: this.downloadFolder }, ); const tarballName = tarOutput?.trim(); try { await asyncExec(`tar -xzf ${tarballName} -C ${packageDirectory} --strip-components=1`, { - cwd: downloadFolder, + cwd: this.downloadFolder, }); // Strip dev, optional, and peer dependencies before running `npm install` const packageJsonPath = `${packageDirectory}/package.json`; const packageJsonContent = await readFile(packageJsonPath, 'utf-8'); // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - const { devDependencies, peerDependencies, optionalDependencies, ...packageJson } = - JSON.parse(packageJsonContent); + const { + devDependencies, + peerDependencies, + optionalDependencies, + ...packageJson + }: { + version: string; + devDependencies: Record; + peerDependencies: Record; + optionalDependencies: Record; + } = JSON.parse(packageJsonContent); await writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2), 'utf-8'); await asyncExec(`npm install ${this.getNpmInstallArgs()}`, { cwd: packageDirectory }); + await this.updatePackageJsonDependency(packageName, packageJson.version); } finally { - await asyncExec(`rm ${tarballName}`, { cwd: downloadFolder }); + await rm(join(this.downloadFolder, tarballName)); } return packageDirectory; @@ -483,4 +517,11 @@ export class CommunityPackagesService { const packageDirectory = this.resolvePackageDirectory(packageName); await rm(packageDirectory, { recursive: true, force: true }); } + + async updatePackageJsonDependency(packageName: string, version: string) { + const existingContent = await readFile(this.packageJsonPath, 'utf-8'); + const packageJson = jsonParse(existingContent); + packageJson.dependencies[packageName] = version; + await writeFile(this.packageJsonPath, JSON.stringify(packageJson, null, 2), 'utf-8'); + } } diff --git a/packages/cli/test/integration/commands/worker.cmd.test.ts b/packages/cli/test/integration/commands/worker.cmd.test.ts index a21da2ac235de..cb7c46146c134 100644 --- a/packages/cli/test/integration/commands/worker.cmd.test.ts +++ b/packages/cli/test/integration/commands/worker.cmd.test.ts @@ -16,6 +16,7 @@ import { Push } from '@/push'; import { Publisher } from '@/scaling/pubsub/publisher.service'; import { Subscriber } from '@/scaling/pubsub/subscriber.service'; import { ScalingService } from '@/scaling/scaling.service'; +import { CommunityPackagesService } from '@/services/community-packages.service'; import { TaskBrokerServer } from '@/task-runners/task-broker/task-broker-server'; import { TaskRunnerProcess } from '@/task-runners/task-runner-process'; import { Telemetry } from '@/telemetry'; @@ -28,6 +29,7 @@ config.set('binaryDataManager.availableModes', 'filesystem'); Container.get(TaskRunnersConfig).enabled = true; mockInstance(LoadNodesAndCredentials); const binaryDataService = mockInstance(BinaryDataService); +const communityPackagesService = mockInstance(CommunityPackagesService); const externalHooks = mockInstance(ExternalHooks); const externalSecretsManager = mockInstance(ExternalSecretsManager); const license = mockInstance(License, { loadCertStr: async () => '' }); @@ -50,6 +52,7 @@ test('worker initializes all its components', async () => { expect(license.init).toHaveBeenCalledTimes(1); expect(binaryDataService.init).toHaveBeenCalledTimes(1); + expect(communityPackagesService.init).toHaveBeenCalledTimes(1); expect(externalHooks.init).toHaveBeenCalledTimes(1); expect(externalSecretsManager.init).toHaveBeenCalledTimes(1); expect(messageEventBus.initialize).toHaveBeenCalledTimes(1); From 4e61ff7e842802685e9e739cd3742c58de028d30 Mon Sep 17 00:00:00 2001 From: jeanpaul Date: Tue, 27 May 2025 11:20:50 +0200 Subject: [PATCH 2/3] fix(editor): Change feature flag for evaluations (no-changelog) (#15728) --- packages/frontend/editor-ui/src/constants.ts | 2 +- packages/frontend/editor-ui/src/stores/evaluation.store.ee.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/frontend/editor-ui/src/constants.ts b/packages/frontend/editor-ui/src/constants.ts index 7d0e3f3f1db08..3f5d3789e8e29 100644 --- a/packages/frontend/editor-ui/src/constants.ts +++ b/packages/frontend/editor-ui/src/constants.ts @@ -741,7 +741,7 @@ export const EXPERIMENTS_TO_TRACK = [ WORKFLOW_BUILDER_EXPERIMENT.name, ]; -export const WORKFLOW_EVALUATION_EXPERIMENT = '025_workflow_evaluation'; +export const WORKFLOW_EVALUATION_EXPERIMENT = '032_evaluation_mvp'; export const MFA_FORM = { MFA_TOKEN: 'MFA_TOKEN', diff --git a/packages/frontend/editor-ui/src/stores/evaluation.store.ee.ts b/packages/frontend/editor-ui/src/stores/evaluation.store.ee.ts index 2b4ea06e88b4c..a0903a74ac74a 100644 --- a/packages/frontend/editor-ui/src/stores/evaluation.store.ee.ts +++ b/packages/frontend/editor-ui/src/stores/evaluation.store.ee.ts @@ -30,7 +30,7 @@ export const useEvaluationStore = defineStore( // Computed - // Enable with `window.featureFlags.override('025_workflow_evaluation', true)` + // Enable with `window.featureFlags.override('032_evaluation_mvp', true)` const isFeatureEnabled = computed(() => posthogStore.isFeatureEnabled(WORKFLOW_EVALUATION_EXPERIMENT), ); From 2946ce6cc4d714d8db6798de19969c8043a30bab Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 27 May 2025 12:07:22 +0200 Subject: [PATCH 3/3] :rocket: Release 1.95.1 (#15735) Co-authored-by: elsmr <8850410+elsmr@users.noreply.github.com> --- CHANGELOG.md | 9 +++++++++ package.json | 2 +- packages/cli/package.json | 2 +- packages/frontend/editor-ui/package.json | 2 +- 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8fb145e9cacfd..2fc722a4bb11f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,12 @@ +## [1.95.1](https://github.com/n8n-io/n8n/compare/n8n@1.95.0...n8n@1.95.1) (2025-05-27) + + +### Bug Fixes + +* Community packages update check ([#15684](https://github.com/n8n-io/n8n/issues/15684)) ([5ed1306](https://github.com/n8n-io/n8n/commit/5ed13065f1d9b7c84a2f97a915c754563a04b6c3)) + + + # [1.95.0](https://github.com/n8n-io/n8n/compare/n8n@1.94.0...n8n@1.95.0) (2025-05-26) diff --git a/package.json b/package.json index 7034e91058ac6..89e917fe0b879 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "n8n-monorepo", - "version": "1.95.0", + "version": "1.95.1", "private": true, "engines": { "node": ">=20.15", diff --git a/packages/cli/package.json b/packages/cli/package.json index 549095b3bf372..d859eed3350e1 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "n8n", - "version": "1.95.0", + "version": "1.95.1", "description": "n8n Workflow Automation Tool", "main": "dist/index", "types": "dist/index.d.ts", diff --git a/packages/frontend/editor-ui/package.json b/packages/frontend/editor-ui/package.json index b03cfe919a78f..e9c296e7d324b 100644 --- a/packages/frontend/editor-ui/package.json +++ b/packages/frontend/editor-ui/package.json @@ -1,6 +1,6 @@ { "name": "n8n-editor-ui", - "version": "1.95.0", + "version": "1.95.1", "description": "Workflow Editor UI for n8n", "main": "index.js", "scripts": {