رفتن به محتوا

به عامل ماشینی برای تست بدهید

ریگ ماشینی یک‌بارمصرف است که یک عامل وقتی می‌خواهد واقعاً چیزی را امتحان کند بازش می‌کند: یک صفحه را بار کند، در یک برنامهٔ رومیزی کلیک کند، روی یک گوشی این‌ور و آن‌ور بزند. می‌توانید در زمان واقعی کارش را تماشا کنید و هر وقت خواستید کنترل را به دست بگیرید.

ریگ‌های ترمینال، مرورگر و میزکارِ محفظه‌ای VMهایی با دیسک‌های دورریختنی و شبکه‌های دروازه‌بانی‌شده هستند. Android و iOS شبیه‌سازهایی هستند که میزبان مدیریتشان می‌کند: وضعیت دستگاهشان دورریختنی است، اما شبکهٔ آن‌ها از میزبان سرور تبعیت می‌کند.

ریگ‌ها روی ماشینی اجرا می‌شوند که cc_server را میزبانی می‌کند — همان ماشین، نه ماشینی که به آن نگاه می‌کنید. پنج سطح کاربرمحور وجود دارد؛ سطوح رومیزی و موبایلی به راه‌اندازی روی میزبان نیاز دارند:

سطح روی چه چیزی اجرا می‌شود راه‌اندازی
ترمینال (VM) یک microVM که تصویر پین‌شدهٔ Ubuntu را بوت می‌کند هیچ — تصویر در نخستین استفاده خودکار واکشی می‌شود
مرورگر (VM) یک microVM که تصویر پین‌شدهٔ Debian را بوت می‌کند و در نخستین استفاده Chromium، Firefox یا WebKit در آن نصب می‌شود هیچ — همان
رایانه (VM) QEMU با یک تصویر میزکار Linux که Chromium، Firefox، WebKit و سطح پایهٔ CLI یک رانر میزبانی‌شدهٔ GitHub را همراه دارد QEMU را نصب کنید (brew install qemu)، سپس زیر تنظیمات → سرور → ریگ‌ها یک تصویر میزکار را وارد کنید
Android شبیه‌ساز Android گوگل Android SDK را نصب کنید، یک AVD بسازید و آن را شروع کنید (پایین‌تر)
شبیه‌ساز iOS CoreSimulator روی Mac سرور Xcode و رانتایم iOS درخواستی را نصب کنید، سپس زیر تنظیمات → سرور → ریگ‌ها پل اتوماسیون را نصب کنید

ماشین‌های ترمینال و مرورگر از تصاویر پین‌شده با دایجست بوت می‌شوند: بایت‌های دقیق از پیش نام‌برده می‌شوند، یک بار در نخستین بوت واکشی و کش می‌شوند — پس از نخستین بوت هر نوع، ماشین‌های بعدی یکسره از دانلود صرف‌نظر می‌کنند و در چند ثانیه بالا می‌آیند. تنظیمات → سرور → ریگ‌ها نشان می‌دهد سرور شما چه چیزی می‌تواند بوت کند و همین حالا چه ماشین‌هایی در حال اجرا هستند.

یک تصویر میزکارِ نصب‌شده از همان صفحه قابل مدیریت می‌ماند: وارد کردن آن را با یک qcow2 تازه‌تر جایگزین می‌کند، و حذف آن را برمی‌دارد تا بتوانید یک بیلد تازه نصب کنید.

دستگاه‌های موبایلی متفاوت راه‌اندازی می‌شوند

بخش با عنوان “دستگاه‌های موبایلی متفاوت راه‌اندازی می‌شوند”

تصویری از ما برای Android یا iOS وجود ندارد، و این یک از قلم افتادگی نیست. تصاویر سیستمی Android از SDK گوگل می‌آیند؛ رانتایم‌های iOS از Xcode. برای Android، Android Studio (یا ابزارهای SDK خط فرمان) را نصب کنید، در مدیر دستگاه یک دستگاه مجازی اضافه کنید و آن را شروع کنید. صفحهٔ ریگ‌ها سپس Android را به‌عنوان در دسترس گزارش می‌کند.

