Confluence文档集成Skill confluence-docs

这是一个用于自动化企业文档管理的技能,提供 Atlassian Confluence 与 Markdown 之间的双向同步、内容迁移和空间管理。通过 API 实现文档的自动创建、更新、导出,支持附件处理和标签管理,适用于 DevOps 文档自动化、知识库建设和团队协作流程优化。关键词:Confluence API,文档自动化,Markdown 转换,企业知识库,DevOps 文档,内容迁移,团队协作工具。

DevOps 0 次安装 0 次浏览 更新于 2/26/2026

name: confluence-docs description: Atlassian Confluence 企业文档集成。通过 API 创建和更新页面,管理空间和权限,处理内容迁移,并在 Markdown 和 Confluence 之间同步。 allowed-tools: Read, Write, Edit, Bash, Glob, Grep backlog-id: SK-013 metadata: author: babysitter-sdk version: “1.0.0”

Confluence 集成技能

Atlassian Confluence 企业文档集成。

能力

  • 通过 API 创建和更新页面
  • 空间管理和权限控制
  • 宏和模板管理
  • 内容迁移(Markdown 到 Confluence)
  • 附件处理
  • 标签和元数据管理
  • 支持 Confluence Cloud 和 Server
  • Confluence 到 Markdown 导出

用法

当您需要时调用此技能:

  • 将文档同步到 Confluence
  • 在不同格式之间迁移内容
  • 以编程方式管理 Confluence 空间
  • 从 CI/CD 自动化页面更新
  • 将 Confluence 导出为 Markdown

输入

参数 类型 必填 描述
action string create, update, migrate, export
baseUrl string Confluence 实例 URL
spaceKey string 目标空间键
sourcePath string 源 Markdown 文件路径
pageId string 用于更新的特定页面 ID
parentPageId string 用于层次结构的父页面 ID

输入示例

{
  "action": "migrate",
  "baseUrl": "https://company.atlassian.net/wiki",
  "spaceKey": "DOCS",
  "sourcePath": "./docs",
  "parentPageId": "123456"
}

配置

confluence.config.json

{
  "baseUrl": "https://company.atlassian.net/wiki",
  "auth": {
    "type": "token",
    "email": "${CONFLUENCE_EMAIL}",
    "token": "${CONFLUENCE_TOKEN}"
  },
  "space": {
    "key": "DOCS",
    "name": "Documentation"
  },
  "migration": {
    "preserveStructure": true,
    "convertTables": true,
    "uploadImages": true,
    "macroMapping": {
      "note": "info",
      "warning": "warning",
      "code": "code"
    }
  },
  "sync": {
    "dryRun": false,
    "updateExisting": true,
    "createMissing": true,
    "archiveRemoved": false
  }
}

API 集成

Confluence REST API 客户端

const ConfluenceClient = require('confluence-api');

class ConfluenceManager {
  constructor(config) {
    this.client = new ConfluenceClient({
      username: config.email,
      password: config.token,
      baseUrl: config.baseUrl
    });
  }

  // 创建新页面
  async createPage(spaceKey, title, content, parentId = null) {
    const page = {
      type: 'page',
      title,
      space: { key: spaceKey },
      body: {
        storage: {
          value: content,
          representation: 'storage'
        }
      }
    };

    if (parentId) {
      page.ancestors = [{ id: parentId }];
    }

    return await this.client.postContent(page);
  }

  // 更新现有页面
  async updatePage(pageId, title, content, version) {
    const page = {
      id: pageId,
      type: 'page',
      title,
      version: { number: version + 1 },
      body: {
        storage: {
          value: content,
          representation: 'storage'
        }
      }
    };

    return await this.client.putContent(page);
  }

  // 按标题获取页面
  async getPageByTitle(spaceKey, title) {
    const result = await this.client.getContentBySpaceKey(spaceKey, {
      title,
      expand: 'version,body.storage'
    });
    return result.results[0] || null;
  }

  // 上传附件
  async uploadAttachment(pageId, filePath, comment = '') {
    const form = new FormData();
    form.append('file', fs.createReadStream(filePath));
    form.append('comment', comment);

    return await this.client.createAttachment(pageId, form);
  }

