# Build and release

> electron-forge make output paths, version bump rules, git tagging, dual remotes (origin/public), /build and /release slash-command workflows, and GitHub release artifacts.

- Repository: Parcha-ai/build
- GitHub: https://github.com/Parcha-ai/build
- Human docs: https://grok-wiki.com/public/docs/parcha-ai-build-bea5702b371b
- Complete Markdown: https://grok-wiki.com/public/docs/parcha-ai-build-bea5702b371b/llms-full.txt

## Source Files

- `forge.config.ts`
- `package.json`
- `scripts/build.sh`
- `.claude/commands/build.md`
- `.claude/commands/release.md`
- `CLAUDE.md`

---

---
title: "Build and release"
description: "electron-forge make output paths, version bump rules, git tagging, dual remotes (origin/public), /build and /release slash-command workflows, and GitHub release artifacts."
---

Production distributables are produced by **Electron Forge** via `npm run make`, which reads `package.json` `version`, wipes prior webpack and versioned output, and writes artifacts under `out/v{version}/`. The `forge.config.ts` `postPackage` hook copies native externals and the QMD bundle, optionally signs and notarizes on macOS, and can install `Build.app` into `/Applications`. Release tags follow `v{version}`; maintainer slash-command docs describe pushing `master` and tags to both `origin` and `public` remotes before publishing GitHub release assets.

## npm scripts and clean behavior

| Script | Command | Effect |
|--------|---------|--------|
| `start` | `electron-forge start` | Dev server (prefer `./scripts/dev.sh` wrapper) |
| `package` | `electron-forge package` | Packaged app without installers |
| `clean:build` | Node one-liner | Deletes `.webpack` and `out/v{npm_package_version}` |
| `make` | `npm run clean:build && electron-forge make` | Full production build + makers |
| `publish` | `electron-forge publish` | Forge publish flow (not the GitHub release skill) |

`clean:build` ties cleanup to the **current** `package.json` version. Bump the version before `make` if you intend to keep a prior `out/v*` tree; otherwise the matching directory is removed at the start of every `make`.

<Warning>
Agent-facing build docs say **never** `pkill` Electron before `npm run make` because other worktrees may be running. The repo also ships `scripts/build.sh`, which **does** stop running `Build.app` and forge processes. Use the script only when you intend that behavior.
</Warning>

## Forge output layout

`forge.config.ts` sets a version-scoped output root:

```text
out/v{version}/
├── Build-darwin-arm64/
│   └── Build.app                 # runnable macOS arm64 bundle
└── make/
    ├── zip/darwin/arm64/
    │   └── Build-darwin-arm64-{version}.zip
    └── Build-{version}-arm64.dmg # MakerDMG (ULFO format)
```

Packager metadata from the same config:

| Field | Value |
|-------|--------|
| `outDir` | `./out/v${version}` from `package.json` |
| `packagerConfig.name` | `Build` |
| `packagerConfig.executableName` | `build` |
| `packagerConfig.appBundleId` | `com.parcha.build` |
| macOS bundle path | `Build.app` inside `Build-darwin-arm64/` |

Configured makers: Squirrel (Windows), ZIP (darwin), DMG, RPM, Deb. Day-to-day release docs and the GitHub release skill target **macOS Apple Silicon** zip and dmg only.

`scripts/build.sh` mirrors the app path: `./out/v${VERSION}/Build-darwin-arm64/Build.app`, runs `npm run make`, force-creates `git tag -f v${VERSION}`, and opens the app.

<Note>
Older slash-command text and test notes refer to **Grep Build** paths (`Grep Build-darwin-arm64`, `Grep Build.app`). Current `productName` is **Build**; first-launch migration in `src/main/index.ts` moves user data from `Grep Build` / `G-Build` Application Support folders into **Build**.
</Note>

## postPackage hook (packaging pipeline)

After Electron Packager finishes, `postPackage` runs per output path:

```mermaid
flowchart TB
  subgraph forge [electron-forge make]
    WP[WebpackPlugin build]
    PKG[electron-packager]
    PP[postPackage hook]
  end
  subgraph pp [postPackage]
    NM[Copy webpack externals to Resources/node_modules]
    QMD[Copy resources/qmd platform bundle]
    SIG[Developer ID sign + notarize OR adhoc codesign]
    APP[Copy Build.app to /Applications on darwin]
  end
  WP --> PKG --> PP
  PP --> NM --> QMD --> SIG --> APP
```

