Skip to content

chore: add firmware size history script#1235

Merged
znelson merged 5 commits intocrosspoint-reader:masterfrom
znelson:fw-size
Mar 1, 2026
Merged

chore: add firmware size history script#1235
znelson merged 5 commits intocrosspoint-reader:masterfrom
znelson:fw-size

Conversation

@znelson
Copy link
Contributor

@znelson znelson commented Feb 28, 2026

Summary

What is the goal of this PR?

  • Adds scripts/firmware_size_history.py, a developer tool that builds firmware at selected git commits and reports flash usage with deltas between them.
  • Supports two input modes: --range START END to walk every commit in a range, or --commits REF [REF ...] to compare specific refs (which can span branches).
  • Defaults to a human-readable aligned table; pass --csv for machine-readable output to stdout or --csv FILE to write to a file.

AI Usage

While CrossPoint doesn't have restrictions on AI tools in contributing, please be transparent about their usage as it
helps set the right context for reviewers.

Did you use AI tools to help write this code? YES, fully written by AI

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 28, 2026

📝 Walkthrough

Walkthrough

Adds a new CLI Python script that builds firmware across selected git commits (range or explicit list), collects per-commit flash usage by running PlatformIO builds, computes deltas, and emits results as an aligned table or CSV while preserving and restoring working state.

Changes

Cohort / File(s) Summary
Firmware Size History Script
scripts/firmware_size_history.py
New script implementing two operation modes (range and commits), git ref resolution and commit sequencing, stash/restore of uncommitted changes, detached checkout per commit, PlatformIO build invocation (pio run -e <env>), regex-based flash usage parsing, per-commit success/failure marking, delta computation, table/CSV output, and robust error/progress reporting.

Sequence Diagram(s)

sequenceDiagram
    participant Script as firmware_size_history.py
    participant Git as Git
    participant FS as Filesystem
    participant PIO as PlatformIO
    participant Out as OutputHandler

    Script->>Git: Resolve refs & build commit list
    Script->>Git: Record current ref
    Script->>FS: Stash uncommitted changes (if any)

    loop For each commit
        Script->>Git: Checkout commit (detached HEAD)
        Script->>PIO: Run build (pio run -e <env>)
        PIO->>Script: Return build output
        Script->>Script: Parse flash usage (regex) & record success/failure
        Script->>Script: Compute delta vs previous commit
    end

    Script->>Git: Checkout original ref
    Script->>FS: Restore stashed changes (if any)
    Script->>Out: Write results (table or CSV)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'chore: add firmware size history script' accurately and concisely describes the main change—a new developer tool script for analyzing firmware size across git commits.
Description check ✅ Passed The description clearly explains the PR's purpose, its two input modes (--range and --commits), output formatting options, and even discloses AI usage, all directly relevant to the changeset.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@znelson
Copy link
Contributor Author

znelson commented Feb 28, 2026

Sample output:

% python scripts/firmware_size_history.py --commits 5e95d9a 45a228a
[info] Will restore to 'fw-size' when finished.
[info] Building 2 commits...

