CI/CD+QuickAdd+JS脚本+bat脚本+sh脚本实现自动发版Hugo文章

需求来源

每次手动执行Hugo的命令,手动把public文件夹上传到自己的服务器可以完成发版需求。

但是,作为一个内容创作者,我更希望的关注于自己的内容,而不是关注整个发版过程。最好能只在一个软件上进行简单的几次点击,就完成自动内容到公网上了。

需求1:只在一个软件中,不用切换软件(Obsidian)。

需求2:只需要简单的1-2次点击就可以完成自动部署到公网。

方案分析

备注:我写内容的软件为Obsidian,这是一个很强大的软件。可以安装主题,可以安装很多插件。功能强大,可扩展性很强。这里不介绍Obsidian这款软件。

手动发版方案步骤

  1. 创建一个markdown文件
  2. 编写内容(Typora或者Obsidian都行)
  3. 添加上笔记属性(Hugo文章需要)
  4. 把要发版的文章复制到Hugo的content文件夹的post文件夹中
  5. 手动执行hugo的构建站点命令,hugo --gc --minify
  6. 对public文件夹进行压缩(内容过多会不好传输,所以压缩),并上传到服务器对应位置
  7. 服务器解压
  8. 完成部署

优化分析

  1. 创建markdown。

    优化方案:使用Obsidian的QuickAdd的template完成创建文章,并添加属性,你可以在笔记内容编写完成后回来编辑这些属性。一般我们文章写好后,再去确定打上什么标签,配上什么图片。

  2. 编写内容。这个步骤少不了,无法进行简化。

  3. 复制要发版的文章到Hugo中(一般不是把所有的内容都进行发版的,所以我们一般只复制指定的文章到Hugo中)。

    优化方案:使用Sync to Hugo。Sync to Hugo可以自动添加基本属性,这些基本属性的值他会自己判断,并且Sync to Hugo会把你选择的文章复制到指定位置。这样,我们就可以做到,在一个自己的工作区创作,然后如果需要把某篇文章发布博客,我们就选择那个文章,点击Sync to Hugo,推送到对应的博客位置,等待发版上线。

  4. 手动执行构建,并把构建的内容添加到服务器。

    优化方案

    • 创建git仓库,这里我使用的腾讯的coding。你使用gitee、gitHub、阿里云效都是可以的。
    • 在windows编写bat脚本。脚本的功能为:完成hugo的构建、上传构建好的文件到git仓库。
    • 在服务器编写shell脚本。shell脚本的功能为:删除老的public文件夹,并且拉取git上的已经被windows构建好的public文件夹到linux
    • 使用coding,并在coding的持续集成中创建构建计划,并且编写Jenkinsfile去使用SSH调用服务器已经编写好的sh脚本
  5. 完成部署

**新方案的步骤:**编写文章+3次点击即可。

  1. 点击QuickAdd的命令完成创建文件
  2. 编写内容
  3. 点击Sync to Hugo按钮
  4. 点击QuickAdd的部署命令
  5. 完成部署

整个流程都没有离开Obsidian。做到在一个软件中完成发版博客的效果。

成果展示

步骤一:使用QuickAdd创建文章并编写内容。

image-20250523013255705

image-20250523013318012

步骤二:写文章

image-20250523013537547

步骤三:自己添加修改一下文章的属性,比如图片、标签、描述等信息,可以让博客美观一点。

image-20250523013749011

步骤四:使用Sync to Hugo,把文章推送到Hugo的content文件夹内指定目录。

image-20250523013811543

步骤五:使用QuickAdd,执行发版脚本。

image-20250523013830756

image-20250523013844913

效果展示:

image-20250523013907786

具体搭建过程

绑定Git仓库和public文件夹

创建远程Git仓库

image-20250521224006825

image-20250521223946297

image-20250521224140800

image-20250521224240123

image-20250521224324673

初始化本地仓库

这里我之前把public删除了,所以这里可以重新生成一下。

使用hugo --gc --minify命令即可。

image-20250521224954024

image-20250521225050839

进入public,使用git bash:

image-20250521225145833

执行初始化仓库命令:

git init

image-20250521225318295

image-20250521225335956

创建SSH密钥对

获取SSH密钥。

首先,你需要检查是否已经存在可用的SSH密钥。在命令行中输入以下命令:

ls ~\.ssh

该命令会列出SSH目录中的所有文件。如果已经存在一个名为id_rsa.pub或id_dsa.pub的文件,那么说明已经存在可用的SSH密钥。

这里我看到没有这两个文件,只有一个known_hosts文件。

