عبارت‌ها و کانتکست‌ها در گیت هاب اکشنز (GitHub Actions) ستون فقرات هر workflow پویا و هوشمند هستند و بدون تسلط بر آن‌ها، pipeline شما همیشه شکننده باقی می‌ماند.

در این نوشته، از منظر تجربه‌ی عملی روی CI/CD (Continuous Integration/Continuous Delivery) در پروژه‌های وردپرسی و Node.js، نشان می‌دهیم چرا اکثر توسعه‌دهندگان از expressions و contexts فقط در سطح سطحی استفاده می‌کنند.

از تفاوت context و expression تا توابع داخلی و رفتار پنهان در matrix strategy، همه‌چیز را با نگاه مهندسی بررسی می‌کنیم.

هدف این است که یک مهندس ارشد بتواند workflowهایی بنویسد که در شرایط پیچیده هم پایدار بمانند و در برابر خطاهای رایج مقاوم باشند.

در پایان، یک چارچوب عملی برای دیباگ کردن expressions و contexts در سناریوهای واقعی ارائه می‌شود.

شروع: جایی که workflow بی‌صدا شکست می‌خورد

یک بار workflow‌ای نوشتم که روی branch اصلی عالی کار می‌کرد، اما روی pull request بی‌صدا شکست می‌خورد. علت، یک context اشتباه در شرط if بود که هیچ خطایی تولید نمی‌کرد، فقط step را رد می‌کرد. آن تجربه نگاهم را به expressions و contexts برای همیشه تغییر داد.

عبارت‌ها و کانتکست‌ها در گیت هاب اکشنز، فقط یک ویژگی جانبی نیستند. هر تصمیم‌گیری شرطی، هر انتخاب runner، هر اجرای matrix و هر مدیریت secret، همگی بر پایه‌ی این دو مفهوم ساخته می‌شوند.

چرا expressions و contexts حیاتی هستند

GitHub Actions یک پلتفرم CI/CD (Continuous Integration/Continuous Delivery) است که در سال ۲۰۱۹ معرفی شد و به‌سرعت به استاندارد صنعت تبدیل شد. بر اساس آمار رسمی GitHub، بیش از ۵۰ درصد پروژه‌های متن‌باز روی این پلتفرم حداقل یک workflow فعال دارند.

در قلب هر workflow، expressions (عبارت‌ها) و contexts (کانتکست‌ها) قرار دارند. expressions زبان ارزیابی شرطی هستند و contexts منابع داده‌ای که expressions از آن‌ها می‌خوانند. اطلاعات پایه درباره‌ی CI/CD را می‌توانید در ویکی‌پدیا ببینید؛ اما آنچه در پروژه‌های واقعی اهمیت دارد، رفتار غیرشهودی این دو در سناریوهای پیچیده است.

اگر پیش‌تر روی CI/CD برای پروژه‌های وردپرسی کار کرده‌اید، می‌دانید که pipeline وردپرس چالش‌های خاص خود را دارد. expressions و contexts همان ابزاری هستند که این چالش‌ها را قابل مدیریت می‌کنند.

هر workflow خوب، مجموعه‌ای از expressions دقیق است که contexts را به‌درستی تفسیر می‌کنند.

contexts اصلی در GitHub Actions

GitHub Actions مجموعه‌ای از contexts استاندارد ارائه می‌دهد که هرکدام کاربرد مشخصی دارند. شناخت دقیق این contexts، پیش‌نیاز نوشتن workflow حرفه‌ای است.

contexts کلیدی

  • github: اطلاعات مربوط به repository، event، ref و actor
  • env: متغیرهای محیطی تعریف‌شده در workflow یا step
  • vars: متغیرهای سطح organization یا repository
  • secrets: اطلاعات حساس مانند API key و token
  • strategy: اطلاعات مربوط به matrix و fail-fast
  • matrix: مقادیر matrix فعلی
  • steps: خروجی steps قبلی
  • job: اطلاعات مربوط به job فعلی
  • runner: اطلاعات مربوط به runner
  • needs: خروجی jobs وابسته
  • inputs: ورودی‌های workflow در workflow_call یا workflow_dispatch

هر context در زمان مشخصی از اجرا قابل دسترسی است. برای مطالعه‌ی دقیق‌تر CI/CD در وردپرس، راه‌اندازی CI/CD برای پروژه‌های کوچک منبع کاملی است.

محدودیت‌های دسترسی

نکته‌ی مهمی که در پروژه‌ها زیاد دیده‌ام این است: هر context در هر مرحله از workflow قابل دسترسی نیست. برای مثال، context secrets در شرط if سطح job قابل استفاده نیست، چون در زمان ارزیابی هنوز بارگذاری نشده است. این محدودیت، یکی از بیشترین منابع خطای پنهان در workflows است.