  // 添加标签
  async addLabels(pageId, labels) {
    const labelPayload = labels.map(name => ({
      prefix: 'global',
      name
    }));

    return await this.client.postLabels(pageId, labelPayload);
  }
}

Markdown 到 Confluence 转换

转换器

const marked = require('marked');

class MarkdownToConfluence {
  constructor(options = {}) {
    this.options = options;
    this.attachments = [];
  }

  convert(markdown, metadata = {}) {
    // 解析 front matter
    const { content, frontMatter } = this.parseFrontMatter(markdown);

    // 将 markdown 转换为 HTML
    let html = marked.parse(content);

    // 转换为 Confluence 存储格式
    html = this.convertToStorageFormat(html);

    // 处理宏
    html = this.convertMacros(html);

    // 处理代码块
    html = this.convertCodeBlocks(html);

    // 处理图片
    html = this.convertImages(html);

    // 处理表格
    html = this.convertTables(html);

    return {
      title: frontMatter.title || metadata.title,
      content: html,
      labels: frontMatter.tags || [],
      attachments: this.attachments
    };
  }

  convertMacros(html) {
    // 将 admonitions 转换为 Confluence 宏
    const macroMap = {
      'note': 'info',
      'warning': 'warning',
      'tip': 'tip',
      'danger': 'warning'
    };

    for (const [mdType, confType] of Object.entries(macroMap)) {
      const regex = new RegExp(`<div class="${mdType}">([\\s\\S]*?)</div>`, 'g');
      html = html.replace(regex, (match, content) => {
        return `<ac:structured-macro ac:name="${confType}">
          <ac:rich-text-body>${content}</ac:rich-text-body>
        </ac:structured-macro>`;
      });
    }

    return html;
  }

  convertCodeBlocks(html) {
    // 将代码块转换为 Confluence 代码宏
    return html.replace(
      /<pre><code class="language-(\w+)">([\s\S]*?)<\/code><\/pre>/g,
      (match, language, code) => {
        const decodedCode = this.decodeHtml(code);
        return `<ac:structured-macro ac:name="code">
          <ac:parameter ac:name="language">${language}</ac:parameter>
          <ac:plain-text-body><![CDATA[${decodedCode}]]></ac:plain-text-body>
        </ac:structured-macro>`;
      }
    );
  }

  convertImages(html) {
    // 将图片转换为 Confluence 附件
    return html.replace(
      /<img src="([^"]+)" alt="([^"]*)"[^>]*>/g,
      (match, src, alt) => {
        if (src.startsWith('http')) {
          // 外部图片
          return `<ac:image><ri:url ri:value="${src}" /></ac:image>`;
        } else {
          // 本地附件
          const filename = path.basename(src);
          this.attachments.push({ src, filename });
          return `<ac:image><ri:attachment ri:filename="${filename}" /></ac:image>`;
        }
      }
    );
  }

  convertTables(html) {
    // Confluence 使用标准 HTML 表格但需要特定属性
    return html.replace(/<table>/g, '<table class="wrapped">');
  }
}

Confluence 到 Markdown 导出

导出器

class ConfluenceToMarkdown {
  constructor(client) {
    this.client = client;
  }

  async exportSpace(spaceKey, outputDir) {
    const pages = await this.getAllPages(spaceKey);
    const structure = this.buildHierarchy(pages);

    for (const page of pages) {
      const markdown = await this.exportPage(page);
      const filePath = this.getFilePath(page, structure, outputDir);

      await fs.mkdir(path.dirname(filePath), { recursive: true });
      await fs.writeFile(filePath, markdown);
    }

    return { exported: pages.length };
  }

  async exportPage(page) {
    const content = page.body.storage.value;

    // 将 Confluence 存储格式转换为 Markdown
    let markdown = this.convertToMarkdown(content);

    // 添加 front matter
    const frontMatter = {
      title: page.title,
      confluence_id: page.id,
      last_modified: page.version.when
    };

    return `---
${yaml.stringify(frontMatter)}---

${markdown}`;
  }