image-20250521225730643

解释一下known_hosts文件:known_hosts 是 SSH 客户端用来存储远程服务器公钥指纹的文件,主要用于**防止中间人攻击(Man-in-the-Middle Attack)**的。当你第一次通过 SSH 连接到一个新服务器时,客户端会提示你验证服务器的公钥指纹(类似这样):

The authenticity of host 'github.com (20.205.243.166)' can't be established.
ECDSA key fingerprint is SHA256:p2QAMXNIC1TJYWeIOttrVc98/R1BUFWu3/LiyKgUfQM.
Are you sure you want to continue connecting (yes/no)?

如果你输入 yes,SSH 会将该服务器的公钥指纹保存到 known_hosts 文件中。下次连接同一服务器时,客户端会比对指纹:

  • 匹配 → 直接连接(不提示警告)。
  • 不匹配 → 发出警告(可能遭遇中间人攻击或服务器密钥变更)。

因为没有找到现有的SSH密钥,所以我们使用以下命令生成新的SSH密钥:

ssh-keygen -t rsa -C "xxx@qq.com"

-C "xxx@qq.com" 是为公钥添加一个注释(Comment),通常用邮箱标识密钥所有者。注释仅用于备注,不影响密钥功能。

运行命令 ssh-keygen -t rsa -C "xxx@qq.com" 会在本地生成一对 SSH 密钥(公钥和私钥),用于安全的远程登录(如 GitHub、GitLab、服务器 SSH 连接等)。

在生成SSH密钥的过程中,会提示你输入密钥的保存位置和密码。可以选择使用默认的保存位置(通常是~/.ssh/id_rsa)并且可以选择是否设置密码来保护密钥。

image-20250521230223518

上面内容是询问你生成的 私钥(id_rsa)和公钥(id_rsa.pub 要保存在哪个文件路径。

我们直接按回车就会使用默认路径来保存我们的私钥和公钥(推荐大多数情况)。

回车后,私钥和公钥将会保存为如下路径中。

  • 私钥C:\Users\13780\.ssh\id_rsa。私钥(id_rsa)一般保存在你的本地计算机(默认路径 ~/.ssh/id_rsa),必须严格保密,相当于你的“密码”。
  • 公钥C:\Users\13780\.ssh\id_rsa.pub。公钥(id_rsa.pub)可以自由分发(默认路径 ~/.ssh/id_rsa.pub),一般我们会上传到目标服务(如 GitHub、服务器等)。

然后会让你输入密码,这个是可选的。作用是为私钥设置额外密码保护(提高安全性,但每次使用密钥需输入密码)。若不需要密码保护,直接按两次回车跳过就行。这里我们不用密码,因为私钥已经很安全了,保护再保护,麻烦,我们只要私钥不丢就行。

image-20250521230420243

image-20250521230442646

我们再检查是否已经存在可用的SSH密钥。

ls ~\.ssh

image-20250521230604490

然后复制私钥。

  1. 进入 .ssh 目录(默认存储路径)

    cd ~/.ssh
    

    在命令行中,~(波浪符号)是一个特殊的快捷符号,代表当前用户的家目录(Home Directory)。不用操作系统的home目录不同,但是~都是表示家目录。

  2. 查看文件列表:

    ls
    
  3. 查看公钥内容:

    cat id_rsa.pub
    
  4. 复制公钥

    image-20250521230703159

    公钥为:

    ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDHT……
    
添加本地公钥到Coding

image-20250521232240425

image-20250521232145861

image-20250521232257244

注意:在个人账户设置中添加公钥,就是在全局添加公钥。这里你也可以去Git仓库项目里面去添加部署公钥,这样就是添加项目的公钥了,即,对应私钥只是打开这个项目的锁的钥匙。

绑定本地仓库与远程仓库
git remote add origin git@e.coding.net:g-dvea9576/hugo-blog/hugo-blog.git

image-20250521231857263

将本地的分支版本上传到远程仓库

依次输入下面命令:

git add .
git commit -m "初次提交"
git push -u origin master
  1. 添加所有文件到暂存区:git add .

  2. 提交更改到本地仓库:git commit -m “初次提交:同步Obsidian笔记”

  3. 推送到远程仓库(Gitee):git push -u origin master

    • -u 设置默认推送目标(后续可直接用 git push)。

image-20250521232432578

image-20250521232514983

编写bat脚本

我们随便在一个地方创建一个bat脚本都行。

脚本名:update_blog.bat

@echo off
:: Windows 批处理脚本 - Hugo 构建并推送博客到 Git
:: 版本: 2025-05-18-稳定版setlocal EnableDelayedExpansion
set "EXIT_CODE=0"
set "SUCCESS_MSG=OPERATION_SUCCESS"
set "ERROR_MSG=OPERATION_FAILED":: 1. 进入博客目录
cd /d D:\Obsidian-Test\Obsidian-Hugo\my-blog || (echo !ERROR_MSG!echo 错误:无法进入博客目录exit /b 1
):: 2. 清理 public 目录
if exist public (cd public || (echo !ERROR_MSG!echo 错误:无法进入public目录exit /b 1)echo 正在删除文件...for /f "delims=" %%F in ('dir /b /a-d 2^>nul') do (del /f /q "%%F" >nul 2>&1)echo 正在删除子目录...for /d %%D in (*) do (if /i not "%%D"==".git" (rmdir /s /q "%%D" 2>nul))cd ..echo public目录清理完成
):: 3. 执行 Hugo 构建
echo 正在构建 Hugo 站点...
hugo --gc --minify || (echo !ERROR_MSG!echo 错误:Hugo 构建失败!exit /b 1
):: 4. 进入 public 目录
cd public || (echo !ERROR_MSG!echo 错误:无法进入public目录exit /b 1
):: 5. Git 操作
echo 正在提交并推送 Git 变更...
git add . || (echo !ERROR_MSG!echo 错误:git add 失败exit /b 1
)git commit -m "自动更新: %date% %time%" || (echo 警告:git commit 失败(可能是无变更)
)git push origin master || (echo !ERROR_MSG!echo 错误:git push 失败exit /b 1
):: 6. 完成
echo !SUCCESS_MSG!
echo 操作完成!
exit /b 0

注意上面代码中有一个地方是关键的:

for /d %%D in (*) do (if /i not "%%D"==".git" (      :: 检查目录名是否不是 ".git"rmdir /s /q "%%D" 2>nul    :: 如果不是,则删除该目录)
)
  • for /d %%D in (*):遍历 public 下的所有子目录。
  • if /i not "%%D"==".git"排除 .git 目录/i 表示不区分大小写)。
  • rmdir /s /q "%%D":强制删除其他子目录(/s 递归删除,/q 静默模式)。

