iT邦幫忙

2026 iThome 鐵人賽

DAY 23
0
Build on Google AI

打造專屬 AI 參謀群:以 Gemini Spark、Workspace 與 ADK 構建自動化決策雷達系列 第 23 篇

Day 23:全自動化雲端閉環:GitHub Actions 透過 SSH Deploy Key 自動 Push 至私有 Obsidian Vault

  • 分享至 

  • xImage
  •  

在 Day 21 與 Day 22 中,我們完成了情報沉澱至 Obsidian 的適配器與動態 DataviewJS 戰情看板。然而,當前的同步流程仍存在一個致命痛點:強依賴本地運算環境。

我們必須坐在電腦前、開啟終端機手動執行 python -m scripts.sync_to_obsidian。如果出差在外只有手機或平板,或者本機關機數天,情資沉澱就會中斷。

在現代 PKM 實踐中,許多高階使用者會將 Obsidian Vault 本身建立為一個 GitHub 私有儲存庫(Private Repository),搭配手機端或電腦端的 Git 外掛(如 Obsidian Git)達成多裝置無縫同步。

今天我們將架構推向全自動化——利用 GitHub Actions、SSH Deploy Key 與 Git 跨儲存庫推送,在無人值守巡邏管線執行完畢後,直接在雲端產出結構化 Markdown,並自動提交(Commit & Push)回個人的私有 Vault 儲存庫,達成 100% 雲端自治閉環!


一、架構藍圖:跨儲存庫安全推送流程

在 CI/CD 中,情報雷達的核心儲存庫(intelligence-radar)與個人筆記儲存庫(my-obsidian-vault)通常是兩個完全獨立的 Private Repositories。

為了遵循最小權限原則(Principle of Least Privilege),我們不能也不該使用具備個人全域權限的 Personal Access Token (PAT),而是採用 SSH Deploy Key:

+----------------------------------------------------------------------------------+
| GitHub Actions Runner (intelligence-radar)                                              |
|                                                                                         |
| 1. Scheduled Patrol 巡邏 ➔ 更新 Google Sheets (PROCESSED)                              |
| 2. 載入 SSH Deploy Key (私鑰) ➔ 取得目標 Vault 寫入權限                                  |
| 3. git clone git@github.com:your-user/my-obsidian-vault.git                              |
| 4. 執行 python -m scripts.sync_to_obsidian --vault-dir ./my-obsidian-vault              |
| 5. git add & git commit (若有新筆記) ➔ git push origin main                              |
+----------------------------------------------------------------------------------+
                                           │
                                           ▼ (SSH 加密傳輸)
+----------------------------------------------------------------------------------+
| GitHub Remote: your-user/my-obsidian-vault (Private Repo)                               |
| - 部署公鑰 (Deploy Key, 具備 Write 權限) 驗證通過                                         |
| - 00_Inbox/Radar/2026-XX-XX - [SCIENCE] ....md 寫入成功                                  |
+----------------------------------------------------------------------------------+


二、安全性前置配置:生成專屬 SSH Deploy Key

1. 本地生成一對專屬 Ed25519 金鑰

在終端機執行以下指令(務必留空密碼,直接按 Enter):

ssh-keygen -t ed25519 -C "github-actions-radar-sync" -f id_ed25519_vault_sync -N ""

這會在目錄下生成兩個檔案:

  • id_ed25519_vault_sync.pub(公鑰)
  • id_ed25519_vault_sync(私鑰)

2. 在 Vault 儲存庫設定「部署公鑰 (Deploy Key)」

  1. 打開個人 Obsidian Vault 的 GitHub 儲存庫頁面(例如 your-user/my-obsidian-vault)。
  2. 進入 Settings ➔ Deploy keys ➔ 點擊 Add deploy key。
  3. Title 命名為:Radar Sync CI Key。
  4. Key:將 id_ed25519_vault_sync.pub 的全部內容貼上。
  5. 重要:務必勾選 「Allow write access」(允許寫入權限),點擊儲存。