برای مطالعه‌ی اصول دقیق‌تر کار با Git، دستورات پرکاربرد Git نقطه‌ی شروع خوبی است.

سینتکس expressions و توابع داخلی

expressions در GitHub Actions با سینتکس ${{ ... }} نوشته می‌شوند. این expressions می‌توانند شامل مقادیر ثابت، ارجاع به contexts، اپراتورها و توابع داخلی باشند.

توابع داخلی کلیدی

  • contains(search, item): بررسی وجود یک مقدار در رشته یا آرایه
  • startsWith(searchString, searchValue): بررسی شروع رشته
  • endsWith(searchString, searchValue): بررسی پایان رشته
  • format(string, replaceValue0, ...): قالب‌بندی رشته
  • join(array, optionalSeparator): اتصال عناصر آرایه
  • toJSON(value): تبدیل به JSON برای دیباگ
  • fromJSON(value): تبدیل از JSON
  • hashFiles(path): محاسبه‌ی hash فایل‌ها
  • success(): بررسی موفقیت steps قبلی
  • failure(): بررسی شکست steps قبلی
  • cancelled(): بررسی لغو workflow
  • always(): همیشه اجرا شود

ترکیب این توابع، امکان نوشتن منطق پیچیده را فراهم می‌کند. برای مطالعه‌ی اصول دقیق‌تر Git، برنچ در Git منبع کاملی است.

اپراتورهای منطقی

expressions از اپراتورهای استاندارد پشتیبانی می‌کنند:

  • && برای AND
  • || برای OR
  • ! برای NOT
  • == و != برای مقایسه
  • <، >، <=، >= برای مقایسه‌ی عددی

نکته‌ی مهم این است که در مقایسه، اگر یک طرف رشته و طرف دیگر عدد باشد، هر دو به عدد تبدیل می‌شوند. این رفتار می‌تواند به نتایج غیرشهودی منجر شود.

در expressions، بزرگ‌ترین دشمن، فرض کردن رفتار مشابه با JavaScript است.

matrix strategy و رفتار پنهان contexts

matrix strategy یکی از قدرتمندترین ویژگی‌های GitHub Actions است که امکان اجرای موازی workflow روی ترکیب‌های مختلف را فراهم می‌کند. اما این ویژگی، رفتار پنهان پیچیده‌ای در تعامل با contexts دارد.

ساختار matrix

یک matrix معمولاً به این شکل تعریف می‌شود:

strategy:
  matrix:
    os: [ubuntu-latest, windows-latest, macos-latest]
    node: [18, 20, 22]

این matrix، ۹ job موازی ایجاد می‌کند. دسترسی به مقادیر فعلی، از طریق context matrix انجام می‌شود.

رفتار پنهان در fail-fast

به‌طور پیش‌فرض، اگر یکی از jobs matrix شکست بخورد، بقیه لغو می‌شوند. این رفتار را می‌توان با fail-fast: false تغییر داد. نکته‌ی مهم این است که این تنظیم، خودش بر پایه‌ی expressions قابل تنظیم است.

برای مطالعه‌ی اصول دقیق‌تر مدیریت branch، Pull Request در GitHub منبع کاملی است.

ویژگیپیش‌فرضتوصیه
fail-fasttrueبسته به پروژه
max-parallelنامحدودمحدود کردن بر پایه‌ی منابع
include/excludeخالیاستفاده برای کاهش ترکیب‌ها

مدیریت secrets و env در contexts

مدیریت secrets یکی از حساس‌ترین بخش‌های هر workflow است. context secrets به‌طور خاص برای اطلاعات حساس طراحی شده، اما محدودیت‌های مهمی دارد.

محدودیت‌های secrets

نکته‌ی مهمی که در پروژه‌ها زیاد دیده‌ام این است: secrets در شرط if سطح job قابل استفاده نیست. چون ارزیابی این شرط، قبل از بارگذاری secrets انجام می‌شود.

راه‌حل استاندارد، استفاده از یک step میانی است که مقدار secret را به یک output تبدیل می‌کند و سپس در jobs بعدی از needs استفاده می‌شود.

مدیریت env

context env برای متغیرهای محیطی استفاده می‌شود. این متغیرها می‌توانند در سه سطح تعریف شوند:

  • سطح workflow (با env)
  • سطح job (با env در job)
  • سطح step (با env در step)

اولویت‌بندی از step به workflow است. یعنی اگر یک متغیر در چند سطح تعریف شود، مقدار سطح step استفاده می‌شود. برای مطالعه‌ی بیشتر درباره‌ی محیط توسعه، توسعه وردپرس با محیط لوکال منبع خوبی است.

دیباگ کردن expressions در workflow واقعی

دیباگ کردن expressions یکی از چالش‌برانگیزترین بخش‌های کار با GitHub Actions است، چون خطاها معمولاً بی‌صدا هستند.