因此,.git 目录会被保留,而其他文件和子目录会被清理。

写好后,我们可以双击执行一下。看看能不能进行成功进行构建,并推送。

下面这个结果就是成功推送的输出了:

image-20250521233635559

添加服务器公钥到Coding

因为服务器需要拉代码,所以也需要把服务器的公钥给Coding。

检查是否已有 SSH 密钥:

ls -al ~/.ssh

如果没有就使用

ssh-keygen -t ed25519 -C "xxx@qq.com"
  • -t ed25519:指定密钥类型(也可用 -t rsa -b 4096 生成 RSA 密钥)。
  • -C "注释":可选注释,一般填写邮箱或用途。

image-20250522213513876

通过结果,我们可以知道服务器上已经存在 Ed25519 算法的 SSH 密钥对id_ed25519 是私钥,id_ed25519.pub 是公钥)

cat ~/.ssh/id_ed25519.pub

复制公钥,然后我们在Git仓库上添加公钥:

image-20250522213843676

image-20250522215535371

测试是否配置成功:

ssh -T git@e.coding.net

image-20250522214220522

直接输入 yes 并回车(注意是 yes,不是 y),之后会将该服务器的公钥指纹保存到 ~/.ssh/known_hosts 文件,下次连接就不会再提示了。

image-20250522215656778

看到上面提示,就说明是成功的。

注意:Coding中的公钥有两中,分别是项目内的部署公钥(Deploy Key)个人账户中的SSH公钥(SSH Key)

image-20250522215923699

image-20250522220001858

部署公钥(Deploy Key):为 单个项目 提供的只读或读写权限密钥,通常用于服务器自动化部署(如 CI/CD、生产服务器拉取代码)。绑定到项目,而非个人账户。

个人账户公钥(SSH Key):绑定到 你的个人账户,拥有账户下所有权限(包括访问所有有权限的项目)。对全局有效,可访问账户下所有项目(需项目权限)。权限与账户角色一致。

编写sh脚本

我们在这个站点目录下面创建一个script文件夹,专门用来放脚本:

image-20250522212334153

创建脚本名:update_project.sh

