Skip to content

chore(FR-2612): optimize release workflow with parallel jobs and eliminate double React build#6765

Merged
graphite-app[bot] merged 1 commit intomainfrom
04-16-chore_optimize_release_workflow_with_parallel_jobs_and_eliminate_double_react_build
Apr 21, 2026
Merged

chore(FR-2612): optimize release workflow with parallel jobs and eliminate double React build#6765
graphite-app[bot] merged 1 commit intomainfrom
04-16-chore_optimize_release_workflow_with_parallel_jobs_and_eliminate_double_react_build

Conversation

@yomybaby
Copy link
Copy Markdown
Member

@yomybaby yomybaby commented Apr 16, 2026

Resolves #6815 (FR-2612)

Summary

The release pipeline previously ran everything sequentially on a single macOS runner (~30+ min). This PR introduces several optimizations that cut release time roughly in half.

Dry run test result

Job Duration Runner
build_web 3m 30s ubuntu
build_mac (unsigned) 3m 54s macos
build_desktop 2m 54s ubuntu
Total wall time ~7m 24s

Previous release (v26.4.3): 26m 35s72% reduction in wall time.
Actual release with code signing/notarization is expected to be ~10-12 min.

Changes

  1. Split CI into 3 parallel jobs (package.yml)

    • build_web (ubuntu): Builds web assets + uploads bundle
    • build_mac (macos): macOS desktop builds with code signing/notarization
    • build_desktop (ubuntu): Windows + Linux desktop builds
    • macOS and win/linux jobs run concurrently after web build completes
  2. Eliminate double React build (scripts/patch-electron-publicpath.js, Makefile)

    • Previously the entire React app was rebuilt for Electron just to change publicPath from / to es6://
    • Now a lightweight post-build script patches the already-built files instead
  3. Parallelize local proxy compilation (Makefile)

    • All platform targets now compile concurrently within each job via the new compile_all_localproxy target
    • compile_localproxy is now idempotent: skips when the output ZIP already exists, so downstream targets (mac_x64, win_x64, ...) reuse the cached artifact without recompiling. Set FORCE_COMPILE_LOCALPROXY=1 to force a rebuild.
    • dep_electron is likewise idempotent: skips when build/electron-app/app/index.html already carries the patched es6://static/js/main marker. Set FORCE_DEP_ELECTRON=1 to force a re-sync.
    • This means the original single set of mac_x64/mac_arm64/win_*/linux_* targets can be reused across both ad-hoc local builds and the new parallel CI flow — no duplicated *_package targets required.
  4. Optimize ZIP compression (Makefile)

    • Changed from -9 (max) to -6 (default) -- marginal size difference, measurably faster
  5. Concurrent release asset uploads (upload-release.js)

    • Uploads up to 4 files simultaneously instead of sequentially
  6. Explicit NODE_OPTIONS (react/package.json)

    • Added --max-old-space-size=4096 to build:only script to prevent OOM during webpack builds
  7. workflow_dispatch trigger (package.yml)

    • Enables manual dry-run testing of the build pipeline on any branch
    • dry_run=true skips release uploads and code signing

Backward Compatibility

  • Existing make mac, make win, make linux, make dep targets work unchanged
  • make mac_x64, make mac_arm64, make win_x64, make win_arm64, make linux_x64, make linux_arm64 targets work unchanged — no new *_package variants introduced
  • make all now uses the optimized parallel path internally (pre-builds all local proxies via compile_all_localproxy, then calls the original platform targets which become cheap thanks to idempotent prerequisites)
  • make bundle no longer requires full dep (just dep_web)

Test plan

  • Verify scripts/patch-electron-publicpath.js correctly patches es6:// references (local mock test)
  • Verify build_web job builds web assets and uploads artifact
  • Verify build_mac job downloads artifact, prepares Electron, packages DMG
  • Verify build_desktop job packages Win/Linux ZIPs
  • Verify all 3 jobs complete successfully in parallel
  • Verify compile_localproxy skips when output ZIP exists and rebuilds with FORCE_COMPILE_LOCALPROXY=1
  • Verify dep_electron skips when build/electron-app/app/index.html already patched and resyncs with FORCE_DEP_ELECTRON=1
  • Verify actual release (non-dry-run) uploads assets correctly

