基于事件驱动的轻量级编码计划监控技能设计与实现

张开发
2026/4/30 6:27:33 15 分钟阅读

分享文章

基于事件驱动的轻量级编码计划监控技能设计与实现
1. 项目概述一个为开发者定制的“计划执行监控”技能最近在折腾一个挺有意思的东西我把它叫做“编码计划监控技能”。这名字听起来有点拗口但说白了它就是一个帮你盯着自己“立下的Flag”有没有按时完成的小工具。作为一个写了十几年代码的老兵我太清楚咱们这行的“拖延症”有多普遍了。年初雄心勃勃地列了一堆学习计划、开源项目计划、重构计划结果到了年底一看完成的没几个Flag倒了一地。这个shiquda/coding-plan-monitor-skill项目就是为解决这个痛点而生的。它不是那种复杂的项目管理软件也不是一个需要你花大量时间配置的看板。它的核心定位是轻量、自动化、无侵入。你可以把它理解为一个“计划执行情况的智能哨兵”。你只需要用一种简单的方式比如在代码仓库里放一个配置文件或者通过一个简单的命令把你的计划条目和截止日期告诉它它就会在后台默默地帮你监控进度。一旦发现某个计划临近截止日却毫无动静或者已经超期它就会通过你预设的渠道比如钉钉、飞书机器人或者邮件给你发个提醒让你没法再假装看不见。这个技能特别适合独立开发者、小团队的技术负责人或者任何有自我提升需求的程序员。它不关心你计划的具体内容是什么——可以是“本周完成微服务网关的鉴权模块开发”也可以是“月底前读完《重构》第二版前三章”——它只关心两件事计划是什么以及截止时间到了没。通过将计划执行情况透明化、自动化地推送到你面前它能有效地对抗惰性和遗忘帮你把那些美好的技术愿景一点点变成可交付的代码和可量化的成长。2. 核心设计思路如何用最少的干扰实现最有效的监控2.1 为什么不用现成的项目管理工具市面上优秀的项目管理工具很多Jira, Trello, Notion乃至GitHub Projects功能都非常强大。但它们往往太重了。对于一个纯粹想督促自己完成个人学习或小型Side Project的开发者来说为了一两个计划去创建项目、设置看板、分配任务、更新状态这个启动成本太高了很容易在“开始管理”这一步就放弃了。我们的核心需求其实很简单设定目标 - 定期检查 - 收到提醒。过多的功能和交互反而成了负担。因此这个技能的设计第一原则就是“约定大于配置”。它不应该要求用户改变现有的工作流。最好的方式是能无缝嵌入开发者最熟悉的环境——代码仓库。想象一下你为一个新的学习项目创建了一个Git仓库在根目录下顺手添加一个名为.coding-plan.yml的文件在里面用YAML格式写上几条计划提交并推送。从此这个仓库的每一次提交、每一次PR合并都会自动被这个技能监控并与你定义的计划进行匹配和评估。你几乎感知不到它的存在但它却在持续工作。2.2 核心架构事件驱动与规则匹配为了实现无侵入整个技能采用了事件驱动的架构。它的工作流可以概括为以下几个核心环节计划定义用户在代码仓库的特定配置文件如.coding-plan.yml中以结构化的方式定义自己的计划。每个计划包含几个关键属性唯一ID、描述、关联的代码路径/文件可选、截止日期、检查规则如“需包含至少一次提交”或“需关闭某个特定Issue”。事件监听技能通过集成代码托管平台如GitHub、GitLab、Gitee的Webhook功能监听仓库发生的关键事件。最核心的事件是push代码推送和pull_request合并请求关闭。这些事件携带了谁、在什么时候、对哪个分支、修改了哪些文件等丰富信息。规则引擎与状态评估当监听到事件后技能会拉取该仓库的计划配置文件并结合本次事件的内容运行内置的规则引擎。引擎会遍历所有未完成的计划判断本次事件是否对某个计划的完成有“贡献”。例如规则可以是“计划A要求完成src/auth/目录下的开发”那么当本次推送事件中包含了对该目录下文件的修改引擎就会认为计划A有进展并可能更新其“最后活跃时间”。状态判定与提醒触发规则引擎评估后会更新每个计划的状态如“进行中”、“风险”、“超期”。另一个独立的定时任务或基于事件的延迟检查会周期性地扫描所有计划根据“当前时间”与“计划截止日”以及“最后活跃时间”的关系判定是否需要触发提醒。例如可以设置规则距离截止日还剩3天且无进展 - 触发“风险预警”提醒截止日已过仍未完成 - 触发“超期告警”提醒。通知分发一旦需要提醒技能会调用集成的通知渠道API将格式化好的提醒消息包含计划详情、当前状态、仓库链接等发送给指定的接收者个人或群组。这个架构的关键在于所有的监控和判断都是自动的。开发者只需要在开始时定义好计划之后的提交行为就会自动被纳入评估体系。这极大地降低了维护成本让坚持变得更容易。注意规则引擎的设计需要足够灵活但又不失简单。初期可以支持几种通用规则如“文件路径匹配”、“提交信息关键词匹配”、“关联Issue状态变更”。过于复杂的规则会让配置变得困难违背了“轻量”的初衷。2.3 技术选型考量平衡轻量与能力为了实现上述架构在技术栈上需要做一些务实的选择后端语言选择 Go 或 Python。Go 的优势是编译为单一二进制文件部署极其简单性能好适合做这种常驻的、处理网络请求的服务。Python 的优势是生态丰富尤其是与各大云平台、通知服务的SDK集成起来快开发效率高。考虑到这个技能的“轻量”属性以及可能需要快速迭代和集成各种Webhook我最初选择了Python (FastAPI)。FastAPI能快速构建高性能的API来接收Webhook其异步特性也适合处理IO密集型的通知发送任务。数据存储对于个人或小团队使用计划数据量很小。但我们需要存储计划定义、每次检查的快照、以及用户/仓库的绑定关系。使用传统的关系型数据库如SQLite或PostgreSQL有点杀鸡用牛刀而且增加部署复杂度。更轻量的选择是使用键值存储比如Redis。我们可以将每个仓库的计划配置和状态序列化后存储为一个Value。Redis的过期时间和发布/订阅功能未来也可能用于实现定时扫描和内部消息传递。对于原型或极小规模使用甚至可以考虑将状态直接写入一个文件如.plan-status.json并提交到仓库的特定分支实现完全基于Git的状态管理但这会污染仓库历史需谨慎。部署与触发最理想的部署方式是作为一个Serverless 函数如AWS Lambda阿里云函数计算。Webhook事件直接触发函数执行函数内部完成规则匹配和通知发送。这种模式成本极低按调用次数计费且无需维护服务器。定时扫描任务则可以使用云服务提供的定时触发器Cron来激活另一个函数。这是将“轻量”贯彻到运维层面的选择。通知渠道必须支持开发者最常用的即时通讯工具。国内环境优先集成钉钉和飞书的群机器人国外则考虑Slack和Discord。邮件SMTP作为保底方案也必须支持。通知消息的模板要清晰最好能直接点击跳转到对应的仓库或代码位置。3. 实操要点从配置到上线的完整流程3.1 计划定义文件 (.coding-plan.yml) 的编写规范这是用户与此技能交互的主要界面必须设计得直观易懂。一个完整的配置文件示例如下version: 1.0 plans: - id: learn-auth-week1 title: 学习JWT鉴权并实现基础模块 description: 阅读JWT RFC文档并在项目中实现一个基础的登录签发Token接口。 # 关联的代码路径支持通配符 paths: - src/auth/**/*.py - docs/auth/*.md # 截止日期支持ISO格式或相对日期如‘in 2 weeks’ due_date: 2023-10-27T18:00:0008:00 # 完成条件规则 rules: - type: commit_path # 要求对指定路径有至少一次提交 min_count: 1 - type: pr_merged # 可选要求关联的PR被合并且PR标题或标签匹配 keywords: [auth, jwt] # 提醒规则 alerts: - type: risk # 提前3天无进展则预警 before_days: 3 condition: no_progress channel: dingtalk # 指定通知渠道 - type: overdue # 超期当天告警 after_days: 0 channel: feishu关键字段解析与注意事项id: 计划的唯一标识建议使用英文和短横线便于在日志和数据库中索引。paths: 这是监控的锚点。技能会重点关注对这些路径下文件的修改。支持通配符*和**非常必要因为开发过程中文件可能会新增或移动。如果此项为空则监控整个仓库的提交但这可能噪音较大。due_date: 明确的截止时间点。建议强制使用ISO 8601格式避免时区歧义。也可以考虑支持相对日期由服务端在首次解析时计算为绝对时间但对用户来说写绝对日期更清晰。rules: 定义“怎样才算完成”。commit_path是最通用和推荐的规则它检查对关联路径的提交次数。pr_merged规则更适合有Code Review流程的团队它要求相关改动必须通过PR合并。未来可以扩展issue_closed、file_exists要求生成某个特定文件如设计文档等规则。alerts: 定义何时、通过何种渠道提醒。condition: “no_progress”是核心它需要技能能记录每个计划最后的“有进展”时间即匹配到规则的提交时间。before_days和after_days提供了灵活的提醒时机配置。实操心得在解析用户YAML配置时一定要做严格的模式验证Schema Validation。使用如PydanticPython或structGo这样的库在加载配置时就检查字段类型、必填项、日期格式是否合法。早期不在这一步做好校验后期运行时会出现各种奇怪的错误难以排查。同时要给配置提供合理的默认值比如未指定提醒渠道时使用用户在全局设置中配置的默认渠道。3.2 Webhook的配置与安全验证代码托管平台向你的服务发送Webhook这是数据入口必须保证安全和可靠。获取Webhook URL你需要一个公网可访问的API地址。如果你使用Serverless函数云服务商会提供一个唯一的触发URL。如果你使用自有服务器则需要配置域名和HTTPSGitHub等强制要求Webhook使用HTTPS。在平台上配置以GitHub为例进入仓库的Settings - Webhooks - Add webhook。Payload URL: 填入你的服务API地址例如https://your-api.com/webhook/github。Content type: 选择application/json。Secret:这是关键的安全步骤。生成一个高强度的随机字符串如用openssl rand -base64 32填入此字段。你的服务端代码必须用同样的密钥来验证每个请求的签名防止伪造请求。Events: 选择Let me select individual events。至少勾选Push和Pull request事件。如果你需要监控Issue也可以勾选相应事件。为了减少不必要的调用初期只选必需的。服务端验证逻辑你的Webhook处理接口如/webhook/github在收到请求时第一件事不是处理业务而是验证签名。GitHub会在请求头X-Hub-Signature-256中携带签名HMAC SHA256。你的服务端需要用预设的Secret和请求体Raw Body重新计算HMAC SHA256并与请求头中的签名进行比较。只有一致才处理。常见坑点很多Web框架如Flask、Express会提前解析请求体request.json这会改变原始的二进制数据导致签名校验失败。你必须读取原始的、未解析的请求体字节流来进行签名计算。在FastAPI中可以通过await request.body()来获取原始字节。# FastAPI 中的签名验证示例伪代码 from fastapi import Request, HTTPException import hmac import hashlib GITHUB_SECRET byour-secret-here # 从安全配置中读取 async def verify_github_webhook(request: Request): signature_header request.headers.get(X-Hub-Signature-256) if not signature_header: raise HTTPException(status_code403, detailNo signature header) # 获取原始请求体 body_bytes await request.body() # 计算期望的签名 expected_signature sha256 hmac.new( GITHUB_SECRET, body_bytes, hashlib.sha256 ).hexdigest() # 安全地比较签名使用hmac.compare_digest避免时序攻击 if not hmac.compare_digest(expected_signature, signature_header): raise HTTPException(status_code403, detailInvalid signature) # 验证通过继续解析JSON处理业务 payload await request.json() # ... 后续业务逻辑3.3 核心匹配引擎的实现细节这是整个技能的“大脑”。当收到一个有效的Push事件后引擎需要加载计划配置根据事件中的仓库标识如owner/repo从存储Redis中获取对应的.coding-plan.yml文件内容。如果不存在则忽略该事件。提取事件关键信息提交SHA与消息payload[‘after’](最新提交ID)payload[‘commits’][*][‘message’]。修改的文件列表遍历payload[‘commits’]收集所有added,modified,removed的文件路径。这是匹配commit_path规则的核心数据。PR信息如果是PR合并事件payload[‘pull_request’][‘merged’](是否已合并)payload[‘pull_request’][‘title’]payload[‘pull_request’][‘body’]。遍历计划进行匹配对于每个未完成的计划检查其rules。如果规则类型是commit_path则检查本次提交修改的文件列表是否与计划中定义的paths支持通配符匹配有交集。如果有则计为一次“有效进展”并记录当前时间为该计划的last_progress_time。如果规则类型是pr_merged则检查本次PR合并事件的标题/描述中是否包含关键词并且PR修改的文件是否与计划路径有交集可选更严格。一个计划可能有多条规则可以设计为“满足任意一条”或“满足全部”通常在配置中用一个rule_match_modeany/all字段来控制。更新计划状态将匹配结果哪个计划有进展和新的last_progress_time更新到存储中。这里的状态更新应该是幂等的即同一次事件重复处理可能由于网络重试不会导致状态错误累积。通配符匹配的实现这是路径匹配的难点。Python的fnmatch模块支持*和?但不直接支持**递归匹配。我们可以将**转换为正则表达式或者使用更强大的pathlib结合自定义逻辑。一个简单实用的方法是将用户配置的路径模式如src/auth/**/*.py转换为一个正则表达式如^src/auth/.*\.py$然后对每个修改的文件路径进行匹配。注意处理好路径分隔符/和\在不同系统上的兼容性问题统一转换为/再匹配。3.4 定时扫描与提醒触发Webhook事件驱动了“进展”的检测但“超时”和“预警”需要基于时间的主动检查。这需要一个独立的定时任务。实现方式如果使用Serverless可以创建一个独立的函数由云服务的定时触发器Cron每30分钟或1小时调用一次。如果使用常驻服务可以启动一个后台线程或使用APScheduler等库。扫描逻辑定时任务启动后遍历存储中的所有活跃仓库和计划。对于每个未完成的计划读取其due_date和last_progress_time。计算时间差当前时间 截止日期且(截止日期 - 当前时间) 预警天数同时last_progress_time在预警时间段内无更新 → 触发风险预警。当前时间 截止日期且计划未标记为完成 → 触发超期告警。提醒去重同一个计划可能在多个扫描周期内都满足触发条件。必须避免重复发送相同的提醒造成骚扰。可以在存储中为每个计划记录最近一次发送提醒的类型和时间。只有当满足条件且距离上次发送同类型提醒已超过一定间隔如24小时时才再次发送。通知发送调用对应渠道的API。消息模板要友好且信息充足。例如【编码计划监控】风险预警计划学习JWT鉴权并实现基础模块(ID: learn-auth-week1) 仓库your-org/your-repo状态距离截止时间2023-10-27 18:00还剩3天近期无进展。 关联路径src/auth/**/*.py请及时跟进 查看仓库4. 部署与运维实践4.1 基于Serverless的极简部署以阿里云函数计算为例这是我最推荐的部署方式真正做到了开箱即用和零运维。准备代码将你的技能代码如Python项目打包。确保根目录有requirements.txt列出依赖以及一个index.py文件其中包含一个名为handler的函数作为入口点。创建函数登录阿里云函数计算控制台创建服务Service服务可以理解为一个项目组。在服务下创建函数选择“事件函数”运行时选择“Python 3.9”。上传代码包方式选择“通过文件夹上传”或“通过ZIP包上传”。配置触发器这是关键。创建两个触发器HTTP触发器用于接收GitHub Webhook。生成一个HTTP访问地址这就是你的Webhook URL。认证方式可以选择“匿名”方便测试或“函数计算签名”更安全但GitHub配置稍复杂。定时触发器用于执行计划扫描。触发周期配置为Cron表达式如0 */30 * * * *表示每30分钟一次。环境变量配置在函数配置中设置所有敏感信息和配置项为环境变量如GITHUB_WEBHOOK_SECRET: 你的GitHub Webhook密钥。REDIS_URL: 你的Redis连接地址和密码。DINGTALK_WEBHOOK: 钉钉机器人的Webhook地址。FEISHU_WEBHOOK: 飞书机器人的Webhook地址。LOG_LEVEL:INFO或DEBUG。 这样做避免了将密钥硬编码在代码中也便于在不同环境测试、生产间切换。测试在函数计算控制台配置测试事件模拟一个GitHub的Push事件Payload手动触发函数查看日志输出是否正常。然后去GitHub仓库实际推送一次代码观察Webhook是否成功送达并处理。优势与成本函数计算按调用次数和资源使用量计费对于个人使用每月调用几千次费用几乎为零。无需关心服务器、负载均衡、系统安全补丁真正实现了“专注业务逻辑”。4.2 状态存储方案选型与配置如前所述Redis是一个很好的选择。如果你使用云服务可以直接购买阿里云、腾讯云的云数据库Redis版它们提供高可用和自动备份。对于个人项目甚至可以在同一台服务器或同一个VPC内安装一个Docker版的Redis。# 使用Docker快速启动一个Redis docker run -d --name coding-plan-redis -p 6379:6379 redis:alpine在代码中使用redis-py库进行连接和操作。数据结构设计示例Key 设计仓库配置config:{owner}:{repo}- 存储整个.coding-plan.yml的YAML字符串。计划状态status:{owner}:{repo}:{plan_id}- 存储一个JSON对象包含last_progress_time,last_alert_sent_at,current_state等。全局索引可选index:repos- 一个Set存储所有已配置监控的仓库标识{owner}:{repo}方便定时任务全量扫描。数据过期可以为状态Key设置一个较长的TTL如90天当仓库被删除或长时间无活动后数据自动清理避免存储膨胀。注意事项Redis是内存数据库虽然我们的数据量小但仍要注意序列化/反序列化的性能。使用msgpack或pickle可能比纯JSON字符串更高效。同时所有写操作更新状态要考虑并发情况虽然Webhook并发量通常不高但使用WATCH/MULTI/EXEC事务或SET命令的NX/XX参数可以保证原子性。4.3 日志、监控与问题排查即使是一个小工具良好的可观测性也至关重要。结构化日志不要简单用print。使用structlog或logging模块配置JSON格式的日志。每一条日志都应包含请求ID可以从Webhook事件中提取或生成、仓库、计划ID等上下文信息。这样在排查问题时可以轻松过滤出特定仓库或请求的所有日志。import structlog logger structlog.get_logger() # 在Webhook处理开始时 request_id generate_request_id() logger.info(“webhook.received”, eventevent_type, reporepo_full_name, request_idrequest_id) # 在计划匹配时 logger.info(“plan.matched”, plan_idplan_id, request_idrequest_id)关键指标监控在代码关键节点埋点记录一些指标如webhook_received_total接收到的Webhook总数按事件类型分类。plan_matched_total计划被匹配到的次数。alert_sent_total发送提醒的总数按渠道、类型分类。processing_duration_seconds处理一个Webhook或一次扫描的耗时。 这些指标可以推送到像Prometheus这样的监控系统或者简单地在日志中输出然后通过日志分析工具如阿里云SLS进行聚合查看。监控这些指标能帮你了解技能的健康度和使用情况。常见问题排查清单收不到Webhook首先检查GitHub仓库的Webhook配置页面查看最近的“Deliveries”。红色感叹号表示发送失败。点击查看响应体通常会有明确的错误信息如“签名无效”403或“超时”504。根据错误信息检查你的服务端日志和网络配置。收到了Webhook但无提醒检查日志看是否成功解析了配置文件。可能是YAML语法错误或路径不存在。检查匹配引擎日志看本次提交的文件是否与计划的paths匹配成功。可能是通配符匹配逻辑有bug。检查定时任务是否正常执行日志中是否有扫描记录。检查提醒去重逻辑是否因为短时间内重复而抑制了发送。提醒发送失败检查通知渠道如钉钉机器人的Webhook地址是否配置正确是否有IP白名单限制函数计算的出口IP可能不固定需要关闭IP白名单或使用固定EIP。查看调用通知API返回的错误码和消息。5. 扩展思路与高级玩法一个基础可用的监控技能搭建起来后可以根据实际需求进行扩展让它变得更强大、更智能。5.1 支持更多代码托管平台与事件初期可能只支持GitHub。但国内很多团队使用Gitee或GitLab。它们的Webhook格式和API略有不同。一个好的设计是抽象出一个“平台适配器Platform Adapter”接口。每个平台GitHub, GitLab, Gitee实现自己的适配器负责验证该平台特有的Webhook签名。将平台原生的事件Payload解析转换为技能内部统一的“代码活动事件CodeActivityEvent”模型。这个模型包含仓库信息、提交列表含文件变更、PR信息如果有、事件类型等。提供该平台的API客户端用于未来可能需要主动获取仓库文件等操作。这样核心的规则引擎和提醒逻辑就与具体平台解耦了增加新平台只需要实现一个新的适配器即可。5.2 引入“完成度”与更复杂的规则目前的规则是二元的匹配/不匹配。可以引入“完成度”的概念让进度可视化。量化进度例如一个计划关联了5个文件每完成一个文件的修改并提交进度增加20%。规则可以定义为commit_path且min_count: 5引擎需要统计对不同路径的提交次数并计算完成百分比。复合规则支持逻辑组合如(rule_A AND rule_B) OR rule_C。例如“提交了核心代码文件 AND 更新了单元测试 OR 关联的文档PR被合并”。人工标记完成通过评论特定Issue、打上特定标签如plan:done或者在配置文件中增加一个manual_override字段允许用户手动将某个计划标记为完成。这为一些非代码类的计划如“与技术负责人讨论架构”提供了灵活性。5.3 与CI/CD流水线集成将计划监控深度集成到开发流程中体验会更无缝。CI状态作为完成条件可以在规则中增加ci_success类型。要求关联路径的代码在合并前必须通过CI流水线的所有测试状态为success。这需要技能能监听CI Webhook如GitHub Actions的workflow_run事件或主动查询CI状态API。生成进度报告可以编写一个CI Job在每天定时或每次主分支更新后运行。这个Job调用技能的某个API获取当前仓库所有计划的状态生成一个Markdown格式的进度报告并自动提交到仓库的docs/目录或更新一个README中的徽章。这样项目首页就能直观地看到整体进度。门禁检查甚至可以做一个轻量的“门禁”。在PR的CI流程中增加一个检查步骤如果本次PR关联了某个未完成的计划且计划已超期或处于高风险状态则CI状态标记为失败或给出强烈警告阻止合并不负责任的代码。5.4 数据可视化与历史回顾长期积累的计划数据是宝贵的个人或团队效能分析素材。简单的数据面板提供一个简单的内部看板页面受保护需要认证展示所有仓库计划的总体完成率、超期率、平均完成时间等统计信息。可以按时间范围筛选查看趋势。个人效能分析分析个人在不同类型计划如“学习”、“Bug修复”、“功能开发”上的完成情况和耗时帮助自己发现时间管理上的问题。团队负载观察对小团队如果技能用于团队项目可以观察不同成员关联计划的完成情况辅助进行工作量的平衡和风险评估。注意这部分功能需要谨慎设计避免成为制造焦虑的监控工具其目的应是促进透明和协作而非考核。这个“编码计划监控技能”项目始于一个简单的自我督促想法但在设计和实现过程中你会发现它涉及了Webhook处理、规则引擎、状态管理、定时任务、多平台集成等多个后端开发的经典模式。把它做出来并用起来不仅能让你的Flag立得更稳本身也是一个绝佳的、涵盖全链路的小型项目实践。从我的经验来看工具的价值不在于功能多复杂而在于它能否以极低的成本精准地解决一个高频的痛点。当你不再需要靠意志力去记住那些琐碎的计划而是有一个无声的伙伴在背后适时地推你一把时你会发现完成目标其实可以更轻松一点。

更多文章