如何将你的作品集与 GitHub 代码库同步 😻👌
问题🫤
我们(至少大多数人)都有自己的作品集网站。作为开发者,我们都想展示自己参与过的项目,而这些项目大多都存储在我们的 GitHub 个人资料中。
🤔 但是我们如何在作品集网站上展示它们呢?
之前,我用一个ts文件来存储 GitHub 代码库链接和卡片信息,就像这样:
export const data = [
{
id: Math.random(),
subtitle:
"A Resume Builder with OpenAI that can generate a resume from given data. Supports file upload with Supabase.",
title: "Resume Builder with OpenAI",
github: "https://github.com/shricodev/resume-builder-openai",
noLive: true,
},
{
id: Math.random(),
title: "PDFWhisper - OpenAI",
subtitle: "A PDF Chatbot with OpenAI. It can answer questions from a PDF.",
github: "https://github.com/shricodev/pdfwhisper-openai",
liveUrl: "https://pdfwhisper-openai.vercel.app/",
},
// ...Rest of the project info
];
或者,您也可以使用json文件来存储类似信息。
这种方法的弊端🤨
真正的问题在于这个过程完全是手动操作。而且你也无法显示存储在 README 文件中的项目内容。如果你要创建一个新项目,你需要手动编辑该文件,提交更改,然后将其推送到你的代码仓库。唉,真是太麻烦了😮💨。
为什么不实现自动化,并且还可以选择以 Markdown 格式显示README内容呢?
修复方案🛠️
我对这个过程感到很沮丧,也一直觉得我的作品集网站因此显得过时。我首先想到的是:我们能否使用 GitHub Actions 来自动化这个过程?
答案是肯定的……但是如何做到呢?
解决这个问题有几种方法。你可以使用专门用于与 GitHub API 交互的 JavaScript 库(GitHub 推荐Octokit.js),或者寻找其他替代方案。
我阅读文档时,其中提到需要生成个人访问令牌,但我觉得没必要,因为我只需要从公共仓库获取数据。毕竟,GitHub 提供了一个公开 API(https://api.github.com),并且速率限制很宽松,非常适合获取仓库信息。
所以,我决定放弃使用库,改用一些Bash 的魔法。🪄🧙
直接获取数据curl
我使用一个简单的curl命令,获取了仓库中的所有名称:
curl -s "https://api.github.com/users/shricodev/repos?per_page=300" | jq -r '.[] | select(.private == false and .fork == false) | .name' | uniq
此命令会获取给定用户的所有仓库名称。它会使用 `--fork` 过滤掉 fork 的仓库(在大多数情况下,绝对不希望在作品集中显示任何 fork 的仓库)jq,然后根据唯一性筛选项目(只是为了更安全)。
🤔但是我们如何筛选特定的存储库以在我们的网站上显示呢?
为此,我们将向showcase您想要显示的存储库添加一个独特的主题(您可以随意命名,但请将此独特主题添加到您希望在作品集中显示的所有存储库)。
现在,我们只需要修改上面的命令jq过滤规则,检查主题是否showcase在存储库中,如果在,则仅获取该存储库的所有元数据和其他信息。
curl -s "https://api.github.com/users/shricodev/repos?per_page=300" | jq -r '.[] | select(.private == false and .fork == false and (.topics | index("showcase") != null)) | .name' | uniq
接下来,我从 GitHub 的开发者设置页面生成了一个个人访问令牌 (PAT),并将其添加到存储库的操作密钥(密钥和变量 -> 操作)中。
看起来是这样的:
编写 GitHub Action ✍️
以下是使用上述命令的完整 GitHub Action 工作流程curl:
name: Fetch Public Repos README's
on:
schedule:
# Run once every single day.
- cron: '0 0 * * *'
workflow_dispatch:
permissions:
contents: write
jobs:
fetch-readmes:
runs-on: ubuntu-latest
steps:
- name: Checkout the repository
uses: actions/checkout@v4
- name: Fetch repository list and README's with metadata
env:
TARGET_USER: <username>
TARGET_FOLDER: '<path_to_the_folder_to_store_readme(s)>'
run: |
mkdir -p "$TARGET_FOLDER"
# Fetch list of public repos
repos=$(curl -s "https://api.github.com/users/$TARGET_USER/repos?per_page=300" | jq -r '.[] | select(.private == false and .fork == false and (.topics | index("showcase") != null)) | .name' | uniq)
for repo in $repos; do
readme_file="$TARGET_FOLDER/${repo}_README.mdx"
if [[ -f "$readme_file" ]]; then
echo "README for $repo already exists, skipping..."
continue
fi
repo_metadata=$(curl -s "https://api.github.com/repos/$TARGET_USER/$repo")
title=$(echo "$repo_metadata" | jq -r '.name')
description=$(echo "$repo_metadata" | jq -r '.description // ""')
clone_url=$(echo "$repo_metadata" | jq -r '.clone_url')
language=$(echo "$repo_metadata" | jq -r '.language // ""')
homepage=$(echo "$repo_metadata" | jq -r '.homepage // ""')
topics=$(echo "$repo_metadata" | jq -r '.topics | join(", ")')
created_at=$(echo "$repo_metadata" | jq -r '.created_at')
updated_at=$(echo "$repo_metadata" | jq -r '.updated_at')
status_code=$(curl -o /dev/null -s -w "%{http_code}" https://raw.githubusercontent.com/$TARGET_USER/$repo/main/README.md)
if [ "$status_code" -eq 200 ]; then
readme_content=$(curl -s https://raw.githubusercontent.com/$TARGET_USER/$repo/main/README.md)
{
echo "---"
echo "title: "\"$title\"\""
echo "description: "\"$description\"\""
echo "clone_url: \"$clone_url\""
echo "language: \"$language\""
echo "homepage: \"$homepage\""
echo "topics:"
for topic in $(echo "$topics" | tr ',' '\n'); do
echo " - \"$topic\""
done
echo "created_at: \"$created_at\""
echo "updated_at: \"$updated_at\""
echo "---"
echo ""
echo "$readme_content"
} > "$readme_file"
else
echo "No README found for $repo, skipping..."
fi
done
- name: Commit Changes
run: |
git config --global user.name "github-actions[bot]"
git config --global user.email "github-actions[bot]@users.noreply.github.com"
git add -A
# Commit if there are any changes
if ! git diff --cached --quiet; then
git commit -m "Update READMEs with meta and content from public repos"
git push https://x-access-token:${{ secrets.<secret_name> }}@github.com/<username>/<repository_name>.git HEAD:<branch_name>
else
echo "No changes to commit."
fi
GitHub Actions 工作流每天运行,获取用户的所有存储库,写入其元数据和 README 内容(如果有),并将它们保存为<repository_name>_README.mdx文件。
workflow_dispatch:通过在cron 定时器下方添加,我们可以在需要时手动触发工作流。
如果您正在跟着操作,请务必将占位符文本替换为实际名称。
现在,我们有了所有元数据以及存储库 README 内容,我们可以使用任何 Markdown 解析器来解析mdx内容并将其显示在我们的网站上。
想看看我的项目页面实际效果吗?请访问👇
https://techwithshrijal.com/projects
结论⚡
借助这个简单的 GitHub Action,您的作品集网站将始终与您的 GitHub 项目保持同步。我们使用基本的命令行脚本实现了流程自动化,无需依赖第三方库。
您可以在这里找到完整的源代码:https://github.com/shricodev/portfolio.git
请点击这里查看我的作品集:https://techwithshrijal.com
感谢阅读!🎉