[1/2] 5e95d9a36f feat: Long Click for File Deletion through File Browser (#909)
  Building (env: default)...
  Flash used: 5,911,615 bytes

[2/2] 45a228a645 fix: use HTTPClient::writeToStream for downloading files from OPDS (#1207)
  Building (env: default)...
  Flash used: 5,909,131 bytes

[info] Restoring 'fw-size'...

Commit            Flash    Delta  Title
──────────  ───────────  ───────  ────────────────────────────────────────
5e95d9a36f    5,911,615           feat: Long Click for File Deletion through File Browser (#909)
45a228a645    5,909,131   -2,484  fix: use HTTPClient::writeToStream for downloading files from OPDS (#1207)

@znelson znelson marked this pull request as ready for review February 28, 2026 17:32
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
scripts/firmware_size_history.py (1)

161-161: Consider list unpacking for cleaner syntax.

Using list unpacking is more idiomatic than concatenation.

♻️ Suggested change
-    all_commits = [(start_sha, start_title)] + commits
+    all_commits = [(start_sha, start_title), *commits]
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/firmware_size_history.py` at line 161, The current construction
all_commits = [(start_sha, start_title)] + commits uses concatenation; replace
it with list unpacking to be more idiomatic by creating all_commits using a
single list literal that unpacks commits, e.g., start the list with (start_sha,
start_title) followed by *commits—update the assignment where all_commits is
defined to use this unpacking pattern and keep the rest of the code that
consumes all_commits unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@scripts/firmware_size_history.py`:
- Around line 133-136: The f-string uses a backslash escape inside the
expression which is invalid on Python <3.12; define a module-level constant like
BOX_CHAR = "─" (or BOX_CHAR = "\u2500") and update the formatting in
format_table() to build sep using BOX_CHAR * COL_COMMIT, BOX_CHAR * COL_FLASH,
BOX_CHAR * COL_DELTA and BOX_CHAR * 40 (e.g. f"{BOX_CHAR * COL_COMMIT} 
{BOX_CHAR * COL_FLASH}  {BOX_CHAR * COL_DELTA}  {BOX_CHAR * 40}") so no
backslash appears inside f-string expressions and compatibility with Python 3.8+
is preserved.

---

Nitpick comments:
In `@scripts/firmware_size_history.py`:
- Line 161: The current construction all_commits = [(start_sha, start_title)] +
commits uses concatenation; replace it with list unpacking to be more idiomatic
by creating all_commits using a single list literal that unpacks commits, e.g.,
start the list with (start_sha, start_title) followed by *commits—update the
assignment where all_commits is defined to use this unpacking pattern and keep
the rest of the code that consumes all_commits unchanged.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f67e6c2 and e7d63c1.

📒 Files selected for processing (1)
  • scripts/firmware_size_history.py
📜 Review details
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2026-02-23T19:55:03.054Z
Learnt from: martinbrook
Repo: crosspoint-reader/crosspoint-reader PR: 1136
File: scripts/patch_jpegdec.py:20-23
Timestamp: 2026-02-23T19:55:03.054Z
Learning: In the crosspoint-reader project, enable Python linters (e.g., Ruff or Flake8) in CI for all Python scripts. Run linting as part of the CI workflow and fix reported issues to maintain code quality. Do not skip linting for any Python files.

Applied to files:

  • scripts/firmware_size_history.py
🪛 Ruff (0.15.2)
scripts/firmware_size_history.py

[error] 42-42: subprocess call: check for execution of untrusted input

(S603)


[error] 88-88: subprocess call: check for execution of untrusted input

(S603)


[error] 89-89: Starting a process with a partial executable path

(S607)


[warning] 161-161: Consider [(start_sha, start_title), *commits] instead of concatenation

Replace with [(start_sha, start_title), *commits]

(RUF005)

🔇 Additional comments (6)
scripts/firmware_size_history.py (6)

1-38: LGTM!

Clear documentation with usage examples, and standard library imports are appropriate. The regex pattern correctly captures PlatformIO's flash usage output format.


41-64: LGTM!

The helper functions are well-structured. The resolve_ref function properly validates refs before proceeding and provides clear error messages. The fallback from symbolic-ref to rev-parse in git_current_ref correctly handles both branch and detached HEAD states.

Regarding the static analysis hint (S603) about subprocess with untrusted input: this is acceptable for a developer tool that deliberately invokes git commands with user-provided refs.


67-92: LGTM!

Good implementation choices: --detach prevents unintentional branch movement, and combining stdout/stderr captures all PlatformIO output for parsing. Using pio as a partial executable path (S607) is appropriate since PlatformIO is expected to be in the developer's PATH.


95-107: LGTM!

Clean implementation. Returning None for missing flash data allows the caller to handle failures gracefully, and csv.DictWriter ensures proper CSV formatting.


173-256: LGTM!

Excellent defensive design:

  • Refs are validated before any working tree modifications
  • Auto-stash prevents losing uncommitted work
  • try/finally ensures restoration regardless of success/failure
  • KeyboardInterrupt is handled gracefully with partial results
  • check=False on restore operations avoids masking the real issue

258-290: LGTM!

The delta calculation correctly skips failed builds while preserving the last valid measurement for subsequent comparisons. Output handling cleanly supports both file and stdout CSV modes alongside the default table format.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
scripts/firmware_size_history.py (2)

250-257: Excellent interrupt handling; consider warning on stash pop failure.

The KeyboardInterrupt handling and finally cleanup are well implemented. However, if git stash pop fails (e.g., due to conflicts), the user receives no indication that their changes remain stashed.

✨ Optional improvement to warn on stash restoration failure
         if stash_needed:
             print("[info] Restoring stashed changes...", file=sys.stderr)
-            run(["git", "stash", "pop"], check=False)
+            result = run(["git", "stash", "pop"], check=False)
+            if result.returncode != 0:
+                print("[warn] Failed to restore stash. Run 'git stash list' to recover.", file=sys.stderr)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/firmware_size_history.py` around lines 250 - 257, When restoring
stashed changes in the finally block (use of stash_needed and
run(["git","stash","pop"], check=False)), capture the CompletedProcess result
and check result.returncode; if non‑zero, print a clear warning to stderr
(including that stash pop failed and local changes may still be stashed or there
may be conflicts) so users know to inspect the stash manually. Ensure the
message references the operation ("git stash pop") and suggests next steps
(e.g., run "git stash list" / "git stash pop" manually).

162-162: Consider list unpacking for cleaner syntax.

Per Ruff's suggestion, list unpacking is more idiomatic than concatenation.

✨ Optional style improvement
-    all_commits = [(start_sha, start_title)] + commits
+    all_commits = [(start_sha, start_title), *commits]
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/firmware_size_history.py` at line 162, Replace the concatenation used
to build all_commits with list unpacking for clearer, more idiomatic syntax:
instead of building all_commits via (start_sha, start_title) concatenated with
commits, create a new list that begins with (start_sha, start_title) and expands
commits using the splat operator (refer to the variables all_commits, start_sha,
start_title, and commits to locate the code).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@scripts/firmware_size_history.py`:
- Around line 250-257: When restoring stashed changes in the finally block (use
of stash_needed and run(["git","stash","pop"], check=False)), capture the
CompletedProcess result and check result.returncode; if non‑zero, print a clear
warning to stderr (including that stash pop failed and local changes may still
be stashed or there may be conflicts) so users know to inspect the stash
manually. Ensure the message references the operation ("git stash pop") and
suggests next steps (e.g., run "git stash list" / "git stash pop" manually).
- Line 162: Replace the concatenation used to build all_commits with list
unpacking for clearer, more idiomatic syntax: instead of building all_commits
via (start_sha, start_title) concatenated with commits, create a new list that
begins with (start_sha, start_title) and expands commits using the splat
operator (refer to the variables all_commits, start_sha, start_title, and
commits to locate the code).

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e7d63c1 and 6035f5f.

📒 Files selected for processing (1)
  • scripts/firmware_size_history.py
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: build
  • GitHub Check: cppcheck
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2026-02-23T19:55:03.054Z
Learnt from: martinbrook
Repo: crosspoint-reader/crosspoint-reader PR: 1136
File: scripts/patch_jpegdec.py:20-23
Timestamp: 2026-02-23T19:55:03.054Z
Learning: In the crosspoint-reader project, enable Python linters (e.g., Ruff or Flake8) in CI for all Python scripts. Run linting as part of the CI workflow and fix reported issues to maintain code quality. Do not skip linting for any Python files.

Applied to files:

  • scripts/firmware_size_history.py
🪛 Ruff (0.15.2)
scripts/firmware_size_history.py

[error] 43-43: subprocess call: check for execution of untrusted input

(S603)


[error] 89-89: subprocess call: check for execution of untrusted input

(S603)


[error] 90-90: Starting a process with a partial executable path

(S607)


[warning] 162-162: Consider [(start_sha, start_title), *commits] instead of concatenation

Replace with [(start_sha, start_title), *commits]

(RUF005)

🔇 Additional comments (12)
scripts/firmware_size_history.py (12)

1-39: LGTM!

The docstring is comprehensive with clear usage examples. The BOX_CHAR constant properly addresses Python 3.8+ compatibility by keeping backslash escapes outside f-string expressions.


42-46: LGTM!

Clean subprocess wrapper. The static analysis warning (S603) about untrusted input is a false positive here—this is a local developer tool where the user explicitly provides refs via command line.


49-57: LGTM!

Proper error handling with user-friendly messages when ref resolution fails.


60-65: LGTM!

Correctly handles both attached branch and detached HEAD states for proper restoration.


68-80: LGTM!

Clean parsing of git log output with proper handling of commit titles containing spaces.


83-93: LGTM!

Using --detach for checkout is appropriate for arbitrary commit navigation. The partial path for pio (S607) is acceptable—developers using this tool will have PlatformIO in their PATH.


96-108: LGTM!

Clean implementations for regex parsing and CSV output.


111-149: LGTM!

Good table formatting with proper handling of FAILED builds and empty deltas. The BOX_CHAR constant usage maintains Python 3.8+ compatibility.


167-171: LGTM!

Clean implementation with proper pluralization in the description.


174-219: LGTM!

Excellent defensive design—validating refs before touching the working tree prevents stranding stashed changes on bad input.


259-274: LGTM!

The delta calculation correctly handles failed builds by preserving the last successful size for comparison, which provides meaningful deltas even when some commits fail to build.


276-291: LGTM!

Clean output handling for both CSV and table formats with proper file handling.

@znelson znelson merged commit 620835a into crosspoint-reader:master Mar 1, 2026
6 checks passed
@znelson znelson deleted the fw-size branch March 1, 2026 16:46
trilwu added a commit to trilwu/crosspet that referenced this pull request Mar 2, 2026
…actors)

Merges upstream commits:
- fix: fixed-point x-advance and kerning for text layout (crosspoint-reader#1168)
- chore: add firmware size history script (crosspoint-reader#1235)
- refactor: binary search for font lookups (crosspoint-reader#1202)
- perf: remove unused ConfirmationActivity member (crosspoint-reader#1234)

Resolved conflicts in ubuntu_10/12 bold/regular font headers by accepting
upstream regenerated values (local had no changes to these files).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants