GitHub Actions 文件时间戳丢失问题及解决方案
一次偶然的发现
我有一次创建 GitHub Actions workflow 之后,发现构建完的文章的创建时间和修改时间都变成构建时间了。我试了好多次发现结果都一样,我就明白了这是构建阶段文件时间戳丢失的问题。
问题分析
这又令我思考:什么文件操作会对文件的时间戳造成影响?复制?剪切?粘贴?修改?查了一下资料发现:
操作类型 | 是否影响创建时间 | 是否影响修改时间 | 备注 |
---|---|---|---|
复制+粘贴 | 是 | 是 | 新文件会获得新的时间戳 |
剪切+粘贴 | 否 | 否 | 保留原文件时间戳 |
修改内容 | 否 | 是 | 仅更新修改时间 |
Git克隆/拉取 | 是 | 是 | 获取操作时的时间戳 |
GitHub Action | 是 | 是 | 在工作流程中创建的文件获得新时间戳 |
经过研究,我发现 GitHub Actions 工作流程中的 checkout 操作会导致文件时间戳丢失,这是因为 Git 本身不保存文件的时间戳信息。Git 只记录提交时的作者时间和提交时间,而不会存储每个文件的创建和修改时间。当在 GitHub Actions 中 checkout 代码时,所有文件都会被重新创建,获取当前的时间戳。
有一个常见思路是通过 git log
来获取时间,但这存在几个问题:
- 文件的创建时间可能早于首次 Git 提交的时间
- 更新时间与最后一次修改提交时间可能不完全一致
- 重命名或移动的文件可能会丢失历史记录
思来想去,脑海里产生了一个可能的解决办法。
解决办法
考虑到git push
之前都需要run dev
,我可以用这个阶段的钩子来触发脚本遍历文档获取时间戳。通过静态保存的方法创建file-timestamps.json
,在run build
阶段直接使用该JSON,避免构建时时间戳出错。
具体实现方案
我为博客项目创建了两个关键文件:
file-timestamps.mjs
- 负责管理时间戳的保存和读取timestamp-integration.mjs
- 作为 Astro 集成,在构建过程中应用时间戳
下面是时间戳收集和应用的基本流程:
// 在开发环境保存时间戳function saveTimestamps(timestamps) { // 仅在开发环境写入,生产环境只使用内存缓存 if (process.env.NODE_ENV === 'production') return;
try { writeFileSync( 'file-timestamps.json', JSON.stringify(timestamps, null, 2), 'utf-8' ); // 保存成功提示 console.log('✅ 时间戳已保存'); } catch (error) { // 错误处理 console.error('❌ 保存时间戳失败:', error); }}
// 初始化文件时间戳function initFileTimestamp(filepath) { const timestamps = loadTimestamps(); const relPath = getRelativePath(filepath);
// 如果没有记录,从文件系统获取时间 if (!timestamps[relPath]) { const stats = statSync(filepath); timestamps[relPath] = { created: stats.birthtime.toISOString(), modified: stats.mtime.toISOString() }; saveTimestamps(timestamps); }
return timestamps[relPath];}
然后在 astro.config.mjs
中将其作为集成启用:
import timestampIntegration from './src/integrations/timestamp-integration.mjs';
export default defineConfig({ integrations: [ timestampIntegration(), // 其他集成... ], // 其他配置...});
小问题的解决
即使构建成功了,也会可能有一点小问题,比如说时间有点不对,正好差了8个小时,这应该是 GitHub Action 构建时的时区默认是世界标准时(UTC),需要在环境变量里配一下时区:
- name: 构建博客 env: TZ: 'Asia/Shanghai' # 设置时区为中国标准时间 run: bun run build
完成这个设置后,构建出来的文件时间戳就会正确显示了,解决了静态博客在自动部署时的时间戳丢失问题。
总结
通过这个方案,我们解决了 GitHub Actions 中文件时间戳丢失的问题:
- 在本地开发时,自动保存所有文件的时间戳信息到 JSON 文件
- 将这个 JSON 文件与代码一起提交到仓库
- 在 GitHub Actions 构建过程中,通过插件从 JSON 恢复文件的时间戳
- 设置正确的时区环境变量避免时区差异
以下是这个系统在 Astro 中工作的示例:
import { loadTimestamps } from './file-timestamps.mjs';
/** * Astro 集成,用于恢复文件时间戳 */export default function() { return { name: 'timestamp-integration', hooks: { 'astro:config:setup': ({ updateConfig }) => { // 加载已保存的时间戳数据 const timestamps = loadTimestamps();
updateConfig({ markdown: { remarkPlugins: [ () => (tree, file) => { // 获取文件相对路径 const relativePath = file.history[0].replace(process.cwd(), '').replace(/^\//, '');
// 应用时间戳到文章的 frontmatter if (timestamps[relativePath]) { file.data.astro.frontmatter.pubDate = timestamps[relativePath].created; file.data.astro.frontmatter.updatedDate = timestamps[relativePath].modified; } } ] } }); } } };}
虽然我主要讲的是 Astro 构建,但对于其他的构建来说,也是等价的,本质上都是把时间信息保存下来再读取,从而绕过文件本身的时间戳
留言评论