obsidian md github站点

会自动渲染md 到 gh-pages 分支,然后到pages (注意,需要public 仓库)

源码:

复制代码
name: Deploy Docs to GitHub Pages

on:
  push:
    branches:
      - main
    paths:
      - "**/*.md"
      - ".obsidian/workspace.json"
      - "book.json"
      - ".github/workflows/deploy-pages.yml"
  workflow_dispatch:

permissions:
  contents: write

concurrency:
  group: deploy-pages
  cancel-in-progress: true

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Setup Node
        uses: actions/setup-node@v4
        with:
          node-version: "20"

      - name: Build with HonKit
        shell: bash
        run: |
          set -euo pipefail

          ordered_md_file="$(mktemp)"
          all_md_file="$(mktemp)"
          candidate_md_file="$(mktemp)"

          # Collect all markdown files in repository (relative paths).
          while IFS= read -r -d '' md; do
            printf '%s\n' "${md#./}" >> "$all_md_file"
          done < <(
            find . -type f -name '*.md' \
              -not -path './.git/*' \
              -not -path './node_modules/*' \
              -print0 | sort -z
          )

          # Prefer Obsidian workspace order: active doc -> main tabs -> last open files.
          if [ -f .obsidian/workspace.json ]; then
            node <<'NODE' > "$ordered_md_file"
          const fs = require('fs');
          const wsPath = '.obsidian/workspace.json';
          let ws;
          try {
            ws = JSON.parse(fs.readFileSync(wsPath, 'utf8'));
          } catch {
            process.exit(0);
          }

          const output = [];
          const idToMarkdown = new Map();

          function walk(node, fn) {
            if (!node || typeof node !== 'object') return;
            fn(node);
            if (Array.isArray(node.children)) {
              for (const child of node.children) walk(child, fn);
            }
          }

          walk(ws, (node) => {
            if (node.type !== 'leaf') return;
            const file = node?.state?.state?.file;
            if (node?.state?.type === 'markdown' && typeof file === 'string' && file.endsWith('.md')) {
              if (typeof node.id === 'string') idToMarkdown.set(node.id, file);
            }
          });

          if (typeof ws.active === 'string' && idToMarkdown.has(ws.active)) {
            output.push(idToMarkdown.get(ws.active));
          }

          function collectMainTabs(node) {
            if (!node || typeof node !== 'object') return;
            if (node.type === 'tabs' && Array.isArray(node.children)) {
              const currentIndex = Number.isInteger(node.currentTab) ? node.currentTab : -1;
              if (currentIndex >= 0 && currentIndex < node.children.length) {
                const activeFile = node.children[currentIndex]?.state?.state?.file;
                if (typeof activeFile === 'string' && activeFile.endsWith('.md')) output.push(activeFile);
              }
              for (const child of node.children) {
                const tabFile = child?.state?.state?.file;
                if (typeof tabFile === 'string' && tabFile.endsWith('.md')) output.push(tabFile);
              }
            }
            if (Array.isArray(node.children)) {
              for (const child of node.children) collectMainTabs(child);
            }
          }

          collectMainTabs(ws.main);

          if (Array.isArray(ws.lastOpenFiles)) {
            for (const file of ws.lastOpenFiles) {
              if (typeof file === 'string' && file.endsWith('.md')) output.push(file);
            }
          }

          for (const file of output) {
            if (typeof file === 'string' && file.length > 0) {
              console.log(file.replace(/^\.\//, ''));
            }
          }
          NODE
          fi

          # Merge and deduplicate while preserving order.
          cat "$ordered_md_file" "$all_md_file" | awk 'NF && !seen[$0]++' > "$candidate_md_file"

          home_source=""
          if [ ! -f README.md ]; then
            if [ -s "$ordered_md_file" ]; then
              first_preferred="$(head -n 1 "$ordered_md_file")"
              if [ -n "$first_preferred" ] && [ -f "$first_preferred" ]; then
                home_source="$first_preferred"
                cp "$home_source" README.md
              fi
            fi

            if [ ! -f README.md ] && [ -f Welcome.md ]; then
              home_source="Welcome.md"
              cp Welcome.md README.md
            fi

            if [ ! -f README.md ]; then
              first_md="$(head -n 1 "$all_md_file")"
              if [ -n "$first_md" ] && [ -f "$first_md" ]; then
                home_source="$first_md"
                cp "$home_source" README.md
              fi
            fi
          fi

          {
            echo "# Summary"
            echo
            echo "* [Home](README.md)"
            while IFS= read -r md; do
              [ -z "$md" ] && continue
              [ "$md" = "README.md" ] && continue
              [ "$md" = "SUMMARY.md" ] && continue
              [ -n "$home_source" ] && [ "$md" = "$home_source" ] && continue
              [ -f "$md" ] || continue
              title="$(basename "${md%.md}")"
              printf "* [%s](%s)\n" "$title" "$md"
            done < "$candidate_md_file"
          } > SUMMARY.md

          # Ensure Osiris theme plugin is configured, even if book.json is absent.
          node <<'NODE'
          const fs = require('fs');
          const path = 'book.json';
          let book = {};
          if (fs.existsSync(path)) {
            try {
              book = JSON.parse(fs.readFileSync(path, 'utf8'));
            } catch (err) {
              throw new Error(`Invalid book.json: ${err.message}`);
            }
          }

          const targetPlugin = 'honkit-plugin-theme-osiris';
          const plugins = Array.isArray(book.plugins) ? book.plugins : [];
          const filtered = plugins.filter(
            (p) => p !== targetPlugin && p !== 'theme-osiris'
          );
          filtered.push(targetPlugin);
          book.plugins = filtered;
          fs.writeFileSync(path, `${JSON.stringify(book, null, 2)}\n`);
          NODE

          npm install --no-save honkit@5.1.4 honkit-plugin-theme-osiris@1.0.3
          ./node_modules/.bin/honkit build . _book

          # Workaround: plugin generates osiris.css under gitbook-plugin-* path,
          # but HonKit references it from honkit-plugin-* path.
          osiris_generated="_book/gitbook/gitbook-plugin-honkit-plugin-theme-osiris/osiris.css"
          osiris_expected_dir="_book/gitbook/honkit-plugin-theme-osiris"
          if [ -f "$osiris_generated" ]; then
            mkdir -p "$osiris_expected_dir"
            cp "$osiris_generated" "$osiris_expected_dir/osiris.css"
          fi

          touch _book/.nojekyll

      - name: Deploy to gh-pages
        uses: peaceiris/actions-gh-pages@v4
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          publish_dir: ./_book
          publish_branch: gh-pages
          force_orphan: true
相关推荐
jack@london2 小时前
使用Railway
github
给老吕螺丝5 小时前
提升国内访问GitHub稳定性的解决方案:Steamcommunity 302工具详解 (Ver.13.0.05+)
经验分享·github
阿里嘎多学长5 小时前
2026-02-25 GitHub 热点项目精选
开发语言·程序员·github·代码托管
黑白极客6 小时前
ACP大模型认证刷题工具开源,助力高效备考
java·ai·github·llama·认证
无限进步_7 小时前
138. 随机链表的复制 - 题解与详细分析
c语言·开发语言·数据结构·算法·链表·github·visual studio
AI大模型..13 小时前
Dify 本地部署安装教程(Windows + Docker),大模型入门到精通,收藏这篇就足够了!
人工智能·程序员·开源·llm·github·deepseek·本地化部署
CoderJia程序员甲16 小时前
GitHub 热榜项目 - 日榜(2026-02-24)
人工智能·ai·大模型·github·ai教程
无名的小白17 小时前
宝塔计划任务实现定时备份Openclaw/Workspace到Github
github
散峰而望19 小时前
C++ 启程:从历史到实战,揭开命名空间的神秘面纱
c语言·开发语言·数据结构·c++·算法·github·visual studio