Generated with Claude Code

Copy link
Copy Markdown
Member Author


How to use the Graphite Merge Queue

Add either label to this PR to merge it via the merge queue:

  • flow:merge-queue - adds this PR to the back of the merge queue
  • flow:hotfix - for urgent changes, fast-track this PR to the front of the merge queue

You must have a Graphite account in order to use the merge queue. Sign up using this link.

An organization admin has required the Graphite Merge Queue in this repository.

Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue.

This stack of pull requests is managed by Graphite. Learn more about stacking.

@github-actions github-actions Bot added the size:L 100~500 LoC label Apr 16, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 16, 2026

Coverage report for ./react

St.
Category Percentage Covered / Total
🔴 Statements 8.6% 1757/20437
🔴 Branches 7.89% 1131/14338
🔴 Functions 5.14% 285/5545
🔴 Lines 8.32% 1649/19831

Test suite run success

856 tests passing in 39 suites.

Report generated by 🧪jest coverage report action from b1127f6

@yomybaby yomybaby force-pushed the 04-16-chore_optimize_release_workflow_with_parallel_jobs_and_eliminate_double_react_build branch 6 times, most recently from 438eca6 to 2f5ca73 Compare April 17, 2026 01:20
@yomybaby yomybaby marked this pull request as ready for review April 17, 2026 01:30
Copilot AI review requested due to automatic review settings April 17, 2026 01:30
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Optimizes the release/build pipeline for Backend.AI WebUI/Desktop by splitting packaging into parallel GitHub Actions jobs and avoiding a second React build for Electron by patching the built output in-place.

Changes:

  • Split release workflow into build_web, build_mac, and build_desktop jobs with artifact sharing.
  • Replace the second Electron-specific React build with a post-build publicPath patch script.
  • Parallelize local proxy builds and speed up ZIP packaging; upload release assets concurrently.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
