From 1a82b957fc561d35ef6a7787a628787079c76148 Mon Sep 17 00:00:00 2001 From: javierbrea Date: Tue, 10 Jun 2025 10:46:57 +0200 Subject: [PATCH 1/8] feat: Add dryRun option to markdown-confluence-sync --- .github/workflows/test-e2e.yml | 1 + .../markdown-confluence-sync/CHANGELOG.md | 6 ++ components/markdown-confluence-sync/README.md | 1 + .../markdown-confluence-sync.config.cjs | 1 + .../markdown-confluence-sync/package.json | 2 +- .../src/lib/MarkdownConfluenceSync.ts | 27 ++++++++- .../src/lib/MarkdownConfluenceSync.types.ts | 12 ++++ .../component/fixtures/dry-run/docs/foo.md | 6 ++ .../markdown-confluence-sync.config.cjs | 13 ++++ .../test/component/specs/dryRun.spec.ts | 59 +++++++++++++++++++ .../unit/specs/MarkdownConfluenceSync.test.ts | 14 +++++ 11 files changed, 138 insertions(+), 4 deletions(-) create mode 100644 components/markdown-confluence-sync/test/component/fixtures/dry-run/docs/foo.md create mode 100644 components/markdown-confluence-sync/test/component/fixtures/dry-run/markdown-confluence-sync.config.cjs create mode 100644 components/markdown-confluence-sync/test/component/specs/dryRun.spec.ts diff --git a/.github/workflows/test-e2e.yml b/.github/workflows/test-e2e.yml index 595447c8..a354a6a5 100644 --- a/.github/workflows/test-e2e.yml +++ b/.github/workflows/test-e2e.yml @@ -7,6 +7,7 @@ on: push: branches: - main + - feat/dry-run permissions: contents: read diff --git a/components/markdown-confluence-sync/CHANGELOG.md b/components/markdown-confluence-sync/CHANGELOG.md index 018d8609..45f1eb50 100644 --- a/components/markdown-confluence-sync/CHANGELOG.md +++ b/components/markdown-confluence-sync/CHANGELOG.md @@ -11,6 +11,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 #### Deprecated #### Removed +## [2.1.0] - 2025-06-10 + +### Added + +* feat: Add `dryRun` option, enabling to run the sync process without actually sending the content to Confluence. This is useful for testing the configuration and markdown files without making changes in Confluence. + ## [2.0.1] - 2025-04-15 ### Changed diff --git a/components/markdown-confluence-sync/README.md b/components/markdown-confluence-sync/README.md index 2d0578c7..f43e4db6 100644 --- a/components/markdown-confluence-sync/README.md +++ b/components/markdown-confluence-sync/README.md @@ -283,6 +283,7 @@ The namespace for the configuration of this library is `markdown-confluence-sync | `confluence.noticeMessage` | `string` | Notice message to add at the beginning of the Confluence pages. | | | `confluence.noticeTemplate` | `string` | Template string to use for the notice message. | | | `confluence.dryRun` | `boolean` | Log create, update or delete requests to Confluence instead of really making them | `false` | +| `dryRun` | `boolean` | Process markdown files without sending them to confluence-sync. Useful to early detection of possible errors in configuration, etc. | `false` | | `config.readArguments` | `boolean` | Read configuration from arguments or not | `false` | | `config.readFile` | `boolean` | Read configuration from file or not | `false` | | `config.readEnvironment` | `boolean` | Read configuration from environment or not | `false` | diff --git a/components/markdown-confluence-sync/markdown-confluence-sync.config.cjs b/components/markdown-confluence-sync/markdown-confluence-sync.config.cjs index 4b4b21b9..f3b733a9 100644 --- a/components/markdown-confluence-sync/markdown-confluence-sync.config.cjs +++ b/components/markdown-confluence-sync/markdown-confluence-sync.config.cjs @@ -16,6 +16,7 @@ module.exports = { mode: "flat", docsDir: ".", filesPattern: "*(README.md|CHANGELOG.md)", + dryRun: true, // TODO: Remove before merging to main. Just added to check if the config is working in CI. filesMetadata: [ { path: "README.md", diff --git a/components/markdown-confluence-sync/package.json b/components/markdown-confluence-sync/package.json index e12e6d62..ebb7df51 100644 --- a/components/markdown-confluence-sync/package.json +++ b/components/markdown-confluence-sync/package.json @@ -1,7 +1,7 @@ { "name": "@telefonica/markdown-confluence-sync", "description": "Creates/updates/deletes Confluence pages based on markdown files in a directory. Supports Mermaid diagrams and per-page configuration using frontmatter metadata. Works great with Docusaurus", - "version": "2.0.1", + "version": "2.1.0", "license": "Apache-2.0", "author": "Telefónica Innovación Digital", "repository": { diff --git a/components/markdown-confluence-sync/src/lib/MarkdownConfluenceSync.ts b/components/markdown-confluence-sync/src/lib/MarkdownConfluenceSync.ts index af2b2789..2b979fae 100644 --- a/components/markdown-confluence-sync/src/lib/MarkdownConfluenceSync.ts +++ b/components/markdown-confluence-sync/src/lib/MarkdownConfluenceSync.ts @@ -34,6 +34,8 @@ import type { FilesMetadataOption, ContentPreprocessorOptionDefinition, ContentPreprocessorOption, + MainDryRunOptionDefinition, + MainDryRunOption, } from "./MarkdownConfluenceSync.types.js"; const MODULE_NAME = "markdown-confluence-sync"; @@ -78,6 +80,13 @@ const contentPreprocessorOption: ContentPreprocessorOptionDefinition = { type: "unknown", }; +const dryRunOption: MainDryRunOptionDefinition = { + name: "dryRun", + type: "boolean", + default: false, + description: "Process markdown files without sending them to confluence-sync", +}; + export const MarkdownConfluenceSync: MarkdownConfluenceSyncConstructor = class MarkdownConfluenceSync implements MarkdownConfluenceSyncInterface { @@ -93,6 +102,7 @@ export const MarkdownConfluenceSync: MarkdownConfluenceSyncConstructor = class M private _filesMetadataOption: FilesMetadataOption; private _filesIgnoreOption: FilesIgnoreOption; private _contentPreprocessorOption: ContentPreprocessorOption; + private _dryRunOption: MainDryRunOption; private _cwd: string; constructor(config: Configuration) { @@ -128,6 +138,10 @@ export const MarkdownConfluenceSync: MarkdownConfluenceSyncConstructor = class M contentPreprocessorOption as ContentPreprocessorOptionDefinition, ) as unknown as ContentPreprocessorOption; + this._dryRunOption = this._configuration.addOption( + dryRunOption as MainDryRunOptionDefinition, + ); + const markdownLogger = this._logger.namespace(MARKDOWN_NAMESPACE); const confluenceConfig = @@ -154,9 +168,16 @@ export const MarkdownConfluenceSync: MarkdownConfluenceSyncConstructor = class M public async sync(): Promise { await this._init(); const pages = await this._markdownDocuments.read(); - await this._confluenceSync.sync( - this._markdownPagesToConfluencePages(pages), - ); + const convertedPages = this._markdownPagesToConfluencePages(pages); + const dryRun = this._dryRunOption.value; + if (dryRun) { + this._logger.info( + "Dry run mode is enabled. No changes will be made to Confluence.", + ); + return; + } + + await this._confluenceSync.sync(convertedPages); } private async _init() { diff --git a/components/markdown-confluence-sync/src/lib/MarkdownConfluenceSync.types.ts b/components/markdown-confluence-sync/src/lib/MarkdownConfluenceSync.types.ts index 53485d4c..f9fc084b 100644 --- a/components/markdown-confluence-sync/src/lib/MarkdownConfluenceSync.types.ts +++ b/components/markdown-confluence-sync/src/lib/MarkdownConfluenceSync.types.ts @@ -27,6 +27,8 @@ export type FilesMetadata = FileMetadata[]; export type ContentPreprocessor = (content: string, path: string) => string; +export type DryRun = boolean; + declare global { //eslint-disable-next-line @typescript-eslint/no-namespace namespace MarkdownConfluenceSync { @@ -61,6 +63,9 @@ declare global { /** Hook enabling to modify the content of files before processing them */ preprocessor?: ContentPreprocessor; + + /** Process markdown files without sending them to confluence-sync */ + dryRun?: DryRun; } } } @@ -103,6 +108,13 @@ export type ContentPreprocessorOptionDefinition = export type ContentPreprocessorOption = OptionInterfaceOfType; +export type MainDryRunOptionDefinition = OptionDefinition< + boolean, + { hasDefault: true } +>; + +export type MainDryRunOption = OptionInterfaceOfType; + /** Creates a MarkdownConfluenceSync interface */ export interface MarkdownConfluenceSyncConstructor { /** Returns MarkdownConfluenceSync interface diff --git a/components/markdown-confluence-sync/test/component/fixtures/dry-run/docs/foo.md b/components/markdown-confluence-sync/test/component/fixtures/dry-run/docs/foo.md new file mode 100644 index 00000000..6e9ffa0d --- /dev/null +++ b/components/markdown-confluence-sync/test/component/fixtures/dry-run/docs/foo.md @@ -0,0 +1,6 @@ +id: foo-ignored-index +title: foo-ignored-index-title +sync_to_confluence: true +--- + +# Hello World diff --git a/components/markdown-confluence-sync/test/component/fixtures/dry-run/markdown-confluence-sync.config.cjs b/components/markdown-confluence-sync/test/component/fixtures/dry-run/markdown-confluence-sync.config.cjs new file mode 100644 index 00000000..55eaab4b --- /dev/null +++ b/components/markdown-confluence-sync/test/component/fixtures/dry-run/markdown-confluence-sync.config.cjs @@ -0,0 +1,13 @@ +// SPDX-FileCopyrightText: 2024 Telefónica Innovación Digital +// SPDX-License-Identifier: MIT + +const path = require("node:path"); + +module.exports = { + confluence: { + url: "https://my-confluence.com", + spaceKey: "CTO", + }, + docsDir: path.join(__dirname, "./docs"), + dryRun: true, +}; diff --git a/components/markdown-confluence-sync/test/component/specs/dryRun.spec.ts b/components/markdown-confluence-sync/test/component/specs/dryRun.spec.ts new file mode 100644 index 00000000..29e88d58 --- /dev/null +++ b/components/markdown-confluence-sync/test/component/specs/dryRun.spec.ts @@ -0,0 +1,59 @@ +// SPDX-FileCopyrightText: 2024 Telefónica Innovación Digital +// SPDX-License-Identifier: Apache-2.0 + +import { ChildProcessManager } from "@telefonica/child-process-manager"; +import type { ChildProcessManagerInterface } from "@telefonica/child-process-manager"; + +import { cleanLogs } from "../support/Logs"; +import { + getFixtureFolder, + getBinaryPathFromFixtureFolder, +} from "../support/Paths"; + +describe("dryRun mode", () => { + let cli: ChildProcessManagerInterface; + + beforeEach(() => { + process.env.MARKDOWN_CONFLUENCE_SYNC_LOG_LEVEL = "debug"; + cli = new ChildProcessManager([getBinaryPathFromFixtureFolder()], { + cwd: getFixtureFolder("dry-run"), + silent: true, + }); + }); + + afterEach(async () => { + await cli.kill(); + }); + + describe("when dryRun option is true", () => { + it("should exit with code 1 when there are errors in markdown", async () => { + const { exitCode, logs } = await cli.run(); + + const allLogs = cleanLogs(logs).join("\n"); + + expect(allLogs).toContain(`Title is required:`); + expect(allLogs).toContain(`dry-run/docs/foo.md`); + expect(allLogs).toContain( + `Please provide it using frontmatter or filesMetadata option`, + ); + expect(exitCode).toBe(1); + }); + + it("should not call to synchronize", async () => { + cli = new ChildProcessManager([getBinaryPathFromFixtureFolder()], { + cwd: getFixtureFolder("mock-server-with-confluence-title"), + silent: true, + env: { + MARKDOWN_CONFLUENCE_SYNC_DRY_RUN: "true", + }, + }); + const { logs } = await cli.run(); + + const allLogs = cleanLogs(logs).join("\n"); + + expect(allLogs).toContain( + `Dry run mode is enabled. No changes will be made to Confluence.`, + ); + }); + }); +}); diff --git a/components/markdown-confluence-sync/test/unit/specs/MarkdownConfluenceSync.test.ts b/components/markdown-confluence-sync/test/unit/specs/MarkdownConfluenceSync.test.ts index a1edf2f5..7ae7d5b9 100644 --- a/components/markdown-confluence-sync/test/unit/specs/MarkdownConfluenceSync.test.ts +++ b/components/markdown-confluence-sync/test/unit/specs/MarkdownConfluenceSync.test.ts @@ -31,6 +31,20 @@ describe("markdownConfluenceSync", () => { ); }); + it("should not call to synchronize if dryRun is true", async () => { + // Arrange + const markdownConfluenceSync = new MarkdownConfluenceSync({ + ...CONFIG, + dryRun: true, + }); + + // Act + await markdownConfluenceSync.sync(); + + // Assert + expect(customConfluenceSync.sync).not.toHaveBeenCalled(); + }); + it("when called twice, it should send to synchronize the pages to confluence twice", async () => { // Arrange const markdownConfluenceSync = new MarkdownConfluenceSync(CONFIG); From 0f7280a469f75dc0fe7f1ae88f7afe82ef091b53 Mon Sep 17 00:00:00 2001 From: javierbrea Date: Tue, 10 Jun 2025 10:53:52 +0200 Subject: [PATCH 2/8] chore: Do not run E2E tests in branch --- .github/workflows/test-e2e.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/test-e2e.yml b/.github/workflows/test-e2e.yml index a354a6a5..595447c8 100644 --- a/.github/workflows/test-e2e.yml +++ b/.github/workflows/test-e2e.yml @@ -7,7 +7,6 @@ on: push: branches: - main - - feat/dry-run permissions: contents: read From b573862fd4215bcab50358144e2a6abd890645cf Mon Sep 17 00:00:00 2001 From: javierbrea Date: Tue, 10 Jun 2025 11:01:59 +0200 Subject: [PATCH 3/8] chore: Remove dryRun option, used for E2E tests of the new option --- .../markdown-confluence-sync/markdown-confluence-sync.config.cjs | 1 - 1 file changed, 1 deletion(-) diff --git a/components/markdown-confluence-sync/markdown-confluence-sync.config.cjs b/components/markdown-confluence-sync/markdown-confluence-sync.config.cjs index f3b733a9..4b4b21b9 100644 --- a/components/markdown-confluence-sync/markdown-confluence-sync.config.cjs +++ b/components/markdown-confluence-sync/markdown-confluence-sync.config.cjs @@ -16,7 +16,6 @@ module.exports = { mode: "flat", docsDir: ".", filesPattern: "*(README.md|CHANGELOG.md)", - dryRun: true, // TODO: Remove before merging to main. Just added to check if the config is working in CI. filesMetadata: [ { path: "README.md", From 227e4413eb61c60e71d2aa993b3fd7b0f2d556bc Mon Sep 17 00:00:00 2001 From: javierbrea Date: Tue, 10 Jun 2025 11:03:44 +0200 Subject: [PATCH 4/8] chore: Use fake identifier in component tests fixture --- .../fixtures/dry-run/markdown-confluence-sync.config.cjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/markdown-confluence-sync/test/component/fixtures/dry-run/markdown-confluence-sync.config.cjs b/components/markdown-confluence-sync/test/component/fixtures/dry-run/markdown-confluence-sync.config.cjs index 55eaab4b..0bb7d0e5 100644 --- a/components/markdown-confluence-sync/test/component/fixtures/dry-run/markdown-confluence-sync.config.cjs +++ b/components/markdown-confluence-sync/test/component/fixtures/dry-run/markdown-confluence-sync.config.cjs @@ -6,7 +6,7 @@ const path = require("node:path"); module.exports = { confluence: { url: "https://my-confluence.com", - spaceKey: "CTO", + spaceKey: "FOO", }, docsDir: path.join(__dirname, "./docs"), dryRun: true, From dbbbd566c5288b8a66c14b90959b25d8127dad28 Mon Sep 17 00:00:00 2001 From: javierbrea Date: Tue, 10 Jun 2025 11:08:41 +0200 Subject: [PATCH 5/8] docs: Clarify difference between dryRun and confluence.dryRun options --- components/markdown-confluence-sync/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/markdown-confluence-sync/README.md b/components/markdown-confluence-sync/README.md index f43e4db6..0f753e4a 100644 --- a/components/markdown-confluence-sync/README.md +++ b/components/markdown-confluence-sync/README.md @@ -283,7 +283,7 @@ The namespace for the configuration of this library is `markdown-confluence-sync | `confluence.noticeMessage` | `string` | Notice message to add at the beginning of the Confluence pages. | | | `confluence.noticeTemplate` | `string` | Template string to use for the notice message. | | | `confluence.dryRun` | `boolean` | Log create, update or delete requests to Confluence instead of really making them | `false` | -| `dryRun` | `boolean` | Process markdown files without sending them to confluence-sync. Useful to early detection of possible errors in configuration, etc. | `false` | +| `dryRun` | `boolean` | Process markdown files without sending them to `confluence-sync`. Useful to early detection of possible errors in configuration, etc. Note that, requests that would be made to Confluence won't be logged, use `confluence.dryRun` for that, which also connects to Confluence to calculate the requests to do | `false` | | `config.readArguments` | `boolean` | Read configuration from arguments or not | `false` | | `config.readFile` | `boolean` | Read configuration from file or not | `false` | | `config.readEnvironment` | `boolean` | Read configuration from environment or not | `false` | From eea6dd15ea4e0c6390938872fdfe12ccff1f69f0 Mon Sep 17 00:00:00 2001 From: javierbrea Date: Tue, 10 Jun 2025 11:11:19 +0200 Subject: [PATCH 6/8] style: Fix lint --- .../src/lib/MarkdownConfluenceSync.types.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/components/markdown-confluence-sync/src/lib/MarkdownConfluenceSync.types.ts b/components/markdown-confluence-sync/src/lib/MarkdownConfluenceSync.types.ts index f9fc084b..95df82e3 100644 --- a/components/markdown-confluence-sync/src/lib/MarkdownConfluenceSync.types.ts +++ b/components/markdown-confluence-sync/src/lib/MarkdownConfluenceSync.types.ts @@ -113,7 +113,10 @@ export type MainDryRunOptionDefinition = OptionDefinition< { hasDefault: true } >; -export type MainDryRunOption = OptionInterfaceOfType; +export type MainDryRunOption = OptionInterfaceOfType< + boolean, + { hasDefault: true } +>; /** Creates a MarkdownConfluenceSync interface */ export interface MarkdownConfluenceSyncConstructor { From 9ea02ff3c8f19ce787d428f723f2c5fb98bc931b Mon Sep 17 00:00:00 2001 From: javierbrea Date: Tue, 10 Jun 2025 11:14:08 +0200 Subject: [PATCH 7/8] chore: Change license year --- .../fixtures/dry-run/markdown-confluence-sync.config.cjs | 2 +- .../test/component/specs/dryRun.spec.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/components/markdown-confluence-sync/test/component/fixtures/dry-run/markdown-confluence-sync.config.cjs b/components/markdown-confluence-sync/test/component/fixtures/dry-run/markdown-confluence-sync.config.cjs index 0bb7d0e5..dd73bd96 100644 --- a/components/markdown-confluence-sync/test/component/fixtures/dry-run/markdown-confluence-sync.config.cjs +++ b/components/markdown-confluence-sync/test/component/fixtures/dry-run/markdown-confluence-sync.config.cjs @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2024 Telefónica Innovación Digital +// SPDX-FileCopyrightText: 2025 Telefónica Innovación Digital // SPDX-License-Identifier: MIT const path = require("node:path"); diff --git a/components/markdown-confluence-sync/test/component/specs/dryRun.spec.ts b/components/markdown-confluence-sync/test/component/specs/dryRun.spec.ts index 29e88d58..459ab4dd 100644 --- a/components/markdown-confluence-sync/test/component/specs/dryRun.spec.ts +++ b/components/markdown-confluence-sync/test/component/specs/dryRun.spec.ts @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2024 Telefónica Innovación Digital +// SPDX-FileCopyrightText: 2025 Telefónica Innovación Digital // SPDX-License-Identifier: Apache-2.0 import { ChildProcessManager } from "@telefonica/child-process-manager"; From 252eb1a74a029865c1c5f7e375025b71a002ab93 Mon Sep 17 00:00:00 2001 From: javierbrea Date: Tue, 10 Jun 2025 11:19:47 +0200 Subject: [PATCH 8/8] test: Fix unit test --- .../test/unit/specs/MarkdownConfluenceSync.test.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/components/markdown-confluence-sync/test/unit/specs/MarkdownConfluenceSync.test.ts b/components/markdown-confluence-sync/test/unit/specs/MarkdownConfluenceSync.test.ts index 7ae7d5b9..9402fc6e 100644 --- a/components/markdown-confluence-sync/test/unit/specs/MarkdownConfluenceSync.test.ts +++ b/components/markdown-confluence-sync/test/unit/specs/MarkdownConfluenceSync.test.ts @@ -37,6 +37,7 @@ describe("markdownConfluenceSync", () => { ...CONFIG, dryRun: true, }); + customDocusaurusPages.read.mockResolvedValue([]); // Act await markdownConfluenceSync.sync();