From 374aa9dcea15cd2deee85b78a1be3368db37c353 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ir=C3=A9n=C3=A9e?= Date: Thu, 18 Sep 2025 12:05:00 +0100 Subject: [PATCH 1/2] fix: Always generate tags.json file when using environments (#19698) --- .../source-control-export.service.test.ts | 45 +++++++++++++++++-- .../source-control-export.service.ee.ts | 17 ++++--- 2 files changed, 49 insertions(+), 13 deletions(-) diff --git a/packages/cli/src/environments.ee/source-control/__tests__/source-control-export.service.test.ts b/packages/cli/src/environments.ee/source-control/__tests__/source-control-export.service.test.ts index 0ad2df5bf3689..900adc72da7f0 100644 --- a/packages/cli/src/environments.ee/source-control/__tests__/source-control-export.service.test.ts +++ b/packages/cli/src/environments.ee/source-control/__tests__/source-control-export.service.test.ts @@ -9,6 +9,8 @@ import type { SharedCredentialsRepository, SharedWorkflowRepository, WorkflowRepository, + TagEntity, + WorkflowTagMapping, } from '@n8n/db'; import { Container } from '@n8n/di'; import { mock, captor } from 'jest-mock-extended'; @@ -181,27 +183,62 @@ describe('SourceControlExportService', () => { describe('exportTagsToWorkFolder', () => { it('should export tags to work folder', async () => { // Arrange - tagRepository.find.mockResolvedValue([mock()]); - workflowTagMappingRepository.find.mockResolvedValue([mock()]); + const mockTag = mock({ + id: 'tag1', + name: 'Tag 1', + createdAt: new Date(), + updatedAt: new Date(), + }); + + const mockWorkflow = mock({ + tagId: 'tag1', + workflowId: 'workflow1', + }); + tagRepository.find.mockResolvedValue([mockTag]); + workflowTagMappingRepository.find.mockResolvedValue([mockWorkflow]); + const fileName = '/mock/n8n/git/tags.json'; // Act const result = await service.exportTagsToWorkFolder(globalAdminContext); // Assert + expect(fsWriteFile).toHaveBeenCalledWith( + fileName, + JSON.stringify( + { + tags: [ + { + id: mockTag.id, + name: mockTag.name, + }, + ], + mappings: [mockWorkflow], + }, + null, + 2, + ), + ); expect(result.count).toBe(1); expect(result.files).toHaveLength(1); + expect(result.files[0]).toMatchObject({ id: '', name: fileName }); }); - it('should not export empty tags', async () => { + it('should clear tags file and export it when there are no tags', async () => { // Arrange tagRepository.find.mockResolvedValue([]); + const fileName = '/mock/n8n/git/tags.json'; // Act const result = await service.exportTagsToWorkFolder(globalAdminContext); // Assert + expect(fsWriteFile).toHaveBeenCalledWith( + fileName, + JSON.stringify({ tags: [], mappings: [] }, null, 2), + ); expect(result.count).toBe(0); - expect(result.files).toHaveLength(0); + expect(result.files).toHaveLength(1); + expect(result.files[0]).toMatchObject({ id: '', name: fileName }); }); }); diff --git a/packages/cli/src/environments.ee/source-control/source-control-export.service.ee.ts b/packages/cli/src/environments.ee/source-control/source-control-export.service.ee.ts index e8646e0e38deb..58a4bd83aa606 100644 --- a/packages/cli/src/environments.ee/source-control/source-control-export.service.ee.ts +++ b/packages/cli/src/environments.ee/source-control/source-control-export.service.ee.ts @@ -305,14 +305,17 @@ export class SourceControlExportService { async exportTagsToWorkFolder(context: SourceControlContext): Promise { try { + const fileName = path.join(this.gitFolder, SOURCE_CONTROL_TAGS_EXPORT_FILE); sourceControlFoldersExistCheck([this.gitFolder]); const tags = await this.tagRepository.find(); - // do not export empty tags + if (tags.length === 0) { + await fsWriteFile(fileName, JSON.stringify({ tags: [], mappings: [] }, null, 2)); + return { count: 0, folder: this.gitFolder, - files: [], + files: [{ id: '', name: fileName }], }; } const mappingsOfAllowedWorkflows = await this.workflowTagMappingRepository.find({ @@ -321,11 +324,12 @@ export class SourceControlExportService { context, ), }); + const allowedWorkflows = await this.workflowRepository.find({ where: this.sourceControlScopedService.getWorkflowsInAdminProjectsFromContextFilter(context), }); - const fileName = path.join(this.gitFolder, SOURCE_CONTROL_TAGS_EXPORT_FILE); + const existingTagsAndMapping = await readTagAndMappingsFromSourceControlFile(fileName); // keep all mappings that are not accessible by the current user @@ -350,12 +354,7 @@ export class SourceControlExportService { return { count: tags.length, folder: this.gitFolder, - files: [ - { - id: '', - name: fileName, - }, - ], + files: [{ id: '', name: fileName }], }; } catch (error) { this.logger.error('Failed to export tags to work folder', { error }); From c44a3b1d6e564930a21f734977cbe07242360973 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 18 Sep 2025 15:03:03 +0200 Subject: [PATCH 2/2] :rocket: Release 1.112.2 (#19709) Co-authored-by: r00gm <22072110+r00gm@users.noreply.github.com> --- CHANGELOG.md | 9 +++++++++ package.json | 2 +- packages/cli/package.json | 2 +- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8aaa566f288bb..7b5eda39389b4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,12 @@ +## [1.112.2](https://github.com/n8n-io/n8n/compare/n8n@1.112.1...n8n@1.112.2) (2025-09-18) + + +### Bug Fixes + +* Always generate tags.json file when using environments ([#19698](https://github.com/n8n-io/n8n/issues/19698)) ([374aa9d](https://github.com/n8n-io/n8n/commit/374aa9dcea15cd2deee85b78a1be3368db37c353)) + + + ## [1.112.1](https://github.com/n8n-io/n8n/compare/n8n@1.112.0...n8n@1.112.1) (2025-09-17) diff --git a/package.json b/package.json index 794b4d9d916e0..21818b12207fb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "n8n-monorepo", - "version": "1.112.1", + "version": "1.112.2", "private": true, "engines": { "node": ">=22.16", diff --git a/packages/cli/package.json b/packages/cli/package.json index 2de7907684f95..3969c8a8be945 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "n8n", - "version": "1.112.1", + "version": "1.112.2", "description": "n8n Workflow Automation Tool", "main": "dist/index", "types": "dist/index.d.ts",