**Externals copied** (must match `webpack.main.config.ts`): `node-pty`, Claude/Cursor SDK packages, `docx`, `monaco-editor`, and their dependency trees.

**QMD**: Copied from `resources/qmd/{platform}-{arch}` into `Resources/qmd`. Missing platform bundles log a warning to run `npm run setup-qmd` (or `setup-qmd:all`).

**macOS signing**:

| Condition | Behavior |
|-----------|----------|
| `APPLE_ID`, `APPLE_PASSWORD`, `APPLE_TEAM_ID` set | `@electron/osx-sign` with Developer ID + `@electron/notarize` |
| Credentials absent | Adhoc `codesign --force --sign -` on `Build.app` |

`packagerConfig.osxSign` is `false` during packager so post-copy signing stays valid.

**Install shortcut**: On darwin, signed or adhoc bundle is copied to `/Applications/Build.app` (failures are logged, not fatal).

### Offline / cache environment variables

<ParamField body="GREP_ELECTRON_ZIP_DIR" type="string">
Local Electron zip directory passed to `packagerConfig.electronZipDir` when set.
</ParamField>

<ParamField body="GREP_ELECTRON_CACHE_ROOT" type="string">
Download cache root when checksum skip is enabled. Default: `~/Library/Caches/electron`.
</ParamField>

<ParamField body="GREP_SKIP_ELECTRON_CHECKSUMS" type="string">
Set to `1` to pass `download.unsafelyDisableChecksums: true` for offline builds.
</ParamField>

## Version bump and verification

The shipped version is `package.json` → `"version"` (for example `0.5.27`). Electron exposes it through `app.getVersion()` (`APP_GET_VERSION` IPC). The status bar loads it on mount and renders **`G-BUILD v{appVersion}`**; dev mode also shows `[DEV_INSTANCE_NAME]` from `./scripts/dev.sh`.

<Steps>
<Step title="Bump patch version">
Increment the **patch** segment in `package.json` before each production release (slash-command docs use `0.0.68` → `0.0.69` style examples).
</Step>
<Step title="Validate in dev">
Run `./scripts/dev.sh` (not bare `npm run start`). Confirm behavior; standard release flow expects explicit QA approval before `make`.
</Step>
<Step title="Build on master">
Merge feature work to `master` first (see merge workflow below), checkout `master`, then `npm run make`.
</Step>
<Step title="Confirm version in UI">
Open the build and check the status bar shows the new `G-BUILD v*` string.
</Step>
</Steps>

## Git tagging and dual remotes

Release tags use the **`v` prefix** matching `package.json` without the prefix: version `0.5.27` → tag `v0.5.27`.

Maintainer slash-command documentation defines two remotes:

| Remote | Documented target | Role |
|--------|-------------------|------|
| `origin` | `Parcha-ai/claudette` | Private development repo |
| `public` | `Parcha-ai/grep-build` | Public mirror; GitHub **releases** attach here |

After build, push **both** remotes:

```bash
git push origin master && git push origin v{version}
git push public master && git push public v{version}
```

Create the tag locally if missing: `git tag v{version}`.

