使用 ox-publish 发布博客

可参考资料

(info "(org) Exporting")                ;https://orgmode.org/manual/Exporting.html
(info "(org) Publishing")               ;https://orgmode.org/manual/Publishing.html

基础可用配置

(setq org-publish-project-alist
      '(("blog-posts"                        ;设置两个项目,一个用于文章一个用于附件
         :base-directory "~/org/blog/posts/" ;存放 org 文件的目录
         :base-extension "org"
         :publishing-directory "~/org/blog/publish/" ;导出目录
         :recursive t                                ;递归导出
         :publishing-function org-html-publish-to-html
         :headline-levels 5
         :with-toc nil                ;去掉toc
         :with-author nil             ;去掉author,email,creator
         :with-email nil
         :with-creator nil
         :with-timestamp t
         :time-stamp-file t           ;导出修改时间
         :html-home/up-format nil     ;去掉右上角的 'UP | HOME'
         :html-validation-link nil    ;去掉 validation 链接
         :html-doctype "html5"
         :html-html5-fancy t
         :html-head "<link rel=\"stylesheet\" href=\"css/style.css\"/>" ;指定css
         :html-preamble "<header> <nav>
<a href=\"index.html\">Blog</a> |
<a href=\"https://git.trogloxene.org\">Git</a> |
<a href=\"about.html\">About</a>
</nav> </header>"                      ;自定义页头
         :auto-preamble t
         :section-numbers nil
         :auto-sitemap t              ;索引页配置
         :sitemap-filename "index.org"
         :sitemap-title ""
         :sitemap-sort-files anti-chronologically)

        ("blog-static"
         :base-directory "~/org/blog/posts/"
         :base-extension "css\\|js\\|png\\|jpg\\|gif\\|pdf\\|mp3\\|ogg\\|swf\\|woof2"
         :publishing-directory "~/org/blog/publish/"
         :publishing-function org-publish-attachment
         :recursive t)

        ("blog" :components ("blog-posts" "blog-static")))) ;统一入口

优化 sitemap

为索引页条目添加时间戳,并在生成索引时跳过指定文章

(defun my-sitemap-entry (entry style project)
  "Custom sitemap entry format for ox-publish."
  (format "%s [[file:%s][%s]]"
          (format-time-string "%Y-%m-%d"
                              (org-publish-find-date entry project))
          entry
          (org-publish-find-title entry project)))

(defvar my-sitemap-skip-files '("file-name.org")
  "Files that do not show in the sitemap.")

(defun my-org-publish-sitemap-skip (title list)
  "Like `org-publish-sitemap-default', but skip Specified file."
  (org-publish-sitemap-default
   title
   (cons (car list)
         (cl-remove-if
          (lambda (item)
            (and (stringp (car item))
                 (string-match-p (regexp-opt my-sitemap-skip-files)
                                 (car item))))
          (cdr list)))))

然后在 org-publish-project-alist 中添加:

:sitemap-format-entry my-sitemap-entry
:sitemap-function my-org-publish-sitemap-skip

使用

org-publish 发布,默认只导出修改过的内容, C-u M-x org-publish 强制导出所有内容。

或者从本地机器推送原始文章,在服务器上利用 Git hook 执行脚本完成发布,附件可以使用 rsync 上传。

示例脚本:

#!/bin/bash

export GIT_DIR=/path/to/your/repo
export GIT_WORK_TREE=/path/to/checkout

if [ -t 0 ]; then
    publish=true
else
    publish=false
    while read old new ref; do
        [ "$ref" = "refs/heads/master" ] && publish=true
    done
fi
$publish || exit 0

mkdir -p "$GIT_WORK_TREE"
git checkout -f master || exit 0

/usr/bin/emacs -Q --batch --load "$GIT_WORK_TREE/publish.el" 2>&1 || \
    echo "publish failed" >&2
exit 0

脚本会在 master 收到推送后将文章 checkout 到 $GIT_WORK_TREE 下,并使用 Emacs 执行仓库中的 publish.el 。按需修改路径,然后将其添加到 bare 仓库的 hooks/post-receive 即可。

Date: 2026-07-26 Sun 05:09