尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

基于 JSON 和 GitHub Actions 打造个人技能花园:工程化技能管理实践

基于 JSON 和 GitHub Actions 打造个人技能花园:工程化技能管理实践 看到 ConardLi 维护的garden-skills仓库时很容易被这个命名吸引住。garden在开发者语境里经常被引申为“数字花园”skills指个人能力沉淀连起来可以理解为“用打理花园的方式管理技术技能”。不同人对这个项目的实际定位可能不一样但这类仓库真正值得讨论的不是某个具体功能而是一套可复用的技能管理工程思路如何把分散在文档、笔记、收藏夹和代码里的技术点整理成一份能持续更新、能自动统计、能展示给团队或社区的技能清单。这篇文章不打算逐行复述某个仓库的源码。这里会从garden-skills这个项目名出发设计一个适配个人开发者和小型团队的技术技能管理仓库。你可以把它理解成一种通用工程模板用 JSON 定义技能树用 Node.js 脚本做校验和统计用 GitHub Actions 自动生成 README 看板。哪怕你本地的仓库结构和文中示例不同这套思路也可以直接迁移。1. 先理解为什么需要“技能花园”技能管理不是写文档而是持续修剪1.1 技术技能零散靠记忆和收藏夹根本不可靠大多数开发者的技能积累过程是随机的。某个项目里用了 Redis就查两天资料另一个项目里接触了 Docker就写几个 Dockerfile看到一篇讲 TypeScript 类型体操的文章收藏进浏览器之后再也没有打开。几年下来简历上能写的技术名词不少但如果被问到“你对 Redis 到底掌握到什么程度”往往回答不出准确等级也拿不出能证明自己的项目或笔记。这种状态的根本原因不是学习不够努力而是缺少一个结构化的技能记录层。普通文档适合记录一篇文章的知识不适合归纳一个技能的成长状态。书签收藏夹只能保存入口不能保存掌握程度、练习记录和产出证据。技能管理需要的是一个能被机器读取和统计的数据结构让“我会什么、学到哪一步、下一步做什么”变成可视化的清单而不是脑海里的模糊印象。1.2garden-skills代表的是一种“数字花园”式学习观“数字花园”这个比喻在技术社区里出现频率很高。传统博客讲究排版完整、一次性发布数字花园则更像一个私人花园种子、幼苗、盛开的花并存每一篇笔记都可以随时修改、继续生长。garden-skills这个名字把“花园”和“技能”放在一起恰好点出了技能管理的核心技能不是静态的证书清单而是一个需要持续浇水、修剪、移植的生态系统。具体到工程实现技能花园需要三层能力第一层是数据层用结构化文件记录技能点包含名称、分类、熟练度、学习状态、证据链接和更新时间。第二层是工具层用脚本校验数据格式、统计各分类进度、生成可读的 Markdown 摘要。第三层是展示层把统计数据放到 README 或静态页面里供自己和他人查看。这三层加在一起才是一个能长期维护的技能仓库而不是随手写下的待办事项。1.3 仓库型技能管理的优势版本化、可追踪、可展示把技能信息放进 Git 仓库能获得很多额外收益。每一次修改都有提交记录可以追踪某项技能是什么时候从“学习知识”变成“项目实践”的。技能清单本身可以评审团队里其他人能通过 Pull Request 帮你补充或纠偏。配合 GitHub Actions每次 push 后自动重新计算技能总览更新 README整个过程不需要手动维护展示文件。对个人来说这套仓库可以成为写简历时的素材库对团队来说它可以演变成成员技能矩阵帮助 Leader 在分配任务时判断谁适合做前端性能优化谁更适合处理数据管道。这些价值都来自一开始把技能当作“工程数据”来对待而不是当作零散文档。2. 设计一份可扩展的技能树数据结构JSON 是比 Markdown 更好的底层格式2.1 为什么不用 Markdown 管理技能点Markdown 写起来确实方便但它是给人看的不是给程序分析的。技能管理里需要统计熟练度、按分类汇总、过滤某个状态的技能这些操作如果都从 Markdown 里做正则提取很快就会失控。更合理的做法是用 JSON 作为数据源用 Markdown 作为展示结果。JSON 结构清晰所有技能条目都遵循同样的字段格式脚本可以用统一逻辑读取。GitHub 对 JSON 文件也有语法高亮编辑时不容易出错。如果在编辑器里装了 JSON 校验插件语法问题能在保存前发现。缺点是手写大段 JSON 容易遗漏逗号或括号所以后面会引入一个校验脚本在 commit 前自动检查。2.2 技能条目应该包含哪些核心字段先定义最基础的技能条目模型。每个技能都对应一个独立对象包含以下字段字段类型说明idstring技能唯一标识建议用短横线命名如react-performancenamestring技能展示名称如React 性能优化categorystring一级分类如frontend、backend、devopssubcategorystring二级分类可空如framework、databaselevelnumber熟练度1 到 5 的整数5 表示能独立设计解决方案statusstring当前状态seed、growing、blooming、maintaining四选一evidencearray证据链接列表可以是代码仓库、文章、演示地址、证书链接milestonestring一个最能说明掌握程度的具体成就如“开发过日活 10 万的报表系统”updatedAtstring最近更新时间ISO 格式如2025-03-01nextActionstring下一步学习或实践计划帮助你持续生长status字段对应“数字花园”的成长阶段。seed表示刚接触只看了资料growing表示正在实践或学习blooming表示已经产出过可展示的成果maintaining表示已经进入维护期只需要跟随版本更新做少量学习。这样的状态设计比单纯的“已学/未学”更符合技术学习的真实节奏。2.3 一份完整的最小 JSON 示例创建一个名为skills.json的文件放在仓库根目录内容如下{ version: 1.0.0, owner: 你的名字, updatedAt: 2025-03-01T10:00:0008:00, categories: [ { key: frontend, name: 前端 }, { key: backend, name: 后端 }, { key: devops, name: 运维与工程化 }, { key: soft, name: 软技能 } ], skills: [ { id: react-performance, name: React 性能优化, category: frontend, subcategory: framework, level: 4, status: blooming, evidence: [ https://github.com/example/react-performance-notes, https://example.com/blog/react-render-optimization ], milestone: 主导过复杂列表页的渲染优化首屏时间降低 40%, updatedAt: 2025-02-18, nextAction: 阅读 React 19 编译器源码分析整理 useMemo 替代方案 }, { id: node-cli, name: Node.js CLI 工具开发, category: backend, subcategory: tooling, level: 3, status: growing, evidence: [ https://github.com/example/file-rename-cli ], milestone: 完成一个批量文件重命名工具并用 npm 发布, updatedAt: 2025-02-25, nextAction: 为 CLI 补充单元测试和参数校验 }, { id: docker-compose, name: Docker Compose 环境编排, category: devops, subcategory: container, level: 2, status: seed, evidence: [], milestone: , updatedAt: 2025-03-01, nextAction: 用 Docker Compose 搭一套 Node.js MySQL Redis 的本地开发环境 } ] }示例中只放了三条技能实际仓库里可以根据需要扩充。这个 JSON 文件是整个技能花园的数据底稿后续的统计脚本和展示层都依赖它。2.4 分类和数据校验参数要提前约定categories数组里的key需要和skills中条目的category字段保持一致。如果某条技能的category在分类表里不存在统计时就会落到“未分类”里导致看板数据不准。level字段用 1 到 5 的整数不要传入小数或字符串。status字段只能是seed、growing、blooming、maintaining四种脚本会展开校验。evidence允许为空数组但不能为null。milestone允许为空字符串但一旦填写建议控制在 50 字以内方便在表格中展示。这些约定看起来琐碎但能避免后续脚本解析时出现各种边界问题。校验脚本会把这些规则全部变成自动检查项提交数据前先跑一遍不通过就不允许 commit。3. 搭建本地维护流程用 Node.js 脚本保证技能数据永远“干净”3.1 项目目录结构要一眼能看懂技能仓库的目录不需要很复杂按功能划分为四处即可garden-skills/ ├── skills.json ├── scripts/ │ ├── validate.js │ └── generate-readme.js ├── .github/ │ └── workflows/ │ └── update-skills.yml ├── README.md └── package.jsonskills.json是数据源scripts存放校验和生成本地脚本.github/workflows存放自动更新任务README.md是最后生成的展示文件。package.json用于声明脚本命令和依赖即使本地不想引入复杂工具链也可以只用内置的 Node.js 模块完成所有功能避免安装额外依赖。3.2 使用 Node.js 内置模块写一个零依赖校验脚本校验脚本要解决的问题有两类一类是 JSON 语法错误另一类是字段规则错误。JSON 语法错误可以直接用JSON.parse捕获字段规则错误则通过遍历所有技能条目判断。下面是一个最小可用版本const fs require(fs); const path require(path); const dataPath path.join(__dirname, .., skills.json); const raw fs.readFileSync(dataPath, utf8); let data; try { data JSON.parse(raw); } catch (error) { console.error([校验失败] skills.json 不是合法 JSON: ${error.message}); process.exit(1); } const allowedStatus [seed, growing, blooming, maintaining]; const categoryKeys new Set((data.categories || []).map(item item.key)); const errors []; if (!Array.isArray(data.skills)) { errors.push(skills 字段必须是数组); } else { data.skills.forEach((skill, index) { const position skills[${index}] (${skill.id || 未知 id}); if (!skill.id || typeof skill.id ! string) { errors.push(${position}: id 不能为空且必须是字符串); } if (!skill.name) { errors.push(${position}: name 不能为空); } if (!categoryKeys.has(skill.category)) { errors.push(${position}: category 值 ${skill.category} 不在 categories 中); } if (!Number.isInteger(skill.level) || skill.level 1 || skill.level 5) { errors.push(${position}: level 必须是 1-5 的整数); } if (!allowedStatus.includes(skill.status)) { errors.push(${position}: status 必须是 ${allowedStatus.join(/)}); } if (!Array.isArray(skill.evidence)) { errors.push(${position}: evidence 必须是数组); } if (typeof skill.milestone ! string) { errors.push(${position}: milestone 必须是字符串); } if (!/^\d{4}-\d{2}-\d{2}$/.test(skill.updatedAt || )) { errors.push(${position}: updatedAt 格式应为 YYYY-MM-DD); } }); } if (errors.length 0) { console.error([校验失败] 发现以下问题); errors.forEach((msg, i) console.error( ${i 1}. ${msg})); process.exit(1); } console.log([校验通过] 共 ${data.skills.length} 条技能格式正确。);脚本会在不依赖任何第三方包的情况下完成基本校验。在package.json里添加命令{ scripts: { validate: node scripts/validate.js, generate: node scripts/generate-readme.js } }之后每次修改skills.json都可以运行npm run validate确认数据没有结构性错误。如果想要更严格可以再加一个 Git Hook在 commit 之前自动执行校验防止错误数据进入仓库。3.3 用生成脚本把 JSON 变成 README 中的技能看板校验只是第一步让数据变成可读内容才是关键。写一个generate-readme.js读取skills.json统计各分类的数量和平均熟练度然后拼接 Markdown 表格。const fs require(fs); const path require(path); const dataPath path.join(__dirname, .., skills.json); const readmePath path.join(__dirname, .., README.md); const data JSON.parse(fs.readFileSync(dataPath, utf8)); const total data.skills.length; const categoryStats data.categories.map(category { const items data.skills.filter(skill skill.category category.key); const count items.length; const avgLevel count ? (items.reduce((sum, item) sum item.level, 0) / count).toFixed(1) : 0.0; return { key: category.key, name: category.name, count, avgLevel }; }); const statusCount { seed: data.skills.filter(item item.status seed).length, growing: data.skills.filter(item item.status growing).length, blooming: data.skills.filter(item item.status blooming).length, maintaining: data.skills.filter(item item.status maintaining).length }; const rows data.skills.map(skill { const evidenceText skill.evidence.length ? skill.evidence.map(link [链接](${link})).join( ) : 暂无; return | ${skill.name} | ${skill.category} | ${skill.level} | ${skill.status} | ${evidenceText} | ${skill.nextAction || 无} |; }); const markdown # Garden Skills 自动化生成的技能看板更新时间为 ${data.updatedAt}。 ## 总览 - 技能总数${total} - 种子阶段${statusCount.seed} - 成长阶段${statusCount.growing} - 开花阶段${statusCount.blooming} - 维护阶段${statusCount.maintaining} ## 分类统计 | 分类 | 技能数 | 平均熟练度 | | --- | --- | --- | ${categoryStats.map(item | ${item.name} | ${item.count} | ${item.avgLevel} |).join(\n)} ## 技能明细 | 技能 | 分类 | 熟练度 | 状态 | 证据 | 下一步 | | --- | --- | --- | --- | --- | --- | ${rows.join(\n)} ; fs.writeFileSync(readmePath, markdown, utf8); console.log(README.md 已生成);这个脚本目前只生成了基础表格。实际操作中你还可以把“种子阶段”和“成长阶段”的技能单独列出作为当前学习重点把“开花阶段”的技能放到简历素材区。一切取决于自己的展示偏好。3.4 个人使用和团队协作使用分别需要什么个人使用这套流程时重点放在“低摩擦记录”。每次学完一个新东西只花 30 秒改skills.json然后运行npm run validate npm run generate本地就能看到更新后的 README。不需要搭建服务不需要数据库不需要前端页面整个仓库就是一个数据驱动的技能清单。团队协作时需要额外注意三点数据所有权技能数据属于个人还是团队需要提前约定避免成员担心隐私问题。评审流程技能熟练度是自评结果团队可以引入互评或依据项目产出校准。自动化和权限GitHub Actions 会修改 README需要给机器人账号分配写权限或者在 workflow 中配置permissions字段。学习环境里只要本地能跑通脚本就算成功生产环境也就是真正对外展示或团队使用的阶段还需要考虑分支保护、Pull Request 模板和定期数据清理。4. 用 GitHub Actions 自动生成技能看板push 之后无需手动操作4.1 为什么需要自动化看板技能清单的时效性很重要。如果每次更新 JSON 后还要手动重新生成 README很容易出现“数据已经更新展示还是旧版”的情况。GitHub Actions 可以把“生成 README”这一步变成仓库的自动化行为每次 push 到主分支或者每天定时触发都自动运行生成脚本并提交新的 README。这样技能花园始终处于生长状态不需要依赖本地环境。4.2 最小可运行的 GitHub Actions 工作流在.github/workflows/update-skills.yml中创建以下内容name: Update Skills README on: push: branches: - main paths: - skills.json workflow_dispatch: permissions: contents: write jobs: generate: runs-on: ubuntu-latest steps: - name: Checkout repository uses: actions/checkoutv4 - name: Set up Node.js uses: actions/setup-nodev4 with: node-version: 20 - name: Validate skills.json run: node scripts/validate.js - name: Generate README run: node scripts/generate-readme.js - name: Commit changes run: | git config user.name github-actions[bot] git config user.email github-actions[bot]users.noreply.github.com git add README.md if git diff --cached --quiet; then echo no changes to commit else git commit -m chore: update skills readme git push fi工作流做了四件事拉取仓库代码安装 Node.js运行校验脚本运行生成脚本最后把新生成的 README 提交回仓库。paths字段严格控制了只有skills.json变更时才触发避免无关提交也跑一遍流程。workflow_dispatch允许你在需要时手动点击“Run workflow”强制更新。4.3 为什么要在 CI 里也跑一次校验脚本本地校验能挡住大多数错误但校验动作不能只靠个人自觉。把validate.js放进 GitHub Actions等于在云端加了一道防线。即使某次修改没有在本地运行npm run validatepush 之后 CI 也会先校验校验不通过就不会走到生成步骤更不会产生错误的 README。这比本地的 Git Hook 更可靠因为本地 Git Hook 可以被跳过而 CI 是强制执行的。4.4 定时更新与手动更新如何协同除了 push 触发技能仓库还适合加入定时触发。可以添加一个schedule事件例如每周一早上 8 点自动生成一次 README这样即使本周没有修改技能数据README 头部的更新时间也会刷新提醒自己该回顾了。on: schedule: - cron: 0 0 * * 1这里要注意GitHub Actions 的schedule有延迟不代表它定义的时刻绝对精确。定时任务适合做提醒不适合做严格的生产调度。如果你需要精确到分钟级应该使用云上的 cron 服务而不是 GitHub Actions。5. 常见问题排查从 JSON 报错到 Actions 失败按链路逐层定位5.1 JSON 解析失败错误信息直接指出问题但根因往往是手写习惯现象运行npm run validate时出现Unexpected token } in JSON。可能原因多写了一个逗号、括号不匹配、字符串没有闭合、中文引号混入。检查方式查看skills.json的第一行报错附近用编辑器自带的 JSON 校验功能定位。VS Code 中右下角会显示“Problems”也可以使用JSON.parse的报错信息定位。解决方案把报错字符附近的代码改成合法 JSON。如果文件较大可以先格式化再编辑。使用 VS Code 的Format Document快捷键或者安装 Prettier 插件。预防建议不要在skills.json中用 Excel 或记事本直接编辑最好使用支持 JSON Schema 的编辑器配置自动补全和校验后手写错误会大幅减少。5.2 校验脚本提示 category 不在列表中现象skills[0] (xxx): category 值 unknown 不在 categories 中。可能原因分类命名不一致。比如在categories里写的是front-end在skills条目里写的是frontend。检查方式打开skills.json对比categories数组的key和skills中所有条目的category字段是否完全一致注意大小写和连字符。解决方案统一分类命名。建议使用小写加短横线风格如frontend、backend、devops。预防建议在validate.js中增加一个“未使用分类”警告如果categories中存在没有任何技能对应的分类提示是否需要删除或补充技能。5.3 GitHub Actions 生成了新 README 但没推送成功现象Actions 日志显示Commit changes步骤成功但仓库里 README 没有变化。可能原因git push可能因为分支保护规则被拒绝或者工作流权限没有勾选“Allow GitHub Actions to create and approve pull requests”。检查方式查看 workflow 运行日志中是否有remote: rejected或! [rejected]字样。检查仓库 Settings - Actions - General 中的 Workflow permissions 是否为“Read and write permissions”。检查分支保护规则是否禁止机器人直接推送。解决方案在 workflow 的permissions中显式声明contents: write并确认分支保护规则允许github-actions[bot]推送。预防建议如果分支保护必须保留可以改成“生成 README 后创建 Pull Request”而不是直接推送到 main 分支。5.4 生成的 README 表格出现乱码或链接错误现象Markdown 表格中证据列显示为[链接](url)但点击后无法打开。可能原因evidence数组里的 URL 写错了协议头比如写成http://导致自动跳转或者 Markdown 对特殊字符处理不当。检查方式在浏览器中直接访问skills.json中的 URL确认可访问性。解决方案统一使用https://开头的有效链接不要把项目相对路径写进evidence数组。如果链接本身有括号在生成脚本里做编码处理。预防建议加一个 URL 格式校验evidence中的每一项必须以https://或http://开头。5.5 排查顺序建议遇到技能仓库相关问题时建议按以下顺序排查skills.json是否能被正常解析。npm run validate输出的错误列表。本地执行node scripts/generate-readme.js是否成功。本地生成的 README 是否符合预期。再次查看 GitHub Actions 的日志定位是校验失败、生成失败还是推送失败。检查分支保护、权限和 workflow 的paths条件。按照这个顺序大多数问题都能在源头处解决而不是在展示层反复折腾。6. 最佳实践与扩展方向让技能花园真正融入你的工作流6.1 从个人技能仓库升级为团队技能矩阵一个人维护garden-skills时skills.json只有自己的数据。如果团队里多个人都想使用这套体系可以把数据拆分到members/目录每个人一个 JSON 文件再通过脚本合并统计。这样既可以保留个人技能树的粒度又能生成团队维度的能力矩阵前端有多少人达到 level 4容器技术覆盖了多少人还有哪些关键技能处于无人维护状态。团队使用时建议把level的自我评估标准写进 README熟练度定义1听说过能说出基本概念2在教程或练习中用过能解决简单问题3在真实项目中使用过能独立完成常见任务4主导过相关模块能指导他人并做技术方案5能设计该领域的整体架构并解决复杂疑难问题标准明确后技能数据才有横向对比的意义。6.2 与学习笔记、博客和代码仓库联动技能条目中的evidence字段是连接技能花园与日常产出的桥梁。每当你在博客上写一篇关于性能优化的文章或提交一个开源的 CLI 工具都可以把这个链接补到对应技能的evidence数组里。这个过程不只是在记录技能更是在反向逼迫自己“不要把技能停留在口头上”。建议每周花 15 分钟做一次技能回顾打开skills.json检查是否有新学习的内容没有加入是否有技能可以更新status和level是否需要清掉已经不再关注的技术方向。这种回顾比“每天学习 3 小时”更实际因为它的对象不是输入量而是技能状态。6.3 可持续维护的几个关键习惯不要让技能数量无限膨胀。每个分类最多保留 10 个左右关键技能超出后应该合并或归档。每次修改只做一件事增加新技能、调整熟练度、更新证据不要在一次提交里混改多个无关技能否则历史追踪会混乱。GitHub Actions 的 README 自动推送可能造成大量“chore: update skills readme”提交如果觉得噪音大可以改成只在手动触发时生成。不要在skills.json中写中文标点或全角引号JSON 标准里它们会造成解析失败。如果仓库是公开的不要在其中填写隐私信息如果只想给自己看建议使用私有仓库。6.4 进一步扩展从技能看板到简历生成和可视化项目跑到稳定阶段后可以继续扩展简历生成器根据level 3且status blooming的技能自动生成简历中的“专业技能”列表。可视化看板用 GitHub Pages ECharts 读取skills.json生成雷达图或分类柱状图。定时提醒在 workflow 中增加一个 Issue 创建步骤每周自动创建“本周技能回顾”提醒。学习路径推荐在 JSON 中为每个技能增加dependsOn字段自动计算学习前置依赖。这些扩展都不需要推翻已有结构只要在skills.json的基础上增加少量字段和脚本即可。这就是把技能当作数据来管理的好处任何展示层都可以随时更换但底层的数据资产是稳定且可复用的。回到最初提到的ConardLi / garden-skills这个仓库名给你我的最大启发也许不是某段具体代码而是“用工程思维管理个人成长”这个动作本身。技能管理不需要等某一天突然开始也不需要一个大而全的系统。你现在就可以创建一个skills.json写下第一个技能条目然后让脚本和 Actions 帮你把花园慢慢养起来。
返回列表