Skip to content
Donghai's Blog
Go back

如何使用Git Hooks设置创建和修改日期(译)

原文章:How to use Git Hooks to set Created and Modified Dates

在这篇文章中,我将解释如何使用 pre-commit Git hook来自动输入AstroPaper博客主题前言中的创建日期(pubDatetime)和修改日期(modDatetime)。

Table of contents

Open Table of contents

无处不在

Git hooks非常适合自动化任务,比如添加检查提交消息中的分支名称,或者防止你提交纯文本密钥。它们最大的缺点是,客户端钩子是按机器设置的。

你可以通过拥有一个 hooks 目录并手动将其复制到 .git/hooks 目录或设置符号链接来解决此问题,但这都需要你记得进行设置,而这正不是我擅长的事情。

由于这个项目使用npm,我们可以利用一个叫做Husky的包(在AstroPaper中已经安装)来自动为我们安装钩子。

更新!在AstroPaper v4.3.0中,pre-commit钩子已被移除,改为使用GitHub Actions。然而,你可以轻松地自行安装Husky

钩子

由于我们希望在提交代码时运行此钩子以更新日期,并将其作为我们更改的一部分,因此我们将使用 pre-commit 钩子。这个钩子已经由AstroPaper项目设置好,但如果没有,你可以运行 npx husky add .husky/pre-commit 'echo "This is our new pre-commit hook"'

导航到 hooks/pre-commit 文件,我们将添加以下一个或两个代码片段

编辑文件时更新修改日期


更新:

此部分已更新为一个更智能的新版本。现在在发布之前不会递增 modDatetime。在第一次发布时,将草稿状态设置为 first,然后等待魔法发生


# 修改文件,更新modDatetime
git diff --cached --name-status |
grep -i '^M.*\.md$' |
while read _ file; do
  filecontent=$(cat "$file")
  frontmatter=$(echo "$filecontent" | awk -v RS='---' 'NR==2{print}')
  draft=$(echo "$frontmatter" | awk '/^draft: /{print $2}')
  if [ "$draft" = "false" ]; then
    echo "$file modDateTime updated"
    cat $file | sed "/---.*/,/---.*/s/^modDatetime:.*$/modDatetime: $(date -u "+%Y-%m-%dT%H:%M:%SZ")/" > tmp
    mv tmp $file
    git add $file
  fi
  if [ "$draft" = "first" ]; then
    echo "First release of $file, draft set to false and modDateTime removed"
    cat $file | sed "/---.*/,/---.*/s/^modDatetime:.*$/modDatetime:/" | sed "/---.*/,/---.*/s/^draft:.*$/draft: false/" > tmp
    mv tmp $file
    git add $file
  fi
done

git diff --cached --name-status 获取已暂存以进行提交的文件。输出如下::

A       src/content/blog/setting-dates-via-git-hooks.md

开头的字母表示已采取的操作,在上述示例中,文件已被添加。修改过的文件标记为 M

我们将该输出通过管道传递给grep命令,查找每一行中已修改的文件。该行需要以 M 开头(^(M)),后面可以有任意数量的字符(.*),并以 .md 文件扩展名结尾(.(md)$)。这将过滤掉未修改的Markdown文件 egrep -i "^(M).*\.(md)$"


改进 - 更明确

这可以仅添加到查找 blog 目录中的Markdown文件,因为这些文件才会有正确的Frontmatter


正则表达式将捕获两个部分,即字母和文件路径。我们将把这个列表通过管道传递给while循环,以迭代匹配的行,并将字母赋值给 a,将路径赋值给 b。我们现在将忽略 a

要知道文件的草稿状态,我们需要它的前言。在以下代码中,我们使用 cat 获取文件内容,然后使用 awk 在前言分隔符(---)上拆分文件,并取第二个块(即前言部分,--- 之间的内容)。接下来我们再次使用 awk 查找草稿键并打印其值。

  filecontent=$(cat "$file")
  frontmatter=$(echo "$filecontent" | awk -v RS='---' 'NR==2{print}')
  draft=$(echo "$frontmatter" | awk '/^draft: /{print $2}')

