summaryrefslogtreecommitdiff
path: root/.github/workflows
diff options
context:
space:
mode:
authorMichael Rogov Papernov <[email protected]>2026-01-10 17:58:41 +0000
committerMichael Rogov Papernov <[email protected]>2026-01-16 21:49:16 +0000
commit5ad42064ea544080d47c893dafb1b41447ab9f32 (patch)
treeae3c7f5ee5919a7ffa92658fb14e8cda26e01695 /.github/workflows
parent71ed9d1498e04f99f69fc8c0336ca41ec7a576af (diff)
Integrate MemBrowse
Diffstat (limited to '.github/workflows')
-rw-r--r--.github/workflows/build.yml137
-rw-r--r--.github/workflows/build_util.yml14
-rw-r--r--.github/workflows/membrowse-comment.yml44
-rw-r--r--.github/workflows/membrowse-onboard.yml58
-rw-r--r--.github/workflows/membrowse-report.yml101
5 files changed, 324 insertions, 30 deletions
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index bb8c6d65d..dc5b1975e 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -3,30 +3,8 @@ name: Build
on:
workflow_dispatch:
push:
- paths:
- - 'src/**'
- - 'examples/**'
- - 'lib/**'
- - 'hw/**'
- - 'tools/build.py'
- - 'tools/get_deps.py'
- - '.github/actions/**'
- - '.github/workflows/build.yml'
- - '.github/workflows/build_util.yml'
- - '.github/workflows/ci_set_matrix.py'
+ branches: [master]
pull_request:
- paths:
- - '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'
release:
types: [ published ]
concurrency:
@@ -37,7 +15,44 @@ env:
HIL_JSON: test/hil/tinyusb.json
jobs:
+ # Check if code paths changed (skip builds if doc-only)
+ check-paths:
+ if: github.event_name == 'pull_request' || github.event_name == 'push'
+ 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@v3
+ 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:
+ needs: [check-paths]
+ if: |
+ always() && (
+ github.event_name == 'release' ||
+ github.event_name == 'workflow_dispatch' ||
+ needs.check-paths.outputs.code_changed == 'true'
+ )
runs-on: ubuntu-latest
outputs:
json: ${{ steps.set-matrix-json.outputs.matrix }}
@@ -82,6 +97,7 @@ jobs:
build-args: ${{ toJSON(fromJSON(needs.set-matrix.outputs.json)[matrix.toolchain]) }}
build-options: '--one-first'
upload-metrics: true
+ upload-artifacts: true
code-metrics:
needs: cmake
@@ -181,7 +197,8 @@ jobs:
# Build Make/CMake on Windows/MacOS
# ---------------------------------------
build-os:
- if: github.event_name == 'pull_request'
+ needs: [check-paths]
+ if: needs.check-paths.outputs.code_changed == 'true'
uses: ./.github/workflows/build_util.yml
strategy:
fail-fast: false
@@ -198,7 +215,8 @@ jobs:
# Zephyr
# ---------------------------------------
zephyr:
- if: github.event_name == 'push'
+ needs: [check-paths]
+ if: needs.check-paths.outputs.code_changed == 'true'
runs-on: ubuntu-latest
steps:
- name: Checkout TinyUSB
@@ -220,10 +238,10 @@ jobs:
# Run on PR only (hil-tinyusb), hil-hfp only run on non-forked PR
# ---------------------------------------
hil-build:
+ needs: [check-paths, set-matrix]
if: |
github.repository_owner == 'hathach' &&
- (github.event_name == 'pull_request' || github.event_name == 'workflow_dispatch')
- needs: set-matrix
+ (github.event_name == 'workflow_dispatch' || needs.check-paths.outputs.code_changed == 'true')
uses: ./.github/workflows/build_util.yml
strategy:
fail-fast: false
@@ -242,8 +260,10 @@ jobs:
# self-hosted on local VM, for attached hardware checkout HIL_JSON
# ---------------------------------------
hil-tinyusb:
- if: github.repository_owner == 'hathach' && github.event_name != 'push'
- needs: hil-build
+ needs: [check-paths, hil-build]
+ if: |
+ github.repository_owner == 'hathach' &&
+ (github.event_name == 'release' || github.event_name == 'workflow_dispatch' || needs.check-paths.outputs.code_changed == 'true')
runs-on: [ self-hosted, X64, hathach, hardware-in-the-loop ]
steps:
- name: Get Skip Boards from previous run
@@ -283,10 +303,11 @@ jobs:
# Since IAR Token secret is not passed to forked PR, only build non-forked PR
# ---------------------------------------
hil-hfp:
+ needs: [check-paths]
if: |
github.repository_owner == 'hathach' &&
github.event.pull_request.head.repo.fork == false &&
- github.event_name != 'push'
+ (github.event_name == 'release' || github.event_name == 'workflow_dispatch' || needs.check-paths.outputs.code_changed == 'true')
runs-on: [ self-hosted, Linux, X64, hifiphile ]
env:
IAR_LMS_BEARER_TOKEN: ${{ secrets.IAR_LMS_BEARER_TOKEN }}
@@ -319,3 +340,59 @@ jobs:
- name: Test on actual hardware (hardware in the loop)
run: python3 test/hil/hil_test.py hfp.json
+
+ # ---------------------------------------
+ # Membrowse Memory Analysis
+ # Push: always runs (uses identical for doc-only to maintain commit chain)
+ # PR: only runs if code changed (doc-only PRs skip entirely)
+ # ---------------------------------------
+ membrowse:
+ needs: [check-paths, cmake]
+ if: |
+ always() && !cancelled() && (
+ github.event_name == 'push' ||
+ github.event_name == 'release' ||
+ github.event_name == 'workflow_dispatch' ||
+ (github.event_name == 'pull_request' && needs.check-paths.outputs.code_changed == 'true')
+ )
+ permissions:
+ contents: read
+ actions: read
+ uses: ./.github/workflows/membrowse-report.yml
+ with:
+ code_changed: ${{ needs.check-paths.outputs.code_changed == 'true' || github.event_name == 'release' || github.event_name == 'workflow_dispatch' }}
+ secrets: inherit
+
+ membrowse-comment:
+ needs: [check-paths, membrowse]
+ if: >
+ always() &&
+ github.event_name == 'pull_request' &&
+ needs.check-paths.outputs.code_changed == 'true'
+ runs-on: ubuntu-latest
+ permissions:
+ contents: read
+ actions: read
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v6
+
+ - name: Download report artifacts
+ id: download
+ uses: actions/download-artifact@v5
+ with:
+ pattern: membrowse-report-*
+ path: reports
+ merge-multiple: true
+ continue-on-error: true
+
+ - name: Save PR number
+ if: steps.download.outcome == 'success'
+ run: echo ${{ github.event.number }} > reports/pr_number.txt
+
+ - name: Upload Membrowse Comment Artifact
+ if: steps.download.outcome == 'success'
+ uses: actions/upload-artifact@v5
+ with:
+ name: membrowse-comment
+ path: reports/
diff --git a/.github/workflows/build_util.yml b/.github/workflows/build_util.yml
index 540ee8b47..e0fef7ce4 100644
--- a/.github/workflows/build_util.yml
+++ b/.github/workflows/build_util.yml
@@ -73,6 +73,19 @@ jobs:
name: metrics-${{ matrix.arg }}
path: cmake-build/cmake-build-*/metrics.json
+ - name: Copy linker scripts for artifacts
+ if: ${{ inputs.upload-artifacts }}
+ run: |
+ for dir in cmake-build/cmake-build-*; do
+ board=$(basename "$dir" | sed 's/cmake-build-//')
+ ld_path=$(jq -r --arg b "$board" '.[] | select(.board == $b) | .ld // empty' .github/membrowse-targets.json)
+ if [ -n "$ld_path" ] && [ -f "$ld_path" ]; then
+ mkdir -p "cmake-build/$(dirname "$ld_path")"
+ cp "$ld_path" "cmake-build/$ld_path"
+ fi
+ done
+ shell: bash
+
- name: Upload Artifacts for Hardware Testing
if: ${{ inputs.upload-artifacts }}
uses: actions/upload-artifact@v5
@@ -86,3 +99,4 @@ jobs:
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
diff --git a/.github/workflows/membrowse-comment.yml b/.github/workflows/membrowse-comment.yml
new file mode 100644
index 000000000..a3db37360
--- /dev/null
+++ b/.github/workflows/membrowse-comment.yml
@@ -0,0 +1,44 @@
+name: Membrowse Comment
+
+on:
+ workflow_run:
+ workflows: ["Build"]
+ types:
+ - completed
+
+jobs:
+ post-comment:
+ runs-on: ubuntu-latest
+ if: >
+ github.event.workflow_run.event == 'pull_request' &&
+ github.event.workflow_run.conclusion == 'success'
+ permissions:
+ actions: read
+ pull-requests: write
+ steps:
+ - name: Download Artifacts
+ id: download
+ uses: actions/download-artifact@v5
+ with:
+ run-id: ${{ github.event.workflow_run.id }}
+ github-token: ${{ secrets.GITHUB_TOKEN }}
+ name: membrowse-comment
+ path: reports
+ continue-on-error: true
+
+ - name: Read PR Number
+ if: steps.download.outcome == 'success'
+ id: pr_number
+ run: |
+ if [ -f reports/pr_number.txt ]; then
+ echo "number=$(cat reports/pr_number.txt)" >> $GITHUB_OUTPUT
+ fi
+
+ - name: Post Membrowse PR comment
+ if: steps.download.outcome == 'success' && steps.pr_number.outputs.number != ''
+ uses: membrowse/membrowse-action/comment-action@v1
+ with:
+ json_files: 'reports/*.json'
+ pr_number: ${{ steps.pr_number.outputs.number }}
+ env:
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.github/workflows/membrowse-onboard.yml b/.github/workflows/membrowse-onboard.yml
new file mode 100644
index 000000000..a183297b2
--- /dev/null
+++ b/.github/workflows/membrowse-onboard.yml
@@ -0,0 +1,58 @@
+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:
+ matrix: ${{ steps.set-matrix.outputs.matrix }}
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v5
+
+ - name: Load target matrix
+ id: set-matrix
+ run: echo "matrix=$(jq -c '.' .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.matrix) }}
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v5
+ with:
+ fetch-depth: 0
+ submodules: recursive
+
+ - name: Install packages
+ run: ${{ matrix.setup_cmd }}
+
+ - name: Setup ccache
+ uses: hendrikmuhs/[email protected]
+ with:
+ key: ${{ matrix.port }}-${{ matrix.board }}
+
+ - name: Run Membrowse Onboard Action
+ uses: membrowse/membrowse-action/onboard-action@v1
+ with:
+ target_name: ${{ matrix.target_name }}
+ num_commits: ${{ github.event.inputs.num_commits }}
+ build_script: ${{ matrix.build_cmd }}
+ elf: ${{ matrix.elf }}
+ ld: ${{ matrix.ld }}
+ linker_vars: ${{ matrix.linker_vars }}
+ api_key: ${{ secrets.MEMBROWSE_API_KEY }}
+ api_url: ${{ vars.MEMBROWSE_API_URL }}
diff --git a/.github/workflows/membrowse-report.yml b/.github/workflows/membrowse-report.yml
new file mode 100644
index 000000000..aff9fe138
--- /dev/null
+++ b/.github/workflows/membrowse-report.yml
@@ -0,0 +1,101 @@
+name: Membrowse Memory Report
+
+on:
+ workflow_call:
+ inputs:
+ code_changed:
+ description: 'Whether code paths changed (true) or doc-only (false)'
+ type: boolean
+ required: true
+
+permissions:
+ contents: read
+ actions: read
+
+jobs:
+ load-targets:
+ runs-on: ubuntu-latest
+ outputs:
+ matrix: ${{ steps.set-matrix.outputs.matrix }}
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v6
+
+ - name: Load target matrix
+ id: set-matrix
+ run: echo "matrix=$(jq -c '.' .github/membrowse-targets.json)" >> $GITHUB_OUTPUT
+
+ analyze:
+ needs: [load-targets]
+ runs-on: ubuntu-latest
+ strategy:
+ fail-fast: false
+ matrix:
+ include: ${{ fromJson(needs.load-targets.outputs.matrix) }}
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v6
+ with:
+ fetch-depth: 0
+ submodules: recursive
+
+ # Download artifacts when code changed (build artifacts available)
+ - name: Download build artifacts
+ if: inputs.code_changed
+ id: download
+ uses: actions/download-artifact@v5
+ with:
+ pattern: binaries-*
+ path: cmake-build
+ merge-multiple: true
+ continue-on-error: true
+
+ - name: Restore linker scripts
+ if: inputs.code_changed
+ run: cp -r cmake-build/hw . 2>/dev/null || true
+
+ - name: Check if ELF exists
+ id: check-elf
+ run: |
+ if [ -f "${{ matrix.elf }}" ]; then
+ echo "exists=true" >> $GITHUB_OUTPUT
+ else
+ echo "exists=false" >> $GITHUB_OUTPUT
+ fi
+
+ # Run with actual ELF analysis when build artifacts available
+ - name: Run Membrowse Analysis
+ if: steps.check-elf.outputs.exists == 'true'
+ id: membrowse
+ continue-on-error: true
+ uses: membrowse/membrowse-action@v1
+ with:
+ target_name: ${{ matrix.target_name }}
+ elf: ${{ matrix.elf }}
+ ld: ${{ matrix.ld }}
+ linker_vars: ${{ matrix.linker_vars }}
+ api_key: ${{ secrets.MEMBROWSE_API_KEY }}
+ api_url: ${{ vars.MEMBROWSE_API_URL }}
+ verbose: INFO
+
+ # Run with identical=true when no ELF (doc-only push)
+ # Preserves the chain of commits in membrowse tracking
+ - name: Run Membrowse Identical Report
+ if: steps.check-elf.outputs.exists == 'false'
+ id: membrowse-identical
+ continue-on-error: true
+ uses: membrowse/membrowse-action@v1
+ with:
+ target_name: ${{ matrix.target_name }}
+ identical: true
+ api_key: ${{ secrets.MEMBROWSE_API_KEY }}
+ api_url: ${{ vars.MEMBROWSE_API_URL }}
+ verbose: INFO
+
+ - name: Upload report artifact
+ if: steps.membrowse.outcome == 'success' || steps.membrowse-identical.outcome == 'success'
+ uses: actions/upload-artifact@v5
+ with:
+ name: membrowse-report-${{ matrix.target_name }}
+ path: ${{ steps.membrowse.outputs.report_path || steps.membrowse-identical.outputs.report_path }}