跳至內容

給代理一部測試用機器

測試機是代理在真正要試點東西時開啟的一部用完即棄的機器:載入一個頁面、 在桌面應用程式中四處點擊、在電話上點來點去。你可以實時看着它工作,並隨時 接管控制。

隔離終端、瀏覽器與桌面測試機,都是配有用完即棄磁碟、網絡受關卡管控的 VM。 Android 與 iOS 則是由主機管理的模擬器:其裝置狀態用完即棄,但網絡連線跟隨 伺服器主機。

測試機運行在託管 cc_server 的那部機器上 —— 是那部機器,而不是你眼前這部。 共有五種面向使用者的介面;桌面與流動裝置介面需要主機設定:

介面 運行於 設定
終端(VM) 從釘定 Ubuntu 映像啟動的 microVM 無 —— 首次使用時會自動擷取該映像
瀏覽器(VM) 從釘定 Debian 映像啟動的 microVM,首次使用時安裝 Chromium、Firefox 或 WebKit 無 —— 相同
電腦(VM) QEMU 配上一套內建 Chromium、Firefox、WebKit 及 GitHub 託管執行器 CLI 基準的 Linux 桌面映像 安裝 QEMU(brew install qemu),然後在 設定 → 伺服器 → 測試機 下匯入一個桌面映像
Android Google 的 Android 模擬器 安裝 Android SDK、建立 AVD 並啟動它(見下文)
iOS Simulator 伺服器 Mac 上的 CoreSimulator 安裝 Xcode 及所需的 iOS runtime,然後在 設定 → 伺服器 → 測試機 下安裝自動化橋接器

終端與瀏覽器機器都從以摘要釘定的映像啟動:確切的位元組會事先指名,首次開機 時擷取一次並加以快取 —— 同類機器第一次啟動之後,往後的機器會完全略過下載, 數秒內就能起來。設定 → 伺服器 → 測試機 會顯示你的伺服器能啟動什麼,以及 目前正在運行的每一部機器。

已安裝的桌面映像仍然可以在同一頁管理:匯入會以較新的 qcow2 取代它, 刪除則會移除它,讓你可以安裝全新的組建。

我們沒有為 Android 或 iOS 提供映像,而這並非疏漏。Android 系統映像來自 Google 的 SDK;iOS runtime 則來自 Xcode。就 Android 而言,請安裝 Android Studio(或命令列版 SDK 工具),在 裝置管理員 中新增一部虛擬裝置 並啟動它。之後「測試機」頁面便會回報 Android 可用。

它也會明確告訴你缺少哪一步 —— 沒有 SDK、沒有模擬器、沒有虛擬裝置,或者 裝置根本沒有在運行 —— 因為這四者的修法各不相同,而當中只有一個是下載。 如果你從看不到 SDK 的 shell 啟動 cc_server,請匯出它:

終端視窗
export ANDROID_HOME="$HOME/Library/Android/sdk"

就 iOS 而言,伺服器本身必須運行於已安裝 Xcode 和 iOS Simulator runtime 的 macOS 上。開啟 設定 → 伺服器 → 測試機,然後按 安裝 iOS 自動化橋接器。 伺服器會擷取以校驗和釘定的 WebDriverAgent 成品、驗證它,並安裝到伺服器專屬的 自動化儲存區。設定過程中不會啟動任何模擬器。

機器以分頁的形式存在,就在聊天與 diff 旁邊 —— 無論在空間還是 PR 頁面, 都在同一個 + 選單下的機器之中:

  • 電腦 —— 內建 Chromium、Firefox 與 WebKit 的 Linux 桌面
  • Chromium、Firefox 或 WebKit —— 一個瀏覽器
  • Android —— 正在運行的 Android 模擬器
  • iOS Simulator —— 為這則對話建立的用完即棄模擬器

挑一個並按 啟動機器。分頁會說明開機處於哪個階段(「正在啟動 microVM」、 「正在等待客體起來」),而不是只在那裡轉圈。