3. 在雷達儲存庫設定「機密私鑰 (Secret)」

  1. 打開情報雷達的主儲存庫(intelligence-radar)。
  2. 進入 Settings ➔ Secrets and variables ➔ Actions ➔ New repository secret。
  3. Name 設為:VAULT_DEPLOY_KEY。
  4. Secret:將私鑰 id_ed25519_vault_sync 的全部內容(包含 -----BEGIN OPENSSH PRIVATE KEY----- 等標頭)完整貼上。
  5. 另外新增一個 Secret:VAULT_REPO_SSH,內容為目標 Vault 的 SSH Clone URL,例如:git@github.com:your-user/my-obsidian-vault.git。

三、腳本改造:支援動態傳入 Vault 目錄

為了讓 scripts/sync_to_obsidian.py 同時支援本機開發(讀取環境變數)與 CI/CD Runner(讀取命令列參數),我們使用 argparse 微調入口:

# scripts/sync_to_obsidian.py
import os
import argparse
import logging
from pathlib import Path
from src.adapters.sheets_adapter import SheetsAdapter
from src.adapters.obsidian_adapter import ObsidianVaultAdapter

logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s - [%(levelname)s] - %(message)s"
)
logger = logging.getLogger(__name__)

def main():
    parser = argparse.ArgumentParser(description="將 Google Sheets 已審查高分情資同步至 Obsidian Vault")
    parser.add_argument(
        "--vault-dir",
        type=str,
        default=os.getenv("OBSIDIAN_VAULT_PATH", "~/ObsidianVault"),
        help="目標 Obsidian Vault 路徑"
    )
    parser.add_argument(
        "--subfolder",
        type=str,
        default="00_Inbox/Radar",
        help="Vault 內存放情報的子目錄"
    )
    args = parser.parse_args()

    vault_path = Path(args.vault_dir).expanduser().resolve()
    logger.info(f"初始化 Obsidian 轉接器,目標路徑: {vault_path}")

    obsidian_adapter = ObsidianVaultAdapter(
        vault_path=vault_path,
        subfolder=args.subfolder
    )

    logger.info("連線至 Google Sheets 讀取已處理情資...")
    sheets_adapter = SheetsAdapter()
    candidates = sheets_adapter.fetch_processed_items(min_score=4)
    logger.info(f"[*] 找到 {len(candidates)} 筆高價值情報候選項目...")

    results = obsidian_adapter.batch_sync(candidates)
    logger.info(f"[+] 同步完成!新增: {results['synced']} 篇,略過: {results['skipped']} 篇。")

if __name__ == "__main__":
    main()


四、GitHub Actions Workflow 整合實戰

我們將情報同步邏輯無縫掛接在排程巡航(Patrol)之後,或作為獨立的後置 Step。

修改 .github/workflows/patrol.yml(或新建 .github/workflows/obsidian_sync.yml):

name: Intelligence Radar Patrol & Vault Sync

on:
  schedule:
    # 每天定時 UTC 00:00 與 12:00 自動觸發 (台灣時間 08:00 與 20:00)
    - cron: '0 0,12 * * *'
  workflow_dispatch: # 支援手動一鍵觸發