همچنین دقیقاً می‌گوید کدام گام را جا انداخته‌اید — نبودن SDK، نبودن شبیه‌ساز، نبودن دستگاه مجازی، یا دستگاهی که به‌سادگی اجرا نمی‌شود — چون این چهار مورد چهار راه‌حل متفاوت دارند و فقط یکی از آن‌ها یک دانلود است. اگر cc_server را از پوسته‌ای شروع می‌کنید که SDK را نمی‌بیند، آن را export کنید:

پنجرهٔ ترمینال
export ANDROID_HOME="$HOME/Library/Android/sdk"

برای iOS، خود سرور باید روی macOS با Xcode و یک رانتایم شبیه‌ساز iOS نصب‌شده اجرا شود. تنظیمات → سرور → ریگ‌ها را باز کنید و نصب پل اتوماسیون iOS را فشار دهید. سرور آرتیفکت WebDriverAgent پین‌شده با چک‌سام را واکشی می‌کند، تأییدش می‌کند و آن را در انبار اتوماسیون تحت مالکیت سرور نصب می‌کند. در جریان راه‌اندازی هیچ شبیه‌سازی بوت نمی‌شود.

ماشین‌ها به‌صورت زبانه زندگی می‌کنند، کنار گفتگو و diff — در یک فضا یا روی صفحهٔ PR، در همان منوی +، زیر دستگاه‌ها:

  • رایانه — یک میزکار Linux با Chromium، Firefox و WebKit
  • Chromium، Firefox یا WebKit — یک مرورگر
  • Android — شبیه‌ساز Android در حال اجرا
  • شبیه‌ساز iOS — یک شبیه‌ساز یک‌بارمصرف که برای همین گفتگو ساخته می‌شود

یکی را انتخاب کنید و شروع ماشین را فشار دهید. زبانه می‌گوید بوت در کدام مرحله است («در حال شروع microVM»، «در انتظار بالا آمدن مهمان») و فقط نمی‌چرخد.

زبانه محدود به گفتگوست، و ابزار عامل هم همین‌طور. زبانهٔ Chromium، Firefox یا WebKit و browser_use، Android و mobile_use، یا شبیه‌ساز iOS و ios_use به همان ماشین پیش‌فرض خطاب می‌شوند. شکاف‌های رابط کاربری اضافی ماشین‌های مقایسهٔ جدایند؛ ابزارهای عامل همیشه از پیش‌فرض استفاده می‌کنند.

برای ساخت، نصب و شروع یک برنامهٔ Flutter روی Android:

پنجرهٔ ترمینال
fvm flutter build apk --debug

سپس mobile_use را با install_apk و build/app/outputs/flutter-apk/app-debug.apk صدا بزنید، و پس از آن start_app و نام بستهٔ Android. ui_dump، نماگرفت‌ها و ورودی همان دستگاهی را می‌رانند که در زبانهٔ انسان نشان داده می‌شود. stop_app، clear_app_data، uninstall_app، open_url و فراخوانی‌های shell با شکل argv بقیهٔ چرخهٔ توسعه را پوشش می‌دهند، بدون اینکه پوسته‌ای روی میزبان سرور فراخوانی شود.

برای ساخت، نصب و شروع یک برنامهٔ Flutter روی شبیه‌ساز iOS:

پنجرهٔ ترمینال
fvm flutter build ios --simulator --debug
/usr/bin/plutil -extract CFBundleIdentifier raw -o - \
build/ios/iphonesimulator/Runner.app/Info.plist

سپس ios_use را با install_app و پوشهٔ .app صدا بزنید، و پس از آن start_app و شناسهٔ بستهٔ چاپ‌شده توسط plutil. ابزار iOS همچنین stop_app، uninstall_app، open_url و یک کنش spawn با شکل argv را برای تشخیص‌های محلی شبیه‌ساز در اختیار می‌گذارد.

