أتمتة Release Notes: افتح PR جاهز من commits
هتطلع مسودة Release Notes قابلة للمراجعة بدل ما تقرأ كل commits يدويًا قبل كل إصدار. المكسب العملي: تقليل وقت التحضير من حوالي 45 دقيقة إلى 7 دقائق في مشروع متوسط.
مستوى القارئ: متوسط
المشكلة باختصار
الطريقة الشائعة إنك تفتح GitHub، تقرأ آخر commits، تكتب Changelog يدوي، ثم تنسى commit مهم أو تخلط bug fix مع breaking change. الطريقة دي بتفشل لما الفريق يطلع إصدار كل أسبوع، أو لما عندك أكثر من خدمة وكل خدمة لها commits منفصلة.
الافتراض هنا إن عندك repository يستخدم Git، وفريق صغير أو متوسط يكتب commits بشكل قريب من Conventional Commits مثل feat: وfix:. لو الرسائل عشوائية جدًا، الأتمتة هتنتج مسودة ضعيفة، لكنها لا تزال تكشف لك أين المشكلة بالظبط.
الفكرة بمثال واضح
ركز في المثال ده: عندك تطبيق SaaS صغير. خلال أسبوع الفريق أضاف تصدير CSV، أصلح خطأ في صفحة الدفع، وعدّل docs. بدل ما مدير الإصدار يقرأ 38 commit، السكربت يقرأ الرسائل ويقسمها إلى Features وBug Fixes وDocumentation. بعد كده يفتح PR فيه ملف CHANGELOG.md محدث.
علميًا، الفكرة اسمها release-note generation من metadata موجودة بالفعل في Git history. الأداة لا تفهم نية الفريق بالسحر. هي تعتمد على convention واضح في أسماء commits. لذلك Conventional Commits مهم: feat تعني ميزة، fix تعني إصلاح، ووجود ! أو BREAKING CHANGE يرفع التغيير لقسم أخطر.
الخطوات العملية
- اتفق مع الفريق على رسائل commits بسيطة:
feat(auth): add magic link loginوfix(billing): handle failed invoice retry. - أضف
git-cliffلتوليد Changelog من Git history بدل الكتابة اليدوية. - شغّل workflow في GitHub Actions يدويًا قبل الإصدار، وليس على كل push.
- افتح PR تلقائي يحتوي على
CHANGELOG.mdعشان يراجعه شخص قبل tag النهائي.
هذا ملف إعداد بسيط لـ git-cliff يحافظ على الأقسام واضحة:
# cliff.toml
[changelog]
header = "# Changelog\n\n"
body = """
{% for group, commits in commits | group_by(attribute="group") %}
## {{ group | upper_first }}
{% for commit in commits %}
- {{ commit.message | upper_first }} ({{ commit.id | truncate(length=7, end="") }})
{% endfor %}
{% endfor %}
"""
trim = true
[git]
conventional_commits = true
filter_unconventional = true
commit_parsers = [
{ message = "^feat", group = "features" },
{ message = "^fix", group = "bug fixes" },
{ message = "^docs", group = "documentation" },
{ message = "^perf", group = "performance" },
]
وهذا workflow يفتح PR بالملف الناتج. استخدم workflow_dispatch عشان قرار الإصدار يفضل بيدك:
name: Draft release notes
on:
workflow_dispatch:
inputs:
version:
description: "Next version, example: v1.8.0"
required: true
permissions:
contents: write
pull-requests: write
jobs:
changelog:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Generate CHANGELOG.md
uses: orhun/git-cliff-action@v4
with:
config: cliff.toml
args: --tag ${{ inputs.version }} --output CHANGELOG.md
- name: Open release notes PR
uses: peter-evans/create-pull-request@v6
with:
branch: release-notes/${{ inputs.version }}
title: "Release notes for ${{ inputs.version }}"
commit-message: "docs: update changelog for ${{ inputs.version }}"
body: "Review generated release notes before creating the tag."
القياس قبل وبعد
في سيناريو واقعي لفريق يطلع إصدار أسبوعي من 25 إلى 60 commit، القراءة اليدوية وكتابة الملخص تأخذ 30 إلى 45 دقيقة. بعد الأتمتة، التشغيل والمراجعة يأخذان غالبًا 5 إلى 10 دقائق. الرقم مش وعد ثابت؛ هو قياس مناسب لما تكون رسائل commits نظيفة بنسبة 80% على الأقل.
الـ trade-off هنا واضح. بتكسب سرعة واتساق ومراجعة أسهل. بتخسر جزءًا من المرونة لو الفريق لا يكتب commits مفهومة. كمان ممكن يطلع changelog ناقص لو squash merge بيحول كل PR إلى رسالة عامة مثل update stuff. الحل إنك تفرض template للـ PR أو squash title واضح.
ما يجب الانتباه له
- لا تنشر تلقائيًا مباشرة. افتح PR أولًا. Release notes نص يراه المستخدمون، وأي صياغة خاطئة ممكن تعمل ارتباك.
- قلل صلاحيات GitHub Token. workflow يحتاج
contents: writeوpull-requests: writeفقط، بدل permissions واسعة. - قسّم breaking changes. لو عندك APIs عامة، اجعل breaking changes قسمًا مستقلًا يراجعه شخص مسؤول.
- لا تعتمد على التاريخ وحده. tag السابق أفضل من تاريخ عشوائي، لأن release branches أحيانًا تتحرك بطريقة غير خطية.
متى لا تستخدم هذه الطريقة
لا تستخدمها لو المشروع لا يملك Git history واضح، أو لو الفريق لا يكتب commits مفهومة نهائيًا. ابدأ أولًا بإصلاح طريقة كتابة commits. ولا تستخدمها كبديل لمراجعة المنتج في الإصدارات الكبيرة؛ لو الإصدار فيه تغيير أسعار أو migration خطير، محتاج release notes بشرية أكثر دقة.
المصادر
- توثيق git-cliff لتوليد Changelog من Git history
- مواصفة Conventional Commits
- توثيق GitHub Actions لتشغيل workflow يدويًا
- توثيق create-pull-request action
الخطوة التالية
افتح آخر 20 commit في مشروعك. لو قدرت تصنف 16 منهم كـ feat أو fix أو docs، أضف cliff.toml وشغّل workflow يدوي لأول إصدار تجريبي.