jobs:
  patrol-and-sync:
    name: Run Patrol and Sync to Obsidian Vault
    runs-on: ubuntu-latest
    timeout-minutes: 8 # 防禦型超時中斷

    steps:
      - name: Checkout Radar Repo
        uses: actions/checkout@v4

      - name: Setup Python
        uses: actions/setup-python@v5
        with:
          python-version: '3.11'
          cache: 'pip'

      - name: Install Dependencies
        run: |
          python -m pip install --upgrade pip
          pip install -r requirements.txt

      - name: Decrypt GCP Credentials
        env:
          GCP_SA_BASE64: ${{ secrets.GCP_SA_BASE64 }}
        run: |
          echo "$GCP_SA_BASE64" | base64 --decode > service_account.json

      - name: Run Pipeline (Reviewer -> Domain Expert -> Synthesizer -> Editor)
        env:
          GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
          SPREADSHEET_KEY: ${{ secrets.SPREADSHEET_KEY }}
          DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }}
        run: |
          python -m src.services.pipeline

      # =========================================================================
      # 關鍵步驟:設定 SSH Deploy Key 並 Clone 私有 Obsidian Vault
      # =========================================================================
      - name: Setup SSH Agent for Obsidian Vault
        uses: webfactory/ssh-agent@v0.9.0
        with:
          ssh-private-key: ${{ secrets.VAULT_DEPLOY_KEY }}

      - name: Clone Private Obsidian Vault
        env:
          VAULT_REPO: ${{ secrets.VAULT_REPO_SSH }}
        run: |
          git clone "$VAULT_REPO" obsidian_vault_target

      - name: Export High-Score Intelligence to Obsidian
        env:
          SPREADSHEET_KEY: ${{ secrets.SPREADSHEET_KEY }}
        run: |
          python -m scripts.sync_to_obsidian --vault-dir ./obsidian_vault_target

      # =========================================================================
      # 關鍵步驟:檢查異動並自動 Commit & Push
      # =========================================================================
      - name: Commit and Push Notes to Obsidian Vault
        run: |
          cd obsidian_vault_target
          git config user.name "Radar Sentinel Bot"
          git config user.email "bot@intelligence-radar.internal"
          
          # 檢查是否有新增或修改的 .md 檔案
          if [ -n "$(git status --porcelain)" ]; then
            echo "[+] 檢測到新情報筆記,正在提交變更..."
            git add 00_Inbox/Radar/
            git commit -m "chore(radar): auto sync high-value intelligence [$(date +'%Y-%m-%d %H:%M')]"
            git push origin main
          else
            echo "[*] 無新增情報筆記,略過 Commit。"
          fi

      - name: Cleanup Sensitive Credentials
        if: always()
        run: |
          rm -f service_account.json


五、維運踩坑與工程防禦細節

在雲端自動跨 Repo 推送筆記時,有兩個常見的實務問題必須提前防範:

1. SSH Host Key Verification 阻擋

  • 問題:如果在 Runner 裡單純下 ssh -i id_rsa,可能會因為 GitHub 的 Known Hosts 未預先註冊而噴出 Host key verification failed。
  • 解法:使用官方與社群廣泛推薦的 webfactory/ssh-agent@v0.9.0 Action。它會自動在背景啟動 ssh-agent、導入私鑰並安全寫入 GitHub 官方的 public keys 進入 ~/.ssh/known_hosts,完全杜絕互動式確認卡死。

2. 避免「無異動時 Commit 失敗」

  • 問題:若某次巡航沒有評分 $\ge 4$ 的新情報,執行 git commit 會因為沒有 staged changes 拋出非零結束碼(Exit code 1),導致整場 GitHub Actions 被標記為紅色失敗。
  • 解法:在腳本中先以 git status --porcelain 判斷字串是否非空。只有在確認有變更時才執行 git add、commit 與 push,確保管線在無情報時也能優雅綠燈通過。

總結與下一步

今天我們完成了「雲端無人值守情報沉澱閉環」:

  • 透過 SSH Deploy Key,在嚴格的最小權限隔離下,讓主巡邏管線安全具備推送到私有筆記庫的權限。
  • 透過 Git 冪等檢查,使 GitHub Actions 能夠自動辨識新情資、提交並推送到遠端儲存庫。
  • 當你出門在外拿起手機打開 Obsidian,筆記庫會自動從 GitHub 拉取最新沉澱的 .md 檔案,Day 22 寫好的 Dataview 看板立即呈現最新的前沿技術進展。

目前我們的輸出端(Egress Port)已經完美覆蓋了檔案型知識庫(Obsidian)。然而在許多企業協作或多維資料庫愛好者的工作流中,Notion 是另一個不可忽視的知識中樞。

在 Day 24,我們將延續六角架構的精神:「跨平台延伸:實作 Notion API Adapter 將情報同步至 Notion 關聯資料庫」,展示如何將同樣的 IntelligenceItem 模型無縫適配至 Notion 的 Rich Text Blocks 與 Multi-select 結構!


上一篇
Day 22:雙腦共振:Obsidian DataviewJS 看板與雙向鏈結實戰
下一篇
Day 24:跨平台延伸:實作 Notion API Adapter 將情報同步至 Notion 關聯資料庫
系列文
打造專屬 AI 參謀群:以 Gemini Spark、Workspace 與 ADK 構建自動化決策雷達 共 24 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言