نمایش‌های رومیزی و مرورگر با پنل تغییر اندازه می‌دهند. iOS اندازهٔ نقطه‌ای ثابت شبیه‌ساز را نگه می‌دارد و آن را با حاشیه در قاب می‌نشاند. هر سطح همچنان دو مسیر مستقل دارد: عامل نماگرفت‌های کوچک و ارزان می‌گیرد و شما نمای زنده را. جابه‌جایی بین زبانه‌ها جریان انسان را متوقف می‌کند، پس یک ماشین پس‌زمینه هیچ پهنای‌باند نمایشی هزینه نمی‌کند.

یک زبانهٔ ریگ خودش ماشینی شروع نمی‌کند، حتی در جریان بازیابی چیدمان. این قاعده جلوی آن را می‌گیرد که اجرای برنامه به‌طور غیرمنتظره VM بوت کند یا دستگاه شبیه‌ساز بسازد.

هر زمان که فرد دیگری کنترل انحصاری را در دست ندارد — یا وقتی خودتان آن را دارید — بوم در اختیار شماست تا در آن تایپ و کلیک کنید. تا وقتی شما آن قفل را دارید، برچسب وضعیت کنترل با شماست را می‌خواند، عامل همچنان می‌تواند نماگرفت بگیرد، و سرور کنش‌های تغییردهندهٔ آن را در همان گلوگاه واحدی که هر کنشی از آن می‌گذرد رد می‌کند. هر کاری که می‌کنید به نام شما ثبت می‌شود.

وقتی کارتان تمام شد، توقف ماشین را فشار دهید.

کنترل عمداً انحصاری است: دو کنشگر که در یک ماشین تایپ کنند ورودی‌ای تولید می‌کنند که هیچ‌یک از آن دو قصدش را نداشته است.

کپی و جای‌گذاری در هر دو جهت روی بوم ریگ کار می‌کند، و کشیدن فایل‌ها هم همین‌طور.

کپی و جای‌گذاری. میانبر کپی را روی بوم فشار دهید تا هر چیزی که ماشین کپی کرده است — متن، یک تصویر، فهرستی از فایل‌ها — روی بریده‌دان شما بنشیند. جای‌گذاری مال شما را در جهت دیگر می‌فرستد و درون ماشین جای‌گذاری را فشار می‌دهد. روی macOS میانبر ⌘C/⌘V است؛ ⌃C همچنان به‌عنوان یک وقفه به پوستهٔ مهمان می‌رسد. روی Windows و Linux از ⌃C/⌃V استفاده کنید — مهمان همان فشردن کلیدی را می‌گیرد که در هر حال می‌گرفت، پس ⌃C در ترمینال مهمان همچنان وقفه می‌اندازد و بریده‌دان خودتان دست‌نخورده می‌ماند.

هیچ‌چیز در پس‌زمینه همگام‌سازی نمی‌شود. گذرواژه‌ای که روی ماشین خودتان کپی می‌کنید، به‌خاطر اجرای یک زمان‌سنج درون یک VM ظاهر نمی‌شود؛ وقتی جای‌گذاری کنید از مرز می‌گذرد.

جای‌گذاری درون یک ریگ به‌صورت پیش‌فرض مجاز است. کپی از یک ریگ بیرون اول می‌پرسد — یک مجوز ده‌دقیقه‌ای، یا همیشه اجازه بده زیر تنظیمات → سرور → ریگ‌ها → دسترسی به بریده‌دان. این دو جهت مستقل‌اند و از شما تبعیت می‌کنند، نه از فضای کاری.

