Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 89 additions & 0 deletions .github/workflows/size-comment.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# Posts the size reports produced by the `Size` workflow (size.yml) as one pull request
# comment, updating it on every push.
#
# Separate from `size.yml` on purpose: that workflow runs the pull request's code with a
# read-only token, so it cannot comment on pull requests from forks. This one runs on
# `workflow_run` in the base repository with a write token, and neither checks out nor
# runs anything from the pull request. The reports are taken from the artifacts and only
# ever end up inside the comment body.
name: Size comment

on:
workflow_run:
workflows: [Size]
types: [completed]

jobs:
comment:
name: Comment
if: github.event.workflow_run.event == 'pull_request'
runs-on: ubuntu-latest
permissions:
actions: read
pull-requests: write

steps:
- name: Find the pull request
id: pr
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
HEAD_SHA: ${{ github.event.workflow_run.head_sha }}
run: |
# Look the pull request up by the commit the reports were built from, rather
# than trusting anything in the artifacts. Nothing is found when the pull
# request got another push meanwhile; that run will report instead.
pr=$(gh api "repos/$GITHUB_REPOSITORY/commits/$HEAD_SHA/pulls" \
--jq "[.[] | select(.head.sha == \"$HEAD_SHA\")][0] | \"\(.number) \(.base.sha)\"")
if [ -z "$pr" ] || [ "$pr" = "null null" ]; then
echo "No open pull request has $HEAD_SHA as its head; nothing to comment on"
exit 0
fi
echo "number=${pr% *}" >> "$GITHUB_OUTPUT"
echo "base=${pr#* }" >> "$GITHUB_OUTPUT"

- name: Download the size reports
if: steps.pr.outputs.number
uses: actions/download-artifact@v4
with:
pattern: size-*
path: size
merge-multiple: true
run-id: ${{ github.event.workflow_run.id }}
github-token: ${{ secrets.GITHUB_TOKEN }}