روش‌های دیباگ

روش‌های مؤثر برای دیباگ expressions:

  • استفاده از toJSON(context) برای چاپ کل context
  • استفاده از echo در step برای چاپ مقادیر میانی
  • استفاده از ACTIONS_STEP_DEBUG برای فعال‌سازی لاگ دقیق
  • استفاده از ACTIONS_RUNNER_DEBUG برای لاگ runner
  • تست expressions به‌صورت جداگانه در یک workflow ساده

در پروژه‌های متعددی دیده‌ام که استفاده از toJSON() می‌تواند ساعت‌ها دیباگ را به چند دقیقه کاهش دهد. برای مطالعه‌ی بیشتر درباره‌ی workflowهای حرفه‌ای، GitHub Actions راهنمای خودکارسازی گردش کار منبع کاملی است.

در دیباگ workflow، اولین قدم همیشه چاپ کردن context است، نه حدس زدن محتوای آن.

پرسش‌های پرتکرار درباره‌ی expressions و contexts

تفاوت context و expression چیست؟

context یک منبع داده‌ای است که اطلاعات را در خود نگه می‌دارد. expression یک عبارت ارزیابی است که از context می‌خواند و نتیجه تولید می‌کند.

چرا شرط if من همیشه false ارزیابی می‌شود؟

معمولاً به دلیل اشتباه در سینتکس، استفاده از context نامعتبر، یا رفتار غیرشهودی تبدیل نوع در مقایسه. اولین قدم، چاپ مقدار با toJSON() است.

آیا می‌توان از secrets در شرط if سطح job استفاده کرد؟

خیر. secrets در این سطح بارگذاری نشده است. باید از یک step میانی استفاده شود که مقدار secret را به output تبدیل می‌کند.

چگونه مقادیر matrix را در expression بخوانیم؟

با استفاده از context matrix. برای مثال ${{ matrix.node }} مقدار فعلی node را برمی‌گرداند.

آیا توابع داخلی expressions قابل توسعه هستند؟

خیر. GitHub Actions مجموعه‌ی ثابتی از توابع ارائه می‌دهد. برای منطق پیچیده‌تر، باید از steps میانی استفاده کرد.

اشتباهات رایج در استفاده از contexts

در پروژه‌های متعددی این الگوها را دیده‌ام:

  • استفاده از context در مرحله‌ای که بارگذاری نشده است
  • نادیده‌گرفتن تبدیل نوع در مقایسه
  • استفاده از secrets در شرط سطح job
  • فرض رفتار مشابه با JavaScript در expressions
  • عدم استفاده از toJSON() برای دیباگ
  • تعریف نامناسب env در سطوح مختلف
  • نادیده‌گرفتن محدودیت‌های matrix strategy

برای دیدن تصویر کامل‌تر از اشتباهات مرتبط، رفع خطاهای رایج Git را ببینید.

از ارزیابی تنبل تا معماری pipeline مقاوم

در لایه‌ی پیشرفته، expressions و contexts به یک مسئله‌ی ارزیابی تنبل (Lazy Evaluation) و معماری pipeline تبدیل می‌شوند. GitHub Actions در هر مرحله، فقط بخشی از expressions را ارزیابی می‌کند و همین رفتار، منبع بسیاری از خطاهای پنهان است.

یک رویکرد پیشرفته، طراحی workflowهایی است که در آن‌ها هر expression در زمان مناسب ارزیابی شود. این کار نیازمند درک دقیق از چرخه‌ی اجرای workflow است.

در سطح معماری، توصیه می‌شود منطق شرطی پیچیده به یک step یا job جداگانه منتقل شود. این جداسازی، دیباگ را ساده‌تر و pipeline را مقاوم‌تر می‌کند. برای مطالعه‌ی چارچوب‌های پیشرفته، چرا DevOps فقط یک ابزار نیست می‌تواند دیدگاه مفیدی ارائه دهد.

تجربه‌ی عملی نشان می‌دهد که در GitHub Actions، سادگی همیشه برنده است. workflowای که با expressions ساده و قابل فهم نوشته شود، از workflowای که با expressions پیچیده و غیرقابل دیباگ نوشته شود، ارزشمندتر است.

اگر روی یک پروژه‌ی CI/CD کار می‌کنید، پیشنهاد می‌کنم از همان ابتدا expressions و contexts را در سطح عمیق یاد بگیرید. تفاوت بین یک pipeline معمولی و یک pipeline عالی، در همین جزئیات پنهان است. ⚙️

اگر این چالش‌ها را در پروژه‌ی خود تجربه کرده‌اید، جالب است بدانید کدام بخش بیشترین زمان را از شما گرفت. تجربه‌تان را در دیدگاه‌ها بنویسید.