کشیدن فایل‌ها به داخل. آن‌ها را هر جای بوم رها کنید.

  • روی مرورگر (VM)، صفحه در همان نقطه‌ای که رها می‌کنید یک رها کردن واقعی می‌گیرد، پس یک ناحیهٔ بارگذاری دقیقاً مثل فایل محلی رفتار می‌کند.
  • روی رایانه (VM)، فایل‌ها در ~/Drops می‌نشینند و مسیرهایشان روی بریده‌دان ماشین می‌رود، پس ⌃V آن‌ها را در یک مدیر فایل یا یک فیلد بارگذاری جای‌گذاری می‌کند. یک میزبان نمی‌تواند بدون اجرای یک سرویس‌دهندهٔ ممتاز درون VM — که ریگ‌ها عمداً ندارد — کشیدنی را در یک برنامهٔ دلبخواه Linux بسازد؛ پس توست می‌گوید فایل‌ها کجا رفتند، به‌جای اینکه تظاهر کند رها کردنی رخ داده است.
  • روی یک ترمینال، فایل به داخل کپی می‌شود و مسیرش در خط فرمان تایپ می‌شود، آماده برای دادن به یک فرمان. جای‌گذاری یک تصویر در یک ترمینال هم همین کار را می‌کند: در ماشین ذخیره می‌شود و مسیرش را می‌گیرید.

کشیدن فایل‌ها به بیرون از رایانه (VM) کار می‌کند. کشیدن یک فایل را درون ماشین شروع کنید، اشاره‌گر را از بوم بیرون ببرید، و به یک کشیدن واقعی روی میزکار شما تبدیل می‌شود — آن را در Finder یا Explorer رها کنید. کپی کردن فایل‌ها در مدیر فایل ماشین و جای‌گذاری در سمت خودتان هم کار می‌کند. مرورگر (VM) این را ارائه نمی‌کند: یک مرورگر بی‌سر هیچ بار کشیدنی‌ای برای خواندن ندارد، و حدس زدن از روی انتخاب متن صفحه کلیک‌های معمولی را می‌رباید.

ترمینال‌های یک فضا یا صفحهٔ PR می‌توانند به‌جای ماشین خود سرور، درون یک ریگ اجرا شوند. وقتی چنین کنند، پنل نشان VM محفظه‌ای را نمایش می‌دهد.

چه چیزی تغییر می‌کند:

  • هیچ‌چیز از آنچه اجرا می‌کنید به میزبان نمی‌رسد. یک rm -rf در آن‌جا به قیمت از دست دادن ریگ تمام می‌شود.
  • شبکه با پیش‌فرضِ رد است. فقط میزبان‌های فهرست مجاز ریگ قابل‌دسترس‌اند، و همه‌چیز از دروازه‌ای می‌گذرد که آن را اعمال می‌کند.
  • git push در ترمینالی که خودتان باز می‌کنید همچنان کار می‌کند. ریگ هیچ اعتبارنامهٔ ذخیره‌ای ندارد؛ برای هر عملیات یک اعتبارنامهٔ کوتاه‌مدت از سرور می‌خواهد که به دسترسی خود شما به میزبان کد محدود است، و آن توکن با بسته شدن ریگ باطل می‌شود. ریگی که یک عامل باز می‌کند اعتبارنامهٔ فقط‌خواندنی می‌گیرد، پس می‌تواند fetch کند اما نمی‌تواند پوش کند.

