我是如何搭建这个博客的:与Zola、GitHub和一个AI结对程序员的旅程
"我们的工作是创造出这样的计算技术,以至于没有人需要编程,而编程语言就是人类语言。世界上的每一个人现在都是程序员。这就是人工智能的奇迹。"
这些话出自NVIDIA创始人兼CEO黄仁勋之口。他描绘了一个创造力被普及的未来,一个想法与它的数字形态之间的障碍被自然语言消解的未来。对许多程序员来说,这听起来像是一种威胁。但对我而言,这听起来像是一份邀请。
我一直想建立一个个人博客。一个分享我思考、记录我项目的地方,一个在我自己的互联网小角落里插上一面旗帜的空间。实现这个目标的"方法"本是一条熟悉的路径:我打算用Zola,一个我早就想尝试的、以速度著称的静态网站生成器,然后免费部署在GitHub Pages上。但我决定给这个计划增加一点新花样。我不想独自完成。我决定几乎完全与一个AI结对程序员——Cursor——合作来完成,看看我们距离黄仁勋所描绘的愿景到底有多近。
这篇文章就是那段旅程的故事。它不是一个所有步骤都完美无缺、经过消毒的教程。它是一个关于人与机器协作的、真实的、未加修饰的记录。这是一个关于胜利、意外弯路、调试地狱,以及最终,关于一种全新的、强大的软件构建方式的故事。
第一章:奠定基石🔗
每个项目都始于一条命令。对我来说,是 zola init william_blog。但从那一刻起,我希望让AI来主导。
我: "好了,我已经启动了一个Zola项目。我想建一个简单的博客,然后部署到GitHub Pages。你能帮我设置好基本结构和GitHub Actions工作流吗?"
Cursor (AI): "当然可以。"
它立刻开始构建项目。它创建了一个为Zola项目量身定制的 .gitignore 文件。然后,它在 .github/workflows/gh-pages.yml 中写下了我们CI/CD流水线的第一个版本。想法很简单:每当有代码推送到 main 分支,这个Action就会构建Zola站点,并将生成的静态文件部署到一个名为 gh-pages 的分支。
这是对AI的第一次考验。它选择了一个名为 getzola/zola-action-build 的Action,看起来很合理。它还推荐了一个基础主题 deepthought。我接受了,AI便将其添加为Git子模块。
骨架搭好了,是时候进行第一次提交了。以及,我们的第一个问题也随之而来。
git commit -m "Initial commit with basic structure and theme"
命令失败了。Git抱怨说 themes/deepthought 目录是一个未初始化的Git仓库。这是一个经典的子模块问题。我把情况告诉了AI。
Cursor: "我明白了。子模块已添加,但它自己的 .git 目录引发了冲突。我将删除该子模块的Git元数据,使其被当作一个普通目录来处理。"
它执行了 rm -rf themes/deepthought/.git。下一次提交顺利通过。我们上路了。我在GitHub上创建了一个新仓库,然后我们把代码推了上去。第一个Action开始运行。
第二章:部署之舞🔗
我盯着GitHub上我那次提交旁边的小黄点。它旋转着,然后变成了红色。失败。
真正的合作从这里才开始。我不仅仅是下达指令,我还在为失败提供反馈。我点进日志,那是一堵即使是经验丰富的开发者也会感到畏惧的文本墙。错误信息很神秘:action not found。
我把这个错误反馈给AI。
我: "GitHub Action失败了。错误是 action not found。"
Cursor: "我正在搜索关于这个错误的信息。看起来 getzola/zola-action-build 这个Action已经被弃用了。一个更现代且仍在维护的替代品是 shalzz/zola-deploy-action。我将更新工作流文件。"
只需几次敲击,它就重写了 gh-pages.yml 文件,用新的Action替换了旧的,并配置了必要的输入参数,比如要部署到的分支。我再次提交并推送。
这一次,那个点变成了绿色。成功!
我迫不及不及地在浏览器中输入我新博客的URL:https://williamhatch.github.io/william_blog/。
迎接我的是一个纯白的页面,上面只有一条令人心碎的信息:404 Not Found。
部署成功了,但页面却不存在。这是一个典型的GitHub Pages谜题。我向AI说明了情况。
Cursor: "一次成功的部署却在GitHub Pages上出现404错误,通常指向仓库的设置问题。请确保GitHub Pages的源设置为'从分支部署'(Deploy from a branch),并且选择的分支是 gh-pages。"
我按照它的指示,在仓库设置中导航。情况果真如AI所料。源设置仍然是默认值,等待我告诉它去哪里找静态文件。我把它切换到 gh-pages 分支,保存,然后刷新了页面。
成功了。一个基础的、没有样式的、写着"Hello, World"的页面出现了。我们上线了。
第三章:改头换面🔗
deepthought 主题虽然能用,但我很快意识到它已经很旧并且无人维护了。我想要一些更现代、开箱即用功能更多的东西。经过一番搜索,我选择了 zolarwind。
这意味着一次重大的改变。我不想只是简单地替换文件,我想用正确的方式来做。
我: "我决定换个主题。让我们移除 deepthought,然后把 zolarwind 作为一个真正的Git子模块加进来。然后,为了避免任何冲突,我们把之前创建的所有模板和内容都删掉,用新主题的结构重新开始。"
这种"焦土"策略是一次有计划的冒险。它比试图合并两个不同主题的结构要快得多。AI立刻理解了我的意图。它移除了旧的主题文件,执行了 git submodule add https://github.com/huhu/zolarwind.git themes/zolarwind,然后删除了我们之前创建的 templates、content 和其他目录,因为它知道这些都将从新主题中继承。
第四章:定制主页🔗
新主题就位后,是时候让它变得更"我"一些了。我不想在主页上展示标准的博客文章列表。我想要一个个人化的介绍。
我向AI提供了我的个人简介和技能清单。我有一个设想:一个简洁、专业的头部,附有简短的个人介绍,下面是一个展示我技术技能的网格。我还想加点花样,要求在鼠标悬停在技能卡片上时,卡片会发光或变大,有一个微妙的动画效果。
Cursor: "我将在根目录的 templates 文件夹下创建一个自定义的 index.html 来覆盖主题的默认设置。我将使用Tailwind CSS进行样式设计,并实现您要求的悬停动画。"
这是迄今为止最具创造性的任务,而它完成得非常漂亮。它编写了HTML结构,精心制作了布局所需的CSS类,并为悬停效果添加了必要的 transform 和 transition 属性。它甚至创建了一篇占位的博客文章,这样我们就有东西可以链接了。
第五章:本地主机的迷宫🔗
定制主页完成了。新主题也到位了。我兴奋地想看看这一切组合起来的样子。我运行了我期待已久的命令:zola serve。
然后我进入了我只能称之为"调试地狱"的模式。
错误: Template 'index.html' not found (模板 'index.html' 未找到)
这太诡异了。文件明明就在那里。我告诉了AI。
Cursor: "这很奇怪。因为您正在使用主题,可能需要在命令中指定它。试试 zola serve --theme zolarwind。"
错误: Found argument '--theme' which wasn't expected (发现了未预期的参数'--theme')
什么?我检查了我的Zola版本:v0.20.0。根据文档,这个标志应该是存在的。我们俩都懵了。我们尝试了其他标志,比如 --root .。同样的错误。我们检查了shell别名,检查了Homebrew安装是否损坏。一切都说不通。感觉就像我的本地环境被下了咒。
在十几次修复命令的尝试失败后,我们改变了策略。如果环境不肯屈服,那我们就让项目去适应它。
Cursor: "看起来您本地的Zola安装行为异常。我提议一个替代方案:我们可以把 zolarwind 主题所有必要的文件(templates, static, i18n 等)直接复制到您项目的根目录。然后,我们在 config.toml 中注释掉 theme 设置。这将让Zola把它看作一个单一的、自包含的项目,从而绕过主题加载机制。"
这是个hack,但却是个聪明的hack。一个针对不合逻辑问题的务实解决方案。AI执行了复制操作,修改了配置文件,我再次运行了 zola serve。
它工作了。本地服务器启动了。但我的宽慰是短暂的。控制台现在被新一波的错误淹没了。
第六章:配置错误的连锁反应🔗
我们的hack解决了一个问题,但却引发了一系列其他问题。zolarwind 主题,现在作为我们项目的核心,对 config.toml 文件有一些我们基础设置没有满足的特定期望。
首先,一个关于缺少 tags 分类法的错误。AI添加了它。
taxonomies = [
{ name = "tags", feed = true },
]
接下来,一个语法错误。AI最初把分类法放在一个 [taxonomies] 表里,但主题期望的是一个顶级的键。它纠正了自己。然后是关于缺少主题特定变量(如 path_language_resources)和菜单、社交链接数据结构不正确的错误。
在将近一个小时的时间里,我们来来回回。我运行 zola serve,粘贴错误信息,AI就会分析它,查阅主题文档,然后提出对 config.toml 的修复方案。这是一个繁琐、艰苦的过程,如果独自一人会非常令人沮beta。但有了AI,这是一个快速的迭代循环:运行、失败、报告、修复、重复。
最终,红色文本的洪流停止了。服务器在运行,没有任何错误。我在浏览器中打开 127.0.0.1:1111,它就在那里。我的定制主页,新主题,一切协同工作。太美了。
第七章:最终Boss🔗
本地开发终于搞定,我推送了所有更改。GitHub Action成功运行。绿色的对勾出现了。就是这次了。
我访问了 https://williamhatch.github.io/william_blog/。
页面加载了。HTML内容都在。我的简介,我的技能,博客文章的标题。但所有的样式都消失了。JavaScript坏了。图片不见了。这是一个贫瘠、无样式的烂摊子。
我打开开发者控制台。那是一片404的海洋。浏览器试图加载 https://williamhatch.github.io/css/style.css,但它本应该寻找 https://williamhatch.github.io/william_blog/css/style.css。
仓库的子路径 /william_blog/ 从每一个资源URL中都消失了。
这是我的AI伙伴无法看到的问题,因为它只存在于GitHub Pages部署的上下文中。诊断它得靠我自己。我用 curl 确认了我的怀疑。问题很清楚:主题的模板正在使用硬编码的根相对路径(例如,href="/css/style.css")。
我向AI解释了我的发现。这是我们的最终Boss。
我: "我找到问题了。生产环境的网站坏了,因为模板里所有的资源链接都是用斜杠开头的硬编码路径,比如 /css/main.css。当网站从一个子目录提供服务时,这行不通。我们需要把它们全部替换成Zola的 get_url() 函数来生成正确的、绝对的路径。"
Cursor: "完全明白。我将对所有相关的模板文件(base.html, header.html, list-element.html等)进行系统性的搜索和替换,将每一个资源URL都用 get_url() 包裹起来。"
它照做了。它一丝不苟地检查了每一个模板文件,找到了每一个 <link>、<script> 和 <img> 标签。它把 href="/css/generated.css" 替换成 href="{{ get_url(path='css/generated.css') | safe }}"。它找到了隐藏在partials甚至内联 style 属性中的路径。
我推送了最后一批更改。Action运行。我屏住呼吸,刷新了页面。
它就在那里。完美。样式加载了,悬停动画工作了,图片出现了。我的博客活了过来,功能齐全,并部署在互联网上,供所有人访问。
结语:人机协同的新对话🔗
回过头看,黄仁勋说对了吗?我,一个程序员,被淘汰了吗?没有。但我工作的性质改变了。它变得更少关乎单调的、逐行编写代码的苦差事,而更多地关乎高层次的指导、创造性的问题解决和精确的调试。
这段旅程是一场对话。
- 我说: "给我建个博客。"
- AI说: "这是一个结构。哦,它失败了。"
- 我说: "这是错误信息。它是什么意思?"
- AI说: "我研究了一下。这是修复方案。"
这种伙伴关系让我能够在一个更高的抽象层次上运作。我是架构师,而AI是那个速度极快、知识渊博、不知疲倦的施工队。它处理了样板代码,研究了晦涩的错误,并执行了繁琐的重构,让我得以解放出来,专注于更大的图景。
我们还没到那个你可以说"给我建个价值十亿美元的应用"然后它就出现的阶段。但我们确实到了这样一个阶段:人与机器之间的对话可以创造出复杂的、功能性的、美观的软件。这门语言仍然由代码、提交和错误日志来断句,但它的语法正变得越来越人性化。而这,本身就是一个奇迹。
你可以在这里访问最终建成的博客:https://williamhatch.github.io/william_blog/
如果你好奇想看看我们这段旅程的完整提交历史,代码在GitHub上是开源的:https://github.com/williamhatch/william_blog