name: CI Build Major Branch Keymap permissions: contents: read actions: write on: workflow_call: inputs: branch: type: string required: true keymap: type: string required: true slice_length: type: string required: true jobs: generate_targets: name: "Generate targets (${{ inputs.keymap }})" runs-on: ubuntu-latest container: ghcr.io/qmk/qmk_cli outputs: targets: ${{ steps.generate_targets.outputs.targets }} steps: - name: Disable safe.directory check run: | git config --global --add safe.directory '*' - name: Checkout QMK Firmware uses: actions/checkout@v7 - name: Install dependencies run: pip3 install -r requirements-dev.txt - name: Generate build targets shell: 'bash {0}' id: generate_targets run: | { # Intentionally use `shuf` here so that we share manufacturers across all build groups -- some have a lot of ARM-based boards which inherently take longer counter=0 echo -n '{' qmk find -km ${{ inputs.keymap }} 2>/dev/null | sort | uniq | shuf --random-source=<(openssl enc -aes-256-ctr -pass pass:qmk -nosalt /dev/null) | xargs -L${{ inputs.slice_length }} | while IFS=$'\n' read target ; do if [ $counter -gt 0 ]; then echo -n ',' fi counter=$((counter+1)) printf "\"group-%02d\":{" $counter echo -n '"targets":"' echo $target | tr ' ' '\n' | sort | uniq | xargs echo -n echo -n '"}' done echo -n '}' } | sed -e 's@\n@@g' > targets.json # Output the target keys as a variable echo "targets=$(jq -c 'keys' targets.json)" >> $GITHUB_OUTPUT - name: Upload targets json uses: actions/upload-artifact@v7 with: name: targets-${{ inputs.keymap }} path: targets.json build_targets: name: "Compile ${{ matrix.target }} (${{ inputs.keymap }})" needs: generate_targets runs-on: ubuntu-latest container: ghcr.io/qmk/qmk_cli continue-on-error: true env: CCACHE_CONFIGPATH: ~/.cache strategy: matrix: target: ${{ fromJson(needs.generate_targets.outputs.targets) }} steps: - name: Disable safe.directory check run: | git config --global --add safe.directory '*' - name: Checkout QMK Firmware uses: actions/checkout@v7 with: submodules: recursive - name: Install dependencies run: pip3 install -r requirements-dev.txt - name: Get target definitions uses: actions/download-artifact@v8 with: name: targets-${{ inputs.keymap }} path: . - name: Dump targets run: | jq -r '.["${{ matrix.target }}"].targets' targets.json | tr ' ' '\n' | sort - name: Restore Cache id: cache uses: actions/cache/restore@v6 with: path: ${{ env.CCACHE_CONFIGPATH }} key: compile-${{ inputs.keymap }}-${{ matrix.target }} - name: Build targets continue-on-error: true run: | targets=$(jq -r '.["${{ matrix.target }}"].targets' targets.json | tr ' ' '\n' | sort) if [ -z "${targets}" ]; then echo "Zero build targets detected" exit 0 fi qmk mass-compile -t -j $(nproc) -e DUMP_CI_METADATA=yes -e USE_CCACHE=yes $targets || touch .failed - name: Dump ccache stats run: | ccache -s # Delete the old cache on hit to emulate a cache update. See https://github.com/actions/cache/issues/342. - name: Delete old cache env: GH_TOKEN: ${{ github.token }} if: steps.cache.outputs.cache-hit run: | count=$(gh cache list --ref ${{ github.ref }} --key ${{ steps.cache.outputs.cache-primary-key }} --json id | jq length) if [ $count -gt 0 ]; then gh cache delete --ref ${{ github.ref }} ${{ steps.cache.outputs.cache-primary-key }} fi - name: Save Cache uses: actions/cache/save@v6 with: path: ${{ env.CCACHE_CONFIGPATH }} key: compile-${{ inputs.keymap }}-${{ matrix.target }} - name: Upload binaries uses: actions/upload-artifact@v7 with: name: firmware-${{ inputs.keymap }}-${{ matrix.target }} if-no-files-found: ignore path: | *.bin *.hex *.uf2 .build/failed.* .failed - name: Fail build if any group failed run: | # Exit with failure if the compilation stage failed [ ! -f .failed ] || exit 1 repack_firmware: if: always() name: "Repack artifacts" needs: build_targets runs-on: ubuntu-latest steps: - name: Checkout QMK Firmware uses: actions/checkout@v7 - name: Download firmwares uses: actions/download-artifact@v8 with: pattern: firmware-${{ inputs.keymap }}-* path: . merge-multiple: true - name: Upload all firmwares uses: actions/upload-artifact@v7 with: name: firmware-${{ inputs.keymap }} if-no-files-found: ignore path: | *.bin *.hex *.uf2 .build/failed.* .failed - name: Generate output logs run: | # Generate the step summary markdown ./util/ci/generate_failure_markdown.sh > $GITHUB_STEP_SUMMARY || true # Truncate to a maximum of 1MB to deal with GitHub workflow limit truncate --size='<960K' $GITHUB_STEP_SUMMARY || true - name: Delete temporary build artifacts uses: geekyeggo/delete-artifact@v6 with: name: | firmware-${{ inputs.keymap }}-* targets-${{ inputs.keymap }} - name: 'CI Discord Notification' if: always() && !cancelled() working-directory: util/ci/ env: DISCORD_WEBHOOK: ${{ secrets.CI_DISCORD_WEBHOOK }} run: | python3 -m pip install -r requirements.txt python3 ./discord-results.py --branch ${{ inputs.branch || github.ref_name }} --sha $(git rev-parse HEAD) --keymap ${{ inputs.keymap }} --url ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}