分頁的範圍限於該則對話,代理工具也一樣。Chromium、Firefox 或 WebKit 分頁與 browser_use、Android 與 mobile_use,又或者 iOS Simulator 與 ios_use, 指向的是同一部預設機器。額外的介面位置則是各自獨立的比較用機器;代理工具 永遠使用預設那一部。

要在 Android 上建置、安裝並啟動 Flutter 應用程式:

終端視窗
fvm flutter build apk --debug

然後以 install_apk 和 build/app/outputs/flutter-apk/app-debug.apk 呼叫 mobile_use,接着以 start_app 和 Android 套件名稱呼叫。ui_dump、 螢幕截圖與輸入操作所驅動的,正是人類分頁中顯示的同一部裝置。stop_app、 clear_app_data、uninstall_app、open_url,以及以 argv 形式傳入的 shell 呼叫,涵蓋開發週期的其餘環節,而無需在伺服器主機上叫用 shell。

要在 iOS Simulator 上建置、安裝並啟動 Flutter 應用程式:

終端視窗
fvm flutter build ios --simulator --debug
/usr/bin/plutil -extract CFBundleIdentifier raw -o - \
build/ios/iphonesimulator/Runner.app/Info.plist

然後以 install_app 和 .app 目錄呼叫 ios_use,接着以 start_app 和 plutil 印出的 bundle identifier 呼叫。iOS 工具亦提供 stop_app、 uninstall_app、open_url,以及一個以 argv 形式傳入的 spawn 動作, 用於模擬器本機的診斷。

桌面與瀏覽器的顯示畫面會隨面板調整大小。iOS 則維持模擬器固定的點尺寸, 並為它補上黑邊。每個介面仍然有兩條互相獨立的通道:代理取得細小、便宜的 螢幕截圖,而你取得即時畫面。切換分頁會停止人類串流,因此背景機器不耗用 任何顯示頻寬。

測試機分頁不會自行啟動機器,在還原版面配置時亦然。這條規則可避免應用程式 一啟動就意外開機 VM 或建立模擬器裝置。

只要沒有其他人持有獨佔控制權 —— 或者持有者就是你 —— 畫布就任你輸入和點擊。 在你持有那道鎖期間,狀態標籤會顯示 你已獲得控制權,代理仍然可以截圖, 而伺服器會在每個動作都必經的那一個關口拒絕它的變更類動作。你所做的一切都會 記錄在你的名下。

完成後按 停止機器。

控制權刻意設計成獨佔:兩個人同時在一部機器上輸入,會產生兩人都不是本意的 輸入。

複製與貼上在測試機畫布的兩個方向都可運作,拖曳檔案也一樣。

複製與貼上。 在畫布上按下複製組合鍵,機器所複製的任何東西 —— 文字、 一張影像、一組檔案 —— 都會落到你的剪貼板。貼上則把你的內容送往另一方向, 並在機器內按下貼上。在 macOS 上,組合鍵是 ⌘C/⌘V;⌃C 仍會以中斷訊號的本來 身分送達客體 shell。在 Windows 與 Linux 上請用 ⌃C/⌃V —— 客體會原樣收到它 本來就會收到的按鍵,因此客體終端機中的 ⌃C 仍然會中斷,而你的剪貼板則不受 影響。

後台不會同步任何東西。你在自己機器上複製的密碼,不會因為某個計時器觸發就 出現在 VM 之內;它只在你貼上時才跨越邊界。

貼上到測試機預設允許。從測試機複製出去則會先彈出詢問 —— 可授予十分鐘權限, 或選擇 始終允許(位於 設定 → 伺服器 → 測試機 → 剪貼板存取 之下)。 這兩個方向互相獨立,而且跟隨的是你本人,而不是工作區。