آنچه باید بدانید:

  • یک ترمینال محفظه‌ایِ بازیابی‌شده منتظر شما می‌ماند. مثل یک زبانهٔ ریگ، ترمینال microvm که همراه چیدمان شما برمی‌گردد به‌جای بوت کردن VMاش هنگام اجرا، دکمهٔ باز کردن پوسته را نشان می‌دهد. هزینهٔ یک ترمینال پوستهٔ میزبان یک فرایند است؛ هزینهٔ یک ترمینال محفظه‌ای یک ماشین.
  • فایل‌های رهاشده روی ترمینال تایپ می‌شوند، باز نمی‌شوند. یک CSV را روی یک ترمینال محفظه‌ای رها کنید: به داخل ماشین کپی می‌شود و مسیر مهمانش در خط فرمان ظاهر می‌شود — مسیر میزبان فرمانی می‌شد که آن‌جا شکست می‌خورد.
  • نسخهٔ درون ریگ یک نسخه است. worktree شما روی میزبان مرجع می‌ماند. کامیت‌هایی که درون یک ریگ می‌سازید وقتی بخواهید به refs/rigs/<id>/* برگردانده می‌شوند — هرگز شاخهٔ شما را جابه‌جا نمی‌کنند و به درخت کاری شما دست نمی‌زنند.
  • کارِ هدفِ میزبان همچنان روی میزبان اجرا می‌شود. یک مهمان Linux نمی‌تواند برای macOS بسازد یا xcodebuild را اجرا کند. برای آن صریحاً بک‌اند بومی را انتخاب کنید؛ برنامه به‌جای جابه‌جایی بی‌صدا از شما تأیید می‌خواهد.

یک سرور توسعه در ترمینال (VM) اجرا کنید — مثلاً pnpm dev روی پورت 3000 — و تا چند ثانیه آیکون دوشاخه در گوشهٔ ترمینال روشن می‌شود: پورت به localhost:3000 روی ماشین شما فوروارد می‌شود، درون مرورگر (VM) روی localhost:3000 قابل‌دسترس است، روی شبکهٔ شما قابل اشتراک‌گذاری است، و می‌توانید نام https://myapp.test را به آن بدهید. کل این جریان — فوروارد خودکار، اشتراک‌گذاری LAN، دامنه‌های dev با HTTPS — راهنمای خودش را دارد: هدایت پورت از ترمینال.

یک فضای کاری می‌تواند ترمینال (VM) و مرورگر (VM) را به تصاویر خودش اشاره دهد — زیر تنظیمات → سرور → ریگ‌ها → تصاویر سفارشی (این فضای کاری). کاربرد معمول: تصویر پیش‌فرض را با زنجیرهٔ ابزاری که پروژه‌تان لازم دارد گسترش دهید تا هر ترمینال آماده شروع شود، یا تصویری را که تیم‌تان از قبل منتشر می‌کند دوباره به کار ببرید.

ماشین همچنان درون همان محفظه بوت می‌شود: همان دروازهٔ خروج، همان کارگزار اعتبارنامه، همان چرخهٔ عمر. فقط مدیران فضای کاری می‌توانند این تنظیم را عوض کنند، ماشین‌های جدید آن را برمی‌دارند (ماشین‌های در حال اجرا مال خودشان را نگه می‌دارند)، و ارجاع باید یک تصویر رجیستری باشد — مسیرهای محلی رد می‌شوند.

یک تصویر باید چه چیزی فراهم کند:

  • ترمینال (VM) — یک تصویر Linux با bash. به git، curl و socat هم نیاز دارد — یا از پیش نصب‌شده، یا در نخستین شروع نصب‌شدنی (هر تصویر مبتنی بر Debian/Ubuntu کار می‌کند: خودکار نصب می‌شوند، به‌همراه مجموعه‌ای از ابزارهای CLI به سبک رانر میزبانی‌شدهٔ GitHub: کامپایلرها، Python 3، jq، ابزارهای بایگانی، rsync. رانتایم‌های زبانی مثل Node/Go/Java و Docker از پیش نصب نیستند). پورت مهمان 7911 برای فورواردر پورت رزرو شده است.
  • مرورگر (VM) — یک تصویر Debian/Ubuntu با bash و socat، به‌علاوهٔ یک باینری chromium. سرور خودش Chromium را به‌صورت chromium --headless=new شروع می‌کند و DevTools را از مهمان 9223 به 9222 رله می‌کند. PulseAudio و ffmpeg در نخستین شروع نصب می‌شوند. از ساخت FROM chromedp/headless-shell پرهیز کنید — آن باینری صدا ندارد. این بازنویسی فقط به ریگ‌های Chromium اعمال می‌شود؛ Firefox و WebKit نادیده‌اش می‌گیرند و تصویر پین‌شدهٔ Debian را بوت می‌کنند. پورت‌های مهمان 9222/9223 رزرو شده‌اند.

دو توصیه: ارجاع را با دایجست پین کنید (@sha256:…) تا بایت‌ها زیر دستتان تغییر نکنند، و آن را روی Docker Hub یا GitHub Container Registry میزبانی کنید — میزبان‌های دانلود آن رجیستری‌ها خودکار از دروازهٔ خروج پذیرفته می‌شوند؛ دیگران ممکن است برای pull کردن نیاز داشته باشند میزبان‌های تحویل محتوایشان قابل‌دسترس باشند.

ریگ‌ها برای موقت‌بودن ساخته شده‌اند:

  • بیکاری — پس از حدود 15 دقیقه بی‌واقعه‌ای، یک ریگ مرورگر، میزکار یا موبایل پارک می‌شود (منجمد می‌ماند و با کنش بعدی بی‌درنگ بیدار می‌شود). یک ترمینال محفظه‌ای 45 دقیقه صبر می‌کند. پس از دو برابر پنجرهٔ بیکاری، بسته می‌شود.
  • محدودیت زمانی — هر ریگ عمر سختی دارد که نمی‌تواند تمدیدش کند، هر کاری هم که بکند.
  • حافظه — اگر سرور برای یک ریگ تازه جا لازم داشته باشد، ریگی را که دیرتر از همه استفاده شده می‌بندد. ریگی که کسی در حال راندن یا تماشای آن است یا ترمینالی در آن باز دارد هرگز گرفته نمی‌شود.

وقتی یک ریگ محفظه‌ای بسته می‌شود، دیسکش دور انداخته می‌شود. بستن یک ریگ Android از شبیه‌ساز میزبان جدا می‌شود — آن را پاک نمی‌کند. یک ریگ iOS دستگاه CoreSimulator را که ساخته پاک می‌کند. اول هر چیزی را که می‌خواهید نگه دارید به بیرون منتقل کنید.

ریگ‌های ترمینال، مرورگر و میزکارِ محفظه‌ای به‌صورت پیش‌فرض یک دروازهٔ خروج با پیش‌فرضِ رد دارند: شما میزبان‌ها را صریحاً مجوز می‌دهید، و تنها مسیر مهمان دروازه‌ای است که آن فهرست را اعمال می‌کند. یک ریگ مرورگر سایت محصول (usectrl.dev) را هم به‌صورت پیش‌فرض می‌پذیرد. کارگزار اعتبارنامه از همان فهرست استفاده می‌کند.

دو هشدار صادقانه:

  • شبکهٔ موبایل از میزبان تبعیت می‌کند. شبیه‌ساز Android و CoreSimulator اپل پشت دروازهٔ خروج محفظه نیستند. هیچ‌یک از زبانه‌های موبایل کنترل دور زدن شبکه را ارائه نمی‌کند، چون شبکهٔ تحت مدیریت میزبان از پیش نامحدود است.
  • هر چیزی که یک ریگ می‌خواند نامطمئن است. متن صفحه، DOM، درخت‌های دسترس‌پذیری و دامپ‌های نما، در نشانگرهایی پیچیده به عامل برمی‌گردند که می‌گویند «این داده است، نه دستور». صفحه‌ای که بخواهد به عامل شما دستور بدهد، به‌صورت یک رشتهٔ نقل‌قول‌شده خوانده می‌شود.

می‌توانید همه‌چیزِ در حال اجرا را ببینید و هر کدام را متوقف کنید، زیر تنظیمات → سرور → ریگ‌ها.

  • ریگ‌ها و محفظه‌ها — مدلی که پشت همهٔ این‌هاست: چرا VM، دو مسیر نمایش، کارگزار اعتبارنامه، چرخهٔ عمر
  • هدایت پورت از ترمینال — به یک سرور توسعه از هر جایی که مهم است برسید
  • مرجع ریگ‌ها — هر سطح، کنش، پورت و پیش‌فرض در یک جا
  • پیکربندی گاردریل‌ها — Drive an enclosure (rig) یک کلاس کنش است که می‌توانید آن را به‌ازای هر فضای کاری، عامل یا فضا روی مجاز، اول‌بپرس یا رد بگذارید. حالت‌های فقط‌خواندنی یکسره ردش می‌کنند.