ci: fix layer validation by simplifying to focus on meta-dynamicdevic… #299
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: KAS Build CI | |
| on: | |
| push: | |
| branches: [ main, develop ] | |
| paths: | |
| - 'recipes-**' | |
| - 'conf/**' | |
| - 'kas/**' | |
| - 'scripts/**' | |
| - 'classes/**' | |
| - 'meta-dynamicdevices-bsp/**' | |
| - 'meta-dynamicdevices-distro/**' | |
| - 'meta-lmp-base/**' | |
| - 'meta-dynamicdevices-bsp' | |
| - 'meta-dynamicdevices-distro' | |
| - 'meta-lmp-base' | |
| - '.github/workflows/kas-build-ci.yml' | |
| pull_request: | |
| branches: [ main, develop ] | |
| paths: | |
| - 'recipes-**' | |
| - 'conf/**' | |
| - 'kas/**' | |
| - 'scripts/**' | |
| - 'classes/**' | |
| - 'meta-dynamicdevices-bsp/**' | |
| - 'meta-dynamicdevices-distro/**' | |
| - 'meta-lmp-base/**' | |
| - 'meta-dynamicdevices-bsp' | |
| - 'meta-dynamicdevices-distro' | |
| - 'meta-lmp-base' | |
| - '.github/workflows/kas-build-ci.yml' | |
| workflow_dispatch: | |
| inputs: | |
| machine: | |
| description: 'Specific machine to build (optional)' | |
| required: false | |
| type: choice | |
| options: | |
| - 'all' | |
| - 'imx8mm-jaguar-sentai' | |
| - 'imx93-jaguar-eink' | |
| default: 'all' | |
| jobs: | |
| # Layer validation job - runs once before builds | |
| validate: | |
| name: Validate Yocto Layer Compliance | |
| runs-on: self-hosted | |
| container: | |
| image: dynamicdevices/yocto-ci-build:latest | |
| options: --privileged --platform linux/amd64 -v /dev/net/tun:/dev/net/tun -v /dev/kvm:/dev/kvm | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| with: | |
| submodules: recursive | |
| - name: Cache KAS layers | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| build/layers | |
| build/cache | |
| key: kas-layers-${{ hashFiles('kas/lmp-dynamicdevices-base.yml') }}-${{ github.sha }} | |
| restore-keys: | | |
| kas-layers-${{ hashFiles('kas/lmp-dynamicdevices-base.yml') }}- | |
| kas-layers- | |
| - name: Cache Yocto downloads | |
| uses: actions/cache@v4 | |
| with: | |
| path: ~/yocto/downloads | |
| key: yocto-downloads-${{ runner.os }}-${{ hashFiles('kas/lmp-dynamicdevices-base.yml') }} | |
| restore-keys: | | |
| yocto-downloads-${{ runner.os }}- | |
| - name: Validate Yocto Layers | |
| timeout-minutes: 5 | |
| run: | | |
| echo "🏅 Validating meta-dynamicdevices layers (simplified validation)" | |
| echo "📋 Testing layers: meta-dynamicdevices, meta-dynamicdevices-bsp, meta-dynamicdevices-distro" | |
| # Simple validation approach - just check layer structure and syntax | |
| echo "🔧 Performing basic layer validation..." | |
| validation_failed=false | |
| # Function to validate a single layer | |
| validate_layer() { | |
| local layer_path="$1" | |
| local layer_name="$2" | |
| echo "🔍 Validating $layer_name..." | |
| # Check if layer directory exists | |
| if [ ! -d "$layer_path" ]; then | |
| echo "❌ Layer directory not found: $layer_path" | |
| return 1 | |
| fi | |
| # Check required files | |
| local required_files=("conf/layer.conf" "README.md") | |
| local missing_files=() | |
| for file in "${required_files[@]}"; do | |
| if [ ! -f "$layer_path/$file" ]; then | |
| missing_files+=("$file") | |
| fi | |
| done | |
| if [ ${#missing_files[@]} -gt 0 ]; then | |
| echo "❌ Missing required files in $layer_name:" | |
| for file in "${missing_files[@]}"; do | |
| echo " - $file" | |
| done | |
| return 1 | |
| fi | |
| # Validate layer.conf syntax and required variables | |
| echo " 📋 Checking layer.conf syntax..." | |
| local layer_conf="$layer_path/conf/layer.conf" | |
| # Check for required variables | |
| local required_vars=("BBFILE_COLLECTIONS" "BBFILE_PATTERN" "BBFILE_PRIORITY" "LAYERVERSION") | |
| local missing_vars=() | |
| for var in "${required_vars[@]}"; do | |
| if ! grep -q "^[[:space:]]*$var" "$layer_conf"; then | |
| missing_vars+=("$var") | |
| fi | |
| done | |
| if [ ${#missing_vars[@]} -gt 0 ]; then | |
| echo "❌ Missing required variables in $layer_name layer.conf:" | |
| for var in "${missing_vars[@]}"; do | |
| echo " - $var" | |
| done | |
| return 1 | |
| fi | |
| # Check for valid collection name (no spaces, special chars) | |
| local collection=$(grep "BBFILE_COLLECTIONS" "$layer_conf" | head -1 | sed 's/.*[=+][[:space:]]*["\x27]\([^"\x27]*\)["\x27].*/\1/') | |
| if [[ "$collection" =~ [[:space:]] ]] || [[ "$collection" =~ [^a-zA-Z0-9_-] ]]; then | |
| echo "❌ Invalid collection name in $layer_name: '$collection'" | |
| echo " Collection names should only contain letters, numbers, underscores, and hyphens" | |
| return 1 | |
| fi | |
| # Check for reasonable priority (0-99) | |
| local priority=$(grep "BBFILE_PRIORITY" "$layer_conf" | head -1 | sed 's/.*[=+][[:space:]]*["\x27]\?\([0-9]*\)["\x27]\?.*/\1/') | |
| if [ -z "$priority" ] || [ "$priority" -lt 0 ] || [ "$priority" -gt 99 ]; then | |
| echo "❌ Invalid priority in $layer_name: '$priority' (should be 0-99)" | |
| return 1 | |
| fi | |
| # Basic recipe count check | |
| local recipe_count=$(find "$layer_path" -name "*.bb" -o -name "*.bbappend" 2>/dev/null | wc -l) | |
| echo " 📦 Found $recipe_count recipes/bbappends" | |
| echo "✅ $layer_name validation passed" | |
| return 0 | |
| } | |
| # Validate each layer | |
| echo "" | |
| echo "1️⃣ Validating meta-dynamicdevices-bsp..." | |
| if ! validate_layer "meta-dynamicdevices-bsp" "meta-dynamicdevices-bsp"; then | |
| validation_failed=true | |
| fi | |
| echo "" | |
| echo "2️⃣ Validating meta-dynamicdevices-distro..." | |
| if ! validate_layer "meta-dynamicdevices-distro" "meta-dynamicdevices-distro"; then | |
| validation_failed=true | |
| fi | |
| echo "" | |
| echo "3️⃣ Validating meta-dynamicdevices (main layer)..." | |
| if ! validate_layer "." "meta-dynamicdevices"; then | |
| validation_failed=true | |
| fi | |
| # Check for collection name conflicts between layers | |
| echo "" | |
| echo "4️⃣ Checking for collection name conflicts..." | |
| declare -A collections | |
| for layer_path in "." "meta-dynamicdevices-bsp" "meta-dynamicdevices-distro"; do | |
| if [ -f "$layer_path/conf/layer.conf" ]; then | |
| collection=$(grep "BBFILE_COLLECTIONS" "$layer_path/conf/layer.conf" | head -1 | sed 's/.*[=+][[:space:]]*["\x27]\([^"\x27]*\)["\x27].*/\1/') | |
| layer_name=$(basename "$layer_path") | |
| if [ "$layer_path" = "." ]; then layer_name="meta-dynamicdevices"; fi | |
| if [ -n "${collections[$collection]}" ]; then | |
| echo "❌ Collection name conflict: '$collection' used by both ${collections[$collection]} and $layer_name" | |
| validation_failed=true | |
| else | |
| collections[$collection]="$layer_name" | |
| echo "✅ Collection '$collection' registered for $layer_name" | |
| fi | |
| fi | |
| done | |
| # Final result | |
| echo "" | |
| if [ "$validation_failed" = true ]; then | |
| echo "❌ Layer validation FAILED" | |
| echo "Please fix the issues above before proceeding" | |
| exit 1 | |
| else | |
| echo "✅ All layer validations PASSED" | |
| echo "All meta-dynamicdevices layers are properly structured and configured" | |
| fi | |
| # Build job with matrix for specific machines and targets | |
| build: | |
| name: Build ${{ matrix.machine }} - ${{ matrix.target }} | |
| runs-on: self-hosted | |
| container: | |
| image: dynamicdevices/yocto-ci-build:latest | |
| options: --privileged --platform linux/amd64 -v /dev/net/tun:/dev/net/tun -v /dev/kvm:/dev/kvm | |
| needs: [validate] | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| machine: [imx8mm-jaguar-sentai, imx93-jaguar-eink] | |
| target: [lmp-factory-image, mfgtool-files] | |
| env: | |
| KAS_MACHINE: ${{ matrix.machine }} | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| with: | |
| submodules: recursive | |
| - name: Cache KAS layers | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| build/layers | |
| build/cache | |
| key: kas-layers-${{ hashFiles('kas/lmp-dynamicdevices-base.yml', 'kas/lmp-dynamicdevices-mfgtool.yml') }}-${{ github.sha }} | |
| restore-keys: | | |
| kas-layers-${{ hashFiles('kas/lmp-dynamicdevices-base.yml', 'kas/lmp-dynamicdevices-mfgtool.yml') }}- | |
| kas-layers- | |
| - name: Cache Yocto downloads | |
| uses: actions/cache@v4 | |
| with: | |
| path: ~/yocto/downloads | |
| key: yocto-downloads-${{ runner.os }}-${{ hashFiles('kas/lmp-dynamicdevices-base.yml', 'kas/lmp-dynamicdevices-mfgtool.yml') }} | |
| restore-keys: | | |
| yocto-downloads-${{ runner.os }}- | |
| - name: Cache Yocto sstate | |
| uses: actions/cache@v4 | |
| with: | |
| path: ~/yocto/sstate | |
| key: yocto-sstate-${{ runner.os }}-${{ matrix.machine }}-${{ matrix.target }}-${{ hashFiles('kas/lmp-dynamicdevices-base.yml', 'kas/lmp-dynamicdevices-mfgtool.yml') }}-${{ hashFiles('recipes-**') }} | |
| restore-keys: | | |
| yocto-sstate-${{ runner.os }}-${{ matrix.machine }}-${{ matrix.target }}-${{ hashFiles('kas/lmp-dynamicdevices-base.yml', 'kas/lmp-dynamicdevices-mfgtool.yml') }}- | |
| yocto-sstate-${{ runner.os }}-${{ matrix.machine }}-${{ matrix.target }}- | |
| yocto-sstate-${{ runner.os }}-${{ matrix.machine }}- | |
| yocto-sstate-${{ runner.os }}- | |
| - name: Cache build tmp directory | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| ~/yocto/persistent | |
| build/tmp/cache | |
| key: yocto-build-cache-${{ runner.os }}-${{ matrix.machine }}-${{ matrix.target }}-${{ hashFiles('recipes-**') }} | |
| restore-keys: | | |
| yocto-build-cache-${{ runner.os }}-${{ matrix.machine }}-${{ matrix.target }}- | |
| yocto-build-cache-${{ runner.os }}-${{ matrix.machine }}- | |
| yocto-build-cache-${{ runner.os }}- | |
| - name: Setup build cache | |
| run: | | |
| echo "🗄️ Setting up persistent build cache" | |
| mkdir -p ~/yocto/{downloads,sstate,persistent} | |
| chmod 755 ~/yocto/{downloads,sstate,persistent} | |
| - name: Build with KAS | |
| timeout-minutes: 360 | |
| run: | | |
| echo "🔨 Starting KAS build: ${{ matrix.machine }} (${{ matrix.target }})" | |
| # Select appropriate KAS configuration and build target | |
| if [ "${{ matrix.target }}" = "mfgtool-files" ]; then | |
| kas_config="kas/lmp-dynamicdevices-mfgtool.yml" | |
| build_target="mfgtool-files" | |
| else | |
| kas_config="kas/lmp-dynamicdevices-base.yml" | |
| build_target="lmp-factory-image" | |
| fi | |
| echo "Using KAS config: $kas_config" | |
| echo "Build target: $build_target" | |
| echo "Machine: $KAS_MACHINE" | |
| # Run KAS build (directly in container) | |
| kas build "$kas_config:$build_target" | |
| echo "✅ Build completed successfully" | |
| - name: Collect build artifacts | |
| if: always() | |
| run: | | |
| echo "📦 Collecting build artifacts for ${{ matrix.machine }} (${{ matrix.target }})" | |
| # Create artifacts directory | |
| mkdir -p artifacts/${{ matrix.machine }}/${{ matrix.target }} | |
| # Find the deploy directory | |
| deploy_dir=$(find . -path "*/tmp*/deploy/images/${{ matrix.machine }}" -type d | head -1) | |
| if [ -d "$deploy_dir" ]; then | |
| echo "✅ Found deploy directory: $deploy_dir" | |
| # Copy key artifacts based on target | |
| if [ "${{ matrix.target }}" = "lmp-factory-image" ]; then | |
| # Factory image artifacts | |
| for pattern in "*.wic.gz" "*.wic.bmap" "imx-boot-*" "u-boot-*.itb"; do | |
| find "$deploy_dir" -name "$pattern" -exec cp {} artifacts/${{ matrix.machine }}/${{ matrix.target }}/ \; 2>/dev/null || true | |
| done | |
| else | |
| # MFGtool artifacts | |
| for pattern in "imx-boot-mfgtool*" "u-boot-mfgtool*"; do | |
| find "$deploy_dir" -name "$pattern" -exec cp {} artifacts/${{ matrix.machine }}/${{ matrix.target }}/ \; 2>/dev/null || true | |
| done | |
| fi | |
| echo "📋 Artifacts collected:" | |
| ls -la artifacts/${{ matrix.machine }}/${{ matrix.target }}/ | |
| else | |
| echo "❌ Deploy directory not found" | |
| echo "# Build Failed" > artifacts/${{ matrix.machine }}/${{ matrix.target }}/build-failed.txt | |
| fi | |
| - name: Upload build artifacts | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: build-artifacts-${{ matrix.machine }}-${{ matrix.target }} | |
| path: artifacts/ | |
| retention-days: 30 | |
| if-no-files-found: warn | |
| # Summary job | |
| summary: | |
| name: Build Summary | |
| runs-on: self-hosted | |
| needs: [validate, build] | |
| if: always() | |
| steps: | |
| - name: Generate build summary | |
| run: | | |
| echo "# KAS Build CI Summary" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "**Workflow:** ${{ github.workflow }}" >> $GITHUB_STEP_SUMMARY | |
| echo "**Trigger:** ${{ github.event_name }}" >> $GITHUB_STEP_SUMMARY | |
| echo "**Commit:** ${{ github.sha }}" >> $GITHUB_STEP_SUMMARY | |
| echo "**Branch:** ${{ github.ref_name }}" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| # Determine overall status | |
| if [ "${{ needs.validate.result }}" = "success" ] && [ "${{ needs.build.result }}" = "success" ]; then | |
| echo "## ✅ Build Status: SUCCESS" >> $GITHUB_STEP_SUMMARY | |
| echo "All layer validations and builds completed successfully!" >> $GITHUB_STEP_SUMMARY | |
| else | |
| echo "## ❌ Build Status: FAILED" >> $GITHUB_STEP_SUMMARY | |
| echo "- Layer validation: ${{ needs.validate.result }}" >> $GITHUB_STEP_SUMMARY | |
| echo "- Build: ${{ needs.build.result }}" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "## Build Matrix" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Machines:** imx8mm-jaguar-sentai, imx93-jaguar-eink" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Targets:** lmp-factory-image, mfgtool-files" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Total builds:** 4 (2 machines × 2 targets)" >> $GITHUB_STEP_SUMMARY |