برنامه نویسی

یک سیستم CI کوچک – انجمن DEV

این یک نمایش کوچک از میزان کمی است که شما برای میزبانی مخازن git خود و داشتن یک سیستم یکپارچه سازی مداوم برای آنها نیاز دارید. تنها چیزی که نیاز دارید یک سرور یونیکسی است که می توانید به آن ssh کنید، اما مسلماً می توانید این کار را به صورت محلی نیز امتحان کنید.

ما در یک نقطه از Redis برای صف بندی وظایف استفاده خواهیم کرد، اما به طور دقیق می توان این کار را بدون نرم افزار اضافی انجام داد. برای ساده نگه داشتن همه چیز، این فقط با یک مخزن کار می کند، زیرا این فقط یک الگو را توصیف می کند.

کد منبع همه موارد زیر را می توانید در اینجا بیابید.

میزبانی مخازن گیت خالی

با فرض اینکه می توانید به یک سرور ssh کنید و یک دایرکتوری ایجاد کنید، این تنها چیزی است که برای ایجاد یک مخزن git قابل اشتراک گذاری نیاز دارید:

$ git init --bare
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

در حالت ایده آل شما از یک کاربر مجزا برای آن استفاده می کنید (با نام git) و آن را برای استفاده تنظیم کنید git-shell به عنوان پوسته پیش فرض آن. طبق قرارداد، مخازن خالی در دایرکتوری هایی ذخیره می شوند که به پایان می رسند .git. اکنون می توانید این مخزن را از دستگاه خود با استفاده از:

$ git clone ssh://git@host.example.com/~git/repo.git
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

قلاب های پس از دریافت

یک قلاب پس از دریافت، یک فایل اجرایی است که می تواند به محض اینکه چیز جدیدی به مخزن فرستاده شود، برخی کارها را انجام دهد. ما از یک شل اسکریپت اجرایی استفاده خواهیم کرد که باید داخل آن برود hooks دایرکتوری مخزن (لخت) در سمت سرور.

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

اینجاست که Redis برای صف کار وارد عمل می شود. فرض می کنیم که redis نصب شده و در حال اجرا است و از redis-cli برای دسترسی به آن از روی اسکریپت استفاده می کنیم. ما از دو ساختار داده استفاده خواهیم کرد: فهرستی از کارهایی که در انتظار اجرا هستند، که توسط یک UUID که ما تولید خواهیم کرد به آنها ارجاع داده می شود و یک هش که در آن می توانیم ویرایش git و وضعیت مربوط به یک کار معین و همچنین خروجی آن را ذخیره کنیم.

توجه داشته باشید که git سه آرگومان را از طریق stdin به اسکریپت ارسال می کند: ویرایش قدیمی قبل از فشار، ویرایش جدید و ref فعلی.

#!/bin/bash
while read -r _ newrev ref
do
    id=$(uuid)
    echo "Starting CI job $id"
    redis-cli hset "$id" rev "$newrev" >/dev/null
    redis-cli hset "$id" ref "$ref" >/dev/null
    redis-cli lpush jobs "$id" >/dev/null
done
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

تعریف مشاغل ساخت

طبق قرارداد، سیستم ما هر چیزی را که در یک اسکریپت اجرایی به نام وجود دارد اجرا می کند ci.sh. اشکال این است که این فقط با سیستم های قابل اعتماد کار می کند و دسترسی به مخزن باید محافظت شود تا از اجرای کد تصادفی جلوگیری شود. مزیت بزرگ این است که ما نیازی به ارائه یک تعریف شغلی DSL یا فرمت فایل دست و پا گیر نداریم.

قرارداد ما همچنین این خواهد بود که یک آرگومان به اسکریپت داده شود: نام git ref، بنابراین ما می‌توانیم بر اساس شاخه‌ای که در آن هستیم تصمیم بگیریم چه کار کنیم.

اجازه دهید فقط این را در فایلی به نام قرار دهیم ci.sh:

#!/usr/bin/env bash

# the git ref gets passed in as the only argument
ref="$1"

# pretend we're running tests
echo "running tests"

# only deploy if we're on the main branch
[[ "$ref" == "refs/heads/main" ]] && echo "Deploying"
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

بیلد رانر

اکنون که کارها در صف هستند، آخرین قطعه گم شده یک جابجایی است. ما از دستور BLPOP Redis برای مسدود کردن استفاده خواهیم کرد تا زمانی که لیست مشاغل یک کار جدید برای ما پیدا کند. آن شناسه شغلی ویرایشی را که باید بررسی کنیم به ما می دهد و به ما امکان می دهد خروجی و وضعیت کار را بازنویسی کنیم.

