From 49c1411f80fe6af5994c2282a0c6ca9a5706d463 Mon Sep 17 00:00:00 2001 From: Nick Chapman Date: Fri, 2 Jan 2026 11:39:09 -0800 Subject: [PATCH 1/3] Prebuild and cache QA Docker container in GHCR. Avoids rebuilding the dev container on every CI run by: - Adding docker-build job that hashes Dockerfile and checks GHCR - Only builds/pushes when the image doesn't exist for that hash - lint/analyze/test jobs pull the prebuilt image from GHCR Makefile.qa now pulls from GHCR by default. Use FORCE_DOCKER_BUILD=1 to build locally when iterating on the Dockerfile. --- .github/workflows/qa.yml | 67 ++++++++++++++++++++++++++++++++++------ Makefile.qa | 22 +++++++++++-- 2 files changed, 77 insertions(+), 12 deletions(-) diff --git a/.github/workflows/qa.yml b/.github/workflows/qa.yml index 3b4f56e9..47dc2754 100644 --- a/.github/workflows/qa.yml +++ b/.github/workflows/qa.yml @@ -6,25 +6,79 @@ on: pull_request: branches: [main, develop] +permissions: + contents: read + packages: write + jobs: - lint: - name: Lint + docker-build: + name: Build Dev Container runs-on: ubuntu-latest + timeout-minutes: 15 steps: - name: Checkout code uses: actions/checkout@v4 + + - name: Calculate Dockerfile hash + id: dockerfile-hash + run: | + HASH=$(sha256sum Dockerfile | awk '{print $1}') + echo "hash=$HASH" >> $GITHUB_OUTPUT + echo "Dockerfile hash: $HASH" + + - name: Login to GitHub Container Registry + uses: docker/login-action@v3 with: - submodules: true + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Check if image exists in GHCR + id: check-image + run: | + IMAGE="ghcr.io/lessui-hq/lessui-dev:${{ steps.dockerfile-hash.outputs.hash }}" + if docker pull "$IMAGE" 2>/dev/null; then + echo "✓ Image found in GHCR (skipping build)" + echo "exists=true" >> $GITHUB_OUTPUT + else + echo "Image not found, will build" + echo "exists=false" >> $GITHUB_OUTPUT + fi - name: Set up Docker Buildx + if: steps.check-image.outputs.exists == 'false' uses: docker/setup-buildx-action@v3 + - name: Build and push Docker image + if: steps.check-image.outputs.exists == 'false' + uses: docker/build-push-action@v6 + with: + context: . + push: true + tags: | + ghcr.io/lessui-hq/lessui-dev:${{ steps.dockerfile-hash.outputs.hash }} + ghcr.io/lessui-hq/lessui-dev:latest + cache-from: type=gha + cache-to: type=gha,mode=max + + lint: + name: Lint + needs: docker-build + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + submodules: true + - name: Run all linting checks run: make lint analyze: name: Static Analysis + needs: docker-build runs-on: ubuntu-latest steps: @@ -33,14 +87,12 @@ jobs: with: submodules: true - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - name: Run Clang Static Analyzer run: make analyze test: name: Test + needs: docker-build runs-on: ubuntu-latest steps: @@ -49,8 +101,5 @@ jobs: with: submodules: true - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - name: Run all tests run: make test diff --git a/Makefile.qa b/Makefile.qa index f5763f1e..4ec03689 100644 --- a/Makefile.qa +++ b/Makefile.qa @@ -14,7 +14,7 @@ # The main makefile forwards to this file for test/lint/format targets. # Run 'make -f Makefile.qa help' for complete target list. -.PHONY: help lint lint-code lint-full lint-shell analyze analyze-native test test-native test-asan test-asan-native format format-native format-check format-shell format-shell-check format-make format-make-check format-prettier format-prettier-check clean-qa clean-tests docker-build docker-test docker-test-asan docker-lint docker-analyze docker-format docker-format-check docker-shell lint-native report coverage docker-coverage clean-coverage +.PHONY: help lint lint-code lint-full lint-shell analyze analyze-native test test-native test-asan test-asan-native format format-native format-check format-shell format-shell-check format-make format-make-check format-prettier format-prettier-check clean-qa clean-tests docker-build docker-pull docker-test docker-test-asan docker-lint docker-analyze docker-format docker-format-check docker-shell lint-native report coverage docker-coverage clean-coverage help: @echo "LessUI Quality Assurance Tools" @@ -811,12 +811,28 @@ report: check-clang-tidy ########################################################### # Docker QA (recommended for consistency with CI) DEV_IMAGE = lessui-dev +GHCR_IMAGE = ghcr.io/lessui-hq/lessui-dev:latest DEV_RUN = docker run --rm -v $(shell pwd):/lessui -w /lessui $(DEV_IMAGE) +# Set FORCE_DOCKER_BUILD=1 to build locally instead of pulling from GHCR +FORCE_DOCKER_BUILD ?= 0 + +# Pull prebuilt image from GHCR and tag it locally +docker-pull: + @echo "Pulling dev image from GHCR..." + @docker pull $(GHCR_IMAGE) + @docker tag $(GHCR_IMAGE) $(DEV_IMAGE) + @echo "✓ Dev image ready (from GHCR)" + +# Default: pull from GHCR. Use FORCE_DOCKER_BUILD=1 to build locally. docker-build: - @echo "Building dev Docker image (Ubuntu 24.04, matches GitHub CI)..." +ifeq ($(FORCE_DOCKER_BUILD),1) + @echo "Building dev Docker image locally..." @docker build -q -t $(DEV_IMAGE) -f Dockerfile . - @echo "✓ Dev image ready" + @echo "✓ Dev image ready (built locally)" +else + @$(MAKE) -f Makefile.qa docker-pull +endif docker-test: docker-build @echo "Running tests in Docker container (Ubuntu 24.04)..." From 98503835efff07a164ae3a1faff8a1130b633972 Mon Sep 17 00:00:00 2001 From: Nick Chapman Date: Fri, 2 Jan 2026 11:46:17 -0800 Subject: [PATCH 2/3] Run platform smoke tests in parallel. Use matrix strategy to build miyoomini and tg5040 concurrently. Changed from trimuismart to tg5040 for better code coverage. --- .github/workflows/build.yml | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index fa75b48f..ddda44d4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -8,8 +8,12 @@ on: jobs: build: - name: Build + name: Build ${{ matrix.platform }} runs-on: sprinters:aws:ubuntu-24.04-arm:c7gd.xlarge + strategy: + matrix: + platform: [miyoomini, tg5040] + fail-fast: false steps: - name: Checkout code @@ -28,8 +32,5 @@ jobs: sudo apt-get update sudo apt-get install -y jq - - name: Build miyoomini (arm64 native) - run: make PLATFORM=miyoomini build - - - name: Build trimuismart (amd64 emulation verification) - run: make PLATFORM=trimuismart build + - name: Build ${{ matrix.platform }} + run: make PLATFORM=${{ matrix.platform }} build From 927783821f0f2ffd401a3d72ea725c40585449f8 Mon Sep 17 00:00:00 2001 From: Nick Chapman Date: Fri, 2 Jan 2026 12:02:27 -0800 Subject: [PATCH 3/3] Fix SDL include in gl_video for cross-platform builds. Use project's sdl.h wrapper instead of directly. The wrapper handles SDL1 vs SDL2 include paths correctly ( vs ). --- .github/workflows/qa.yml | 4 ++-- workspace/all/common/gl_video.c | 1 - workspace/all/common/gl_video.h | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/qa.yml b/.github/workflows/qa.yml index 47dc2754..f790cf1b 100644 --- a/.github/workflows/qa.yml +++ b/.github/workflows/qa.yml @@ -38,8 +38,8 @@ jobs: id: check-image run: | IMAGE="ghcr.io/lessui-hq/lessui-dev:${{ steps.dockerfile-hash.outputs.hash }}" - if docker pull "$IMAGE" 2>/dev/null; then - echo "✓ Image found in GHCR (skipping build)" + if docker manifest inspect "$IMAGE" >/dev/null 2>&1; then + echo "✓ Image exists in GHCR (skipping build)" echo "exists=true" >> $GITHUB_OUTPUT else echo "Image not found, will build" diff --git a/workspace/all/common/gl_video.c b/workspace/all/common/gl_video.c index 38518e49..e26e4936 100644 --- a/workspace/all/common/gl_video.c +++ b/workspace/all/common/gl_video.c @@ -15,7 +15,6 @@ #include "libretro.h" #include "log.h" #include "scaler.h" // For scaling constants/types if needed -#include #include #include #include diff --git a/workspace/all/common/gl_video.h b/workspace/all/common/gl_video.h index fea1184b..7be3e112 100644 --- a/workspace/all/common/gl_video.h +++ b/workspace/all/common/gl_video.h @@ -32,7 +32,7 @@ #if HAS_OPENGLES -#include +#include "sdl.h" #include // Forward declarations to avoid including libretro.h in non-player components