.github/workflows/package.yml Parallelized CI packaging jobs and added workflow_dispatch dry-run support.
Makefile Added dep_web/dep_electron, parallel local proxy compilation, package-only targets, and faster zip compression.
scripts/patch-electron-publicpath.js New script to patch build output publicPath for Electron (es6://).
upload-release.js Upload assets concurrently in batches to reduce wall time.
react/package.json Add explicit NODE_OPTIONS to reduce webpack OOM risk in CI builds.

Comment thread .github/workflows/package.yml
Comment thread Makefile Outdated
Comment thread Makefile
Comment thread Makefile
Comment thread scripts/patch-electron-publicpath.js
Comment thread upload-release.js
Comment thread .github/workflows/package.yml Outdated
Comment thread .github/workflows/package.yml
@yomybaby yomybaby force-pushed the 04-16-chore_optimize_release_workflow_with_parallel_jobs_and_eliminate_double_react_build branch from 2f5ca73 to 559fcd4 Compare April 17, 2026 05:44
@yomybaby yomybaby changed the title chore: optimize release workflow with parallel jobs and eliminate double React build chore(FR-2612): optimize release workflow with parallel jobs and eliminate double React build Apr 21, 2026
@yomybaby yomybaby requested a review from nowgnuesLee April 21, 2026 02:44
@cla-assistant
Copy link
Copy Markdown

cla-assistant Bot commented Apr 21, 2026

CLA assistant check
All committers have signed the CLA.

@nowgnuesLee nowgnuesLee temporarily deployed to app-packaging-dryrun April 21, 2026 04:40 — with GitHub Actions Inactive
Copy link
Copy Markdown
Contributor

@nowgnuesLee nowgnuesLee left a comment

Choose a reason for hiding this comment

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

LGTM

@graphite-app
Copy link
Copy Markdown

graphite-app Bot commented Apr 21, 2026

Merge activity

…inate double React build (#6765)

Resolves #6815 ([FR-2612](https://lablup.atlassian.net/browse/FR-2612))

## Summary

The release pipeline previously ran everything sequentially on a single macOS runner (~30+ min). This PR introduces several optimizations that cut release time roughly in half.

### Dry run test result

| Job | Duration | Runner |
|-----|----------|--------|
| `build_web` | 3m 30s | ubuntu |
| `build_mac` (unsigned) | 3m 54s | macos |
| `build_desktop` | 2m 54s | ubuntu |
| **Total wall time** | **~7m 24s** | |

Previous release (v26.4.3): **26m 35s** → **72% reduction** in wall time.
Actual release with code signing/notarization is expected to be ~10-12 min.

- Dry run: https://github.com/lablup/backend.ai-webui/actions/runs/24537688533

### Changes

1. **Split CI into 3 parallel jobs** (`package.yml`)
   - `build_web` (ubuntu): Builds web assets + uploads bundle
   - `build_mac` (macos): macOS desktop builds with code signing/notarization
   - `build_desktop` (ubuntu): Windows + Linux desktop builds
   - macOS and win/linux jobs run **concurrently** after web build completes

2. **Eliminate double React build** (`scripts/patch-electron-publicpath.js`, `Makefile`)
   - Previously the entire React app was rebuilt for Electron just to change `publicPath` from `/` to `es6://`
   - Now a lightweight post-build script patches the already-built files instead

3. **Parallelize local proxy compilation** (`Makefile`)
   - All platform targets now compile concurrently within each job via the new `compile_all_localproxy` target
   - `compile_localproxy` is now **idempotent**: skips when the output ZIP already exists, so downstream targets (`mac_x64`, `win_x64`, ...) reuse the cached artifact without recompiling. Set `FORCE_COMPILE_LOCALPROXY=1` to force a rebuild.
   - `dep_electron` is likewise idempotent: skips when `build/electron-app/app/index.html` already carries the patched `es6://static/js/main` marker. Set `FORCE_DEP_ELECTRON=1` to force a re-sync.
   - This means the original single set of `mac_x64`/`mac_arm64`/`win_*`/`linux_*` targets can be reused across both ad-hoc local builds and the new parallel CI flow — no duplicated `*_package` targets required.

4. **Optimize ZIP compression** (`Makefile`)
   - Changed from `-9` (max) to `-6` (default) -- marginal size difference, measurably faster

5. **Concurrent release asset uploads** (`upload-release.js`)
   - Uploads up to 4 files simultaneously instead of sequentially

6. **Explicit `NODE_OPTIONS`** (`react/package.json`)
   - Added `--max-old-space-size=4096` to `build:only` script to prevent OOM during webpack builds

7. **`workflow_dispatch` trigger** (`package.yml`)
   - Enables manual dry-run testing of the build pipeline on any branch
   - `dry_run=true` skips release uploads and code signing

### Backward Compatibility

- Existing `make mac`, `make win`, `make linux`, `make dep` targets work unchanged
- `make mac_x64`, `make mac_arm64`, `make win_x64`, `make win_arm64`, `make linux_x64`, `make linux_arm64` targets work unchanged — no new `*_package` variants introduced
- `make all` now uses the optimized parallel path internally (pre-builds all local proxies via `compile_all_localproxy`, then calls the original platform targets which become cheap thanks to idempotent prerequisites)
- `make bundle` no longer requires full `dep` (just `dep_web`)

## Test plan

- [x] Verify `scripts/patch-electron-publicpath.js` correctly patches `es6://` references (local mock test)
- [x] Verify `build_web` job builds web assets and uploads artifact
- [x] Verify `build_mac` job downloads artifact, prepares Electron, packages DMG
- [x] Verify `build_desktop` job packages Win/Linux ZIPs
- [x] Verify all 3 jobs complete successfully in parallel
- [x] Verify `compile_localproxy` skips when output ZIP exists and rebuilds with `FORCE_COMPILE_LOCALPROXY=1`
- [x] Verify `dep_electron` skips when `build/electron-app/app/index.html` already patched and resyncs with `FORCE_DEP_ELECTRON=1`
- [ ] Verify actual release (non-dry-run) uploads assets correctly

Generated with [Claude Code](https://claude.com/claude-code)

[FR-2612]: https://lablup.atlassian.net/browse/FR-2612?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
@graphite-app graphite-app Bot force-pushed the 04-16-chore_optimize_release_workflow_with_parallel_jobs_and_eliminate_double_react_build branch from 245813b to b1127f6 Compare April 21, 2026 04:46
@graphite-app graphite-app Bot merged commit b1127f6 into main Apr 21, 2026
11 checks passed
@graphite-app graphite-app Bot deleted the 04-16-chore_optimize_release_workflow_with_parallel_jobs_and_eliminate_double_react_build branch April 21, 2026 04:47
yomybaby added a commit that referenced this pull request Apr 30, 2026
Resolves the gap between PR #6871 (Vite spike for `es6://` publicPath via
`renderBuiltUrl`) and PR #6765 / FR-2612 (single React build + post-patch
policy). The post-patch script was still searching for CRA-era `/static/...`
paths after the Vite migration emitted `/assets/...`, so `make dep` produced
an Electron staging dir that referenced root-relative `/assets/...` that
Electron could not serve via the `es6://` scheme handler.

- `bash scripts/verify.sh` → Relay / Lint / Format / TypeScript ALL PASS
- `make clean && make dep` → "Patched 2 file(s). Verification passed: index.html contains es6://assets/"
- `make dep` run twice → idempotent skip ("Electron app already prepared, skipping")
- `pnpm run electron:d` → renderer spawns with `es6://` scheme registered, login UI renders, all stylesheets load (rule counts > 0), body bg = rgb(247, 247, 246), `did-fail-load` / `webRequest err` / 404s = 0
- `make mac_arm64` → `compile_localproxy` + `electron-packager` (.app) produced cleanly. (DMG step fails on a local `NODE_MODULE_VERSION` mismatch in a globally-installed `electron-installer-dmg`; unrelated to this fix and does not affect CI which uses `npx`.)

- Replace CRA-era patch patterns (`/static/js`, `/static/css`, `asset-manifest.json`, webpack runtime `.p='/'` rewrites) with Vite-aware patterns.
- Patch `index.html`: rewrite `="/assets/` → `="es6://assets/` on `src`/`href` attributes only.
- Patch CSS files under `assets/`: `url(/assets/...)` → `url(es6://assets/...)` in all quote styles.
- Drop dead Webpack-specific code paths (`asset-manifest.json`, `static/{js,css}`, webpack runtime `publicPath` assignment) that no longer exist in Vite output.
- Update verification marker from `es6://static/js/main` to `es6://assets/`.
- Leave `<base href="/">` untouched: the renderer loads `index.html` via `file://`, and rewriting `<base>` to `es6://` would break `window.location`/origin alignment (history API, same-origin checks). Webpack-era patch script also did not touch `<base>`.
- Verification failure now exits non-zero so the Makefile recipe aborts instead of silently continuing.

- Update `dep_electron` idempotency marker from `es6://static/js/main` to `es6://assets/` to match the new patch script.
- Replace `;` chaining with `&&` between the `dep_electron` commands so a failed patch script aborts the recipe.
- Update inline comment that referenced the old marker.

- Remove the `BUILD_TARGET=electron` + `experimental.renderBuiltUrl` branch (PR #6871 spike). FR-2612 mandates a single web build with post-patch; the `renderBuiltUrl` path would require a second `vite build` per release and was never wired into the Makefile, making it dead code with regression risk if anyone toggled `BUILD_TARGET=electron`.
- Drop the now-unused `command` parameter from `defineConfig`.

- Remove three unused CRA-era devDependencies: `@svgr/webpack` (Vite uses `vite-plugin-svgr`), `react-scripts` (CRA core, retired in FR-2611), `react-dev-utils` (no imports anywhere). All three were verified to have zero references in source.

Sits on top of #7113 (FR-2606 residual build-time warnings).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
graphite-app Bot pushed a commit that referenced this pull request Apr 30, 2026
Resolves the gap between PR #6871 (Vite spike for `es6://` publicPath via
`renderBuiltUrl`) and PR #6765 / FR-2612 (single React build + post-patch
policy). The post-patch script was still searching for CRA-era `/static/...`
paths after the Vite migration emitted `/assets/...`, so `make dep` produced
an Electron staging dir that referenced root-relative `/assets/...` that
Electron could not serve via the `es6://` scheme handler.