#!/bin/bash# 配置变量(根据实际情况修改)
GIT_REPO="git@e.coding.net:g-dvea9576/hugo-blog/hugo-blog.git"  # Git仓库地址
GIT_BRANCH="master"                                     # 分支(默认master)
TARGET_DIR="/www/wwwroot/blog.yimengtut.online/public"  # 代码存放目录# 1. 清理旧目录
if [ -d "$TARGET_DIR" ]; thenecho "删除旧目录: $TARGET_DIR"sudo rm -rf "$TARGET_DIR"
fi# 2. 创建目录并设置权限
echo "创建目录: $TARGET_DIR"
sudo mkdir -p "$TARGET_DIR"
# 将目录所有者设为当前用户(避免后续sudo)
sudo chown -R $(whoami):$(whoami) "$TARGET_DIR"# 3. 拉取代码
echo "正在从 $GIT_REPO 拉取代码到 $TARGET_DIR..."
git clone --branch "$GIT_BRANCH" "$GIT_REPO" "$TARGET_DIR"# 4. 检查是否成功
if [ $? -eq 0 ]; thenecho "代码拉取成功!"ls -l "$TARGET_DIR"
elseecho "错误:代码拉取失败!"# 提示可能的SSH问题echo "请检查:"echo "1. SSH公钥是否已添加到Coding.net账户;"echo "2. 是否可通过以下命令手动克隆:"echo "   git clone $GIT_REPO"exit 1
fi

image-20250522220244660

测试:

注意:这里还需要设置一下执行权限。

sudo chmod +x update_project.sh

image-20250522220510695

看到上面信息,说明执行成功。

添加服务器访问凭证

我们将会使用jenkinsfile让Git代码更新时自动去调用服务器的脚本。所以我们需要添加服务器的访问凭证(不是所有人都能访问你的服务器的,只能让有服务器访问凭证的才能访问,这里我们使用SSH Keys的方式来去访问。使用密码或者SSH Keys等方式都是可以的,他们都是访问凭证中的一种)。

image-20250522222149658

自动创建密钥对:由系统自动生成一个密钥对。

  • 公钥会被保存到Linux边缘实例的~/.ssh/authorized_keys 文件中。
  • 私钥会以.pem格式自动下载到您的本地计算机。请注意,火山引擎不会保存您的私钥。因此请您务必妥善保管私钥文件、勿将私钥文件分享给其他人。

创建后,会自动下载.pem格式的文件到本地计算机。这个就是私钥,要好好保管。

image-20250522222302955

我们先绑定实例:

image-20250522225341983

image-20250522225403927

image-20250522225423896

火山引擎的服务器绑定好密钥后需要重启服务器才行,并且要注意,和阿里云一样,服务器绑定密钥后,密码登录将会自动失效。

image-20250522230255549

image-20250522230357647

重启好,我们测试一下:

image-20250522230511416

image-20250522230524445

然后去Coding添加凭证:

image-20250521231039813

image-20250522222506688

image-20250522222548309

image-20250522222605010

image-20250522222823082

编写jenkinsfile

在项目中创建构建计划:

image-20250522223121560

image-20250522223212943

如果想使用密码发起SSH使用“通过账号密码发起的 SSH 连接”即可,这里我们使用“通过私钥发起的 SSH 连接”来操作。

image-20250522223555857

image-20250522223610514

确定。确定后,他会自动构建。

但是我这里会失败,因为这里我正好使用的Ubuntu 24.04。这个版本的系统需要去服务器里面额外设置一下,其他的版本系统不会出现这个问题(这里的报错也困惑了我好一会,“明明直接使用凭证可以访问火山引擎服务器的,但是为什么在Coding中无法访问到?”,最终在腾讯云文档中找到了解决方案):

image-20250522234346772

构建失败的解决方案:

编辑sshd_config文件。

vim /etc/ssh/sshd_config

添加PubkeyAcceptedKeyTypes=+ssh-rsa到文件中,然后执行systemctl restart ssh重启SSH。

ssh-rsa 使用 SHA-1 作为哈希算法,而 SHA-1 已被证明存在 碰撞攻击 漏洞。所以从 OpenSSH 8.8(2021年发布) 开始,官方默认禁用了 ssh-rsa 公钥签名算法。Ubuntu 24.04 搭载的 OpenSSH 版本是比较新的,因此也继承了这一变更。

而Jenkins ssh的插件比较旧,所以还是使用SHA-1的哈希算法,所以会出现上面的问题。

上面步骤中的做法,设置PubkeyAcceptedKeyTypes=+ssh-rsa ,是一种临时兼容性的配置,他的效果是:添加后,可以允许 OpenSSH 继续接受 ssh-rsa 密钥,所以能解决上面的这个问题,做到让Jenkins ssh插件能成功链接服务器。

