diff --git a/.github/workflows/chromium-headful-image.yaml b/.github/workflows/chromium-headful-image.yaml index ea56f29d..b0f392e3 100644 --- a/.github/workflows/chromium-headful-image.yaml +++ b/.github/workflows/chromium-headful-image.yaml @@ -1,37 +1,79 @@ name: chromium-headful-image on: + push: + branches: [ main ] + pull_request: workflow_call: +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: docker: - runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + include: + - arch: amd64 + runner: ubuntu-latest + - arch: arm64 + runner: ubuntu-24.04-arm64 + runs-on: ${{ matrix.runner }} permissions: contents: read + env: + UKC_TOKEN: dummy + UKC_METRO: ci + NAME: ci-chromium-headful + DETACH: "true" + SERVICE_EXCEPTIONS: pulseaudio + CACHE_DIR: .buildx-cache-headful-${{ matrix.runner }} steps: - name: Checkout uses: actions/checkout@v4 - - name: Compute short SHA - id: vars + - name: Restore Docker layer cache + uses: actions/cache@v4 + with: + path: ${{ env.CACHE_DIR }} + key: buildx-${{ runner.os }}-${{ matrix.runner }}-headful-${{ hashFiles('images/chromium-headful/Dockerfile', 'images/chromium-headful/wrapper.sh', 'images/chromium-headful/client/package.json', 'images/chromium-headful/client/package-lock.json', 'server/go.mod', 'server/go.sum') }} + restore-keys: | + buildx-${{ runner.os }}-${{ matrix.runner }}-headful- + + - name: Build image shell: bash - run: echo "short_sha=${GITHUB_SHA::7}" >> "$GITHUB_OUTPUT" + run: | + bash images/chromium-headful/build-docker.sh - - name: Login to Docker Hub - uses: docker/login-action@v3 - with: - username: ${{ vars.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} + - name: Run container + shell: bash + env: + ENABLE_WEBRTC: true + run: | + bash images/chromium-headful/run-docker.sh - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 + - name: Wait for supervisor services (allow pulseaudio non-RUNNING) + shell: bash + run: | + trap "docker stop ci-chromium-headful >/dev/null 2>&1 || true" EXIT + attempts=30 + sleep_seconds=1 + for i in $(seq 1 "$attempts"); do + out=$(docker exec ci-chromium-headful supervisorctl status || true) + echo "$out" + total=$(echo "$out" | grep -v '^$' | wc -l | tr -d ' ') + exceptions="${SERVICE_EXCEPTIONS:-}" + pattern=$(printf '%s' "$exceptions" | tr ', ' '\n' | sed '/^$/d' | paste -sd '|' -) + ok=$(echo "$out" | awk -v pat="$pattern" 'BEGIN{IGNORECASE=1} { if (pat != "" && $0 ~ pat) print; else if ($0 ~ /RUNNING/) print }' | wc -l | tr -d ' ') + if [ "$total" -gt 0 ] && [ "$ok" -eq "$total" ]; then + echo "All required services are RUNNING" + exit 0 + fi + sleep "$sleep_seconds" + done + echo "Supervisor services not ready in time" + docker logs --tail=200 ci-chromium-headful || true + exit 1 - - name: Build and push - uses: docker/build-push-action@v6 - with: - context: . - file: images/chromium-headful/Dockerfile - push: true - tags: onkernel/chromium-headful:${{ steps.vars.outputs.short_sha }} - cache-from: type=gha - cache-to: type=gha,mode=max diff --git a/.github/workflows/chromium-headless-image.yaml b/.github/workflows/chromium-headless-image.yaml index be00d67b..5e4a3f04 100644 --- a/.github/workflows/chromium-headless-image.yaml +++ b/.github/workflows/chromium-headless-image.yaml @@ -1,37 +1,77 @@ name: chromium-headless-image on: + push: + branches: [ main ] + pull_request: workflow_call: +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: docker: - runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + include: + - arch: amd64 + runner: ubuntu-latest + - arch: arm64 + runner: ubuntu-24.04-arm64 + runs-on: ${{ matrix.runner }} permissions: contents: read + env: + UKC_TOKEN: dummy + UKC_METRO: ci + NAME: ci-chromium-headless + DETACH: "true" + SERVICE_EXCEPTIONS: pulseaudio + CACHE_DIR: .buildx-cache-headless-${{ matrix.runner }} steps: - name: Checkout uses: actions/checkout@v4 - - name: Compute short SHA - id: vars + - name: Restore Docker layer cache + uses: actions/cache@v4 + with: + path: ${{ env.CACHE_DIR }} + key: buildx-${{ runner.os }}-${{ matrix.runner }}-headless-${{ hashFiles('images/chromium-headless/image/Dockerfile', 'images/chromium-headless/image/wrapper.sh', 'server/go.mod', 'server/go.sum') }} + restore-keys: | + buildx-${{ runner.os }}-${{ matrix.runner }}-headless- + + - name: Build image shell: bash - run: echo "short_sha=${GITHUB_SHA::7}" >> "$GITHUB_OUTPUT" + run: | + bash images/chromium-headless/build-docker.sh - - name: Login to Docker Hub - uses: docker/login-action@v3 - with: - username: ${{ vars.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} + - name: Run container + shell: bash + run: | + bash images/chromium-headless/run-docker.sh - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 + - name: Wait for supervisor services (allow pulseaudio non-RUNNING) + shell: bash + run: | + trap "docker stop ci-chromium-headless >/dev/null 2>&1 || true" EXIT + attempts=30 + sleep_seconds=1 + for i in $(seq 1 "$attempts"); do + out=$(docker exec ci-chromium-headless supervisorctl status || true) + echo "$out" + total=$(echo "$out" | grep -v '^$' | wc -l | tr -d ' ') + exceptions="${SERVICE_EXCEPTIONS:-}" + pattern=$(printf '%s' "$exceptions" | tr ', ' '\n' | sed '/^$/d' | paste -sd '|' -) + ok=$(echo "$out" | awk -v pat="$pattern" 'BEGIN{IGNORECASE=1} { if (pat != "" && $0 ~ pat) print; else if ($0 ~ /RUNNING/) print }' | wc -l | tr -d ' ') + if [ "$total" -gt 0 ] && [ "$ok" -eq "$total" ]; then + echo "All required services are RUNNING" + exit 0 + fi + sleep "$sleep_seconds" + done + echo "Supervisor services not ready in time" + docker logs --tail=200 ci-chromium-headless || true + exit 1 - - name: Build and push - uses: docker/build-push-action@v6 - with: - context: . - file: images/chromium-headless/image/Dockerfile - push: true - tags: onkernel/chromium-headless:${{ steps.vars.outputs.short_sha }} - cache-from: type=gha - cache-to: type=gha,mode=max diff --git a/.github/workflows/server-test.yaml b/.github/workflows/server-test.yaml index 620b8fb7..c4d3c1c7 100644 --- a/.github/workflows/server-test.yaml +++ b/.github/workflows/server-test.yaml @@ -7,20 +7,18 @@ on: branches: ["main"] workflow_dispatch: -jobs: - build-headful: - uses: ./.github/workflows/chromium-headful-image.yaml - secrets: inherit - - build-headless: - uses: ./.github/workflows/chromium-headless-image.yaml - secrets: inherit +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true +jobs: test: runs-on: ubuntu-latest - needs: [build-headful, build-headless] + timeout-minutes: 45 permissions: contents: read + env: + CACHE_DIR: .buildx-cache-${{ runner.os }}-${{ runner.arch }} steps: - name: Checkout code @@ -33,6 +31,8 @@ jobs: uses: actions/setup-node@v4 with: node-version: 22 + cache: pnpm + cache-dependency-path: server/e2e/playwright/pnpm-lock.yaml - name: Set up pnpm uses: pnpm/action-setup@v4 @@ -45,20 +45,33 @@ jobs: go-version-file: "server/go.mod" cache: true - - name: Compute short SHA for images - id: vars + - name: Restore Docker layer cache + uses: actions/cache@v4 + with: + path: ${{ env.CACHE_DIR }} + key: buildx-${{ runner.os }}-${{ runner.arch }}-e2e-${{ hashFiles('images/**/Dockerfile', 'images/**/wrapper.sh', 'images/chromium-headful/client/package.json', 'images/chromium-headful/client/package-lock.json', 'server/go.mod', 'server/go.sum') }} + restore-keys: | + buildx-${{ runner.os }}-${{ runner.arch }}-e2e- + + - name: Build chromium-headless image shell: bash - run: echo "short_sha=${GITHUB_SHA::7}" >> "$GITHUB_OUTPUT" + env: + UKC_TOKEN: dummy + UKC_METRO: ci + run: | + bash images/chromium-headless/build-docker.sh - - name: Login to Docker Hub - uses: docker/login-action@v3 - with: - username: ${{ vars.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} + - name: Build chromium-headful image + shell: bash + env: + UKC_TOKEN: dummy + UKC_METRO: ci + run: | + bash images/chromium-headful/build-docker.sh - name: Run server Makefile tests run: make test working-directory: server env: - # Prefer explicit images if passed from the caller; otherwise use the commit short sha - E2E_IMAGE_TAG: ${{ steps.vars.outputs.short_sha }} + E2E_CHROMIUM_HEADFUL_IMAGE: onkernel/chromium-headful-test:latest + E2E_CHROMIUM_HEADLESS_IMAGE: onkernel/chromium-headless-test:latest diff --git a/.gitignore b/.gitignore index 69f77654..a674d779 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,7 @@ lerna-debug.log* # Caches .cache +.buildx-cache-* # Diagnostic reports (https://nodejs.org/api/report.html) diff --git a/images/chromium-headful/build-docker.sh b/images/chromium-headful/build-docker.sh index c2cd3320..b8909cf4 100755 --- a/images/chromium-headful/build-docker.sh +++ b/images/chromium-headful/build-docker.sh @@ -10,4 +10,4 @@ source ../../shared/start-buildkit.sh # Build the Docker image using the repo root as build context # so the Dockerfile's first stage can access the server sources -(cd "$SCRIPT_DIR/../.." && docker build -f images/chromium-headful/Dockerfile -t "$IMAGE" .) +(cd "$SCRIPT_DIR/../.." && docker build -f images/chromium-headful/Dockerfile "${CACHE_ARGS[@]}" -t "$IMAGE" .) diff --git a/images/chromium-headful/run-docker.sh b/images/chromium-headful/run-docker.sh index 0202b91a..72b77c41 100755 --- a/images/chromium-headful/run-docker.sh +++ b/images/chromium-headful/run-docker.sh @@ -81,4 +81,9 @@ if [[ "${ENABLE_WEBRTC:-}" == "true" ]]; then fi docker rm -f "$NAME" 2>/dev/null || true -docker run -it "${RUN_ARGS[@]}" "$IMAGE" + +if [[ "${DETACH:-false}" == "true" ]]; then + docker run -d --rm "${RUN_ARGS[@]}" "$IMAGE" +else + docker run -it "${RUN_ARGS[@]}" "$IMAGE" +fi diff --git a/images/chromium-headless/build-docker.sh b/images/chromium-headless/build-docker.sh index e616f528..2149ab8e 100755 --- a/images/chromium-headless/build-docker.sh +++ b/images/chromium-headless/build-docker.sh @@ -10,4 +10,4 @@ source ../../shared/start-buildkit.sh # Build the Docker image using the repo root as build context # so the Dockerfile's first stage can access the server sources -(cd "$SCRIPT_DIR/../.." && docker build -f images/chromium-headless/image/Dockerfile -t "$IMAGE" .) +(cd "$SCRIPT_DIR/../.." && docker build -f images/chromium-headless/image/Dockerfile "${CACHE_ARGS[@]}" -t "$IMAGE" .) diff --git a/images/chromium-headless/run-docker.sh b/images/chromium-headless/run-docker.sh index 62d74337..477e1254 100755 --- a/images/chromium-headless/run-docker.sh +++ b/images/chromium-headless/run-docker.sh @@ -26,4 +26,9 @@ if [[ $# -ge 1 && -n "$1" ]]; then fi docker rm -f "$NAME" 2>/dev/null || true -docker run -it --rm "${ENTRYPOINT_ARG[@]}" "${RUN_ARGS[@]}" "$IMAGE" + +if [[ "${DETACH:-false}" == "true" ]]; then + docker run -d --rm "${ENTRYPOINT_ARG[@]}" "${RUN_ARGS[@]}" "$IMAGE" +else + docker run -it --rm "${ENTRYPOINT_ARG[@]}" "${RUN_ARGS[@]}" "$IMAGE" +fi diff --git a/package.json b/package.json deleted file mode 100644 index 8d0473d8..00000000 --- a/package.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "name": "kernel-images", - "type": "module", - "scripts": { - "kernel:build": "docker build -t kernel-chromium -f containers/docker/Dockerfile .", - "kernel:run": "docker run -p 8501:8501 -p 8080:8080 -p 6080:6080 -p 9222:9222 kernel-chromium" - }, - "devDependencies": { - "@types/bun": "latest" - } -} diff --git a/shared/start-buildkit.sh b/shared/start-buildkit.sh index f7b67890..d5ef8844 100755 --- a/shared/start-buildkit.sh +++ b/shared/start-buildkit.sh @@ -21,3 +21,11 @@ else docker start buildkit return $? fi + +CACHE_ARGS=() +if [ -n "${CACHE_DIR:-}" ]; then + mkdir -p "$CACHE_DIR" + CACHE_ARGS+=(--cache-from type=local,src="$CACHE_DIR") + CACHE_ARGS+=(--cache-to type=local,dest="$CACHE_DIR",mode=max) +fi +export CACHE_ARGS