把檔案拖入。 把它們放到畫布上任何位置。

  • 在 瀏覽器(VM) 上,頁面會在你鬆手的位置收到一個 真實 的拖放事件, 因此上載區的行為與拖入本機檔案時完全相同。
  • 在 電腦(VM) 上,檔案會落到 ~/Drops,其路徑則放進機器的剪貼板, 因此 ⌃V 可把它們貼到檔案管理員或上載欄位。主機無法在不於 VM 內執行特權 守護程序的情況下,向任意 Linux 應用程式合成一次拖曳 —— 而測試機刻意不具備 這種東西 —— 因此提示訊息會告訴你檔案去了哪裡,而不是假裝發生過一次拖放。
  • 在終端上,檔案會被複製進去,其路徑則在提示符號處輸入,準備好傳給指令。 把一張 影像 貼到終端也一樣:它會儲存在機器內,而你會取得其路徑。

把檔案拖出在電腦(VM)上可用。在機器內開始拖曳一個檔案,把指標帶出畫布, 它就會變成你桌面上的真實拖曳操作 —— 放進 Finder 或 Explorer 即可。在機器的 檔案管理員中複製檔案,再於你自己這邊貼上也行。瀏覽器(VM)則不提供這項功能: 無介面瀏覽器沒有可讀取的拖曳載荷,而從頁面的文字選取去猜測,只會騎劫一般的 點擊。

空間內或 PR 頁面上的終端,可以選擇在測試機內運行,而不是在伺服器自身的機器 上。當它們這樣做時,面板會顯示 封閉虛擬機 標籤。

有什麼改變:

  • 你運行的任何東西都碰不到主機。在裡面跑一個 rm -rf,賠上的只是那部測試機。
  • 網絡預設拒絕。只有測試機允許清單上的主機可以觸及,而一切都經由一道強制 執行該清單的關卡。
  • git push 在你自己開啟的終端機中仍然可用。測試機不儲存任何憑證;它會按每次 操作向伺服器索取一個短時效憑證,其權限受你自己的程式碼託管存取範圍約束,而該 權杖會在測試機關閉時被撤銷。由代理開啟的測試機只會取得唯讀憑證,因此能 fetch, 但不能推送。