现在我们有了 draft 的值,我们将执行三种操作中的一种:当草稿为 false 时将 modDatetime 设置为当前时间(if [ "$draft" = "false" ]; then),清除 modDatetime 并将草稿设置为false(当草稿设置为first时 if [ "$draft" = "first" ]; then),或者在其他情况下不进行任何操作。

接下来的 sed 命令对我来说有点神奇,因为我不常用它,它是从另一篇类似的博文中复制的。实际上,它是在查找文件的前言标签(---)内,找到 pubDatetime: 键,获取完整行并将其替换为 pubDatetime: $(date -u "+%Y-%m-%dT%H:%M:%SZ")/ 的相同键和当前格式正确的日期时间。

这个替换是在整个文件的上下文中进行的,因此我们将其放入一个临时文件(> tmp),然后将新文件移动(mv)到旧文件的位置,覆盖它。然后将其添加到 git 中,准备提交,仿佛我们自己进行了更改。


注意

为了使 sed 正常工作,Frontmatter 中需要已经存在 modDatetime 键。如果你希望应用程序在日期为空的情况下构建,还需要进行一些其他更改,请参见 下面


为新文件添加日期

为新文件添加日期的过程与上述相同,但这次我们查找的是已添加的行(A),并将替换 pubDatetime 的值。

# 新文件,添加/更新pubDatetime
git diff --cached --name-status | egrep -i "^(A).*\.(md)$" | while read a b; do
  cat $b | sed "/---.*/,/---.*/s/^pubDatetime:.*$/pubDatetime: $(date -u "+%Y-%m-%dT%H:%M:%SZ")/" > tmp
  mv tmp $b
  git add $b
done

改进 - 只循环一次

我们可以在循环中使用 a 变量来切换,更新 modDatetime 或在一个循环中添加 pubDatetime


填充 frontmatter

如果你的IDE支持代码片段,那么可以创建一个自定义代码片段来填充前言。在 AstroPaper v4 中,将默认带有一个VSCode的代码片段

空的 modDatetime 更改

为了使 Astro 能够编译 Markdown 并执行其操作,它需要知道前言中预期的内容。它通过 src/content/config.ts 中的配置来实现。

为了允许键存在但没有值,我们需要编辑第10行以添加 .nullable() 函数。

const blog = defineCollection({
  type: "content",
  schema: ({ image }) =>
    z.object({
      author: z.string().default(SITE.author),
      pubDatetime: z.date(),
      modDatetime: z.date().optional(),
      modDatetime: z.date().optional().nullable(),
      title: z.string(),
      featured: z.boolean().optional(),
      draft: z.boolean().optional(),
      tags: z.array(z.string()).default(["others"]),
      ogImage: image().or(z.string()).optional(),
      description: z.string(),
      canonicalURL: z.string().optional(),
      readingTime: z.string().optional(),
    }),
});

为了防止 IDE 在博客引擎文件中出现错误,我还做了以下更改:

  1. src/layouts/Layout.astro 的第15行添加 | null,使其看起来像这样:

    export interface Props {
      title?: string;
      author?: string;
      description?: string;
      ogImage?: string;
      canonicalURL?: string;
      pubDatetime?: Date;
      modDatetime?: Date; 
      modDatetime?: Date | null; 
    }
  2. src/components/Datetime.tsx 的第5行添加 | null,使其看起来像这样:

    interface DatetimesProps {
      pubDatetime: string | Date;
      modDatetime: string | Date | undefined; 
      modDatetime: string | Date | undefined | null; 
    }

Share this post on:

Previous Post
AstroPaper 4.0(译)
Next Post
AstroPaper 3.0(译)
BlogsClub Meo Forever Blog