توجه داشته باشید که همانطور که بحث شد، این یک مخزن به نام فرض می کند test قبلاً درست در کنار فیلمنامه بررسی شده است.

tiny-ci.sh

#!/usr/bin/env bash

# ./runner.sh is supposed to run on the server where your git repository lives

# the logic in here will run in an infinite loop:
# * (block and) wait for a job
# * run it
while :
do

# Announce that we're waiting
echo "Job runner waiting"

# We are using https://redis.io/commands/blpop to block until we have a new
# message on the "jobs" list. We use `tail` to get the last line because the
# output of BLPOP is of the form "list-that-got-an-element\nelement"
jobid=$(redis-cli blpop jobs 0 | tail -n 1)

# The message we received will have the job uuid
echo "Running job $jobid"

# Get the git revision we're supposed to check out
rev=$(redis-cli hget "${jobid}" "rev")
echo Checking out revision "$rev"

# Get the git ref
ref=$(redis-cli hget "${jobid}" "ref")

# Prepare the repository (hardcoded path) by getting that commit
cd test || exit; git fetch && git reset --hard "$rev";

# Actually runs the job and saves the output
if ! output=$(./ci.sh "$ref" 2>&1);
then
    status="failed";
else
    status="success";
fi;

# Update the result status
redis-cli hset "${jobid}" "status" $status;

# Update the job output
redis-cli hset "${jobid}" "output" "$output";

echo "Job ${jobid} done"

done
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

اجرا کردنش

جمع بندی:

  • یک مخزن گیت خالی در جایی وجود دارد که به آن می گویند test.git
  • ما می توانیم مخزن خالی را شبیه سازی کنیم (یا یک مخزن جدید ایجاد کنیم و ریموت مربوطه را اضافه کنیم)
  • در سرور میزبان مخزن git که ما کلون می کنیم test.git به test و مکان tiny-ci.sh در کنار آن
  • ما بیلدها را با شروع اجرا می کنیم tiny-ci.sh در سرور میزبان مخزن

حالا اگر ما git push یک تعهد جدید به main شعبه با ci.sh فایل از بالا، خروجی شناسه job را برمی گرداند

Enumerating objects: 5, done.
...
remote: Starting CI job dab82634-21cc-11eb-b3b3-9b8767dff47c
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

بررسی وضعیت ساخت

دانستن یک شغل، ساده ترین راه برای به دست آوردن وضعیت
یک ساخت با استفاده از --csv خروجی سبک دستور HGETALL از redis.

$ ssh example.com redis-cli --csv hgetall $JOB_UUID
"rev","f0706ea18a22031f84619b1161c8fbdb0dcd6850","ref","refs/heads/master","status","success","output","running tests\nDeploying"
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

بهبودهای احتمالی بیشتر

این به معنای تغییراتی در post-receive قلاب برای قرار دادن مشاغل در لیستی به نام job-${REPONAME} و سپس کارگر را نیز بر اساس آن واکنش نشان دهید. توجه کنید که چگونه redis-cli blpop چندین لیست را برای تماشا می گیرد و همچنین نام لیست را برمی گرداند.

ایجاد یک کلید برای هر شغل، پایگاه داده redis را به طور غیر ضروری آلوده می کند. قرار دادن کار می تواند از طریق SETEX انجام شود تا کلیدها پس از یک ساعت / یک روز / یک هفته از بین بروند. هدف Redis در اینجا ذخیره سازی کوتاه مدت است و نه آرشیو طولانی مدت نتایج کار

مقیاس بندی به چندین کارگر در یک دستگاه به پوشه های کاری متفاوت نیاز دارد (و بسته به وظایفی که در آنجا اجرا می شوند، مقداری جداسازی فرآیندها). مقیاس گذاری به چندین ماشین نیاز به دسترسی به یک نمونه redis مرکزی برای توزیع کار دارد.

  • انزوای کارگر / سندباکس

برای کارهای پیچیده تر، نوعی فرآیند و جداسازی سیستم فایل ضروری است. کارگر می‌تواند ماشین‌های مجازی یا کانتینرهای Docker را بچرخاند. به عنوان مثال، سیستم ساخت مورد استفاده در builds.sr.ht از یک کانتینر Docker استفاده می کند که به عنوان یک کاربر غیرمجاز در ماشین کی وی ام qemu اجرا می شود.

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

هر سیستم CI دارای نوعی اعلان است و ساده ترین شکل انجام کاری در اسکریپت، درست در انتها است. اما این فقط موارد موفقیت را پوشش می دهد، بنابراین یک رویکرد بهتر ایجاد یک صف اعلان و واداشتن یک کارگر اطلاع رسانی در مورد آن است.

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

همچنین ببینید
بستن
دکمه بازگشت به بالا