Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions components/markdown-confluence-sync/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions components/markdown-confluence-sync/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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. 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` |
Expand Down
2 changes: 1 addition & 1 deletion components/markdown-confluence-sync/package.json
Original file line number Diff line number Diff line change
@@ -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": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ import type {
FilesMetadataOption,
ContentPreprocessorOptionDefinition,
ContentPreprocessorOption,
MainDryRunOptionDefinition,
MainDryRunOption,
} from "./MarkdownConfluenceSync.types.js";

const MODULE_NAME = "markdown-confluence-sync";
Expand Down Expand Up @@ -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
{
Expand All @@ -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) {
Expand Down Expand Up @@ -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 =
Expand All @@ -154,9 +168,16 @@ export const MarkdownConfluenceSync: MarkdownConfluenceSyncConstructor = class M
public async sync(): Promise<void> {
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() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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;
}
}
}
Expand Down Expand Up @@ -103,6 +108,16 @@ export type ContentPreprocessorOptionDefinition =
export type ContentPreprocessorOption =
OptionInterfaceOfType<ContentPreprocessor>;

export type MainDryRunOptionDefinition = OptionDefinition<
boolean,
{ hasDefault: true }
>;

export type MainDryRunOption = OptionInterfaceOfType<
boolean,
{ hasDefault: true }
>;

/** Creates a MarkdownConfluenceSync interface */
export interface MarkdownConfluenceSyncConstructor {
/** Returns MarkdownConfluenceSync interface
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
id: foo-ignored-index
title: foo-ignored-index-title
sync_to_confluence: true
---

# Hello World
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// SPDX-FileCopyrightText: 2025 Telefónica Innovación Digital
// SPDX-License-Identifier: MIT

const path = require("node:path");

module.exports = {
confluence: {
url: "https://my-confluence.com",
spaceKey: "FOO",
},
docsDir: path.join(__dirname, "./docs"),
dryRun: true,
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// SPDX-FileCopyrightText: 2025 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.`,
);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,21 @@ describe("markdownConfluenceSync", () => {
);
});

it("should not call to synchronize if dryRun is true", async () => {
// Arrange
const markdownConfluenceSync = new MarkdownConfluenceSync({
...CONFIG,
dryRun: true,
});
customDocusaurusPages.read.mockResolvedValue([]);

// 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);
Expand Down
Loading