## Verification (PR #7113 head, this fix applied)

- `bash scripts/verify.sh` → Relay / Lint / Format / TypeScript ALL PASS
- `make clean && make dep` → "Patched 2 file(s). Verification passed: index.html contains es6://assets/"
- `make dep` run twice → idempotent skip ("Electron app already prepared, skipping")
- `pnpm run electron:d` → renderer spawns with `es6://` scheme registered, login UI renders, all stylesheets load (rule counts > 0), body bg = rgb(247, 247, 246), `did-fail-load` / `webRequest err` / 404s = 0
- `make mac_arm64` → `compile_localproxy` + `electron-packager` (.app) produced cleanly. (DMG step fails on a local `NODE_MODULE_VERSION` mismatch in a globally-installed `electron-installer-dmg`; unrelated to this fix and does not affect CI which uses `npx`.)

## Changes

### `scripts/patch-electron-publicpath.js`
- Replace CRA-era patch patterns (`/static/js`, `/static/css`, `asset-manifest.json`, webpack runtime `.p='/'` rewrites) with Vite-aware patterns.
- Patch `index.html`: rewrite `="/assets/` → `="es6://assets/` on `src`/`href` attributes only.
- Patch CSS files under `assets/`: `url(/assets/...)` → `url(es6://assets/...)` in all quote styles.
- Drop dead Webpack-specific code paths (`asset-manifest.json`, `static/{js,css}`, webpack runtime `publicPath` assignment) that no longer exist in Vite output.
- Update verification marker from `es6://static/js/main` to `es6://assets/`.
- Leave `<base href="/">` untouched: the renderer loads `index.html` via `file://`, and rewriting `<base>` to `es6://` would break `window.location`/origin alignment (history API, same-origin checks). Webpack-era patch script also did not touch `<base>`.
- Verification failure now exits non-zero so the Makefile recipe aborts instead of silently continuing.

### `Makefile`
- Update `dep_electron` idempotency marker from `es6://static/js/main` to `es6://assets/` to match the new patch script.
- Replace `;` chaining with `&&` between the `dep_electron` commands so a failed patch script aborts the recipe.
- Update inline comment that referenced the old marker.

### `react/vite.config.ts`
- Remove the `BUILD_TARGET=electron` + `experimental.renderBuiltUrl` branch (PR #6871 spike). FR-2612 mandates a single web build with post-patch; the `renderBuiltUrl` path would require a second `vite build` per release and was never wired into the Makefile, making it dead code with regression risk if anyone toggled `BUILD_TARGET=electron`.
- Drop the now-unused `command` parameter from `defineConfig`.

### `react/package.json`
- Remove three unused CRA-era devDependencies: `@svgr/webpack` (Vite uses `vite-plugin-svgr`), `react-scripts` (CRA core, retired in FR-2611), `react-dev-utils` (no imports anywhere). All three were verified to have zero references in source.

## Stack

Sits on top of #7113 (FR-2606 residual build-time warnings).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L 100~500 LoC

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Optimize release workflow with parallel jobs and eliminate double React build

3 participants