疑惑:我们服务器生成的访问密钥是ed25519的。然后我要使用腾讯的Coding中的Jenkins ssh的插件,这个插件比较旧需要允许服务器的 OpenSSH 继续接受 ssh-rsa 密钥才行。然后我在服务器设置了PubkeyAcceptedKeyTypes=+ssh-rsa,就可以链接了。但是问题来了,为什么一个是ed25519,一个是ssh-rsa?

解答:旧版本的 Jenkins SSH 插件在 SSH 握手时会优先尝试 ssh-rsa(基于 SHA-1 的签名算法)。如果服务器未声明支持 ssh-rsa,插件可能因其过时逻辑而中止连接,即使你配置了 ed25519 私钥。服务器通过设置 PubkeyAcceptedKeyTypes=+ssh-rsa,可以做到在默认支持 ed25519 的基础上额外声明支持 ssh-rsa,从而满足插件的握手要求。握手成功,然后Jenkins 底层 SSH 客户端库(支持 ed25519)会使用你提供的 ed25519 密钥进行认证。

上面这个密钥不匹配的解决方案有如下几种

  1. 推荐升级 Jenkins SSH 插件
  2. 推荐:如果无法升级插件,可以生成 RSA 密钥(SHA-2),他兼容旧插件
  3. 临时:继续使用 PubkeyAcceptedKeyTypes=+ssh-rsa,但 ssh-rsa(SHA-1)不安全,仅作为过渡。

这里说的通过私钥连接,相当于是登录服务器哈。

完成上面步骤后,就可以构建成功了:

image-20250522234128974

目前这里我们还是使用默认的模板来构建的。

我们可以看到他执行的命令行内容和结果:

image-20250522234857566

这里分析一下模板的执行的效果。

模板内容如下:

def remoteConfig = [:]
remoteConfig.name = "my-remote-server"
remoteConfig.host = "${REMOTE_HOST}"
remoteConfig.allowAnyHosts = true
remoteConfig.port = 22node {// 使用当前项目下的凭据管理中的 SSH 私钥 凭据withCredentials([sshUserPrivateKey(credentialsId: "${REMOTE_CRED}",keyFileVariable: "privateKeyFilePath")]) {// SSH 登录用户名remoteConfig.user = "${REMOTE_USER_NAME}"// SSH 私钥文件地址remoteConfig.identityFile = privateKeyFilePathstage("通过 SSH 执行命令") {// 本地创建一个 test.sh 脚本,用来发送到远端执行writeFile(file: 'test.sh', text: 'ls')sshCommand(remote: remoteConfig, command: 'for i in {1..5}; do echo -n \"Loop \$i \"; date ; sleep 1; done')sshScript(remote: remoteConfig, script: 'test.sh')sshPut(remote: remoteConfig, from: 'test.sh', into: '.')sshGet(remote: remoteConfig, from: 'test.sh', into: 'test_new.sh', override: true)sshRemove(remote: remoteConfig, path: 'test.sh')}}
}

模板做的事情如下:

  1. 在Jenkins工作区生成 test.sh(内容为 ls),注意,不是在我们服务器创建文件哈。
  2. 在远程服务器运行循环打印日志的命令,就是为了验证SSH连接和命令执行能力。
  3. 将本地的 test.sh中的内容发送到远程执行(实际执行 ls),其实就是为了测试脚本远程执行功能。
  4. test.sh 上传到远程服务器,就是模拟部署文件,测试文件传输功能。
  5. 从远程下载文件到本地并重命名,其实就是为了验证文件获取能力。
  6. 删除远程的 test.sh。不改变我们服务器原本的内容。

全部都成功后,就验证了自动构建的功能是完整支持的。

下面我们进行编写自己的jenkinsfile:

def remoteConfig = [:]
remoteConfig.name = "my-remote-server"
remoteConfig.host = "${REMOTE_HOST}"      // 通过变量传入远程服务器IP
remoteConfig.allowAnyHosts = true        // 跳过主机验证(生产环境建议关闭)
remoteConfig.port = 22                   // SSH端口node {withCredentials([sshUserPrivateKey(credentialsId: "${REMOTE_CRED}", // Jenkins中配置的SSH私钥凭据IDkeyFileVariable: "privateKeyFilePath")]) {remoteConfig.user = "${REMOTE_USER_NAME}"  // SSH用户名(如root)remoteConfig.identityFile = privateKeyFilePathstage("执行远程脚本") {// 1. 直接执行远程服务器上的脚本sshCommand(remote: remoteConfig,command: "cd /www/wwwroot/blog.yimengtut.online/script && chmod +x update_project.sh && ./update_project.sh")// 2. (可选)捕获脚本输出def scriptOutput = sshCommand(remote: remoteConfig,command: "cd /www/wwwroot/blog.yimengtut.online/script && ./update_project.sh",returnStdout: true)echo "脚本输出:${scriptOutput}"}}
}

