<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Obsidian on 瞬息科技 blogs</title>
    <link>https://blog.ouob.net/tags/obsidian/</link>
    <description>Recent content in Obsidian on 瞬息科技 blogs</description>
    <generator>Hugo -- 0.152.2</generator>
    <language>zh-hant</language>
    <lastBuildDate>Mon, 28 Apr 2025 00:00:00 +0000</lastBuildDate>
    <atom:link href="https://blog.ouob.net/tags/obsidian/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>打造專屬知識網站：Obsidian 筆記秒變 Hugo 網站</title>
      <link>https://blog.ouob.net/posts/blogging---obsidian-to-hugo/</link>
      <pubDate>Mon, 28 Apr 2025 00:00:00 +0000</pubDate>
      <guid>https://blog.ouob.net/posts/blogging---obsidian-to-hugo/</guid>
      <description>&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;💡 此文章主要是紀錄建站過程，不是教學。
&lt;/code&gt;&lt;/pre&gt;&lt;h1 id=&#34;目標&#34;&gt;目標&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;在obsidian寫作；博客網站展示&lt;/li&gt;
&lt;li&gt;一鍵發布文章（自動化流程）&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&#34;前置要求&#34;&gt;前置要求&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;筆者使用macOS&lt;/li&gt;
&lt;li&gt;已安裝好docker&lt;/li&gt;
&lt;li&gt;遠端Linux主機的SSH&lt;/li&gt;
&lt;li&gt;遠端Linux主機公開的port端口&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&#34;建置網站&#34;&gt;建置網站&lt;/h1&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;💡 影片中將網站部屬至Hostinger；本文將使用自己主機部屬。
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;主要參考此影片來建置：&lt;/p&gt;</description>
      <content:encoded><![CDATA[<pre tabindex="0"><code>💡 此文章主要是紀錄建站過程，不是教學。
</code></pre><h1 id="目標">目標</h1>
<ul>
<li>在obsidian寫作；博客網站展示</li>
<li>一鍵發布文章（自動化流程）</li>
</ul>
<h1 id="前置要求">前置要求</h1>
<ul>
<li>筆者使用macOS</li>
<li>已安裝好docker</li>
<li>遠端Linux主機的SSH</li>
<li>遠端Linux主機公開的port端口</li>
</ul>
<h1 id="建置網站">建置網站</h1>
<pre tabindex="0"><code>💡 影片中將網站部屬至Hostinger；本文將使用自己主機部屬。
</code></pre><p>主要參考此影片來建置：</p>
<p><a href="https://www.youtube.com/watch?v=dnE7c0ELEH8">I started a blog&hellip;..in 2024 (why you should too) - YouTube</a>。</p>
<p>影片中的內容這裡就不重複撰寫了，直接看他的筆記 <a href="https://blog.networkchuck.com/posts/my-insane-blog-pipeline/">My Insane Blog Pipeline :: Terminal</a>。</p>
<h2 id="主要步驟">主要步驟</h2>
<ol>
<li>Obsidian準備好一個資料夾（放置要公開的文章）</li>
<li>安裝hugo</li>
<li>產生hugo專案檔案</li>
<li>安裝hugo主題</li>
<li>編寫圖片遷移腳本</li>
<li>編寫自動化流程腳本</li>
<li>部屬至主機</li>
</ol>
<h1 id="我的設定">我的設定</h1>
<h3 id="主題">主題</h3>
<p>筆者使用<a href="https://adityatelange.github.io/hugo-PaperMod/">PaperMod</a>，官網教學推薦使用git submodule安裝</p>
<pre tabindex="0"><code>git submodule add --depth=1 https://github.com/adityatelange/hugo-PaperMod.git themes/PaperMod
</code></pre><details>
<summary>什麼是git submodule? 🤖GPT explain</summary>
Git submodule 是 Git 版本控制系統中的一個功能，它允許你將一個 Git 儲存庫作為另一個 Git 儲存庫的子目錄。這樣做的主要好處是可以在你的專案中包含和使用其他專案的程式碼，同時保持兩個專案的獨立性。
<p>這個命令的作用是：</p>
<ol>
<li>將 hugo-PaperMod 這個 Hugo 主題的 Git 儲存庫添加為你當前專案的一個子模組</li>
<li>子模組會被克隆到 themes/PaperMod 目錄下</li>
<li><code>--depth=1</code> 參數表示只克隆最新的一個版本的代碼，不包含完整的歷史記錄，這樣可以節省空間和下載時間</li>
</ol>
<p>使用 submodule 的優點：</p>
<ul>
<li>可以在你的專案中使用其他專案的程式碼，而不需要複製貼上或手動更新</li>
<li>當原始專案（在這個例子中是 hugo-PaperMod 主題）更新時，你可以輕鬆地更新你的子模組</li>
<li>你可以保持對使用的特定版本的控制</li>
</ul>
</details>
<hr>
<p>作者推薦使用yaml來設定，筆者還是習慣使用toml</p>
<p>使用 <a href="https://transform.tools/yaml-to-toml">YAML to TOML</a> 來轉換格式</p>
<p>如果是複製 <a href="https://adityatelange.github.io/hugo-PaperMod/posts/papermod/papermod-installation/#sample-configyml">PaperMod 的範例設定檔</a>，<br>
記得<code>hugo.toml</code> 要移除<code>paginate = 5</code>這行設定，因新版hugo已經棄用，不然會報錯。</p>
<h2 id="自動化腳本">自動化腳本</h2>
<p>筆者在影片提供的腳本基礎上修改。</p>
<h3 id="圖片遷移腳本">圖片遷移腳本</h3>
<ul>
<li>修改了regex，支援png、jpg</li>
<li>顯示Markdown檔案修改統計與圖片數量</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">os</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">pathlib</span> <span class="kn">import</span> <span class="n">Path</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">re</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">shutil</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Paths</span>
</span></span><span class="line"><span class="cl"><span class="n">BASE_PATH</span> <span class="o">=</span> <span class="n">Path</span><span class="p">(</span><span class="s2">&#34;/Users/rrrfff/&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">POSTS_DIR</span> <span class="o">=</span> <span class="n">BASE_PATH</span> <span class="o">/</span> <span class="s2">&#34;ouob-hugo/content/posts/&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">ATTACHMENTS_DIR</span> <span class="o">=</span> <span class="n">BASE_PATH</span> <span class="o">/</span> <span class="s2">&#34;Resilio/Obsidian/z_attach_img/&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">STATIC_IMAGES_DIR</span> <span class="o">=</span> <span class="n">BASE_PATH</span> <span class="o">/</span> <span class="s2">&#34;ouob-blogs/static/images/&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">count</span> <span class="o">=</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Step 1: Process each markdown file in the posts directory</span>
</span></span><span class="line"><span class="cl"><span class="k">for</span> <span class="n">file_path</span> <span class="ow">in</span> <span class="n">POSTS_DIR</span><span class="o">.</span><span class="n">glob</span><span class="p">(</span><span class="s2">&#34;*.md&#34;</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="n">file_path</span><span class="p">,</span> <span class="s2">&#34;r&#34;</span><span class="p">)</span> <span class="k">as</span> <span class="n">file</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">content</span> <span class="o">=</span> <span class="n">file</span><span class="o">.</span><span class="n">read</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="c1"># Step 2: Find all image links in the format ![Image Description](/images/Pasted%20image%20...%20.png)</span>
</span></span><span class="line"><span class="cl">        <span class="n">images</span> <span class="o">=</span> <span class="n">re</span><span class="o">.</span><span class="n">findall</span><span class="p">(</span><span class="sa">r</span><span class="s2">&#34;\[\[([^\]]+\.(?:png|jpg|jpeg))\]\]&#34;</span><span class="p">,</span> <span class="n">content</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="c1"># Step 3: Replace image links and ensure URLs are correctly formatted</span>
</span></span><span class="line"><span class="cl">        <span class="k">for</span> <span class="n">image</span> <span class="ow">in</span> <span class="n">images</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="c1"># Prepare the Markdown-compatible link with %20 replacing spaces</span>
</span></span><span class="line"><span class="cl">            <span class="n">markdown_image</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">&#34;[Image Description](/images/</span><span class="si">{</span><span class="n">image</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="s1">&#39; &#39;</span><span class="p">,</span> <span class="s1">&#39;%20&#39;</span><span class="p">)</span><span class="si">}</span><span class="s2">)&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="n">content</span> <span class="o">=</span> <span class="n">content</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;[[</span><span class="si">{</span><span class="n">image</span><span class="si">}</span><span class="s2">]]&#34;</span><span class="p">,</span> <span class="n">markdown_image</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="c1"># Step 4: Copy the image to the Hugo static/images directory if it exists</span>
</span></span><span class="line"><span class="cl">            <span class="n">image_source</span> <span class="o">=</span> <span class="n">ATTACHMENTS_DIR</span> <span class="o">/</span> <span class="n">image</span>
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="n">image_source</span><span class="o">.</span><span class="n">exists</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">                <span class="n">shutil</span><span class="o">.</span><span class="n">copy</span><span class="p">(</span><span class="n">image_source</span><span class="p">,</span> <span class="n">STATIC_IMAGES_DIR</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="n">count</span> <span class="o">+=</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="c1"># Step 5: Write the updated content back to the markdown file</span>
</span></span><span class="line"><span class="cl">        <span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="n">file_path</span><span class="p">,</span> <span class="s2">&#34;w&#34;</span><span class="p">)</span> <span class="k">as</span> <span class="n">file</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="n">file</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="n">content</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="n">count</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;No images found in the markdown files.&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">else</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;Processed </span><span class="si">{</span><span class="nb">len</span><span class="p">(</span><span class="nb">list</span><span class="p">(</span><span class="n">POSTS_DIR</span><span class="o">.</span><span class="n">glob</span><span class="p">(</span><span class="s1">&#39;*.md&#39;</span><span class="p">)))</span><span class="si">}</span><span class="s2"> markdown files.&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;Copied </span><span class="si">{</span><span class="n">count</span><span class="si">}</span><span class="s2"> images to the Hugo static/images directory.&#34;</span><span class="p">)</span>
</span></span></code></pre></div><h3 id="一鍵發布腳本">一鍵發布腳本</h3>
<ul>
<li>根據筆者自己的流程修改</li>
</ul>
<p>步驟：</p>
<ol>
<li>設定變量</li>
<li>驗證所需工具</li>
<li>驗證SSH連線</li>
<li>驗證路徑已存在</li>
<li>rsync: Obsidian文章 至 公開庫 與 hugo專案</li>
<li>圖片遷移</li>
<li>hugo建構靜態檔案</li>
<li>公開庫: git add</li>
<li>公開庫: git commit</li>
<li>公開庫: git push</li>
<li>rsync: hugo靜態檔案 至 遠端伺服器</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="cp">#!/bin/bash
</span></span></span><span class="line"><span class="cl"><span class="cp"></span><span class="nb">set</span> -euo pipefail
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Change to the script&#39;s directory</span>
</span></span><span class="line"><span class="cl"><span class="nv">SCRIPT_DIR</span><span class="o">=</span><span class="s2">&#34;</span><span class="k">$(</span><span class="nb">cd</span> <span class="s2">&#34;</span><span class="k">$(</span>dirname <span class="s2">&#34;</span><span class="si">${</span><span class="nv">BASH_SOURCE</span><span class="p">[0]</span><span class="si">}</span><span class="s2">&#34;</span><span class="k">)</span><span class="s2">&#34;</span> <span class="o">&amp;&amp;</span> <span class="nb">pwd</span><span class="k">)</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nb">cd</span> <span class="s2">&#34;</span><span class="nv">$SCRIPT_DIR</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Set variables for Obsidian to Hugo copy</span>
</span></span><span class="line"><span class="cl"><span class="nv">basePath</span><span class="o">=</span><span class="s2">&#34;/Users/rrrfff&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nv">sourcePath</span><span class="o">=</span><span class="s2">&#34;</span><span class="si">${</span><span class="nv">basePath</span><span class="si">}</span><span class="s2">/Resilio/Obsidian/ouob_posts&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nv">publicPostRepoPath</span><span class="o">=</span><span class="s2">&#34;</span><span class="si">${</span><span class="nv">basePath</span><span class="si">}</span><span class="s2">/ouob-blogs/posts&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nv">destinationPath</span><span class="o">=</span><span class="s2">&#34;</span><span class="si">${</span><span class="nv">basePath</span><span class="si">}</span><span class="s2">/ouob-hugo/content/posts&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nv">hugoPublicPath</span><span class="o">=</span><span class="s2">&#34;</span><span class="si">${</span><span class="nv">basePath</span><span class="si">}</span><span class="s2">/ouob-hugo/public&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Set GitHub Repo</span>
</span></span><span class="line"><span class="cl"><span class="nv">publicRepo</span><span class="o">=</span><span class="s2">&#34;git@github.com:swy641205/ouob-blogs.git&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nv">publicRepoPath</span><span class="o">=</span><span class="s2">&#34;</span><span class="si">${</span><span class="nv">basePath</span><span class="si">}</span><span class="s2">/ouob-blogs&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nv">deployRepo</span><span class="o">=</span><span class="s2">&#34;git@github.com:swy641205/ouob-hugo.git&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">sshName</span><span class="o">=</span><span class="s2">&#34;s5nuuvm_o&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Check for required commands</span>
</span></span><span class="line"><span class="cl"><span class="k">for</span> cmd in git rsync python3 hugo<span class="p">;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> ! <span class="nb">command</span> -v <span class="nv">$cmd</span> <span class="p">&amp;</span>&gt; /dev/null<span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">        <span class="nb">echo</span> <span class="s2">&#34;</span><span class="nv">$cmd</span><span class="s2"> is not installed or not in PATH.&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="nb">exit</span> <span class="m">1</span>
</span></span><span class="line"><span class="cl">    <span class="k">fi</span>
</span></span><span class="line"><span class="cl"><span class="k">done</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># check ssh connection</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> ! ssh -o <span class="nv">BatchMode</span><span class="o">=</span>yes -o <span class="nv">ConnectTimeout</span><span class="o">=</span><span class="m">5</span> <span class="si">${</span><span class="nv">sshName</span><span class="si">}</span> exit<span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">    <span class="nb">echo</span> <span class="s2">&#34;SSH connection failed.&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="nb">exit</span> <span class="m">1</span>
</span></span><span class="line"><span class="cl"><span class="k">fi</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Step 2: Sync posts from Obsidian to Hugo content folder</span>
</span></span><span class="line"><span class="cl"><span class="c1"># and puvblicRepo path using rsync</span>
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;Syncing posts from Obsidian...&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="o">[</span> ! -d <span class="s2">&#34;</span><span class="nv">$sourcePath</span><span class="s2">&#34;</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">    <span class="nb">echo</span> <span class="s2">&#34;Source path does not exist: </span><span class="nv">$sourcePath</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="nb">exit</span> <span class="m">1</span>
</span></span><span class="line"><span class="cl"><span class="k">fi</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="o">[</span> ! -d <span class="s2">&#34;</span><span class="nv">$publicPostRepoPath</span><span class="s2">&#34;</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">    <span class="nb">echo</span> <span class="s2">&#34;Public Repo path does not exist: </span><span class="nv">$publicPostRepoPath</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="nb">exit</span> <span class="m">1</span>
</span></span><span class="line"><span class="cl"><span class="k">fi</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="o">[</span> ! -d <span class="s2">&#34;</span><span class="nv">$destinationPath</span><span class="s2">&#34;</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">    <span class="nb">echo</span> <span class="s2">&#34;Destination path does not exist: </span><span class="nv">$destinationPath</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="nb">exit</span> <span class="m">1</span>
</span></span><span class="line"><span class="cl"><span class="k">fi</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 源路徑 / 代表複製資料夾裡面的檔案 不是直接複製資料夾本身</span>
</span></span><span class="line"><span class="cl">rsync -avuz --delete <span class="s2">&#34;</span><span class="nv">$sourcePath</span><span class="s2">/&#34;</span> <span class="s2">&#34;</span><span class="nv">$destinationPath</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">rsync -avuz --delete <span class="s2">&#34;</span><span class="nv">$sourcePath</span><span class="s2">/&#34;</span> <span class="s2">&#34;</span><span class="nv">$publicPostRepoPath</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Step 3: Process Markdown files with Python script to handle image links</span>
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;Processing image links in Markdown files...&#34;</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="o">[</span> ! -f <span class="s2">&#34;image.py&#34;</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">    <span class="nb">echo</span> <span class="s2">&#34;Python script images.py not found.&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="nb">exit</span> <span class="m">1</span>
</span></span><span class="line"><span class="cl"><span class="k">fi</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">if</span> ! python3 image.py<span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">    <span class="nb">echo</span> <span class="s2">&#34;Failed to process image links.&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="nb">exit</span> <span class="m">1</span>
</span></span><span class="line"><span class="cl"><span class="k">fi</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Step 4: Build the Hugo site</span>
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;Building the Hugo site...&#34;</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> ! hugo --minify --enableGitInfo<span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">    <span class="nb">echo</span> <span class="s2">&#34;Hugo build failed.&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="nb">exit</span> <span class="m">1</span>
</span></span><span class="line"><span class="cl"><span class="k">fi</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Step 5: Add changes to Git</span>
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;Staging changes for Git...&#34;</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 只有當「工作目錄」與「暫存區」完全無差異、</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 且「暫存區」與「最新的 commit」完全無差異 才回傳 true</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> git -C <span class="si">${</span><span class="nv">publicRepoPath</span><span class="si">}</span> diff --quiet <span class="o">&amp;&amp;</span> git diff --cached --quiet<span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">    <span class="nb">echo</span> <span class="s2">&#34;No changes to stage.&#34;</span>
</span></span><span class="line"><span class="cl"><span class="k">else</span>
</span></span><span class="line"><span class="cl">    git -C <span class="si">${</span><span class="nv">publicRepoPath</span><span class="si">}</span> add .
</span></span><span class="line"><span class="cl"><span class="k">fi</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Step 6: Commit changes with a dynamic message</span>
</span></span><span class="line"><span class="cl"><span class="nv">commit_message</span><span class="o">=</span><span class="s2">&#34;New Blog Post on </span><span class="k">$(</span>date +<span class="s1">&#39;%Y-%m-%d %H:%M:%S&#39;</span><span class="k">)</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> git -C <span class="si">${</span><span class="nv">publicRepoPath</span><span class="si">}</span> diff --cached --quiet<span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">    <span class="nb">echo</span> <span class="s2">&#34;</span><span class="si">${</span><span class="nv">publicRepoPath</span><span class="si">}</span><span class="s2">: No changes to commit.&#34;</span>
</span></span><span class="line"><span class="cl"><span class="k">else</span>
</span></span><span class="line"><span class="cl">    <span class="nb">echo</span> <span class="s2">&#34;</span><span class="si">${</span><span class="nv">publicRepoPath</span><span class="si">}</span><span class="s2">: Committing changes to ...&#34;</span>
</span></span><span class="line"><span class="cl">    git -C <span class="si">${</span><span class="nv">publicRepoPath</span><span class="si">}</span> commit -m <span class="s2">&#34;</span><span class="nv">$commit_message</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl"><span class="k">fi</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Step 7: Push all changes to the main branch</span>
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;</span><span class="si">${</span><span class="nv">publicRepoPath</span><span class="si">}</span><span class="s2">: Deploying to GitHub Main...&#34;</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> ! git -C <span class="si">${</span><span class="nv">publicRepoPath</span><span class="si">}</span> push origin main<span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">    <span class="nb">echo</span> <span class="s2">&#34;Failed to push to main branch.&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="nb">exit</span> <span class="m">1</span>
</span></span><span class="line"><span class="cl"><span class="k">fi</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Step 8: rsync the public folder to host</span>
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;Deploying to Host...&#34;</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 複製 public 資料夾裡面的資料 （/）</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> ! rsync -avuz --delete <span class="s2">&#34;</span><span class="si">${</span><span class="nv">hugoPublicPath</span><span class="si">}</span><span class="s2">/&#34;</span> <span class="s2">&#34;</span><span class="si">${</span><span class="nv">sshName</span><span class="si">}</span><span class="s2">:/home/swy/ouob-hugo/public&#34;</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">    <span class="nb">echo</span> <span class="s2">&#34;Failed to deploy to host.&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="nb">exit</span> <span class="m">1</span>
</span></span><span class="line"><span class="cl"><span class="k">fi</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;All done! Site synced, processed, committed, built, and deployed.&#34;</span>
</span></span></code></pre></div><h2 id="部屬">部屬</h2>
<p>可以參考<a href="https://gohugo.io/host-and-deploy/">Host and deploy</a> 選擇適合自己的方案來部屬，筆者選擇部屬到自己主機上。</p>
<p>筆者這裡直接用<a href="https://docker.hugomods.com/docs/ci-cd/nginx/">hugomods提供的nginx映像檔</a>來部屬：</p>
<pre tabindex="0"><code>services:
  hugo-site:
    image: hugomods/hugo:nginx
    restart: unless-stopped
    ports:
      - &#34;${HUGO_PORT:-80}:80&#34;
    volumes:
      - ./nginx/nginx-custom.conf:/etc/nginx/conf.d/default.conf:ro # 可選：自定義 Nginx 配置
      - ./hugo_logs:/var/log/nginx # 持久化 Nginx 日誌
      - ./public:/site
    environment:
      - NGINX_ENTRYPOINT_QUIET_LOGS=1 # 減少 Nginx 啟動時的日誌輸出
    networks:
      - hugo_network
    healthcheck:
      test: [&#34;CMD&#34;, &#34;curl&#34;, &#34;-f&#34;, &#34;http://localhost:80&#34;]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 10s
    deploy:
      resources:
        limits:
          cpus: &#34;0.5&#34;
          memory: 256M
        reservations:
          memory: 128M

  # 可選：添加 Watchtower 自動更新容器
  watchtower:
    image: containrrr/watchtower
    restart: always
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    command: --interval 86400 --cleanup # 每天檢查一次更新
    networks:
      - hugo_network

networks:
  hugo_network:
    driver: bridge
</code></pre><h2 id="支援mermaid流程圖顯示">支援Mermaid流程圖顯示</h2>
<p>看了<a href="https://gohugo.io/render-hooks/code-blocks/">Code block render hooks</a>，但還是不確定怎麼改，就先用claude回答的來改，有更好的辦法可以告訴我🤗</p>
<p>此腳本會查看頁面是否有mermaid的代碼塊，如果有就插入mermaid渲染腳本</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">mkdir -p layouts/_default/_markup/ <span class="o">&amp;&amp;</span>
</span></span><span class="line"><span class="cl">nano layouts/_default/_markup/render-codeblock-mermaid.html
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl">{{ if eq .Type &#34;mermaid&#34; }}
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;mermaid&#34;</span><span class="p">&gt;</span>{{- .Inner | safeHTML }}<span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">{{ if not (.Page.Scratch.Get &#34;mermaid&#34;) }} {{
</span></span><span class="line"><span class="cl">.Page.Scratch.Set &#34;mermaid&#34; true }}
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="nb">window</span><span class="p">.</span><span class="nx">mermaidLoaded</span> <span class="o">===</span> <span class="kc">undefined</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nb">window</span><span class="p">.</span><span class="nx">mermaidLoaded</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nb">document</span><span class="p">.</span><span class="nx">write</span><span class="p">(</span><span class="sb">`
</span></span></span><span class="line"><span class="cl"><span class="sb">      &lt;script src=&#34;https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js&#34;&gt;&lt;\/script&gt;
</span></span></span><span class="line"><span class="cl"><span class="sb">      &lt;script&gt;
</span></span></span><span class="line"><span class="cl"><span class="sb">        mermaid.initialize({
</span></span></span><span class="line"><span class="cl"><span class="sb">          startOnLoad: true, 
</span></span></span><span class="line"><span class="cl"><span class="sb">          theme: &#34;dark&#34;,
</span></span></span><span class="line"><span class="cl"><span class="sb">          align: &#34;center&#34;
</span></span></span><span class="line"><span class="cl"><span class="sb">        });
</span></span></span><span class="line"><span class="cl"><span class="sb">      &lt;\/script&gt;
</span></span></span><span class="line"><span class="cl"><span class="sb">    `</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">{{ end }} {{ else }}
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">pre</span><span class="p">&gt;&lt;</span><span class="nt">code</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;{{ .Type }}&#34;</span><span class="p">&gt;</span>{{ .Inner }}<span class="p">&lt;/</span><span class="nt">code</span><span class="p">&gt;&lt;/</span><span class="nt">pre</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">{{ end }}
</span></span></code></pre></div><p>這樣網站就可以渲染流程圖了，如下：</p>

<div class="mermaid">graph TD
	A-->B</div>
 
<h2 id="加入giscus留言板">加入Giscus留言板</h2>
<p>👍 Giscus 開源免費，依託GitHub Discussion。</p>
<p>👎 disqus 有廣告，可能拖慢網頁載入速度，需要設定延後載入。</p>
<p>安照官網<a href="https://github.com/apps/giscus">giscus</a>教學設定即可，也可看看其他人寫的教學：</p>
<ul>
<li><a href="https://tunan.org/posts/add-comment-system-to-hugopapermo/">给Hugo PaperMod增加giscus评论系统 | 图南博客</a></li>
<li><a href="https://ljhero.github.io/posts/2022-05-02-support-comment-using-giscus/">使用 giscus 给博客添加评论功能 | Ljhero&rsquo;s blog</a></li>
<li><a href="https://jessewei.dev/blog/2023/papermod/#comments">Overview of Hugo/PaperMod, modifying PaperMod, and comparison to al-folio | Jesse Wei</a></li>
</ul>
<p>首先，確保<code>config.toml</code>裡面<code>[params]</code>的 <code>comments = true</code></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">mkdir -p layouts/partials/ <span class="o">&amp;&amp;</span>
</span></span><span class="line"><span class="cl">nano layouts/partials/comments.html
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">&lt;script <span class="nv">src</span><span class="o">=</span><span class="s2">&#34;https://giscus.app/client.js&#34;</span>
</span></span><span class="line"><span class="cl">        data-repo<span class="o">=</span><span class="s2">&#34;ouob-tw/ouob-blogs&#34;</span>
</span></span><span class="line"><span class="cl">        data-repo-id<span class="o">=</span><span class="s2">&#34;xxxxxxxxxxxx&#34;</span>
</span></span><span class="line"><span class="cl">        data-category<span class="o">=</span><span class="s2">&#34;Announcements&#34;</span>
</span></span><span class="line"><span class="cl">        data-category-id<span class="o">=</span><span class="s2">&#34;xxxxxxxxxxxx&#34;</span>
</span></span><span class="line"><span class="cl">        data-mapping<span class="o">=</span><span class="s2">&#34;pathname&#34;</span>
</span></span><span class="line"><span class="cl">        data-strict<span class="o">=</span><span class="s2">&#34;0&#34;</span>
</span></span><span class="line"><span class="cl">        data-reactions-enabled<span class="o">=</span><span class="s2">&#34;1&#34;</span>
</span></span><span class="line"><span class="cl">        data-emit-metadata<span class="o">=</span><span class="s2">&#34;0&#34;</span>
</span></span><span class="line"><span class="cl">        data-input-position<span class="o">=</span><span class="s2">&#34;top&#34;</span>
</span></span><span class="line"><span class="cl">        data-theme<span class="o">=</span><span class="s2">&#34;preferred_color_scheme&#34;</span>
</span></span><span class="line"><span class="cl">        data-lang<span class="o">=</span><span class="s2">&#34;zh-TW&#34;</span>
</span></span><span class="line"><span class="cl">        data-loading<span class="o">=</span><span class="s2">&#34;lazy&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="nv">crossorigin</span><span class="o">=</span><span class="s2">&#34;anonymous&#34;</span>
</span></span><span class="line"><span class="cl">        async&gt;
</span></span><span class="line"><span class="cl">&lt;/script&gt;
</span></span></code></pre></div>]]></content:encoded>
    </item>
  </channel>
</rss>
