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
相关推荐
fthux3 小时前
装闭 RenoPit 源码解析(07):装修闭坑知识库与AI Prompt构建
人工智能·ai·开源·github·open source·renopit
fthux6 小时前
装闭 RenoPit 源码解析(06):SSE如何实时推送AI装修分析进度
人工智能·ai·开源·github·open source·renopit
是烨笙啊8 小时前
拯救 GitHub stars 体验:从 Agent Skill 到浏览器插件
github
小弥儿9 小时前
GitHub今日热榜 | 2026-08-11:图原生基础设施首日登顶
学习·开源·github
码流怪侠10 小时前
用 WiFi 信号数人头:howmanypeoplearearound 项目深度解析
后端·开源·github
大强同学12 小时前
零成本部署0penList,三步搞定,我一分钱没花!
github
IT摆渡者16 小时前
VM EXSI7.0存储硬盘故障处理
大数据·linux·github
Narrastory17 小时前
我用 Claude Code 写代码不到 2 小时,却花了 3 天做完这个软件—Vibe Coding 的正确姿势,是设计不是生成
前端·人工智能·github
AI Dog17 小时前
GitHub 项目整体替换与大文件处理指南
github
March.s17 小时前
Nginx 服务器反向代理实战指南
服务器·nginx·github