执行测试:

image-20250523001313780

image-20250523001326927

image-20250523001515942

看到能成功拉取代码!jenkinsfile配置完成。

测试使用bat脚本运行整套流程

完成前面的步骤后,现在我们就已经可以做到:只要执行bat脚本,就可以直接把Hugo下的文章自动构建,并部署到服务器上了。

测试。

目前的效果如图:

image-20250523001733373

修改博客内容:

image-20250523001905178

image-20250523001934686

双击执行bat脚本:

image-20250523002001165

看看网站:

image-20250523002035683

OK。目前的效果是:只要修改了post中的内容,点击一下bat脚本就可以更新到hugo网站了。

使用sync to hugo插件

但是我们每次都去hugo博客中操作比较不舒服。

一般情况下,我们会在一个工作区内写文章,只是把部分要发版的文章进行发版,那难道我们要手动复制到hugo文件夹内吗?

不,这样可太麻烦了。

所以我们来使用Obsidian来进行优化。(注意,优化前的方案还需要手动添加文章的属性才行,因为Hugo是需要知道文章标题等信息的,所以你也是需要手动设置的。但是使用sync to hugo后,文章标题,发布时间等信息就会自动给我们添加上,所以也会省去我们手动添加基本属性的麻烦事)

优化后的效果是:在Obsidian中写文章,然后如果看到有要推送文章,我们只要选择这个文件,然后点击Obsidian中的一个按钮就完成拷贝并添加基本属性了。

我们使用sync to hugo插件来完成这个步骤(这里我对这个插件进行了一点改造,如果需要可私信)。

image-20250523003338458

注意:hugo路径是指Hugo站点目录。简单的说,你看public在哪个文件夹下,你就选择哪个文件夹。

sync to hugo插件使用效果展示:

  1. 选择要推送的文章,进行推送:

    image-20250523003700593

    注意:这里我使用了OSS作为图床(如果需要教程,可以看我其他文章或者自行百度)。

    image-20250523003739903

  2. 看看Hugo的post文件夹中是否出现我们推送的文章:

    image-20250523003809792

    并且也可以看到,他自动给你添加加入了基本的属性:

    image-20250523003833536

  3. 接下来,如果你想进行发版,就可以直接去执行bat脚本,完成发版了:

    image-20250523003941727

    image-20250523004016471

创建QuickAdd宏,并编写js脚本

上面的操作已经足够简单了,只要写文章,然后点击sync to hugo按钮,然后点击bat脚本即可。

但是我还有一个需求,就是在一个软件中操作,不想去任何其他地方操作。

解决方案就是在Obsidian中使用QuickAdd插件,然后编写宏,调用js脚本,js脚本调用bat脚本实现发版。这样,我们就不用去找bat脚本去点击了。

编写js脚本

我们先编写脚本:

这个js脚本可以在Obsidian工作区的任何一个地方创建。脚本的内容如下。

脚本名:deploy.js

const { exec } = require("child_process");
const path = require("path");async function runUpdateBlog() {new Notice(`🚀 开始推送博客内容...!`, 5000);const batPath = path.join("D:", "Obsidian-Test", "Obsidian-Hugo", "bin", "update_blog.bat");const workingDir = path.join("D:", "Obsidian-Test", "Obsidian-Hugo", "bin");console.log("🚀 开始推送博客内容...");return new Promise((resolve, reject) => {const child = exec(`cmd /c "${batPath}"`, { cwd: workingDir,windowsHide: true,maxBuffer: 1024 * 1024 * 5}, (error, stdout, stderr) => {const output = stdout.toString();if (output.includes("OPERATION_SUCCESS")) {console.log("✅ 博客内容推送完成!");console.log(output);new Notice(`✅ 博客内容推送完成!`, 5000);resolve(output);} else if (output.includes("OPERATION_FAILED")) {console.error("❌ 博客更新失败:");console.log("完整输出:", output);reject(new Error("部署失败"));} else {console.error("⚠️ 无法确定操作状态");console.log("输出:", output);reject(new Error("未知状态"));}});const timeout = setTimeout(() => {if (!child.killed) {child.kill();console.error("⏰ 操作已超过30秒,进程已被终止");new Notice(`⏰ 操作已超过30秒,进程已被终止!`, 5000);reject(new Error("Process timeout"));}}, 30000);child.on("exit", () => clearTimeout(timeout));});
}module.exports = async () => {try {await runUpdateBlog();} catch (error) {console.error("执行失败:", error.message);throw error;}
};

