Skip to content

perf(ci): parallelize release bundle builds - #596

Merged
meganetaaan merged 3 commits into
developfrom
perf/parallel-bundle-artifacts
Jul 25, 2026
Merged

perf(ci): parallelize release bundle builds#596
meganetaaan merged 3 commits into
developfrom
perf/parallel-bundle-artifacts

Conversation

@meganetaaan

@meganetaaan meganetaaan commented Jul 24, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Split firmware bundle generation into per-target release build jobs, then assemble and validate their artifacts in a final job.
  • Remove the duplicate debug firmware build matrix from build.yml while preserving per-platform failure visibility in the bundle workflow.
  • Add named release-build npm scripts for all six supported targets and keep npm run bundle as a local compatibility entry point.
  • Build MOD archives without flashing through mcrun, then discover and write the live xs partition with esptool, including firmware/version/size preflight and digest verification.
  • Keep the normal debug route and add debug:xsdb scripts for interactive debugging.
  • Update English/Japanese firmware documentation and add unit coverage for bundle assembly and MOD flashing.

Why

The bundle workflow rebuilt standard firmware targets serially through mcbundle, making one run take roughly 20 minutes. Reusing the existing target build coverage as release builds makes the critical path the slowest target instead of the sum of all target builds.

The remaining blocker was MOD installation: it depended on the debug-oriented mcrun write path. Moving MOD writes to esptool makes MOD installation independent of whether the host is a debug or release build, so CI can validate release builds without losing the developer debug/xsdb path.

Impact

  • Bundle target builds and web artifacts can run in parallel.
  • All supported firmware platforms still report failures independently (fail-fast: false).
  • The final bundle is assembled only after validating the exact target set, embedded versions, binary sizes, and factory partition limits.
  • npm run mod now writes and verifies the live xs partition instead of using mcrun for transport.