<Info>
`README.md` links end-user downloads to [github.com/Parcha-ai/build/releases](https://github.com/Parcha-ai/build/releases). `CONTRIBUTING.md` still references cloning `grep-build`. The **release** slash command explicitly uses `--repo Parcha-ai/grep-build`. Align the GitHub repo you publish to with your configured `public` remote and org naming at release time.
</Info>

### Merge-to-master before build

Force and standard build flows require **master** as the build branch:

1. Push the feature branch to `origin`.
2. If `master` is checked out in another worktree, open a PR with `gh pr create` / `gh pr merge --merge --delete-branch`.
3. Otherwise merge locally: `git checkout master`, `git merge {branch}`, `git push origin master`.
4. `git pull origin master` on `master`, then run `npm run make`.

## Production build workflows

### Standard release (QA-gated)

Intended when changes are not yet validated in dev:

1. Start `./scripts/dev.sh`.
2. Wait for explicit confirmation that dev behavior is correct.
3. Bump `package.json` version (patch).
4. Merge to `master` and checkout `master`.
5. `npm run make`.
6. `git tag v{version}`.
7. Push `master` and tag to `origin` and `public`.
8. Open `out/v{version}/Build-darwin-arm64/Build.app` (or rely on postPackage `/Applications` copy).

Do **not** kill unrelated Electron processes in other worktrees during agent-driven builds.

### Fast release (skip QA)

For changes already verified in dev:

1. Bump patch version in `package.json`.
2. Merge to `master` and checkout `master`.
3. `npm run make`.
4. Tag and dual-remote push as above.
5. Open the built app and note artifact paths under `out/v{version}/make/`.

### Helper script

`./scripts/build.sh` automates version read, process stop, `npm run make`, `git tag -f v{version}`, and `open` on `Build.app`. Prefer the QA-aware flow when using agent slash commands unless you explicitly want the script’s kill-and-build semantics.

## GitHub release artifacts

The **release** workflow does not build or bump versions. It assumes `npm run make` already produced installers.

<Steps>
<Step title="Sync git">
Push `master` and `v{version}` to `origin` and `public` (create tag locally if needed).
</Step>
<Step title="Verify artifacts">
Confirm both exist (legacy skill names **Grep Build**; current product name **Build**):

- `out/v{version}/make/zip/darwin/arm64/Build-darwin-arm64-{version}.zip`
- `out/v{version}/make/Build-{version}-arm64.dmg`

If missing, run a production build first and stop.
</Step>
<Step title="Generate notes">
`git log $(git tag --sort=-version:refname | sed -n '2p')..v{version} --oneline --no-decorate` → bullet list under `## Changes`.
</Step>
<Step title="Create release">
```bash
gh release create v{version} \
  "out/v{version}/make/zip/darwin/arm64/Build-darwin-arm64-{version}.zip" \
  "out/v{version}/make/Build-{version}-arm64.dmg" \
  --repo Parcha-ai/grep-build \
  --title "Grep Build v{version}" \
  --latest \
  --notes "..."
```
Upload is large (~500MB combined); use `--repo` explicitly. If the release exists: `gh release delete v{version} --repo Parcha-ai/grep-build --yes` after user confirmation.
</Step>
</Steps>

Release title in the skill template still says **Grep Build**; artifact file names follow **Build** from `productName`.

## Dev vs production separation

| Aspect | Dev (`./scripts/dev.sh`) | Production (`npm run make`) |
|--------|--------------------------|-----------------------------|
| User data | `GREP_DEV_USER_DATA=/tmp/grep-build-dev` | `~/Library/Application Support/Build` |
| Webpack logger port | `9001` (avoids prod `9000`) | N/A (packaged) |
| Instance label | Random adjective-noun in status bar | None |
| QMD setup | `npm run setup-qmd` before start | Bundled in `postPackage` |
| Output | `.webpack/` hot reload | `out/v{version}/` |

Production builds under `out/` should stay running when starting dev; `dev.sh` only kills Electron from `node_modules`, not `out/` bundles.

## Prerequisites for distributable quality

- **Node/npm**: Install deps with `npm install`; native modules (`node-pty`) rely on Electron rebuild via Forge.
- **QMD**: Run `npm run setup-qmd` (or `setup-qmd:all`) before `make` so `resources/qmd/darwin-arm64` exists for bundling.
- **Apple distribution**: Set `APPLE_ID`, `APPLE_PASSWORD`, `APPLE_TEAM_ID` for signed, notarized macOS builds (README claims signed/notarized releases for download).
- **Entitlements**: `entitlements.plist` referenced by Developer ID signing path.

## Related pages

<CardGroup>
<Card title="Installation" href="/installation">
macOS release download, from-source setup, and dev vs production user data paths.
</Card>
<Card title="npm scripts reference" href="/npm-scripts-reference">
When to use `./scripts/dev.sh` vs `npm run start`, lint, setup-qmd, and verify scripts.
</Card>
<Card title="Semantic search (QMD)" href="/semantic-search-qmd">
QMD bundling in dev and the postPackage copy step.
</Card>
<Card title="Electron process model" href="/electron-architecture">
Main/renderer split, webpack output, and packaged runtime layout.
</Card>
<Card title="Troubleshooting" href="/troubleshooting">
Port conflicts, PATH in packaged apps, and production `main.log` location.
</Card>
</CardGroup>