- name: Compose the comment
if: steps.pr.outputs.number
env:
HEAD_SHA: ${{ github.event.workflow_run.head_sha }}
BASE_SHA: ${{ steps.pr.outputs.base }}
RUN_URL: ${{ github.event.workflow_run.html_url }}
run: |
{
echo "## Size report"
echo "<!-- size-report -->"
echo
echo "Firmware size at ${HEAD_SHA:0:7} compared to the base branch at ${BASE_SHA:0:7}."
echo "Regions that grew by more than 0.2% are marked. [Workflow run]($RUN_URL)"
echo
for report in size/*.md; do
cat "$report"
echo
done
} > comment.md
cat comment.md

- name: Post or update the comment
if: steps.pr.outputs.number
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR: ${{ steps.pr.outputs.number }}
run: |
id=$(gh api "repos/$GITHUB_REPOSITORY/issues/$PR/comments" --paginate \
--jq '[.[] | select(.user.login == "github-actions[bot]") | select(.body | contains("<!-- size-report -->"))][0].id')
if [ -n "$id" ] && [ "$id" != "null" ]; then
gh api -X PATCH "repos/$GITHUB_REPOSITORY/issues/comments/$id" -F body=@comment.md > /dev/null
echo "Updated comment $id on #$PR"
else
gh api -X POST "repos/$GITHUB_REPOSITORY/issues/$PR/comments" -F body=@comment.md > /dev/null
echo "Commented on #$PR"
fi
132 changes: 132 additions & 0 deletions .github/workflows/size.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
# Firmware size report for every pull request.
#
# One example per MCU family is built twice, at the pull request's merge commit and at the
# tip of the base branch, and the two ELFs are diffed with `cargo elfsize`. The report goes
# to the job summary and is uploaded as an artifact, from where the `Size comment` workflow
# (size-comment.yml) posts it as a pull request comment. On pushes to `main` only the
# sizes of the pushed commit are reported.
#
# Both builds run in the same checkout directory on purpose: panic-location strings embed
# absolute source paths, so building the base in a second directory with a different path
# length shifts `.rodata` by kilobytes.
#
# The examples are built with `force-generate-bindings`, same as in `ci.yml`, so that the
# OpenThread C libraries reflect the pull request's `openthread-sys` configuration rather
# than the committed prebuilt ones, which may lag behind it.
name: Size

on:
pull_request:
push:
branches: [main]
workflow_dispatch:

# A new push to a pull request supersedes the size run already in flight for it
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

env:
CARGO_TERM_COLOR: always
# Growth above this many percent is marked in the report and annotated on the run
SIZE_WARN_PERCENT: "0.2"

jobs:
size:
name: Size
runs-on: ubuntu-latest
permissions: read-all
strategy:
fail-fast: false
matrix:
# [family, examples feature, target, example, report label]
mcu:
- [esp, esp32c6, riscv32imac-unknown-none-elf, basic_udp, esp32c6]
- [esp, esp32h2, riscv32imac-unknown-none-elf, basic_udp, esp32h2]
- [nrf, default, thumbv7em-none-eabi, basic_udp, nrf52840]

steps:
- uses: actions/checkout@v4
with:
# The base commit is checked out below
fetch-depth: 0

- name: openthread init
run: git submodule update --init --recursive

- uses: dtolnay/rust-toolchain@v1
with:
target: x86_64-unknown-linux-gnu
toolchain: nightly
components: rust-src

- name: Install MCU target
run: rustup target add ${{ matrix.mcu[2] }}

- name: Detect host target triple
run: |
export HOST_TARGET=$(rustup show | grep "Default host" | sed -e 's/.* //')
echo "HOST_TARGET=$HOST_TARGET" >> $GITHUB_ENV

- name: Install espup
run: |
curl -LO https://github.com/esp-rs/espup/releases/latest/download/espup-${{ env.HOST_TARGET }}.zip
unzip -o espup-${{ env.HOST_TARGET }}.zip -d "$HOME/.cargo/bin"
chmod +x "$HOME/.cargo/bin/espup"*
echo "ESPUP_EXPORT_FILE=$HOME/exports" >> $GITHUB_ENV

- name: Install espup toolchains (xtensa and riscv)
if: matrix.mcu[0] == 'esp'
run: |
source "$HOME/.cargo/env"
"$HOME/.cargo/bin/espup" install -e -r
source "$HOME/exports"
echo "${LIBCLANG_PATH}/../bin:$PATH" >> "$GITHUB_PATH"
echo "LIBCLANG_PATH=${LIBCLANG_PATH}" >> "$GITHUB_ENV"
echo "CLANG_PATH=${CLANG_PATH}" >> "$GITHUB_ENV"

# Not yet published; until then, built from the `size-check` branch of embuild
- name: Install cargo-elfsize
run: cargo install --git https://github.com/esp-rs/embuild --branch size-check cargo-elfsize

- name: Build - Head
run: |
cd examples/${{ matrix.mcu[0] }}
cargo build --release --no-default-features --features ${{ matrix.mcu[1] }},force-generate-bindings --target ${{ matrix.mcu[2] }} -Zbuild-std=core,alloc,panic_abort --bin ${{ matrix.mcu[3] }}
cp target/${{ matrix.mcu[2] }}/release/${{ matrix.mcu[3] }} "$RUNNER_TEMP/head.elf"

- name: Check out the base commit
if: github.event_name == 'pull_request'
run: |
git checkout --detach ${{ github.event.pull_request.base.sha }}
git submodule update --init --recursive

- name: Build - Base
if: github.event_name == 'pull_request'
run: |
cd examples/${{ matrix.mcu[0] }}
cargo build --release --no-default-features --features ${{ matrix.mcu[1] }},force-generate-bindings --target ${{ matrix.mcu[2] }} -Zbuild-std=core,alloc,panic_abort --bin ${{ matrix.mcu[3] }}
cp target/${{ matrix.mcu[2] }}/release/${{ matrix.mcu[3] }} "$RUNNER_TEMP/base.elf"

- name: Size report
run: |
title="${{ matrix.mcu[3] }} on ${{ matrix.mcu[4] }} (${{ matrix.mcu[2] }})"
if [ -f "$RUNNER_TEMP/base.elf" ]; then
cargo elfsize --title "$title" --warn "$SIZE_WARN_PERCENT" "$RUNNER_TEMP/base.elf" "$RUNNER_TEMP/head.elf" > "$RUNNER_TEMP/report.txt"
else
cargo elfsize --title "$title" "$RUNNER_TEMP/head.elf" > "$RUNNER_TEMP/report.txt"
fi
# Printed as-is so that the runner picks up the `::warning::` annotations;
# the artifact and summary get the markdown alone
cat "$RUNNER_TEMP/report.txt"
mkdir -p size
grep -v '^::' "$RUNNER_TEMP/report.txt" > size/${{ matrix.mcu[4] }}.md
cat size/${{ matrix.mcu[4] }}.md >> "$GITHUB_STEP_SUMMARY"

- name: Upload size report
if: github.event_name == 'pull_request'
uses: actions/upload-artifact@v4
with:
name: size-${{ matrix.mcu[4] }}
path: size/
retention-days: 7
Loading