Validation

  • npm run test:unit — 219 tests passed on the rebased head
  • npm run format
  • npm run lint
  • node --test .github/scripts/*.test.mjs
  • Parsed the modified workflow YAML files and ran git diff --check
  • Built all six release targets locally and validated the packaged ZIP
  • M5StackChan on /dev/ttyACM1: flashed a release host, installed and verified a MOD through esptool, and ran the servo/LED smoke MOD to completion
  • Flashed a debug host and connected xsdb through serial2xsbug; the main thread was reported as running

npm run debug:xsdb itself still requires the expected physical CoreS3 download-button/re-enumeration step; the underlying deploy and xsdb connection paths were verified separately.

Release impact

none — this changes CI/developer tooling and documentation only; it does not change released firmware or web behavior.

Summary by CodeRabbit

  • New Features

    • Added an end-to-end firmware bundling flow that builds per-target release artifacts and assembles them into a final ZIP package.
    • Introduced terminal-based xsdb debug commands and improved MOD installation with device partition discovery, validation, and optional serial port selection.
    • Added web build output integration by generating and bundling a WASM simulator artifact for preview/deployment.
  • Documentation

    • Updated firmware build/flashing guides (English + Japanese) to reflect the new bundle and MOD/flash behavior and output locations.
  • Tests

    • Added automated coverage for firmware bundle assembly/packaging and MOD installation safeguards.
  • Chores

    • Updated CI workflow structure and artifact wiring for firmware vs web builds.

@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: b2a2f339-08d2-457f-ad6b-ab8fd66f1252

📥 Commits

Reviewing files that changed from the base of the PR and between ebf4e73 and 4bc857b.

📒 Files selected for processing (2)
  • firmware/scripts/lib/firmware-bundle.mjs
  • firmware/scripts/lib/firmware-bundle.test.mjs
🚧 Files skipped from review as they are similar to previous changes (2)
  • firmware/scripts/lib/firmware-bundle.test.mjs
  • firmware/scripts/lib/firmware-bundle.mjs

📝 Walkthrough

Walkthrough

This PR adds validated firmware bundle generation, MOD archive flashing and verification, release-target scripts, xsdb commands, and parallel CI jobs for firmware and WASM artifacts. Documentation is updated for the revised build, flashing, debugging, and output flows.

Changes

Firmware release and device tooling

Layer / File(s) Summary
Release bundle generation and validation
firmware/package.json, firmware/scripts/lib/firmware-bundle.mjs, firmware/scripts/lib/moddable-version.mjs, firmware/scripts/bundle*.mjs, firmware/scripts/package-bundle.mjs, firmware/scripts/lib/firmware-bundle.test.mjs
Release targets are built with versioned configuration, validated against partition capacity and embedded versions, assembled, tested, and packaged into a ZIP archive.
MOD archive installation
firmware/scripts/lib/mod-flash.mjs, firmware/scripts/lib/mod-flash.test.mjs
MOD archives are resolved, checked against device partitions and firmware metadata, then written and verified through esptool.
Firmware command and device integration
firmware/scripts/firmware.mjs, firmware/scripts/lib/devices.mjs, firmware/package.json, firmware/scripts/lib/firmware-command.test.mjs
The firmware CLI integrates MOD installation, serial-port options, firmware-version checks, device chip metadata, corrected subprocess handling, and xsdb debug commands.
Parallel CI artifact workflow
.github/workflows/build.yml, .github/workflows/bundle.yml
CI separates firmware and WASM builds, publishes intermediate artifacts, and packages the final firmware bundle after downloading both artifact sets.
Build, flashing, and debugging documentation
AGENTS.md, firmware/README*, firmware/docs/flashing-firmware*
Documentation describes bundle outputs, MOD flashing, xsdb debugging, serial-port selection, and updated command output.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant FirmwareBuild
  participant WebBuild
  participant BundleBuild
  FirmwareBuild->>BundleBuild: publish release target artifacts
  WebBuild->>BundleBuild: publish WASM simulator artifact
  BundleBuild->>BundleBuild: download artifacts and run bundle:package
  BundleBuild-->>BundleBuild: upload final firmware bundle
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 69.70% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main CI change: parallelizing release bundle builds.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch perf/parallel-bundle-artifacts

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.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9cbe23a12e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread firmware/scripts/lib/mod-flash.mjs Outdated
export function resolveModArchivePath({ outputDirectory, mode, projectName }) {
if (!outputDirectory) throw new Error('MOD output directory is required')
if (!/^[^/\\]+$/.test(projectName)) throw new Error(`Invalid MOD project name: ${projectName || 'missing'}`)
const outputMode = mode === 'debug' ? 'debug' : 'release'

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve the instrument archive directory

When npm run mod -- <manifest> --mode=instrument is used, firmware.mjs passes -i to mcrun, so the archive is emitted under the instrument build-mode directory. This mapping treats every non-debug mode as release, causing the subsequent install step to read a nonexistent or stale release archive instead of the newly built instrument archive. Map instrument to its own output directory, as is already done for other managed build outputs.

Useful? React with 👍 / 👎.

@github-actions

github-actions Bot commented Jul 24, 2026

Copy link
Copy Markdown

Cloudflare PR preview

Open the latest preview for commit 4bc857bac9ca.

Immutable deployment: https://93e33283.stack-chan-pr-preview.pages.dev

Warning

Pull request previews contain untrusted web and firmware code. Review the changes before granting WebSerial/Bluetooth permissions or flashing a device.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

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 (2)
firmware/scripts/lib/firmware-bundle.mjs (1)

132-132: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Move the generated wrapper manifest out of host/app.

prepareBundleManifest creates bundleManifestPath under path.dirname(target.manifestPath), while both included manifests are absolute and the override manifest already lives under outputDirectory/generated/bundle-manifests/<target>. Keeping the temporary wrapper in the tracked firmware/host/app source directory is unnecessary and leaves a stray file behind if mcconfig terminates before the cleanup finally block runs.

♻️ Proposed fix to co-locate the wrapper manifest with the override manifest
 function prepareBundleManifest(target, sdkconfigDirectory, outputDirectory) {
   const directory = path.join(outputDirectory, 'generated', 'bundle-manifests', target.name)
   const overrideManifestPath = path.join(directory, 'sdkconfig.json')
-  const bundleManifestPath = path.join(
-    path.dirname(target.manifestPath),
-    `${firmwareBundleName}.${target.name}.${process.pid}.manifest.json`,
-  )
+  const bundleManifestPath = path.join(
+    directory,
+    `${firmwareBundleName}.${target.name}.${process.pid}.manifest.json`,
+  )
   mkdirSync(directory, { recursive: true })
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@firmware/scripts/lib/firmware-bundle.mjs` at line 132, Update
prepareBundleManifest and its caller to create the generated wrapper manifest
under outputDirectory/generated/bundle-manifests/<target>, alongside the
override manifest, instead of deriving its location from
path.dirname(target.manifestPath). Preserve absolute manifest paths and ensure
cleanup uses the new generated location.
firmware/scripts/lib/mod-flash.mjs (1)

60-60: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider defaulting scratch files under firmware/dist/tmp instead of the OS temp dir.

temporaryDirectory defaults to tmpdir() (OS-wide temp), so the partition-table/app-header scratch files land outside the repo's managed output tree. It's cleaned via finally on the happy path, but a killed process leaves stray directories outside npm run clean's reach.

♻️ Suggested default
-  temporaryDirectory = tmpdir(),
+  temporaryDirectory = path.join(buildOutputDirectory, 'tmp'),

As per coding guidelines: "Normal host, MOD, and test build outputs must remain under firmware/dist/bin/ and firmware/dist/tmp/; do not bypass the repository output isolation."

Also applies to: 74-77

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@firmware/scripts/lib/mod-flash.mjs` at line 60, Update the temporaryDirectory
default in the module’s flash workflow to use the repository-managed
firmware/dist/tmp location instead of the OS tmpdir(). Preserve any
caller-provided temporary directory and ensure the default directory is created
or resolved consistently before scratch files are written, so outputs remain
within the repository’s managed tree.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/bundle.yml:
- Line 61: Update the checkout steps in the firmware, web, and build jobs to set
with.persist-credentials to false, matching the existing Pages baseline/preview
checkout configuration. Preserve the current checkout actions and job behavior
otherwise.

---

Nitpick comments:
In `@firmware/scripts/lib/firmware-bundle.mjs`:
- Line 132: Update prepareBundleManifest and its caller to create the generated
wrapper manifest under outputDirectory/generated/bundle-manifests/<target>,
alongside the override manifest, instead of deriving its location from
path.dirname(target.manifestPath). Preserve absolute manifest paths and ensure
cleanup uses the new generated location.

In `@firmware/scripts/lib/mod-flash.mjs`:
- Line 60: Update the temporaryDirectory default in the module’s flash workflow
to use the repository-managed firmware/dist/tmp location instead of the OS
tmpdir(). Preserve any caller-provided temporary directory and ensure the
default directory is created or resolved consistently before scratch files are
written, so outputs remain within the repository’s managed tree.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 21d4485c-15c2-4635-a4d0-29a8873c6e5c

📥 Commits

Reviewing files that changed from the base of the PR and between 08b963d and 9cbe23a.

📒 Files selected for processing (18)
  • .github/workflows/build.yml
  • .github/workflows/bundle.yml
  • AGENTS.md
  • firmware/README.md
  • firmware/README_ja.md
  • firmware/docs/flashing-firmware.md
  • firmware/docs/flashing-firmware_ja.md
  • firmware/package.json
  • firmware/scripts/bundle-target.mjs
  • firmware/scripts/bundle.mjs
  • firmware/scripts/firmware.mjs
  • firmware/scripts/lib/devices.mjs
  • firmware/scripts/lib/firmware-bundle.mjs
  • firmware/scripts/lib/firmware-bundle.test.mjs
  • firmware/scripts/lib/mod-flash.mjs
  • firmware/scripts/lib/mod-flash.test.mjs
  • firmware/scripts/lib/moddable-version.mjs
  • firmware/scripts/package-bundle.mjs
💤 Files with no reviewable changes (1)
  • .github/workflows/build.yml

Comment thread .github/workflows/bundle.yml
@meganetaaan
meganetaaan merged commit e99fe99 into develop Jul 25, 2026
13 checks passed
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.

1 participant