Skip to content

Ubuntu 22.04

Ubuntu 22.04 #210

name: Ubuntu 22.04
run-name: Ubuntu 22.04
on:
push:
branches: [main]
paths: &e2e-paths
- ".github/workflows/midscene-ubuntu-22.04.yml"
- ".github/workflows/deploy-midscene-report.yml"
- "scripts/build-pages-report.mjs"
- "scripts/report-cases.mjs"
- "scripts/render-ci-report-summary.mjs"
- "scripts/write-ci-shard-status.mjs"
- "scripts/verify-pages-report.mjs"
- "tests/contracts/test_pages_report_history.mjs"
- "tests/e2e/package.json"
- "tests/e2e/package-lock.json"
- "tests/e2e/gtk_fixture.py"
- "tests/e2e/gtk_onboarding_fixture.py"
- "tests/e2e/gtk_runtime_fixture.py"
- "tests/e2e/capture-report-preview.mjs"
- "tests/e2e/polish_fixture.py"
- "tests/e2e/cases/**"
- "tests/e2e/midscene.config.ts"
- "src/**"
pull_request:
branches: [main]
paths: *e2e-paths
workflow_dispatch:
inputs:
project:
description: Run one Midscene project for focused verification
type: choice
default: all
options:
- all
- ubuntu-shard-1
- ubuntu-shard-2
- ubuntu-shard-3
- ubuntu-shard-4
- ubuntu-polishing
- ubuntu-providers
permissions:
contents: read
concurrency:
group: midscene-model-e2e-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
jobs:
ubuntu-e2e:
name: Midscene visual (${{ matrix.project }})
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
runs-on: ubuntu-22.04
timeout-minutes: 45
strategy:
# Every matrix child owns its Xvfb display and fixture process. Keep
# concurrency at the Actions layer because ComputerAgent sets DISPLAY
# process-wide and cannot safely isolate concurrent projects in one job.
fail-fast: false
max-parallel: 5
matrix:
project: >-
${{ fromJSON((inputs.project != '' && inputs.project != 'all') &&
format('["{0}"]', inputs.project) ||
'["ubuntu-shard-1","ubuntu-shard-2","ubuntu-shard-3","ubuntu-shard-4","ubuntu-polishing"]') }}
env:
MIDSCENE_MODEL_API_KEY: ${{ secrets.MIDSCENE_MODEL_API_KEY }}
MIDSCENE_MODEL_NAME: ${{ secrets.MIDSCENE_MODEL_NAME }}
MIDSCENE_MODEL_BASE_URL: ${{ secrets.MIDSCENE_MODEL_BASE_URL }}
MIDSCENE_MODEL_FAMILY: ${{ secrets.MIDSCENE_MODEL_FAMILY }}
MIDSCENE_MODEL_REASONING_ENABLED: ${{ secrets.MIDSCENE_MODEL_REASONING_ENABLED }}
MIDSCENE_COMPUTER_HEADLESS_LINUX: "true"
MIDSCENE_REPLANNING_CYCLE_LIMIT: "40"
# The configured model endpoint is reachable over IPv4; disabling Node's
# family race avoids intermittent runner-side IPv6 connection timeouts.
NODE_OPTIONS: "--dns-result-order=ipv4first --no-network-family-autoselection"
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6.5.0
with:
node-version: "22"
cache: npm
cache-dependency-path: tests/e2e/package-lock.json
- name: Require Midscene model configuration
run: |
test -n "$MIDSCENE_MODEL_API_KEY"
test -n "$MIDSCENE_MODEL_NAME"
test -n "$MIDSCENE_MODEL_BASE_URL"
test -n "$MIDSCENE_MODEL_FAMILY"
- name: Check Midscene model connectivity over Node.js
run: |
node --input-type=module <<'NODE'
const baseUrl = process.env.MIDSCENE_MODEL_BASE_URL.replace(/\/+$/, '');
const response = await fetch(`${baseUrl}/chat/completions`, {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.MIDSCENE_MODEL_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
model: process.env.MIDSCENE_MODEL_NAME,
messages: [{ role: 'user', content: 'Reply OK.' }],
max_tokens: 1,
}),
signal: AbortSignal.timeout(90_000),
});
if (!response.ok) {
throw new Error(`Model preflight returned HTTP ${response.status}`);
}
await response.arrayBuffer();
NODE
- name: Install desktop test environment
run: |
sudo apt-get update
sudo apt-get install -y \
adwaita-icon-theme dbus-x11 fluxbox fonts-noto-cjk \
gir1.2-gtk-4.0 gnome-themes-extra imagemagick \
libasound2 libatk-bridge2.0-0 libdrm2 libgbm1 libgtk-3-0 \
libnotify4 libnss3 libpng16-16 libsecret-1-0 libx11-6 \
libxinerama1 libxkbcommon-x11-0 libxss1 libxtst6 \
python3-cairo python3-gi python3-tomli \
x11-xserver-utils xdg-utils xvfb
- name: Install pinned Midscene dependencies
id: test-dependencies
working-directory: tests/e2e
# sharp's platform packages are pinned explicitly. Skipping lifecycle
# scripts avoids npm trying to build sharp before those packages link.
run: |
for attempt in 1 2 3; do
if npm ci --include=optional --ignore-scripts; then
exit 0
fi
echo "npm install attempt $attempt failed; retrying." >&2
sleep 10
done
exit 1
- name: Run Midscene project
id: midscene-test
working-directory: tests/e2e
env:
PROJECT: ${{ matrix.project }}
NODE_OPTIONS: --dns-result-order=ipv4first --no-network-family-autoselection --require=./node_modules/@computer-use/libnut/dist/import_libnut.js
run: >-
timeout --signal=TERM --kill-after=10s 35m
npm test -- --project "$PROJECT"
- name: Capture report evidence
id: capture-report
if: always() && !cancelled() && steps.test-dependencies.outcome == 'success'
env:
PROJECT: ${{ matrix.project }}
run: |
node tests/e2e/capture-report-preview.mjs \
--report-dir tests/e2e/midscene_run \
--projects "$PROJECT"
- name: Record shard result for report aggregation
if: always() && !cancelled()
env:
PROJECT: ${{ matrix.project }}
TEST_OUTCOME: ${{ steps.midscene-test.outcome }}
CAPTURE_OUTCOME: ${{ steps.capture-report.outcome }}
run: |
node scripts/write-ci-shard-status.mjs \
--output "tests/e2e/midscene_run/ci-shard-status-$PROJECT.json" \
--project "$PROJECT" \
--test-outcome "${TEST_OUTCOME:-skipped}" \
--capture-outcome "${CAPTURE_OUTCOME:-skipped}"
- name: Upload Midscene shard report
if: always() && !cancelled()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: midscene-ubuntu-22.04-${{ matrix.project }}
path: tests/e2e/midscene_run/
if-no-files-found: error
retention-days: 7
overwrite: true
report-bundle:
name: Assemble Midscene report bundle
needs: ubuntu-e2e
if: >-
always() && !cancelled() &&
(inputs.project == '' || inputs.project == 'all')
runs-on: ubuntu-latest
outputs:
report-artifact-id: ${{ steps.report.outputs.artifact-id }}
steps:
- name: Create bundle even when every shard failed early
run: |
mkdir -p "$RUNNER_TEMP/midscene-report-shards"
printf '%s\n' '{"bundle":"ubuntu-22.04"}' > "$RUNNER_TEMP/midscene-report-shards/ci-bundle-status.json"
# Keep shard directories separate. Native report filenames can share a
# timestamp, while the Pages builder locates reports by project identity.
- name: Download every shard report
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
pattern: midscene-ubuntu-22.04-*
path: ${{ runner.temp }}/midscene-report-shards
merge-multiple: false
- name: Upload combined report bundle
id: report
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: midscene-ubuntu-22.04-e2e-report
path: ${{ runner.temp }}/midscene-report-shards/
if-no-files-found: error
retention-days: 7
overwrite: true
pages-report:
name: GitHub Pages report
needs: [ubuntu-e2e, report-bundle]
if: >-
always() &&
!cancelled() &&
needs.report-bundle.outputs.report-artifact-id != '' &&
(github.event_name != 'pull_request' ||
github.event.pull_request.head.repo.full_name == github.repository)
permissions:
actions: read
contents: read
pages: write
id-token: write
uses: ./.github/workflows/deploy-midscene-report.yml
with:
artifact-name: midscene-ubuntu-22.04-e2e-report
report-label: Ubuntu 22.04
summary-title: Ubuntu
report-groups: >-
[{"role":"primary","label":"Doubao Say","projects":["ubuntu-shard-1","ubuntu-shard-2","ubuntu-shard-3","ubuntu-shard-4"]},{"role":"auxiliary","label":"Polishing overlay report","projects":["ubuntu-polishing"]}]
run-result: ${{ needs.ubuntu-e2e.result }}