image-20250523003043372

使用QuickAdd

安装QuickAdd插件,然后创建宏。

image-20250523004316023

image-20250523004336302

image-20250523004424870

image-20250523004436655

image-20250523004452776

image-20250523004505730

image-20250523004531367

image-20250523004550924

好了。

测试:

修改文章内容。

image-20250523005138044

先同步:

image-20250523005302186

再点击ctrl+p,选择我们的“Hugo发版”命令:

image-20250523005321469

image-20250523005331555

看到结果已经更新了。

image-20250523005407703

OK,目前已经达到了在一个软件中进行创作、并且可以选择指定内容进行发布博客、并且只要点击一个按钮就自动发版到公网去的效果了。

个人站点链接

我的博客链接:Luca的博客

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/91744.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/91744.shtml
英文地址,请注明出处:http://en.pswp.cn/pingmian/91744.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[硬件电路-141]:模拟电路 - 源电路,信号源与电源,能自己产生确定性波形的电路。

源电路(Source Circuit)是电子系统中为其他电路或负载提供特定信号或能量的基础电路模块,其核心功能是生成、调节或转换所需的物理量(如电压、电流、波形、频率等)。以下是源电路的详细解析:一、源电路的核…

Unity_数据持久化_PlayerPrefs基础

Unity数据持久化 一、数据持久化基础概念 1.1 什么是数据持久化 定义: 数据持久化就是将内存中的数据模型转换为存储模型,以及将存储模型转换为内存中的数据模型的统称。 通俗解释: 将游戏数据存储到硬盘,硬盘中数据读取到游戏中&…

什么是列存储(Columnar Storage)?深度解析其原理与应用场景

列存储的基本概念:颠覆传统的数据组织方式列存储(Column Storage)是一种革命性的数据库存储技术,它通过按列而非按行组织数据,从根本上改变了数据的物理存储结构。与传统行存储数据库不同,列式数据库将每一…

机器人抓取流程介绍与实现——机器人抓取系统基础系列(七)

机器人抓取系统基础系列文章目录 1. UR机械臂的ROS驱动安装官方教程详解——机器人抓取系统基础系列(一) 2. MoveIt控制机械臂的运动实现——机器人抓取系统基础系列(二) 3. 机器人(机械臂)的相机选型与安装…

【Qt】QObject::startTimer: Timers cannot be started from another thread

QTimer对象的 start 函数调用必须和创建QTimer对象是同一个线程。 #include "QtTimerTest.h" #include <QDebug>QtTimerTest::QtTimerTest(QWidget *parent): QMainWindow(parent),m_timer(nullptr),m_timerThread(nullptr), m_workingThread(nullptr) {ui.set…

社会治安满意度调查:为城市安全治理提供精准参考(满意度调查公司)

在社会治理不断深化的背景下&#xff0c;公众对社会治安的感知与评价已成为衡量城市治理水平的重要维度&#xff08;社会治安满意度调查&#xff09;&#xff08;公众满意度调查&#xff09;&#xff08;满意度调查&#xff09;。为全面掌握市民对治安状况的真实反馈&#xff0…

Python篇--- Python 的加载、缓存、覆盖机制

要理解 import 与 if __name__ "__main__": 的关系&#xff0c;以及 Python 的加载、缓存、覆盖机制&#xff0c;我们可以从 “模块的两种身份” 和 “导入的全过程” 入手&#xff0c;用通俗的例子一步步拆解。一、核心&#xff1a;模块的 “双重身份” 与 __name_…

Java设计模式之行为型模式(访问者模式)应用场景分析

访问者模式&#xff08;Visitor Pattern&#xff09;作为Java设计模式中的“隐形冠军”&#xff0c;常被开发者低估其价值。这一模式通过“双分派”机制巧妙解耦数据结构与操作&#xff0c;为复杂系统的扩展提供了强大武器。在大厂项目中&#xff0c;访问者模式往往出现在业务逻…

【IDEA】JavaWeb自定义servlet模板

方法一&#xff1a;&#xff08;推荐去使用方法二&#xff0c;还能创建其它代码模板&#xff09;使用servlet模板创建Servlet类如果创建时找不到servlet模板&#xff1a;File -> Project Structure然后应用 -> OK&#xff0c;如果还是找不到Servlet模板&#xff0c;看看项…

