Reading view

There are new articles available, click to refresh the page.

借助AI将博客从Jekyll迁移至Hugo

将博客从Jekyll迁到Hugo,是我几年前就想做的事了,然而积重难返,力有不逮,这几年也就逐渐淡忘了。为什么想要抛弃Jekyll呢,喜新厌旧的心态远胜于实际需要,Jekyll所被诟病的性能差的问题,在我的环境中并不关键,构建速度从8s提升到2s,看似有4倍之巨,实则并没有那么大的吸引力,还是对未知事物的探索更诱人。

如今有了AI,又有闲心,便想重启这一工程。我想要的是1:1复刻,AI并不能一步到位进行转化,但90%的工作确实都是由它完成。从一种形态切换到另一种形态,最重要的是观念的转变。Jekyll和Hugo虽然相似,但并非完全对应,当了解Hugo构建网页的逻辑后,很多问题也便迎刃而解了,对不懂编程的我来说,这是一个艰难的过程,也有事先未阅读官方文档的原因。

第一步是模板的迁移,Hugo有自己内置的引用逻辑,不如Jekyll用layout指定那么直观,主页、文章、页面需要用什么模板都得重整,对我来说是整个迁移过程中学习曲线最陡峭的部分了。AI的信息有点落后,它提供的架构是老版本的,而新版本进行了结构和逻辑的优化,不管是ChatGPT还是Gemini似乎都不太了然。幸亏我在迷惑之时想到了官方文档,否则就被AI带入落后版本的境地里了。

第二步在迁移资源文件时遇到了最大问题,是Sass一直编译不成功,和AI“讨论”了一两个小时,尝试了不同编译写法、文件结构和语法检查,最终的怀疑点在Sass的解析器上,看过官方文档后确认如此。因为我用了@use的新语法,需要用Dart Sass解析,而Hugo默认使用LibSass。在编译函数上,ChatGPT用的是已废弃的resources.ToCSS,Gemini却知道用新的toCss,这大概是在此次任务中Gemini唯一胜出的一处。

后面都是一些具体而微的问题,捡一些主要的来说。非常重要的一步是posts的适配,Hugo的语法要求更为严格,而我旧文的front matter无法通过检查。文章有几百篇,当然是让AI帮忙写脚本批量处理,Gemini写的脚本引入了新问题,而ChatGPT的脚本一次性就成功了。想来这一步若无AI而让我自己抠脚本,不知要忙到何时。

Hugo更为严格的一个地方在于不能在markdown中执行模板语言,比如partial等,而我在Jekyll中大量使用了此类写法,所以很多页面要重写。Hugo采用的方法是将其编写为shortcode,再在markdown中引用,相比Jekyll多了一步,也完全可以接受。对于图片的插入,我在Jekyll中使用了include的方法,自然也要转换为shortcode,并让AI对功能进行了扩展,支持插入单张或多张图片,这却只是因为某篇文章在新环境中无法再使用模板语言的循环结构。

有一处让人遗憾的功能是Hugo不支持csv格式的数据集,对我来说这是维护读书列表最简单的方式。Hugo也可以处理csv文件,但AI提供的data.GetCSV方式已废弃,用transform.Unmarshal却遇到了csv文件的BOM问题,怎么也修复不了,只能忍痛将读书列表转成了json格式。

Hugo的链接格式生成也不如Jekyll符合心意。我已养成了2025-12-14-hello-world.md风格的文件命名习惯,觉得如此更为条理,但Hugo的:contentbasename不能像Jekyll一样自动取文件名中的英文标题加入链接,:title又带有中文,唯有在front matter中多加一个 slug字段来指定链接内容。

还有一个无伤大雅的功能便是字数统计,前几天才刚让AI写了能较准确统计中英文混排字数的Jekyll插件,现在却突然改弦易帜了,插件也无用武之地。虽说Hugo也能编码实现相同功能,但我图省事就用内置的{{ .WordCount }},在站点配置hasCJKLanguage = true的情况下也还堪用,不过会虚高一些。

Hugo也是老人了,难怪AI掌握的多是一些过时信息,最新的静态博客引擎是什么,我暂时没有兴趣。Hugo就是我以前心中的“白月光”,如今已达夙愿,也可安稳一段时间——Jekyll不也用了七年吗,难道是七年之痒——即便博客再写好多年,Hugo的性能也足够。我觉得Jekyll和Hugo各有千秋,正如开头所说,此一番折腾不过是喜新厌旧而已。而此次工程的两位帮手,俱是免费版的ChatGPT和Gemini两相比较,我觉得ChatGPT写的代码更简洁、更健壮、也更有效。至于一开始订立的1:1还原目标,实际并未完全做到,不是不能,而是没有必要了。

