initial commit
This commit is contained in:
commit
b44ce7bd77
2716 files changed
+559005
No files matched your search
+417
@@ -0,0 +1,417 @@
|
||||
name: Build
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches: [master]
|
||||
pull_request:
|
||||
release:
|
||||
types: [ published ]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event_name == 'push' && github.sha || github.ref }}
|
||||
cancel-in-progress: ${{ github.ref != 'refs/heads/master' }}
|
||||
|
||||
jobs:
|
||||
# Check if the code changes and we need to run ci build
|
||||
# Cannot use paths filter in the on-event since we want this workflow to run even when there are no code changes, to register the commit chain
|
||||
check-paths:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: read
|
||||
outputs:
|
||||
code_changed: ${{ steps.filter.outputs.code }}
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 2 # Needed for push commit comparison
|
||||
- uses: dorny/paths-filter@v4
|
||||
id: filter
|
||||
with:
|
||||
filters: |
|
||||
code:
|
||||
- 'src/**'
|
||||
- 'examples/**'
|
||||
- 'lib/**'
|
||||
- 'hw/**'
|
||||
- 'test/hil/**'
|
||||
- 'tools/build.py'
|
||||
- 'tools/get_deps.py'
|
||||
- '.github/actions/**'
|
||||
- '.github/workflows/build.yml'
|
||||
- '.github/workflows/build_util.yml'
|
||||
- '.github/workflows/ci_set_matrix.py'
|
||||
|
||||
set-matrix:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
json: ${{ steps.set-matrix-json.outputs.matrix }}
|
||||
hil_json: ${{ steps.set-matrix-json.outputs.hil_matrix }}
|
||||
steps:
|
||||
- name: Checkout TinyUSB
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Generate matrix json
|
||||
id: set-matrix-json
|
||||
run: |
|
||||
# build matrix
|
||||
MATRIX_JSON=$(python .github/workflows/ci_set_matrix.py)
|
||||
echo "matrix=$MATRIX_JSON"
|
||||
echo "matrix=$MATRIX_JSON" >> $GITHUB_OUTPUT
|
||||
|
||||
# HIL matrix (merged from tinyusb + hifiphile configs)
|
||||
HIL_MATRIX_JSON=$(python test/hil/hil_ci_set_matrix.py test/hil/tinyusb.json test/hil/hfp.json)
|
||||
echo "hil_matrix=$HIL_MATRIX_JSON"
|
||||
echo "hil_matrix=$HIL_MATRIX_JSON" >> $GITHUB_OUTPUT
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# CMake build: only one board per family (first alphabetically). Full build is done by CircleCI in PR
|
||||
# Note:
|
||||
# For Make and IAR build: will be done on CircleCI only (one random per family as well)
|
||||
# ------------------------------------------------------------------------------
|
||||
cmake:
|
||||
needs: [ check-paths, set-matrix ]
|
||||
uses: ./.github/workflows/build_util.yml
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
toolchain:
|
||||
- 'aarch64-gcc'
|
||||
#- 'arm-clang'
|
||||
- 'arm-gcc'
|
||||
#- 'esp-idf'
|
||||
- 'ft9xx-gcc'
|
||||
- 'msp430-gcc'
|
||||
- 'riscv-gcc'
|
||||
with:
|
||||
build-system: 'cmake'
|
||||
toolchain: ${{ matrix.toolchain }}
|
||||
build-args: ${{ toJSON(fromJSON(needs.set-matrix.outputs.json)[matrix.toolchain]) }}
|
||||
build-options: '--one-first'
|
||||
upload-metrics: true
|
||||
upload-artifacts: false
|
||||
upload-membrowse: true
|
||||
code-changed: ${{ needs.check-paths.outputs.code_changed == 'true' }}
|
||||
secrets: inherit
|
||||
|
||||
code-metrics:
|
||||
needs: [ check-paths, cmake ]
|
||||
if: needs.check-paths.outputs.code_changed == 'true'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
pull-requests: write
|
||||
contents: write
|
||||
steps:
|
||||
- name: Checkout TinyUSB
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-tags: ${{ github.event_name == 'release' }}
|
||||
|
||||
- name: Download Artifacts
|
||||
uses: actions/download-artifact@v5
|
||||
with:
|
||||
pattern: metrics-*
|
||||
path: cmake-build
|
||||
merge-multiple: true
|
||||
|
||||
- name: Aggregate Code Metrics
|
||||
run: |
|
||||
python tools/get_deps.py
|
||||
python tools/metrics.py combine -j -m -f tinyusb/src cmake-build/*/metrics.json
|
||||
|
||||
- name: Upload Metrics Artifact
|
||||
if: github.event_name == 'push' || github.event_name == 'release'
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: metrics-tinyusb
|
||||
path: metrics.json
|
||||
|
||||
- name: Download Base Branch Metrics
|
||||
if: github.event_name == 'pull_request' || github.event_name == 'workflow_dispatch'
|
||||
uses: dawidd6/action-download-artifact@v11
|
||||
with:
|
||||
workflow: build.yml
|
||||
workflow_conclusion: ''
|
||||
branch: ${{ github.base_ref }}
|
||||
name: metrics-tinyusb
|
||||
path: base-metrics
|
||||
continue-on-error: true
|
||||
|
||||
- name: Download Previous Release Asset
|
||||
if: github.event_name == 'release'
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
PREV_TAG=$(git tag --sort=-creatordate | head -n 2 | tail -n 1)
|
||||
echo "Previous Release: $PREV_TAG"
|
||||
echo "PREV_TAG=$PREV_TAG" >> $GITHUB_ENV
|
||||
|
||||
mkdir -p base-metrics
|
||||
gh release download $PREV_TAG -p metrics.json -D base-metrics || echo "No metrics.json found in $PREV_TAG release"
|
||||
|
||||
- name: Compare with Base Branch
|
||||
if: github.event_name != 'push'
|
||||
run: |
|
||||
if [ -f base-metrics/metrics.json ]; then
|
||||
python tools/metrics.py compare -m -f tinyusb/src base-metrics/metrics.json metrics.json
|
||||
cat metrics_compare.md
|
||||
else
|
||||
echo "No base metrics found, skipping comparison"
|
||||
cp metrics.md metrics_compare.md
|
||||
fi
|
||||
|
||||
- name: Upload Release Assets
|
||||
if: github.event_name == 'release'
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
CURR_TAG=${{ github.event.release.tag_name }}
|
||||
COMPARE_FILE="metrics_compare_${CURR_TAG}-${PREV_TAG}.md"
|
||||
mv metrics_compare.md $COMPARE_FILE
|
||||
gh release upload $CURR_TAG metrics.json $COMPARE_FILE
|
||||
|
||||
- name: Upload Metrics Comment Artifact
|
||||
if: github.event_name == 'pull_request'
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: metrics-comment
|
||||
path: |
|
||||
metrics_compare.md
|
||||
metrics.json
|
||||
|
||||
- name: Post Code Metrics as PR Comment
|
||||
if: (github.event_name == 'workflow_dispatch') || (github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == false)
|
||||
uses: marocchino/sticky-pull-request-comment@v2
|
||||
with:
|
||||
header: code-metrics
|
||||
path: metrics_compare.md
|
||||
|
||||
# ---------------------------------------
|
||||
# Keep the metrics baseline available on no-code-change pushes
|
||||
# The code-metrics job only runs (and uploads metrics-tinyusb) when code changed, so a
|
||||
# workflow/docs-only push to master would leave the latest run without a baseline for PRs
|
||||
# to compare against. Carry the previous artifact forward so the baseline is never missing.
|
||||
# ---------------------------------------
|
||||
metrics-carry-forward:
|
||||
needs: [ check-paths ]
|
||||
if: github.event_name == 'push' && needs.check-paths.outputs.code_changed != 'true'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Download previous metrics baseline from this branch
|
||||
uses: dawidd6/action-download-artifact@v11
|
||||
with:
|
||||
workflow: build.yml
|
||||
workflow_conclusion: '' # any conclusion, matching the PR-side baseline download
|
||||
search_artifacts: true # scan back past runs that lack the artifact (e.g. earlier no-code pushes)
|
||||
branch: ${{ github.ref_name }}
|
||||
name: metrics-tinyusb
|
||||
path: .
|
||||
if_no_artifact_found: warn
|
||||
continue-on-error: true # best-effort: never make a no-code push red
|
||||
|
||||
- name: Re-publish baseline so the latest run keeps it
|
||||
if: hashFiles('metrics.json') != ''
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: metrics-tinyusb
|
||||
path: metrics.json
|
||||
|
||||
# ---------------------------------------
|
||||
# Build Make/CMake on Windows/MacOS
|
||||
# ---------------------------------------
|
||||
build-os:
|
||||
needs: [ check-paths ]
|
||||
if: needs.check-paths.outputs.code_changed == 'true'
|
||||
uses: ./.github/workflows/build_util.yml
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ windows-latest, macos-latest ]
|
||||
build-system: [ 'make', 'cmake' ]
|
||||
with:
|
||||
os: ${{ matrix.os }}
|
||||
build-system: ${{ matrix.build-system }}
|
||||
toolchain: 'arm-gcc-${{ matrix.os }}'
|
||||
build-args: '["stm32h7rs"]'
|
||||
build-options: '--one-random'
|
||||
|
||||
# ---------------------------------------
|
||||
# Zephyr
|
||||
# ---------------------------------------
|
||||
zephyr:
|
||||
needs: [ check-paths ]
|
||||
# skip zephyr build due to failed build, fix later
|
||||
if: false
|
||||
#if: needs.check-paths.outputs.code_changed == 'true'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout TinyUSB
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Setup Zephyr project
|
||||
uses: zephyrproject-rtos/action-zephyr-setup@v1
|
||||
with:
|
||||
app-path: examples
|
||||
toolchains: arm-zephyr-eabi
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
west build -b nrf52840dk -d examples/device/cdc_msc/build examples/device/cdc_msc -- -DRTOS=zephyr
|
||||
west build -b nrf52840dk -d examples/device/msc_dual_lun/build examples/device/msc_dual_lun -- -DRTOS=zephyr
|
||||
|
||||
# ---------------------------------------
|
||||
# Hardware in the loop (HIL)
|
||||
# Run on PR only (hil-tinyusb), hil-hfp-iar only run on non-forked PR
|
||||
# ---------------------------------------
|
||||
hil-build:
|
||||
needs: [ check-paths, set-matrix ]
|
||||
if: needs.check-paths.outputs.code_changed == 'true' && github.repository_owner == 'hathach'
|
||||
uses: ./.github/workflows/build_util.yml
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
toolchain:
|
||||
- 'arm-gcc'
|
||||
- 'riscv-gcc'
|
||||
- 'esp-idf'
|
||||
with:
|
||||
build-system: 'cmake'
|
||||
toolchain: ${{ matrix.toolchain }}
|
||||
build-args: ${{ toJSON(fromJSON(needs.set-matrix.outputs.hil_json)[matrix.toolchain]) }}
|
||||
upload-artifacts: true
|
||||
|
||||
# ---------------------------------------
|
||||
# Hardware in the loop (HIL)
|
||||
# self-hosted on local VM, for attached hardware checkout HIL_JSON
|
||||
# ---------------------------------------
|
||||
hil-tinyusb:
|
||||
needs: hil-build
|
||||
name: hil-tinyusb (${{ matrix.display }})
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- display: tinyusb.json
|
||||
runner: [ self-hosted, X64, hathach, hardware-in-the-loop ]
|
||||
hil_json: test/hil/tinyusb.json
|
||||
- display: hfp.json
|
||||
runner: [ self-hosted, Linux, X64, hifiphile ]
|
||||
hil_json: test/hil/hfp.json
|
||||
runs-on: ${{ matrix.runner }}
|
||||
env:
|
||||
HIL_JSON: ${{ matrix.hil_json }}
|
||||
steps:
|
||||
- name: Set HIL report dir (sibling of workspace; persists across run attempts)
|
||||
run: echo "HIL_REPORT_DIR=$(dirname "$GITHUB_WORKSPACE")/hil-report" >> "$GITHUB_ENV"
|
||||
|
||||
- name: Get Skip Boards from previous run
|
||||
if: github.run_attempt != '1'
|
||||
run: |
|
||||
if [ -f "${{ env.HIL_JSON }}.skip" ]; then
|
||||
SKIP_BOARDS=$(cat "${{ env.HIL_JSON }}.skip")
|
||||
else
|
||||
SKIP_BOARDS=""
|
||||
fi
|
||||
echo "SKIP_BOARDS=$SKIP_BOARDS"
|
||||
echo "SKIP_BOARDS=$SKIP_BOARDS" >> $GITHUB_ENV
|
||||
|
||||
- name: Clean workspace
|
||||
run: |
|
||||
echo "Cleaning up for the first run"
|
||||
rm -rf "${{ github.workspace }}"
|
||||
mkdir -p "${{ github.workspace }}"
|
||||
|
||||
- name: Checkout TinyUSB
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Download Artifacts
|
||||
uses: actions/download-artifact@v5
|
||||
with:
|
||||
pattern: binaries-*
|
||||
path: cmake-build
|
||||
merge-multiple: true
|
||||
|
||||
- name: Test on actual hardware
|
||||
run: |
|
||||
python3 test/hil/hil_test.py ${{ env.HIL_JSON }} $SKIP_BOARDS || \
|
||||
(if [ -f "${{ env.HIL_JSON }}.skip" ]; then
|
||||
SKIP_BOARDS=$(cat "${{ env.HIL_JSON }}.skip")
|
||||
echo "Re-running with SKIP_BOARDS=$SKIP_BOARDS"
|
||||
python3 test/hil/hil_test.py ${{ env.HIL_JSON }} $SKIP_BOARDS
|
||||
else
|
||||
exit 1
|
||||
fi)
|
||||
|
||||
- name: Upload HIL report
|
||||
if: always() && github.event_name == 'pull_request'
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: hil-report-${{ matrix.display }}
|
||||
path: ${{ env.HIL_REPORT_DIR }}/hil_report.md
|
||||
if-no-files-found: ignore
|
||||
overwrite: true
|
||||
|
||||
# ---------------------------------------
|
||||
# Hardware in the loop (HIL)
|
||||
# self-hosted by HFP, build with IAR toolchain, for attached hardware checkout test/hil/hfp.json
|
||||
# Since IAR Token secret is not passed to forked PR, only build non-forked PR
|
||||
# ---------------------------------------
|
||||
hil-hfp-iar:
|
||||
needs: [ check-paths ]
|
||||
if: |
|
||||
needs.check-paths.outputs.code_changed == 'true' &&
|
||||
github.repository_owner == 'hathach' &&
|
||||
!(github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == true)
|
||||
runs-on: [ self-hosted, Linux, X64, hifiphile ]
|
||||
timeout-minutes: 30
|
||||
env:
|
||||
IAR_LMS_BEARER_TOKEN: ${{ secrets.IAR_LMS_BEARER_TOKEN }}
|
||||
PYTHONUNBUFFERED: '1'
|
||||
steps:
|
||||
- name: Clean workspace
|
||||
run: |
|
||||
echo "Cleaning up previous run"
|
||||
rm -rf "${{ github.workspace }}"
|
||||
mkdir -p "${{ github.workspace }}"
|
||||
|
||||
- name: Toolchain version
|
||||
run: |
|
||||
iccarm --version
|
||||
|
||||
- name: Checkout TinyUSB
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Get build boards
|
||||
run: |
|
||||
MATRIX_JSON=$(python test/hil/hil_ci_set_matrix.py test/hil/hfp.json)
|
||||
BUILD_ARGS=$(echo $MATRIX_JSON | jq -r '.["arm-gcc"] | join(" ")')
|
||||
echo "BUILD_ARGS=$BUILD_ARGS"
|
||||
echo "BUILD_ARGS=$BUILD_ARGS" >> $GITHUB_ENV
|
||||
|
||||
- name: Get Dependencies
|
||||
run: python3 tools/get_deps.py $BUILD_ARGS
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
# Each variant carries its own --build-name/--cflag, which are global to a
|
||||
# single build.py invocation — so build one matrix entry at a time rather
|
||||
# than joining them (joining would leak a variant's flags onto every board).
|
||||
readarray -t ENTRIES < <(python test/hil/hil_ci_set_matrix.py test/hil/hfp.json | jq -r '.["arm-gcc"][]')
|
||||
for entry in "${ENTRIES[@]}"; do
|
||||
echo "+ tools/build.py --toolchain iar $entry"
|
||||
python3 tools/build.py --toolchain iar $entry
|
||||
done
|
||||
|
||||
- name: Test on actual hardware (hardware in the loop)
|
||||
run: |
|
||||
python3 test/hil/hil_test.py hfp.json
|
||||
|
||||
- name: Upload HIL report
|
||||
if: always() && github.event_name == 'pull_request'
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: hil-report-hfp-iar
|
||||
path: hil_report.md
|
||||
if-no-files-found: ignore
|
||||
overwrite: true
|
||||
@@ -0,0 +1,111 @@
|
||||
name: Reusable build util
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
os:
|
||||
required: false
|
||||
type: string
|
||||
default: 'ubuntu-latest'
|
||||
build-system:
|
||||
required: true
|
||||
type: string
|
||||
toolchain:
|
||||
required: true
|
||||
type: string
|
||||
build-args:
|
||||
required: true
|
||||
type: string
|
||||
build-options:
|
||||
required: false
|
||||
default: ''
|
||||
type: string
|
||||
upload-artifacts:
|
||||
required: false
|
||||
default: false
|
||||
type: boolean
|
||||
upload-metrics:
|
||||
required: false
|
||||
default: false
|
||||
type: boolean
|
||||
upload-membrowse:
|
||||
required: false
|
||||
default: false
|
||||
type: boolean
|
||||
code-changed:
|
||||
required: false
|
||||
default: true
|
||||
type: boolean
|
||||
|
||||
jobs:
|
||||
family:
|
||||
runs-on: ${{ inputs.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
arg: ${{ fromJSON(inputs.build-args) }}
|
||||
steps:
|
||||
- name: Checkout TinyUSB
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: ${{ !inputs.upload-membrowse && 1 || 0 }}
|
||||
|
||||
- name: Setup Toolchain
|
||||
id: setup-toolchain
|
||||
uses: ./.github/actions/setup_toolchain
|
||||
with:
|
||||
toolchain: ${{ inputs.toolchain }}
|
||||
|
||||
- name: Get Dependencies
|
||||
uses: ./.github/actions/get_deps
|
||||
with:
|
||||
arg: ${{ matrix.arg }}
|
||||
|
||||
- name: Build
|
||||
if: ${{ inputs.code-changed }}
|
||||
env:
|
||||
IAR_LMS_BEARER_TOKEN: ${{ secrets.IAR_LMS_BEARER_TOKEN }}
|
||||
run: |
|
||||
if [ "${{ inputs.toolchain }}" == "esp-idf" ]; then
|
||||
docker run --rm -e MEMBROWSE_API_KEY="$MEMBROWSE_API_KEY" -e CI="$CI" -v $PWD:/project -w /project espressif/idf:tinyusb python tools/build.py --target all ${{ matrix.arg }}
|
||||
else
|
||||
BUILD_PY_ARGS="-s ${{ inputs.build-system }} ${{ steps.setup-toolchain.outputs.build_option }} ${{ inputs.build-options }} --target all"
|
||||
if [ "${{ inputs.upload-metrics }}" = "true" ]; then
|
||||
BUILD_PY_ARGS="$BUILD_PY_ARGS --target tinyusb_metrics"
|
||||
fi
|
||||
python tools/build.py $BUILD_PY_ARGS ${{ matrix.arg }}
|
||||
fi
|
||||
shell: bash
|
||||
|
||||
- name: Membrowse Upload
|
||||
if: inputs.toolchain != 'esp-idf' && inputs.upload-membrowse == true
|
||||
continue-on-error: true
|
||||
env:
|
||||
MEMBROWSE_API_KEY: ${{ secrets.MEMBROWSE_API_KEY }}
|
||||
run: |
|
||||
# if code-changed is false --> there is no elf -> membrowse target upload with --identical flag
|
||||
BUILD_PY_ARGS="-s ${{ inputs.build-system }} ${{ steps.setup-toolchain.outputs.build_option }} ${{ inputs.build-options }}"
|
||||
python tools/build.py $BUILD_PY_ARGS --target examples-membrowse-upload -j 1 ${{ matrix.arg }}
|
||||
shell: bash
|
||||
|
||||
- name: Upload Artifacts for Metrics
|
||||
if: inputs.upload-metrics == true && inputs.code-changed == true
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: metrics-${{ matrix.arg }}
|
||||
path: cmake-build/cmake-build-*/metrics.json
|
||||
|
||||
- name: Upload Artifacts for Hardware Testing
|
||||
if: inputs.upload-artifacts == true && inputs.code-changed == true
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: binaries-${{ matrix.arg }}
|
||||
path: |
|
||||
cmake-build/cmake-build-*/*/*/*.elf
|
||||
cmake-build/cmake-build-*/*/*/*.bin
|
||||
cmake-build/cmake-build-*/*/*/*.bin
|
||||
cmake-build/cmake-build-*/*/*/bootloader/bootloader.bin
|
||||
cmake-build/cmake-build-*/*/*/partition_table/partition-table.bin
|
||||
cmake-build/cmake-build-*/*/*/config.env
|
||||
cmake-build/cmake-build-*/*/*/flash_args
|
||||
cmake-build/hw/mcu/**/*.ld
|
||||
@@ -0,0 +1,110 @@
|
||||
#!/usr/bin/env python3
|
||||
import json
|
||||
|
||||
# toolchain, url
|
||||
toolchain_list = [
|
||||
"aarch64-gcc",
|
||||
"arm-clang",
|
||||
"arm-iar",
|
||||
"arm-gcc",
|
||||
"esp-idf",
|
||||
"ft9xx-gcc",
|
||||
"msp430-gcc",
|
||||
"riscv-gcc",
|
||||
"rx-gcc"
|
||||
]
|
||||
|
||||
# family: [supported toolchain]
|
||||
family_list = {
|
||||
"at32f402_405": ["arm-gcc"],
|
||||
"at32f403a_407": ["arm-gcc"],
|
||||
"at32f413": ["arm-gcc"],
|
||||
"at32f415": ["arm-gcc"],
|
||||
"at32f423": ["arm-gcc"],
|
||||
"at32f425": ["arm-gcc"],
|
||||
"at32f435_437": ["arm-gcc"],
|
||||
"at32f45x": ["arm-gcc"],
|
||||
"broadcom_32bit": ["arm-gcc"],
|
||||
"broadcom_64bit": ["aarch64-gcc"],
|
||||
"ch32f20x": ["arm-gcc"],
|
||||
"ch32v10x": ["riscv-gcc"],
|
||||
"ch32v20x": ["riscv-gcc"],
|
||||
"ch32v30x": ["riscv-gcc"],
|
||||
"ch583": ["riscv-gcc"],
|
||||
"da1469x": ["arm-gcc"],
|
||||
"fomu": ["riscv-gcc"],
|
||||
"ft9xx": ["ft9xx-gcc"],
|
||||
"gd32vf103": ["riscv-gcc"],
|
||||
"hpmicro": ["riscv-gcc"],
|
||||
"imxrt": ["arm-gcc", "arm-clang"],
|
||||
"kinetis_k": ["arm-gcc"],
|
||||
"kinetis_k32l": ["arm-gcc"],
|
||||
"kinetis_kl": ["arm-gcc"],
|
||||
"lpc11": ["arm-gcc", "arm-clang"],
|
||||
"lpc13": ["arm-gcc", "arm-clang"],
|
||||
"lpc15": ["arm-gcc", "arm-clang"],
|
||||
"lpc17": ["arm-gcc", "arm-clang"],
|
||||
"lpc18": ["arm-gcc", "arm-clang"],
|
||||
"lpc40": ["arm-gcc", "arm-clang"],
|
||||
"lpc43": ["arm-gcc", "arm-clang"],
|
||||
"lpc51": ["arm-gcc", "arm-clang"],
|
||||
"lpc54": ["arm-gcc", "arm-clang"],
|
||||
"lpc55": ["arm-gcc", "arm-clang"],
|
||||
"maxim": ["arm-gcc"],
|
||||
"mcx": ["arm-gcc"],
|
||||
"mm32": ["arm-gcc"],
|
||||
"msp430": ["msp430-gcc"],
|
||||
"msp432e4": ["arm-gcc"],
|
||||
"nrf": ["arm-gcc", "arm-clang"],
|
||||
"nuc100_120": ["arm-gcc"],
|
||||
"nuc121_125": ["arm-gcc"],
|
||||
"nuc126": ["arm-gcc"],
|
||||
"nuc505": ["arm-gcc"],
|
||||
"ra": ["arm-gcc"],
|
||||
"rp2040": ["arm-gcc"],
|
||||
"rw61x": ["arm-gcc"],
|
||||
"rx": ["rx-gcc"],
|
||||
"samd11": ["arm-gcc", "arm-clang"],
|
||||
"samd2x_l2x": ["arm-gcc", "arm-clang"],
|
||||
"samd5x_e5x": ["arm-gcc", "arm-clang"],
|
||||
"samg": ["arm-gcc", "arm-clang"],
|
||||
"stm32c0": ["arm-gcc", "arm-clang", "arm-iar"],
|
||||
"stm32c5": ["arm-gcc", "arm-clang", "arm-iar"],
|
||||
"stm32f0": ["arm-gcc", "arm-clang", "arm-iar"],
|
||||
"stm32f1": ["arm-gcc", "arm-clang", "arm-iar"],
|
||||
"stm32f2": ["arm-gcc", "arm-clang", "arm-iar"],
|
||||
"stm32f3": ["arm-gcc", "arm-clang", "arm-iar"],
|
||||
"stm32f4": ["arm-gcc", "arm-clang", "arm-iar"],
|
||||
"stm32f7": ["arm-gcc", "arm-clang", "arm-iar"],
|
||||
"stm32g0": ["arm-gcc", "arm-clang", "arm-iar"],
|
||||
"stm32g4": ["arm-gcc", "arm-clang", "arm-iar"],
|
||||
"stm32h5": ["arm-gcc", "arm-clang", "arm-iar"],
|
||||
"stm32h7": ["arm-gcc", "arm-clang", "arm-iar"],
|
||||
"stm32h7rs": ["arm-gcc", "arm-clang", "arm-iar"],
|
||||
"stm32l0": ["arm-gcc", "arm-clang", "arm-iar"],
|
||||
"stm32l4": ["arm-gcc", "arm-clang", "arm-iar"],
|
||||
"stm32n6": ["arm-gcc"],
|
||||
"stm32u0": ["arm-gcc", "arm-clang", "arm-iar"],
|
||||
"stm32u5": ["arm-gcc", "arm-clang", "arm-iar"],
|
||||
"stm32wb": ["arm-gcc", "arm-clang", "arm-iar"],
|
||||
"stm32wba": ["arm-gcc", "arm-clang", "arm-iar"],
|
||||
"tm4c": ["arm-gcc"],
|
||||
"xmc4000": ["arm-gcc"],
|
||||
# S3, P4 will be built by hil test
|
||||
# "-bespressif_s3_devkitm": ["esp-idf"],
|
||||
# "-bespressif_p4_function_ev": ["esp-idf"],
|
||||
}
|
||||
|
||||
|
||||
def set_matrix_json():
|
||||
matrix = {}
|
||||
for toolchain in toolchain_list:
|
||||
filtered_families = [family for family, supported_toolchain in family_list.items() if
|
||||
toolchain in supported_toolchain]
|
||||
matrix[toolchain] = filtered_families
|
||||
|
||||
print(json.dumps(matrix))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
set_matrix_json()
|
||||
@@ -0,0 +1,36 @@
|
||||
name: CIFuzz
|
||||
on:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
paths:
|
||||
- '**.c'
|
||||
- '**.cc'
|
||||
- '**.cpp'
|
||||
- '**.cxx'
|
||||
- '**.h'
|
||||
jobs:
|
||||
Fuzzing:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Build Fuzzers
|
||||
id: build
|
||||
uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master
|
||||
with:
|
||||
oss-fuzz-project-name: 'tinyusb'
|
||||
language: c++
|
||||
|
||||
- name: Run Fuzzers
|
||||
uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master
|
||||
with:
|
||||
oss-fuzz-project-name: 'tinyusb'
|
||||
language: c++
|
||||
fuzz-seconds: 400
|
||||
|
||||
- name: Upload Crash
|
||||
uses: actions/upload-artifact@v7
|
||||
if: failure() && steps.build.outcome == 'success'
|
||||
with:
|
||||
name: artifacts
|
||||
path: ./out/artifacts
|
||||
@@ -0,0 +1,67 @@
|
||||
name: Claude Code Review
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
# opened/reopened/ready_for_review -> first auto review
|
||||
# synchronize -> auto re-review on new pushes
|
||||
#
|
||||
# NOTE: pull_request (not _target) means fork PRs get a read-only GITHUB_TOKEN
|
||||
# and NO repository secrets (CLAUDE_CODE_OAUTH_TOKEN), so they cannot be
|
||||
# auto-reviewed. The job condition below skips them cleanly -> use @claude on
|
||||
# those. Same-repo branches (yours or write-access contributors) auto-review.
|
||||
types: [opened, synchronize, reopened, ready_for_review]
|
||||
|
||||
jobs:
|
||||
claude-review:
|
||||
# Skip drafts, and skip fork PRs (no secrets -> would only fail noisily)
|
||||
if: >
|
||||
github.event.pull_request.draft == false &&
|
||||
github.event.pull_request.head.repo.full_name == github.repository
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
issues: write # Claude posts the review comment via the issues API
|
||||
id-token: write
|
||||
actions: read # Required for Claude to read CI results on PRs
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Run Claude Code Review
|
||||
id: claude-review
|
||||
uses: anthropics/claude-code-action@v1
|
||||
with:
|
||||
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
|
||||
# Allow claude[bot]'s own pushes to be handled gracefully (skip) instead
|
||||
# of erroring out the workflow
|
||||
allowed_bots: 'claude'
|
||||
# Pairs with the actions: read permission so Claude can read CI results
|
||||
additional_permissions: |
|
||||
actions: read
|
||||
plugin_marketplaces: 'https://github.com/anthropics/claude-code.git'
|
||||
plugins: 'code-review@claude-code-plugins'
|
||||
# Post/update a single summary comment every run, so a clean review
|
||||
# ("no issues found") is still visible instead of posting nothing.
|
||||
use_sticky_comment: true
|
||||
# --comment makes the code-review command post its findings to the PR.
|
||||
# Without it the command only prints the review to the Actions log.
|
||||
prompt: '/code-review:code-review ${{ github.repository }}/pull/${{ github.event.pull_request.number }} --comment'
|
||||
# TEMPORARY: expose the full Claude transcript in the Actions log for
|
||||
# debugging. Revert to remove once done.
|
||||
show_full_output: true
|
||||
# The headless review was blocked on two tools: compound Bash pipelines
|
||||
# (sed/python/grep) and Write (it dropped a check_headings.py helper to
|
||||
# inspect the diff). Default permission mode gates both; Read/Grep/Glob/
|
||||
# Task already run un-prompted. Allowlist them. Safe here (unlike
|
||||
# claude.yml's fork-exposed @claude job): same-repo-only + contents:read
|
||||
# token, so any write is ephemeral and nothing can be pushed.
|
||||
claude_args: |
|
||||
--max-turns 50
|
||||
--model claude-opus-4-8
|
||||
--effort max
|
||||
--allowedTools Bash,Write
|
||||
# See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md
|
||||
@@ -0,0 +1,68 @@
|
||||
name: Claude Code
|
||||
|
||||
on:
|
||||
issue_comment:
|
||||
types: [created]
|
||||
pull_request_review_comment:
|
||||
types: [created]
|
||||
issues:
|
||||
# only "opened" — an issue's author_association gates the summon below;
|
||||
# "assigned" would gate on the issue author, not the assigner, so a
|
||||
# maintainer assigning an outsider's issue would be wrongly skipped.
|
||||
types: [opened]
|
||||
pull_request_review:
|
||||
types: [submitted]
|
||||
|
||||
jobs:
|
||||
claude:
|
||||
# Only trusted actors (repo owner/member/collaborator) may summon @claude, so the
|
||||
# write-scoped token and OAuth secret are never issued for an outside contributor's
|
||||
# comment on this public repo. Defense-in-depth on top of the action's own check.
|
||||
if: |
|
||||
(github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude') &&
|
||||
contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.comment.author_association)) ||
|
||||
(github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude') &&
|
||||
contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.comment.author_association)) ||
|
||||
(github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude') &&
|
||||
contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.review.author_association)) ||
|
||||
(github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude')) &&
|
||||
contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.issue.author_association))
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write # allow Claude to push commits/branches when asked
|
||||
pull-requests: write # allow Claude to comment on / update PRs
|
||||
issues: write # allow Claude to comment on / update issues
|
||||
id-token: write
|
||||
actions: read # Required for Claude to read CI results on PRs
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Run Claude Code
|
||||
id: claude
|
||||
uses: anthropics/claude-code-action@v1
|
||||
with:
|
||||
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
|
||||
|
||||
# This is an optional setting that allows Claude to read CI results on PRs
|
||||
additional_permissions: |
|
||||
actions: read
|
||||
|
||||
# Sign the bot's commits so they show as "Verified". The action commits
|
||||
# automatically — on a PR comment it pushes to that PR's branch; on an
|
||||
# issue comment it opens a new claude/* branch + PR with the fix.
|
||||
use_commit_signing: true
|
||||
|
||||
# No custom prompt: Claude performs the instructions in the @claude comment.
|
||||
|
||||
# Deliberately NO Bash in the tool allowlist. @claude can be summoned on a
|
||||
# fork PR (claude-code-review.yml even directs fork PRs here), and this job
|
||||
# holds the OAuth secret + a write token. Any build/interpreter command
|
||||
# (python -c, cmake/make custom targets, etc.) run against attacker-
|
||||
# controlled PR content is arbitrary code + network execution, so no
|
||||
# command allowlist can safely contain it. Claude still edits files and
|
||||
# the action commits/opens the PR; the resulting commit is verified by the
|
||||
# repo's CircleCI matrix. --max-turns gives room to investigate + fix.
|
||||
claude_args: '--max-turns 30'
|
||||
+182
@@ -0,0 +1,182 @@
|
||||
name: Labeler
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: [opened]
|
||||
pull_request_target:
|
||||
types: [opened, synchronize, reopened]
|
||||
discussion:
|
||||
types: [created]
|
||||
|
||||
jobs:
|
||||
label-priority:
|
||||
# Author-based priority labels: only on issue/PR/discussion creation, not on PR updates.
|
||||
if: github.event_name != 'pull_request_target' || github.event.action == 'opened'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
discussions: write
|
||||
steps:
|
||||
- name: Label New Issue, PR or Discussion
|
||||
uses: actions/github-script@v8
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
let labels = [];
|
||||
let username = '';
|
||||
let issueOrPrNumber = 0;
|
||||
let discussionNodeId = '';
|
||||
|
||||
if (context.eventName === 'issues') {
|
||||
username = context.payload.issue.user.login;
|
||||
issueOrPrNumber = context.payload.issue.number;
|
||||
} else if (context.eventName === 'pull_request_target') {
|
||||
username = context.payload.pull_request.user.login;
|
||||
issueOrPrNumber = context.payload.pull_request.number;
|
||||
} else if (context.eventName === 'discussion') {
|
||||
username = context.payload.discussion.user.login;
|
||||
discussionNodeId = context.payload.discussion.node_id;
|
||||
}
|
||||
|
||||
// Maintainer is an Adafruit member; skip the Adafruit perks for their own
|
||||
// issues/PRs and treat them as a plain contributor (Prio only).
|
||||
const isOwner = username.toLowerCase() === 'hathach';
|
||||
|
||||
// Check if an Adafruit member: Adafruit + Sponsor + top priority
|
||||
if (!isOwner) {
|
||||
try {
|
||||
const adafruitResponse = await github.rest.orgs.checkMembershipForUser({
|
||||
org: 'adafruit',
|
||||
username: username
|
||||
});
|
||||
|
||||
if (adafruitResponse.status === 204) {
|
||||
console.log('Adafruit Member');
|
||||
labels = ['Adafruit 🌸', 'Sponsor 💖', 'Prio Top 🚨'];
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('Not an Adafruit member');
|
||||
}
|
||||
}
|
||||
|
||||
// Check if a public GitHub Sponsor of the repo owner.
|
||||
// Word ($32) tier and up get triage priority; DWORD/QWORD ($128+) go to the top.
|
||||
// Private sponsorships are not visible to GITHUB_TOKEN, so only public sponsors are detected.
|
||||
if (labels.length === 0) {
|
||||
try {
|
||||
const result = await github.graphql(`
|
||||
query($sponsorable: String!, $sponsor: String!) {
|
||||
user(login: $sponsorable) {
|
||||
isSponsoredBy(accountLogin: $sponsor)
|
||||
sponsorshipsAsMaintainer(includePrivate: false, first: 100) {
|
||||
nodes {
|
||||
sponsorEntity {
|
||||
... on User { login }
|
||||
... on Organization { login }
|
||||
}
|
||||
tier { monthlyPriceInDollars }
|
||||
}
|
||||
}
|
||||
}
|
||||
}`, { sponsorable: context.repo.owner, sponsor: username });
|
||||
|
||||
const owner = result.user;
|
||||
if (owner && owner.isSponsoredBy) {
|
||||
let monthly = 0;
|
||||
const nodes = (owner.sponsorshipsAsMaintainer && owner.sponsorshipsAsMaintainer.nodes) || [];
|
||||
for (const node of nodes) {
|
||||
const login = node.sponsorEntity && node.sponsorEntity.login;
|
||||
if (login && login.toLowerCase() === username.toLowerCase()) {
|
||||
monthly = (node.tier && node.tier.monthlyPriceInDollars) || 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (monthly >= 128) {
|
||||
console.log('Sponsor (DWORD/QWORD tier)');
|
||||
labels = ['Sponsor 💖', 'Prio Top 🚨'];
|
||||
} else if (monthly >= 32) {
|
||||
console.log('Sponsor (Word tier)');
|
||||
labels = ['Sponsor 💖', 'Prio 📌'];
|
||||
} else {
|
||||
console.log('Sponsor (below Word tier or tier not visible)');
|
||||
labels = ['Sponsor 💖'];
|
||||
}
|
||||
} else {
|
||||
console.log('Not a public sponsor');
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('Sponsor lookup failed: ' + error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Check if a contributor: prioritized in triage queue
|
||||
if (labels.length === 0) {
|
||||
try {
|
||||
const collaboratorResponse = await github.rest.repos.checkCollaborator({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
username: username
|
||||
});
|
||||
|
||||
if (collaboratorResponse.status === 204) {
|
||||
console.log('Contributor');
|
||||
labels = ['Prio 📌'];
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('Not a contributor');
|
||||
}
|
||||
}
|
||||
|
||||
if (labels.length !== 0) {
|
||||
if (context.eventName === 'discussion') {
|
||||
// Discussions are not covered by the REST issues API; resolve the label
|
||||
// names to node IDs and attach them with the GraphQL labelable mutation.
|
||||
const labelIds = [];
|
||||
for (const name of labels) {
|
||||
const res = await github.graphql(`
|
||||
query($owner: String!, $repo: String!, $name: String!) {
|
||||
repository(owner: $owner, name: $repo) {
|
||||
label(name: $name) { id }
|
||||
}
|
||||
}`, { owner: context.repo.owner, repo: context.repo.repo, name: name });
|
||||
if (res.repository.label) {
|
||||
labelIds.push(res.repository.label.id);
|
||||
}
|
||||
}
|
||||
if (labelIds.length !== 0) {
|
||||
await github.graphql(`
|
||||
mutation($labelableId: ID!, $labelIds: [ID!]!) {
|
||||
addLabelsToLabelable(input: { labelableId: $labelableId, labelIds: $labelIds }) {
|
||||
clientMutationId
|
||||
}
|
||||
}`, { labelableId: discussionNodeId, labelIds: labelIds });
|
||||
}
|
||||
} else {
|
||||
await github.rest.issues.addLabels({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: issueOrPrNumber,
|
||||
labels: labels
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
# Path-based Port labels: attach "Port <ip>" when a PR touches the matching
|
||||
# dcd/hcd driver under src/portable/. Mapping lives in .github/labeler.yml.
|
||||
label-port:
|
||||
if: github.event_name == 'pull_request_target'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
issues: write # allow auto-creating a Port label that doesn't exist yet
|
||||
steps:
|
||||
- uses: actions/labeler@v5
|
||||
with:
|
||||
configuration-path: .github/labeler.yml
|
||||
# sync-labels so a Port label is removed once a PR no longer touches
|
||||
# that driver (job reruns on synchronize). Only labels listed in
|
||||
# labeler.yml are managed, so author-based Prio/Sponsor labels are untouched.
|
||||
sync-labels: true
|
||||
@@ -0,0 +1,33 @@
|
||||
name: Membrowse Comment
|
||||
|
||||
on:
|
||||
workflow_run:
|
||||
workflows: ["Build"]
|
||||
types:
|
||||
- completed
|
||||
|
||||
jobs:
|
||||
post-comment:
|
||||
runs-on: ubuntu-latest
|
||||
# Run the comment job even if some of the builds fail
|
||||
if: >
|
||||
github.event.workflow_run.event == 'pull_request' &&
|
||||
github.event.workflow_run.conclusion != 'cancelled'
|
||||
permissions:
|
||||
contents: read
|
||||
actions: read
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Post Membrowse PR comment
|
||||
if: ${{ env.MEMBROWSE_API_KEY != '' }}
|
||||
uses: membrowse/membrowse-action/comment-action@v1
|
||||
with:
|
||||
api_key: ${{ secrets.MEMBROWSE_API_KEY }}
|
||||
commit: ${{ github.event.workflow_run.head_sha }}
|
||||
comment_template: .github/membrowse_pr_message.j2
|
||||
env:
|
||||
MEMBROWSE_API_KEY: ${{ secrets.MEMBROWSE_API_KEY }}
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -0,0 +1,62 @@
|
||||
name: Onboard to Membrowse
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
num_commits:
|
||||
description: 'Number of commits to process'
|
||||
required: true
|
||||
default: '10'
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
load-targets:
|
||||
runs-on: ubuntu-22.04
|
||||
outputs:
|
||||
targets: ${{ steps.load.outputs.targets }}
|
||||
toolchains: ${{ steps.load.outputs.toolchains }}
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Load target matrix
|
||||
id: load
|
||||
run: |
|
||||
echo "targets=$(jq -c '.targets' .github/membrowse-targets.json)" >> $GITHUB_OUTPUT
|
||||
echo "toolchains=$(jq -c '.toolchains' .github/membrowse-targets.json)" >> $GITHUB_OUTPUT
|
||||
|
||||
onboard:
|
||||
needs: load-targets
|
||||
runs-on: ubuntu-22.04
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include: ${{ fromJson(needs.load-targets.outputs.targets) }}
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
submodules: recursive
|
||||
|
||||
- name: Install packages
|
||||
run: |
|
||||
${{ fromJson(needs.load-targets.outputs.toolchains)[matrix.toolchain].setup_cmd }} && python3 tools/get_deps.py ${{ matrix.get_deps || matrix.port }}
|
||||
|
||||
- name: Setup ccache
|
||||
uses: hendrikmuhs/ccache-action@v1
|
||||
with:
|
||||
key: ${{ matrix.port }}-${{ matrix.board }}
|
||||
|
||||
- name: Run Membrowse Onboard Action
|
||||
uses: membrowse/membrowse-action/onboard-action@v1
|
||||
with:
|
||||
target_name: ${{ matrix.port }}-${{ matrix.board }}-${{ matrix.example }}
|
||||
num_commits: ${{ github.event.inputs.num_commits }}
|
||||
build_script: python3 tools/build.py -s cmake -b ${{ matrix.board }}
|
||||
elf: cmake-build/cmake-build-${{ matrix.board }}/device/${{ matrix.example }}/${{ matrix.example }}.elf
|
||||
ld: ${{ matrix.ld }}
|
||||
linker_vars: ${{ matrix.linker_vars || '' }}
|
||||
api_key: ${{ secrets.MEMBROWSE_API_KEY }}
|
||||
api_url: ${{ vars.MEMBROWSE_API_URL }}
|
||||
@@ -0,0 +1,131 @@
|
||||
name: PR Comment
|
||||
|
||||
on:
|
||||
workflow_run:
|
||||
workflows: ["Build"]
|
||||
types:
|
||||
- completed
|
||||
|
||||
jobs:
|
||||
# Resolve the PR number from trusted workflow_run metadata, NOT from build artifacts: a forked PR
|
||||
# controls its own Build run and could plant any number, which the privileged jobs below would
|
||||
# then post to. Same-repo PRs populate workflow_run.pull_requests; for forks it is empty, so look
|
||||
# the PR up by the trusted head SHA.
|
||||
pr_number:
|
||||
if: github.event.workflow_run.event == 'pull_request'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: read
|
||||
outputs:
|
||||
number: ${{ steps.resolve.outputs.number }}
|
||||
steps:
|
||||
- name: Resolve PR number
|
||||
id: resolve
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
REPO: ${{ github.repository }}
|
||||
HEAD_SHA: ${{ github.event.workflow_run.head_sha }}
|
||||
HEAD_BRANCH: ${{ github.event.workflow_run.head_branch }}
|
||||
HEAD_REPO: ${{ github.event.workflow_run.head_repository.full_name }}
|
||||
PRS_JSON: ${{ toJSON(github.event.workflow_run.pull_requests) }}
|
||||
run: |
|
||||
# Every lookup is best-effort: on any miss the number stays empty and the comment jobs
|
||||
# below simply skip (never a failed check).
|
||||
# Same-repo PRs: workflow_run.pull_requests is populated.
|
||||
num=$(printf '%s' "$PRS_JSON" | jq -r '.[0].number // empty')
|
||||
# Fork PRs: pull_requests is empty. Find the open PR by its trusted head ref and confirm
|
||||
# its head SHA matches the built commit.
|
||||
if [ -z "$num" ] && [ -n "$HEAD_BRANCH" ] && [ -n "$HEAD_REPO" ]; then
|
||||
num=$(gh api --method GET "repos/$REPO/pulls" \
|
||||
-f state=open -f head="${HEAD_REPO%%/*}:$HEAD_BRANCH" \
|
||||
--jq '[.[] | select(.head.sha == env.HEAD_SHA)][0].number // empty' 2>/dev/null || true)
|
||||
fi
|
||||
echo "number=$num" >> "$GITHUB_OUTPUT"
|
||||
|
||||
metrics-comment:
|
||||
needs: pr_number
|
||||
if: >
|
||||
github.event.workflow_run.conclusion == 'success' &&
|
||||
needs.pr_number.outputs.number != ''
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Download Artifacts
|
||||
uses: actions/download-artifact@v5
|
||||
with:
|
||||
run-id: ${{ github.event.workflow_run.id }}
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
name: metrics-comment
|
||||
# Best-effort: docs-only PRs skip code-metrics, so the artifact may be absent.
|
||||
continue-on-error: true
|
||||
|
||||
- name: Post Code Metrics as PR Comment
|
||||
if: hashFiles('metrics_compare.md') != ''
|
||||
uses: marocchino/sticky-pull-request-comment@v2
|
||||
with:
|
||||
header: code-metrics
|
||||
path: metrics_compare.md
|
||||
number: ${{ needs.pr_number.outputs.number }}
|
||||
|
||||
# ---------------------------------------
|
||||
# Combine the rigs' HIL reports into one sticky PR comment (one table per rig).
|
||||
# Runs here (workflow_run / base-repo context) rather than in build.yml so it also works on
|
||||
# forked PRs, whose build-side GITHUB_TOKEN is read-only and cannot post comments. Posts even
|
||||
# on build/HIL failure (when the report matters most); skips only on cancellation.
|
||||
# ---------------------------------------
|
||||
hil-comment:
|
||||
needs: pr_number
|
||||
if: >
|
||||
github.event.workflow_run.conclusion != 'cancelled' &&
|
||||
needs.pr_number.outputs.number != ''
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Download HIL reports
|
||||
uses: actions/download-artifact@v5
|
||||
with:
|
||||
run-id: ${{ github.event.workflow_run.id }}
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
pattern: hil-report-*
|
||||
path: hil-reports
|
||||
continue-on-error: true
|
||||
|
||||
- name: Combine rig reports (one table per rig)
|
||||
id: combine
|
||||
run: |
|
||||
shopt -s nullglob
|
||||
dirs=(hil-reports/hil-report-*)
|
||||
if [ ${#dirs[@]} -eq 0 ]; then
|
||||
echo "No HIL reports found"
|
||||
exit 0
|
||||
fi
|
||||
{
|
||||
echo "## Hardware-in-the-loop (HIL) Test Report"
|
||||
echo
|
||||
for d in "${dirs[@]}"; do
|
||||
[ -d "$d" ] || continue
|
||||
echo "### ${d#hil-reports/hil-report-}"
|
||||
echo
|
||||
cat "$d/hil_report.md" 2>/dev/null || echo "_no report produced_"
|
||||
echo
|
||||
done
|
||||
} > hil_combined.md
|
||||
# Fork PRs can influence report content and this job posts in base-repo context, so
|
||||
# neutralize @-mentions (insert a zero-width space) to prevent notification abuse.
|
||||
zwsp=$(printf '\342\200\213')
|
||||
sed -i -E "s/@([A-Za-z0-9_-])/@${zwsp}\1/g" hil_combined.md
|
||||
cat hil_combined.md
|
||||
echo "found=true" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Post HIL report as sticky PR comment
|
||||
if: steps.combine.outputs.found == 'true'
|
||||
uses: marocchino/sticky-pull-request-comment@v2
|
||||
with:
|
||||
header: hil-report
|
||||
path: hil_combined.md
|
||||
number: ${{ needs.pr_number.outputs.number }}
|
||||
@@ -0,0 +1,45 @@
|
||||
name: pre-commit
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
pre-commit:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Setup Ruby
|
||||
uses: ruby/setup-ruby@v1
|
||||
with:
|
||||
ruby-version: '3.0'
|
||||
|
||||
- name: Checkout TinyUSB
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Get Dependencies
|
||||
run: |
|
||||
gem install ceedling
|
||||
#cd test/unit-test
|
||||
#ceedling test:all
|
||||
|
||||
- name: Run pre-commit
|
||||
uses: pre-commit/action@v3.0.1
|
||||
|
||||
- name: Build Fuzzer
|
||||
run: |
|
||||
sudo apt install libc++-dev libc++abi-dev
|
||||
clang --version
|
||||
export CC=clang
|
||||
export CXX=clang++
|
||||
fuzz_harness=$(ls -d test/fuzz/device/*/)
|
||||
for h in $fuzz_harness
|
||||
do
|
||||
make -C $h get-deps
|
||||
make -C $h all
|
||||
done
|
||||
@@ -0,0 +1,242 @@
|
||||
name: Static Analysis
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches: [ master ]
|
||||
paths:
|
||||
- 'src/**'
|
||||
- 'examples/**'
|
||||
- 'hw/bsp/**'
|
||||
- '.github/workflows/static_analysis.yml'
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
paths:
|
||||
- 'src/**'
|
||||
- 'examples/**'
|
||||
- 'hw/bsp/**'
|
||||
- '.github/workflows/static_analysis.yml'
|
||||
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
# pull-requests: write
|
||||
# checks: write
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
CodeQL:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
board:
|
||||
- 'metro_m4_express'
|
||||
steps:
|
||||
- name: Checkout TinyUSB
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Get Dependencies
|
||||
uses: ./.github/actions/get_deps
|
||||
with:
|
||||
arg: -b${{ matrix.board }}
|
||||
|
||||
- name: Setup Toolchain
|
||||
uses: ./.github/actions/setup_toolchain
|
||||
with:
|
||||
toolchain: 'arm-gcc'
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v4
|
||||
with:
|
||||
languages: 'c-cpp'
|
||||
queries: security-and-quality
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
mkdir -p build
|
||||
cmake examples -B build -G Ninja -DBOARD=${{ matrix.board }} -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_BUILD_TYPE=MinSizeRel
|
||||
cmake --build build
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v4
|
||||
with:
|
||||
category: CodeQL
|
||||
upload: false
|
||||
id: analyze
|
||||
|
||||
- name: Filter SARIF report
|
||||
uses: advanced-security/filter-sarif@v1
|
||||
with:
|
||||
patterns: |
|
||||
-hw/mcu/**
|
||||
-lib/**
|
||||
input: ${{ steps.analyze.outputs.sarif-output }}/cpp.sarif
|
||||
output: ${{ steps.analyze.outputs.sarif-output }}/cpp.sarif
|
||||
|
||||
- name: Upload SARIF
|
||||
uses: github/codeql-action/upload-sarif@v4
|
||||
with:
|
||||
sarif_file: ${{ steps.analyze.outputs.sarif-output }}
|
||||
category: CodeQL
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: codeql-${{ matrix.board }}
|
||||
path: ${{ steps.analyze.outputs.sarif-output }}
|
||||
|
||||
PVS-Studio:
|
||||
# Only run on non-forked PR since secrets token is required
|
||||
if: github.repository_owner == 'hathach' && github.event.pull_request.head.repo.fork == false
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
board:
|
||||
- 'raspberry_pi_pico'
|
||||
steps:
|
||||
- name: Checkout TinyUSB
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Get Dependencies
|
||||
uses: ./.github/actions/get_deps
|
||||
with:
|
||||
arg: -b${{ matrix.board }}
|
||||
|
||||
- name: Setup Toolchain
|
||||
uses: ./.github/actions/setup_toolchain
|
||||
with:
|
||||
toolchain: 'arm-gcc'
|
||||
|
||||
- name: Install Tools
|
||||
run: |
|
||||
wget -q -O - https://files.pvs-studio.com/etc/pubkey.txt | sudo apt-key add -
|
||||
sudo wget -O /etc/apt/sources.list.d/viva64.list https://files.pvs-studio.com/etc/viva64.list
|
||||
sudo apt update
|
||||
sudo apt install pvs-studio
|
||||
pvs-studio-analyzer credentials ${{ secrets.PVS_STUDIO_CREDENTIALS }}
|
||||
pvs-studio-analyzer --version
|
||||
|
||||
- name: Analyze
|
||||
run: |
|
||||
mkdir -p build
|
||||
cmake examples -B build -G Ninja -DBOARD=${{ matrix.board }} -DCMAKE_BUILD_TYPE=MinSizeRel
|
||||
cmake --build build
|
||||
pvs-studio-analyzer analyze -f build/compile_commands.json -R .PVS-Studio/.pvsconfig -j4 --security-related-issues --misra-cpp-version 2008 --misra-c-version 2023 --use-old-parser -e lib/ -e hw/mcu/ -e */iar/cxarm/ -e pico-sdk/
|
||||
plog-converter -t sarif -o pvs-studio-${{ matrix.board }}.sarif PVS-Studio.log
|
||||
|
||||
- name: Upload SARIF
|
||||
uses: github/codeql-action/upload-sarif@v4
|
||||
with:
|
||||
sarif_file: pvs-studio-${{ matrix.board }}.sarif
|
||||
category: PVS-Studio
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: pvs-studio-${{ matrix.board }}
|
||||
path: pvs-studio-${{ matrix.board }}.sarif
|
||||
|
||||
SonarQube:
|
||||
# Only run on non-forked PR since secrets token is required
|
||||
if: github.repository_owner == 'hathach' && github.event.pull_request.head.repo.fork == false
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
BUILD_WRAPPER_OUT_DIR: build_wrapper_output_directory
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
board:
|
||||
- 'stm32h743eval'
|
||||
steps:
|
||||
- name: Checkout TinyUSB
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
|
||||
|
||||
- name: Get Dependencies
|
||||
uses: ./.github/actions/get_deps
|
||||
with:
|
||||
arg: -b${{ matrix.board }}
|
||||
|
||||
- name: Setup Toolchain
|
||||
uses: ./.github/actions/setup_toolchain
|
||||
with:
|
||||
toolchain: 'arm-gcc'
|
||||
|
||||
- name: Install Build Wrapper
|
||||
uses: SonarSource/sonarqube-scan-action/install-build-wrapper@v6
|
||||
|
||||
- name: Run Build Wrapper
|
||||
run: |
|
||||
cmake examples -B build -G Ninja -DBOARD=${{ matrix.board }} -DCMAKE_BUILD_TYPE=MinSizeRel
|
||||
build-wrapper-linux-x86-64 --out-dir ${{ env.BUILD_WRAPPER_OUT_DIR }} cmake --build build/
|
||||
|
||||
- name: SonarQube Scan
|
||||
uses: SonarSource/sonarqube-scan-action@v6
|
||||
env:
|
||||
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
||||
with:
|
||||
# Consult https://docs.sonarsource.com/sonarqube-server/latest/analyzing-source-code/scanners/sonarscanner/ for more information and options
|
||||
args: >
|
||||
--define sonar.cfamily.compile-commands=${{ env.BUILD_WRAPPER_OUT_DIR }}/compile_commands.json
|
||||
|
||||
IAR-CStat:
|
||||
# Only run on non-forked PR since secrets token is required
|
||||
#if: github.repository_owner == 'hathach' && github.event.pull_request.head.repo.fork == false
|
||||
if: false
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
board:
|
||||
- 'b_g474e_dpow1'
|
||||
steps:
|
||||
- name: Checkout TinyUSB
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Get Dependencies
|
||||
uses: ./.github/actions/get_deps
|
||||
with:
|
||||
arg: -b${{ matrix.board }}
|
||||
|
||||
- name: Setup Toolchain
|
||||
uses: ./.github/actions/setup_toolchain
|
||||
with:
|
||||
toolchain: 'arm-iar'
|
||||
|
||||
- name: Install CMake 4.2
|
||||
run: |
|
||||
# IAR CSTAT requires CMake >= 4.1
|
||||
wget -q https://github.com/Kitware/CMake/releases/download/v4.2.0-rc1/cmake-4.2.0-rc1-linux-x86_64.tar.gz
|
||||
tar -xzf cmake-4.2.0-rc1-linux-x86_64.tar.gz
|
||||
echo "${{ github.workspace }}/cmake-4.2.0-rc1-linux-x86_64/bin" >> $GITHUB_PATH
|
||||
|
||||
- name: Build and run IAR C-STAT Analysis
|
||||
env:
|
||||
IAR_LMS_BEARER_TOKEN: ${{ secrets.IAR_LMS_BEARER_TOKEN }}
|
||||
run: |
|
||||
# CMake run post build to generate C-STAT SARIF report
|
||||
cmake --version
|
||||
mkdir -p build
|
||||
cmake examples/device/cdc_msc -B build -G Ninja -DBOARD=${{ matrix.board }} -DTOOLCHAIN=iar -DIAR_CSTAT=1 -DCMAKE_BUILD_TYPE=MinSizeRel
|
||||
cmake --build build
|
||||
# Merge sarif files for codeql upload
|
||||
npm i -g @microsoft/sarif-multitool
|
||||
npx @microsoft/sarif-multitool merge --merge-runs --output-file iar-cstat-${{ matrix.board }}.sarif build/cstat_sarif/*.sarif
|
||||
|
||||
- name: Upload SARIF
|
||||
uses: github/codeql-action/upload-sarif@v4
|
||||
with:
|
||||
sarif_file: iar-cstat-${{ matrix.board }}.sarif
|
||||
category: IAR-CStat
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: iar-cstat-${{ matrix.board }}
|
||||
path: iar-cstat-${{ matrix.board }}.sarif
|
||||
@@ -0,0 +1,60 @@
|
||||
name: Trigger Repos
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches: master
|
||||
release:
|
||||
types:
|
||||
- created
|
||||
|
||||
jobs:
|
||||
trigger-mynewt:
|
||||
if: github.repository == 'hathach/tinyusb'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Trigger mynewt-tinyusb-example
|
||||
shell: bash
|
||||
run: |
|
||||
curl -X POST -H "Authorization: token ${{ secrets.API_TOKEN_GITHUB }}" -H "Accept: application/vnd.github.everest-preview+json" -H "Content-Type: application/json" --data '{"event_type": "rebuild"}' https://api.github.com/repos/hathach/mynewt-tinyusb-example/dispatches
|
||||
|
||||
mirror-tinyusb-src:
|
||||
if: github.repository == 'hathach/tinyusb'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Push to tinyusb_src
|
||||
run: |
|
||||
# clone tinyusb_src with PAT
|
||||
git config --global user.email "thach@tinyusb.org"
|
||||
git config --global user.name "hathach"
|
||||
git clone --depth 1 --single-branch --branch main "https://${{ secrets.API_TOKEN_GITHUB }}@github.com/hathach/tinyusb_src.git" tinyusb_src
|
||||
|
||||
# Remove all files
|
||||
rm -rf tinyusb_src/*
|
||||
|
||||
# Copy src and other files
|
||||
cp -r src tinyusb_src/
|
||||
cp LICENSE tinyusb_src/
|
||||
cd tinyusb_src
|
||||
|
||||
# Commit if there is changes
|
||||
if [ -n "$(git status --porcelain)" ]; then
|
||||
git add .
|
||||
git commit --message "Update from https://github.com/$GITHUB_REPOSITORY/commit/$GITHUB_SHA"
|
||||
git push
|
||||
fi
|
||||
|
||||
- name: Create tinyusb_src Release
|
||||
if: ${{ github.event_name == 'release' }}
|
||||
run: |
|
||||
# Push tag
|
||||
cd tinyusb_src
|
||||
git tag ${{ github.event.release.tag_name }}
|
||||
git push origin ${{ github.event.release.tag_name }}
|
||||
|
||||
# Send POST reqwuest to release https://docs.github.com/en/rest/reference/repos#create-a-release
|
||||
bb="For release note, please checkout https://github.com/hathach/tinyusb/releases/tag/${{ github.event.release.tag_name }}"
|
||||
curl -X POST -H "Authorization: token ${{ secrets.API_TOKEN_GITHUB }}" -H "Accept: application/vnd.github.v3+json" --data '{"tag_name": "${{ github.event.release.tag_name }}", "name": "${{ github.event.release.name }}", "body": "$bb", "draft": ${{ github.event.release.draft }}, "prerelease": ${{ github.event.release.prerelease }}}' https://api.github.com/repos/hathach/tinyusb_src/releases
|
||||
Reference in new issue
Block a user