  convertToMarkdown(storage) {
    let md = storage;

    // 转换代码宏
    md = md.replace(
      /<ac:structured-macro ac:name="code"[^>]*>[\s\S]*?<ac:parameter ac:name="language">(\w+)<\/ac:parameter>[\s\S]*?<ac:plain-text-body><!\[CDATA\[([\s\S]*?)\]\]><\/ac:plain-text-body>[\s\S]*?<\/ac:structured-macro>/g,
      (match, lang, code) => `\`\`\`${lang}
${code}
\`\`\``
    );

    // 转换信息宏
    md = md.replace(
      /<ac:structured-macro ac:name="(info|warning|tip)"[^>]*>[\s\S]*?<ac:rich-text-body>([\s\S]*?)<\/ac:rich-text-body>[\s\S]*?<\/ac:structured-macro>/g,
      (match, type, content) => `> **${type.toUpperCase()}:** ${this.stripHtml(content)}`
    );

    // 转换标题、列表等
    md = this.convertHtmlToMarkdown(md);

    return md;
  }
}

同步工作流

双向同步

async function syncDocumentation(config) {
  const confluence = new ConfluenceManager(config);
  const converter = new MarkdownToConfluence(config.migration);

  // 获取本地文件
  const localFiles = await glob('docs/**/*.md');

  // 获取 Confluence 页面
  const pages = await confluence.getSpaceContent(config.space.key);

  const results = {
    created: [],
    updated: [],
    skipped: [],
    errors: []
  };

  for (const file of localFiles) {
    try {
      const markdown = await fs.readFile(file, 'utf8');
      const converted = converter.convert(markdown, { file });

      // 检查页面是否存在
      const existing = await confluence.getPageByTitle(
        config.space.key,
        converted.title
      );

      if (existing) {
        if (config.sync.updateExisting) {
          await confluence.updatePage(
            existing.id,
            converted.title,
            converted.content,
            existing.version.number
          );
          results.updated.push(file);
        } else {
          results.skipped.push(file);
        }
      } else if (config.sync.createMissing) {
        await confluence.createPage(
          config.space.key,
          converted.title,
          converted.content,
          config.parentPageId
        );
        results.created.push(file);
      }

      // 上传附件
      for (const attachment of converted.attachments) {
        await confluence.uploadAttachment(
          existing?.id || results.created[results.created.length - 1].id,
          attachment.src
        );
      }
    } catch (error) {
      results.errors.push({ file, error: error.message });
    }
  }

  return results;
}

空间管理

创建空间

async function createDocumentationSpace(config) {
  const client = new ConfluenceManager(config);

  const space = await client.client.postSpace({
    key: config.space.key,
    name: config.space.name,
    description: {
      plain: { value: config.space.description, representation: 'plain' }
    },
    permissions: [
      {
        subjects: { group: { name: 'confluence-users' } },
        operation: { key: 'read', target: 'space' }
      }
    ]
  });

  // 创建首页
  await client.createPage(
    config.space.key,
    'Home',
    '<h1>Welcome to Documentation</h1>',
    null
  );

  return space;
}

工作流

  1. 配置 - 设置 Confluence 凭据和空间
  2. 转换 - 将 Markdown 转换为 Confluence 格式
  3. 同步 - 通过 API 上传/更新页面
  4. 附件 - 上传图片和文件
  5. 标签 - 应用标签以进行组织
  6. 验证 - 检查页面渲染

依赖项

{
  "devDependencies": {
    "confluence-api": "^1.4.0",
    "marked": "^12.0.0",
    "gray-matter": "^4.0.0",
    "form-data": "^4.0.0"
  }
}

CLI 命令

# 将 Markdown 同步到 Confluence
node scripts/confluence-sync.js --config confluence.config.json

# 将 Confluence 导出为 Markdown
node scripts/confluence-export.js --space DOCS --output ./exported

# 创建新空间
node scripts/confluence-space.js create --key NEWDOCS --name "New Documentation"

应用的最佳实践

  • 使用页面模板以确保一致性
  • 使用父页面进行组织
  • 应用标签以提高可发现性
  • 在 Git 中保留单一事实来源
  • 在合并到主分支时同步
  • 正确处理附件

参考

目标流程

  • knowledge-base-setup.js
  • docs-pr-workflow.js
  • content-strategy.js