Linux选择

在内存中运行着的进程称为&#xff08; 服务 &#xff09;。负责控制systemd系统和服务管理器的工具为&#xff08; systemctl &#xff09;命令。systemd管理系统服务的基本单位是&#xff08; unit &#xff09;。分配和管理资源的基本单位是&#xff08; 进程 &#xf…

【Redis学习路|第一篇】初步认识Redis

概要: 深入探讨NoSQL数据库的核心特性&#xff0c;对比传统关系型数据库的差异&#xff0c;重点介绍Redis作为内存数据库的优势与应用场景。 文章目录认识 NoSQLNoSQL vs SQL 对比1️⃣ 结构化 vs 非结构化2️⃣ 关联 vs 非关联3️⃣ 查询方式对比4️⃣ 事务特性5️⃣ 存储方式…

java局域网聊天室小项目架构思路

java局域网聊天室小项目架构思路 项目需求 创建一个局域网聊天系统&#xff0c;要求&#xff1a;用户在登录界面登录后进入聊天窗口界面&#xff0c;能实现多用户同时在线聊天&#xff0c;并且用户之间可以进行私聊 项目用到的技术栈 java网络编程java多线程java面向对象编…

vulhub-corrosion2靶机

1.安装靶机 https://download.vulnhub.com/corrosion/Corrosion2.ovahttps://download.vulnhub.com/corrosion/Corrosion2.ova 2.扫描IP 3.扫描端口 4.访问端口 首先访问一下80端口 访问一个8080端口发现是一个apache的页面 5.扫描目录与漏洞探测 那么我们扫描一下目录 80…

Mysql深入学习:慢sql执行

目录 慢查询日志 慢查询主要步骤 11种慢查询的场景分析 场景一&#xff1a;SQL 没有建立索引 场景二&#xff1a;索引未生效的典型原因 场景三&#xff1a;LIMIT 深分页导致性能下降 场景四&#xff1a;单表数据量过大导致 SQL 性能下降 场景五&#xff1a;ORDER BY 出现…

李宏毅深度学习教程 第8-9章 生成模型+扩散模型

【2025版】12 生成式对抗网络GAN 一 – 基本概念介紹_哔哩哔哩_bilibili 目录 1. GAN生成式对抗网络 2. GAN的训练 散度差异 3.WGAN 4.训练GAN 5. 如何客观评估GAN 6. 条件型生成&#xff08;按照要求&#xff09; 7. Cycle GAN&#xff08;互转配对&#xff09; 8. d…

1.8 axios详解

Axios的定义与核心特性Axios是一个基于Promise的现代化HTTP客户端库&#xff0c;主要用于在浏览器和Node.js 环境中发送HTTP请求&#xff0c;旨在简化异步数据交互流程。其核心特性如下&#xff1a;跨平台支持&#xff1a;在浏览器中通过XMLHttpRequest对象发送请求&#xff0c…

41.安卓逆向2-frida hook技术-过firda检测(五)-利用ida分析app的so文件中frida检测函数过检测

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 内容参考于&#xff1a;图灵Python学院 工具下载&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1bb8NhJc9eTuLzQr39lF55Q?pwdzy89 提取码&#xff1…

安卓调javaScript Not find method “forceLogout“ implementatidsignature or namesp

核对一下是否实现对应的javaScript或者javaScript的方法参数对不对&#xff0c; 在这里插入图片描述我这里一开始实现了这个方法但是没有给参数&#xff0c;一直报异常&#xff0c;后台说token没给就查token的问题&#xff0c;最后发现是搞偏了&#xff0c;两个原因&#xff0c…

【Linux网络】:UDP(传输层协议)

目录 一、铺垫知识 1、传输层 2、端口号 2.1、五元组表示 一个进程通信 2.2、端口号范围划分 2.3、知名端口 2.4、查看端口号 2.5、问题 3、pidof & netstat 命令 ①netsate 命令 ②pidof命令 二、UDP协议 1、UDP协议格式 2、UDP报文 1.1、UDP数据封装的过…

Effective C++ 条款19: 设计class犹如设计type

Effective C 条款19&#xff1a;设计class犹如设计type核心思想&#xff1a;设计新的class时&#xff0c;应当像语言设计者设计内置类型一样慎重&#xff0c;考虑对象的创建、销毁、初始化、拷贝、类型转换等所有方面。 ⚠️ 1. 类设计的关键问题域 对象生命周期管理&#xff1…