Root旧手机(一加Ace竞速版)

因为耻于说出口的理由,需要一部有root权限的手机。古老的一加6T满足条件却因屏碎无法使用,换屏后又出现自动随机点击界面的问题,但总算勉强堪用,也因此忍痛支付了高于市价许多的换屏费用。然而闲置半年多没用,再次开机却接入不了网络,无法修复只好另谋出路。

正好手里还有另一部退休手机——一加Ace竞速版(PGZ110),便打起了它的主意。刚买来时嫌麻烦没root,现在却还是免不了这一步。虽然root流程已经很成熟,每次实操仍免不了麻烦,故而记录如下。

翻查教程,root最关键的一步便是获取boot.img。坊间有阿木大侠的一加全能工具箱,看描述能轻松完成root流程,特意找出古董Windows笔记本,光开机密码便试了许久才猜中。运行工具箱后,界面的中文竟全是乱码,以为系统语言是英文的关系,切换中文后仍然如此,而且程序运行后似乎会导致系统崩溃,无奈只能放弃这条捷径了。

接下来要自己提取boot.img,阿木大侠的网站有全量ROM,却需要通过百度网盘下载。每次遇到用百度网盘下载的资源,都难以克制想要问候作者的心情,但话说回来又有什么其他选择呢。之前下载大文件,都是从淘宝买的加速,但旧商品已失效,新的方法需要用自己的账号登录。我没有百度网盘账号,还要借用妻的。

费尽工夫下载完ROM,用Python版的payload_dumper提取boot.img时,程序提取三个文件后便卡住了,其中并没有boot.img。陷于绝望之时,抱着试试的心态,用Go版的payload-dumper-go得以完成,难怪ChatGPT推荐后者。

下一步在解锁手机的Bootloader时又遇到问题。一开始不知如何进入fastboot模式,用的是adb reboot fastbootfastboot reboot bootloader两条命令,之后发现用adb reboot bootloader即可。进入fastboot模式,执行fastboot flashing unlock并重启,却发现并没有解锁成功。再次尝试时看到手机界面上有选项,原来需要音量上键选中解锁才行,但窗口期只有5秒,不过也足够操作了。

安装Magisk并给boot.img打补丁比较简单,无事发生,可反过来执行fastboot flash给手机分区反刷镜像时程序却卡住了,经过多番尝试才发现问题所在。手机进入fastboot模式后,只有第一条命令会执行成功,而我是先执行了fastboot getvar current-slot查询分区,再执行的刷包命令。了解这条信息后,事情自然迎刃而解。

到这里root已经完成,后续安装需要的Magisk模块,删除恼人的植入应用,都是轻车熟路的事了。看似很简单的root流程,却耗费了我近一天的时间。辛苦是值得的,现在我这部退休的手机又可以重新上岗,肩负起重要而特殊的任务了。

R's Plots

读研时学过一段时间的R语言,虽未能助我求职,写毕业论文时倒用上了。R社区在Twitter上有个活动,叫做#TidyTuesday,每周二分享一组数据供大家作图。闲来无事,我也参与过一段时间,以下便是当时画的图,收集于此权当纪念那段快乐的学习时光吧。

体重和骑车数据上传Garmin

使用Garmin Forerunner 245M已经快三年半了,当时为了记录跑步数据一冲动就买了,骑车时也当码表用。后来跑步少了,骑车也有了码表,它就用来看看时间、天气和心率,早上看看睡眠。

前段时间表带的卡扣断了,从网上买了一根便宜的表带替换。我对这个手表很满意,会继续用下去等它不能再用的那天,退役之后,下块手表还是买Garmin的。

Garmin记录了我的日常运动数据,便想把体重和骑车数据也整合在一起。我的码表是Bryton的(Garmin码表实在太贵了),数据可以同步Strava但不能连接Garmin,查了一下好像没有从Strava反向同步到Garmin的方案,只能采用手动方式上传数据了。

流程是这样的:

  1. 码表连接手机的Bryton Active同步数据;
  2. 在App上选择记录导出为Fit文件(之前的版本可以直接导出为.fit文件,更新版本后变成了.zip文件,还要再解压);
  3. 登录Gamin Connect网页版,选择上传数据,上传导出的.fit文件。

虽然有点麻烦,但总归是一个办法。在这里要夸一下Bryton的App,一开始非常简陋难用,经过几次更新后,终于变得流畅好用了,这家公司还是有心的。

称体重用的是同事送的小米体脂秤,可以在手机上安装Mi Scale Exporter这款应用,作者有详细的使用指导。称体重竟然也是一个不容易坚持的习惯,对减肥不上心后也就不上称了。

一直担心自己的血压,一度还想买个Garmin Index BPM,记录血压数据,不知是否好用。

❌