需要知道的事:

  • 還原後的隔離終端會等你。 與測試機分頁一樣,隨你的版面配置還原回來的 microvm 終端會顯示 開啟 shell 按鈕,而不是在啟動時就開機它的 VM。 主機 shell 終端耗的是一個行程;隔離終端耗的則是一部機器。
  • 放到終端上的檔案是輸入路徑,不是開啟。 把一個 CSV 放到隔離終端上, 它會被複製到機器內,其客體路徑則出現在提示符號處 —— 主機路徑在那裡只會是 一條失敗的指令。
  • 測試機內的那份複本是複本。 主機上的工作樹始終是權威來源。你在測試機內 所作的提交,只會在你要求時帶回 refs/rigs/<id>/* —— 它們永遠不會移動你的 分支,也不會碰觸你的工作樹。
  • 以主機為目標的工作仍在主機上運行。 Linux 客體無法為 macOS 建置,也無法 執行 xcodebuild。這種情況請明確選擇原生後端;應用程式會要求你確認, 而不是悄悄切換。

在終端(VM)內運行一個開發伺服器 —— 比如說 3000 埠上的 pnpm dev —— 幾秒 之內,終端角落的插頭圖示就會亮起:該連接埠會轉送到你機器上的 localhost:3000, 可在瀏覽器(VM)內以 localhost:3000 觸及,可在你的網絡上分享,也可命名為 https://myapp.test。整個流程 —— 自動轉送、區域網絡分享、帶 HTTPS 的開發 網域 —— 有它自己的一份指南: 從終端轉送連接埠。

一個工作區可以把終端(VM)與瀏覽器(VM)指向自己的映像 —— 就在 設定 → 伺服器 → 測試機 → 自訂映像(此工作區) 之下。典型用法:在預設映像 上補充你的專案所需的工具鏈,讓每個終端一啟動就緒;或者重用你團隊已經發佈的 映像。

機器仍然在同一道隔離邊界內開機:同一道對外連線關卡、同一個憑證代理、同一個 生命週期。只有工作區管理員可以更改這項設定,新機器會採用它(運行中的機器 保留自己的),而且參照必須是一個 registry 映像 —— 本機路徑會被拒絕。

映像必須提供什麼:

  • 終端(VM) —— 一個內含 bash 的 Linux 映像。它還需要 git、curl 和 socat —— 可以是預先安裝,也可以是在首次啟動時安裝(任何以 Debian/Ubuntu 為基礎的映像都可以:它們會自動安裝,連同一套相當於 GitHub 託管執行器的 CLI 基本工具:編譯器、Python 3、jq、封存工具、rsync。 Node/Go/Java 等語言 runtime 和 Docker 則不會預先安裝)。客體連接埠 7911 保留給連接埠轉送器。
  • 瀏覽器(VM) —— 一個內含 bash 和 socat 的 Debian/Ubuntu 映像, 另加一個 chromium 二進位檔。伺服器會自行以 chromium --headless=new 啟動 Chromium,並把 DevTools 從客體 9223 轉接到 9222。PulseAudio 與 ffmpeg 會在首次啟動時安裝。請勿建置 FROM chromedp/headless-shell —— 那個二進位檔沒有音訊。這項覆寫只套用於 Chromium 測試機;Firefox 與 WebKit 會忽略它,並啟動釘定的 Debian 映像。客體連接埠 9222/9223 為保留。

兩項建議:以摘要釘定參照(@sha256:…),這樣位元組就不會在你腳下悄悄改變; 並把它託管於 Docker Hub 或 GitHub Container Registry —— 這些 registry 的下載 主機可自動通過對外連線關卡;其他 registry 則可能需要其內容傳遞主機可觸及, 才能拉取。

測試機生來就是暫時的:

  • 閒置 —— 瀏覽器、桌面或流動裝置測試機在約 15 分鐘沒有任何動靜之後會 停駐(凍結,下一次操作即時喚醒)。隔離終端則等待 45 分鐘。達到閒置時限的 兩倍之後,它就會關閉。
  • 時限 —— 每部測試機都有一個無法延長的硬性存活期限,無論它當下在做什麼。
  • 記憶體 —— 如果伺服器需要空間開新的測試機,它會關閉最久未使用的那一部。 有人正在操控、正在觀看,或開着終端的測試機,永遠不會被拿走。

隔離測試機關閉時,其磁碟會被丟棄。關閉 Android 測試機只是從主機模擬器卸除 —— 它不會刪除模擬器。iOS 測試機則會刪除它所建立的 CoreSimulator 裝置。 想保留任何東西,請先帶回來。

隔離終端、瀏覽器與桌面測試機預設採用一道預設拒絕的對外連線關卡:主機由你 明確放行,而客體唯一的出路就是一道強制執行該清單的關卡。瀏覽器測試機另外 會開箱即放行產品網站(usectrl.dev)。憑證代理使用同一份清單。

兩點誠實的告誡:

  • 流動裝置的網絡跟隨主機。 Android 的模擬器與 Apple 的 CoreSimulator 並不在隔離環境的對外連線關卡之後。兩個流動裝置分頁都不提供繞過網絡的 控制項,因為由主機管理的網絡本來就不受限。
  • 測試機讀到的一切都不受信任。 頁面文字、DOM、無障礙樹與檢視傾印,回到 代理手上時,外面都圍着標記,說明「這是資料,不是指令」。試圖向你的代理下 命令的頁面,讀起來就只是一段被引用的字串。

你可以在 設定 → 伺服器 → 測試機 下看到所有正在運行的東西,並停止其中 任何一項。

  • 測試機與隔離環境 —— 這一切背後的模型:為何是 VM、兩條顯示通道、憑證代理、生命週期
  • 從終端轉送連接埠 —— 在任何需要的地方 觸及開發伺服器
  • 測試機參考 —— 每個介面、動作、連接埠與預設值, 盡在一處
  • 設定護欄 —— Drive an enclosure (rig) 是一個動作類別,你可以按工作區、代理或空間設定為允許、要求核准或拒絕。 唯讀模式則一概拒絕。