feat: quiet live boot to reduce framebuffer log flashes #1330
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: CI/CD | |
| on: | |
| push: | |
| branches: [main, develop] | |
| tags: | |
| - "v*" | |
| pull_request: | |
| branches: [main, develop] | |
| workflow_dispatch: | |
| permissions: | |
| contents: write | |
| concurrency: | |
| group: cicd-${{ github.ref }} | |
| cancel-in-progress: true | |
| env: | |
| ARCHLINUX_IMAGE: ghcr.io/archlinux/archlinux:latest | |
| jobs: | |
| # ================================================================ | |
| # Stage 1: Tests (run in parallel) | |
| # ================================================================ | |
| code-quality: | |
| name: "Code Quality" | |
| runs-on: ubuntu-latest | |
| if: github.event_name != 'workflow_dispatch' | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v5 | |
| - name: Setup Python | |
| uses: actions/setup-python@v6 | |
| with: | |
| python-version: '3.x' | |
| cache: 'pip' | |
| - name: Install dependencies | |
| run: pip install pytest shellcheck-py | |
| - name: Run ShellCheck | |
| run: | | |
| find airootfs/usr/local/bin -type f -executable | while read -r script; do | |
| if head -1 "$script" | grep -qE "(bash|sh)$"; then | |
| shellcheck -s bash --severity=error "$script" | |
| fi | |
| done | |
| if [ -d airootfs/usr/local/lib/mados-hw-quirks.d ]; then | |
| find airootfs/usr/local/lib/mados-hw-quirks.d -type f -name "*.sh" | while read -r script; do | |
| shellcheck -s bash --severity=error "$script" | |
| done | |
| fi | |
| if [ -d airootfs/usr/local/lib/mados_installer/scripts ]; then | |
| find airootfs/usr/local/lib/mados_installer/scripts -type f -name "*.sh" | while read -r script; do | |
| shellcheck -s bash --severity=error "$script" | |
| done | |
| fi | |
| - name: Validate Python syntax | |
| run: | | |
| find airootfs -name "*.py" -type f | while read -r script; do | |
| python3 -m py_compile "$script" | |
| done | |
| - name: Install ruff | |
| run: pip install ruff | |
| - name: Run ruff check | |
| run: ruff check airootfs tests | |
| - name: Run ruff format check | |
| run: ruff format --check airootfs tests | |
| test-unit: | |
| name: "Unit: ${{ matrix.category }}" | |
| runs-on: ubuntu-latest | |
| needs: code-quality | |
| if: github.event_name != 'workflow_dispatch' | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - category: "Core & OTA" | |
| files: "tests/test_boot_scripts.py tests/test_wifi_quirks.py tests/test_mados_chwd.py tests/test_profiledef_permissions.py" | |
| - category: "Desktop Config" | |
| files: "tests/test_gamepad_wm.py tests/test_squeekboard.py tests/test_timezone_detect.py tests/test_vbox_guest.py" | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v5 | |
| - name: Setup Python | |
| uses: actions/setup-python@v6 | |
| with: | |
| python-version: '3.x' | |
| cache: 'pip' | |
| - name: Install pytest | |
| run: pip install pytest | |
| - name: Run tests | |
| run: | | |
| executed=0 | |
| for file in ${{ matrix.files }}; do | |
| if [ ! -f "$file" ]; then | |
| echo "Skipping missing test file: $file" | |
| continue | |
| fi | |
| python3 -m pytest "$file" -v | |
| executed=$((executed + 1)) | |
| done | |
| if [ "$executed" -eq 0 ]; then | |
| echo "No test files executed for matrix category: ${{ matrix.category }}" | |
| exit 1 | |
| fi | |
| test-integration: | |
| name: "Integration" | |
| runs-on: ubuntu-latest | |
| needs: code-quality | |
| if: github.event_name != 'workflow_dispatch' | |
| steps: | |
| - name: Skip (installer in external repo) | |
| run: echo "Skipping integration test - installer is now tested in mados-installer repo" | |
| # ================================================================ | |
| # Stage 2: Validation (only on push to main/develop or tags) | |
| # ================================================================ | |
| validate: | |
| name: "Validate: ${{ matrix.component }}" | |
| runs-on: ubuntu-latest | |
| needs: [test-unit, test-integration] | |
| if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/')) | |
| steps: | |
| - name: Skip (installer in external repo) | |
| run: echo "Skipping validation - installer is now tested in mados-installer repo" | |
| # ================================================================ | |
| # Stage 3: Build ISO + Release (only on tag push) | |
| # ================================================================ | |
| build-and-release: | |
| name: "Build & Release" | |
| runs-on: ubuntu-latest | |
| needs: validate | |
| if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/') | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v5 | |
| with: | |
| fetch-depth: 0 | |
| - name: Build ISO | |
| id: build | |
| run: | | |
| WORKSPACE="${{ github.workspace }}" | |
| rm -rf out/ 2>/dev/null || true | |
| mkdir -p out | |
| chmod 777 out | |
| docker run --privileged --rm \ | |
| --network host \ | |
| -v "$WORKSPACE:/build" \ | |
| ${{ env.ARCHLINUX_IMAGE }} bash -c " | |
| set -e | |
| pacman-key --init && pacman-key --populate | |
| pacman -Syu --noconfirm archiso | |
| cd /build | |
| rm -rf out/* | |
| mkarchiso -v -w /tmp/work -o out . | |
| " | |
| ISO_FILE=$(ls out/*.iso 2>/dev/null | head -1) | |
| if [ -z "$ISO_FILE" ]; then | |
| echo "ERROR: No ISO file found" | |
| exit 1 | |
| fi | |
| sha256sum out/*.iso > out/SHA256SUMS || true | |
| VERSION="${GITHUB_REF_NAME#v}" | |
| [ -n "$VERSION" ] || VERSION="dev-$(date +%Y%m%d)" | |
| ISO_FILE=$(ls out/*.iso | head -1) | |
| NEW_ISO_NAME="madOS-${VERSION}-x86_64.iso" | |
| mv "$ISO_FILE" "out/${NEW_ISO_NAME}" | |
| sed -i "s/$(basename "$ISO_FILE")/${NEW_ISO_NAME}/g" out/SHA256SUMS | |
| ISO_NAME="$NEW_ISO_NAME" | |
| BUILD_DATE=$(date +%Y.%m.%d) | |
| ISO_SIZE=$(stat --format=%s "out/${ISO_NAME}" 2>/dev/null || echo 0) | |
| ISO_SIZE_MB=$((ISO_SIZE / 1024 / 1024)) | |
| SHORT_SHA=$(echo "$GITHUB_SHA" | cut -c1-7) | |
| echo "iso_name=$ISO_NAME" >> $GITHUB_OUTPUT | |
| echo "version=$VERSION" >> $GITHUB_OUTPUT | |
| echo "build_date=$BUILD_DATE" >> $GITHUB_OUTPUT | |
| echo "iso_size_mb=$ISO_SIZE_MB" >> $GITHUB_OUTPUT | |
| echo "short_sha=$SHORT_SHA" >> $GITHUB_OUTPUT | |
| echo "Build successful!" | |
| - name: Upload to Internet Archive | |
| id: ia-upload | |
| env: | |
| IA_ACCESS_KEY: ${{ secrets.IA_ACCESS_KEY }} | |
| IA_SECRET_KEY: ${{ secrets.IA_SECRET_KEY }} | |
| run: | | |
| pip install --user internetarchive | |
| export PATH="$HOME/.local/bin:$PATH" | |
| ISO_NAME="${{ steps.build.outputs.iso_name }}" | |
| VERSION="${{ steps.build.outputs.version }}" | |
| BUILD_DATE="${{ steps.build.outputs.build_date }}" | |
| export IA_ACCESS_KEY IA_SECRET_KEY ISO_NAME VERSION BUILD_DATE | |
| IA_URL=$(python3 "${{ github.workspace }}/.github/scripts/ia_upload.py" 2>&1 | grep "^URL:" | cut -d' ' -f2) | |
| if [ -z "$IA_URL" ]; then | |
| echo "ERROR: Failed to get Internet Archive URL" | |
| exit 1 | |
| fi | |
| echo "ia_url=$IA_URL" >> $GITHUB_OUTPUT | |
| - name: Get tag | |
| id: get_tag | |
| run: echo "TAG=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT | |
| - name: Create GitHub Release | |
| uses: softprops/action-gh-release@v2 | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true | |
| with: | |
| tag_name: ${{ steps.get_tag.outputs.TAG }} | |
| name: ${{ steps.get_tag.outputs.TAG }} | |
| draft: false | |
| prerelease: false | |
| body: | | |
| ## madOS ${{ steps.get_tag.outputs.TAG }} | |
| **Download:** [Internet Archive](${{ steps.ia-upload.outputs.ia_url }}) | |
| --- | |
| Built from: `${{ github.sha }}` | |
| files: | | |
| out/${{ steps.build.outputs.iso_name }} | |
| out/SHA256SUMS |