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
6 changes: 3 additions & 3 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<!-- Thanks for contributing to DevHarbor! Keep PRs focused one logical change each. -->
<!-- Thanks for contributing to DevHarbor! Keep PRs focused - one logical change each. -->

## What & why

Expand All @@ -8,8 +8,8 @@

- [ ] `pnpm typecheck` passes
- [ ] `pnpm test` passes
- [ ] Verified in the running app (`pnpm dev`) if this touches UI or behaviour
- [ ] Updated the relevant `specs/` file + `specs/PROGRESS.md` if behaviour changed
- [ ] Verified in the running app (`pnpm dev`) - if this touches UI or behaviour
- [ ] Updated the relevant `specs/` file + `specs/PROGRESS.md` - if behaviour changed

## Notes

Expand Down
54 changes: 54 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
name: CI

# Typecheck + tests on every push to main and every PR. Until now the only workflow was the
# tag-triggered release, so build/test rot (or a contributor's broken PR) was only discovered
# during a signed release run (IMPROVEMENT-PLAN 12.1). CONTRIBUTING.md promises `pnpm test`
# must pass before merge - this enforces it.

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

permissions:
contents: read

concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true

jobs:
check:
# macOS matches the release runner so the better-sqlite3 / node-pty electron-rebuild
# postinstall is exercised the same way it will be at release time.
runs-on: macos-14
timeout-minutes: 20
steps:
- uses: actions/checkout@v5

- name: Set up Python 3.11 (node-gyp needs distutils)
uses: actions/setup-python@v6
with:
python-version: '3.11'

- uses: pnpm/action-setup@v6
with:
version: 10

- uses: actions/setup-node@v5
with:
node-version: 22
cache: pnpm

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Typecheck
run: pnpm typecheck

- name: Lint
run: pnpm lint

- name: Test
run: pnpm test
32 changes: 25 additions & 7 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,21 +24,37 @@ jobs:
runs-on: macos-14
timeout-minutes: 60
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5

# node-gyp 9 (used by @electron/rebuild when compiling native modules — e.g. node-pty
# Guard (tag pushes only): a release must be cut from a v* tag whose number matches
# package.json. Manual workflow_dispatch runs skip the guard but build with
# --publish never (see the build step) - a safe dry-run for testing the pipeline.
- name: Guard - tag matches package.json version
if: github.event_name == 'push'
run: |
if [[ "${GITHUB_REF}" != refs/tags/v* ]]; then
echo "::error::Releases must be cut from a v* tag (got ${GITHUB_REF})."; exit 1
fi
VERSION=$(grep -m1 '"version"' package.json | sed -E 's/.*"version"[[:space:]]*:[[:space:]]*"([^"]+)".*/\1/')
TAG="${GITHUB_REF_NAME#v}"
if [ "${VERSION}" != "${TAG}" ]; then
echo "::error::package.json version (${VERSION}) does not match tag (${TAG})."; exit 1
fi
echo "Releasing v${VERSION} from tag ${GITHUB_REF_NAME}."

# node-gyp 9 (used by @electron/rebuild when compiling native modules - e.g. node-pty
# for the cross-arch x64 build) imports the stdlib `distutils`, which Python 3.12
# removed (PEP 632). macos-14 runners default to 3.12, so pin 3.11 which still has it.
- name: Set up Python 3.11 (node-gyp needs distutils)
uses: actions/setup-python@v5
uses: actions/setup-python@v6
with:
python-version: '3.11'

- uses: pnpm/action-setup@v4
- uses: pnpm/action-setup@v6
with:
version: 10

- uses: actions/setup-node@v4
- uses: actions/setup-node@v5
with:
node-version: 22
cache: pnpm
Expand Down Expand Up @@ -67,11 +83,13 @@ jobs:
# ~103MB arm64 DMG; Intel users the x64 DMG. electron-builder writes a single
# latest-mac.yml listing both zips, and electron-updater fetches the slice that
# matches the running machine. Smaller downloads than a combined universal binary.
run: pnpm exec electron-vite build && pnpm exec electron-builder --mac --arm64 --x64 --publish always
# Tag pushes publish; manual dispatch runs are dry-runs (build + sign only, artifacts
# uploaded by the next step) so the pipeline can be exercised without touching releases.
run: pnpm exec electron-vite build && pnpm exec electron-builder --mac --arm64 --x64 --publish ${{ github.event_name == 'push' && 'always' || 'never' }}

- name: Upload artifacts (as backup)
if: always()
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
with:
name: mac-dmg
path: |
Expand Down
15 changes: 15 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Generated / machine-managed
pnpm-lock.yaml
dist/
out/
node_modules/
*.tsbuildinfo

# Hand-formatted corpora a blanket `pnpm format` must not churn:
# specs are the project's source-of-truth documents; the workflows are the signed
# release pipeline; CHANGELOG follows keep-a-changelog conventions.
specs/
.github/
CHANGELOG.md
README.md
electron-builder.yml
7 changes: 7 additions & 0 deletions .prettierrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"semi": true,
"singleQuote": true,
"trailingComma": "none",
"printWidth": 100,
"tabWidth": 2
}
82 changes: 75 additions & 7 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,86 @@ All notable changes to DevHarbor are documented here. The format is based on

## [Unreleased]

## [1.0.1] — 2026-06-07
## [1.1.0] - 2026-06-14

A hardening + feature release driven by a comprehensive internal audit (see
`specs/IMPROVEMENT-PLAN.md`).

> **Downgrading:** after running 1.1.0 once, secret env values are encrypted at rest and the
> database schema is upgraded. Going back to 1.0.x is not supported - export a backup first
> (Settings → Danger zone → Export) if you want a safety net.

### Added
- **Menubar (tray) presence** - see aggregate state at a glance; start/stop any app, see its
ports, or stop everything without opening the window. Toggle in Settings.
- **Desktop notifications** when an app crashes (on by default) and, optionally, when an app
becomes ready.
- **Bulk import** - point DevHarbor at a folder of repos and register every detected project
in one pass (sidebar `+` menu, ⌘K, or the welcome screen).
- **Global log search (⌘⇧F)** - search every running task's logs at once; jump to the app.
- **Monorepo detection** - pnpm/yarn/npm workspaces are detected on add, with a one-click
"create a task per workspace package" option.
- **Launch at login** and per-app **start automatically** toggles.
- **Start all / Stop all** per folder, and keyboard reordering for folders (Move up / down).
- **Quit confirmation** that gracefully stops running servers (SIGTERM → grace → kill) before
exiting - auto-update installs included.
- **Port-conflict callout** - a crash caused by a busy port now names the port and, when
known, which app holds it.
- Update banner shows **release notes** before "Quit & install"; **Check for Updates…** and
**Open Logs Folder** in the Help menu; update checks repeat every 6 h on long-running Macs.
- Local **diagnostics log** (`~/Library/Logs/DevHarbor/`) for bug reports - local-only, in
keeping with the no-telemetry promise.
- Window size and position are remembered across launches.

### Changed
- **Secret env values are encrypted at rest** (macOS Keychain-backed `safeStorage`); existing
plaintext secrets are migrated on first launch.
- Project `.env` files can no longer override env vars you set in DevHarbor, nor
process-control variables (`PATH`, `NODE_OPTIONS`, `DYLD_*`) - the UI is the source of
truth, and a checked-in `.env` can't hijack the spawned process.
- `.env` parsing now matches dotenv semantics: multiline quoted values (PEM keys), `export `
prefixes, and trailing comments; `.env.development` / `.env.development.local` variants
load in the conventional order.
- Stopping a task now signals the **whole process tree** (graceful SIGTERM first), so dev
servers get a clean shutdown instead of skipping straight to a hard kill.
- Hardened packaged binaries: Electron fuses disable `ELECTRON_RUN_AS_NODE`, `NODE_OPTIONS`,
and `--inspect`; app code loads only from the asar archive.
- Keyboard + screen-reader support across the app: focus-trapped dialogs with Escape,
arrow-key menus and comboboxes, focusable sidebar rows, accessible status indicators,
screen-reader-readable logs, and a light terminal theme.
- Faster under load: batched process polling (one `ps`/`lsof` per tick instead of dozens of
forks), a global log-memory budget, render-throttled log filtering, and log streaming only
for visible tasks.

### Fixed
- **Restart-on-change** now works (the file watcher had been silently inert after a
dependency major bump) - with regression tests.
- Apps that had ever run can be **removed** again; lifecycle errors (missing Node version,
moved folder, no tasks) now surface as messages from every view instead of failing silently.
- A failed start no longer leaves the app stuck on "Starting"; Stop is instant even while an
app is still starting; readiness probes time out instead of hanging forever.
- One-shot tasks (`exit` readiness) are no longer marked ready before they finish.
- Deleted task env vars no longer resurrect after a relaunch; task-scoped overrides of an
app-level key now save correctly.
- Database export/reset are WAL-safe (backups no longer miss recent changes); a corrupt
database produces a recovery dialog instead of a silent no-window launch.
- Env editor warns before discarding unsaved changes and confirms saves; long app names
truncate instead of breaking layouts; the welcome screen no longer flashes on startup for
existing users; many smaller fixes.

## [1.0.1] - 2026-06-07

### Changed
- Canonical website domain is now **www.devharbor.app** (About panel, Help menu, repo homepage).

## [1.0.0] 2026-06-07
## [1.0.0] - 2026-06-07

First public stable release: code-signed, notarized, and auto-updating.

### Added
- **Code-signed + notarized** macOS builds with hardened runtime; **per-architecture** (Apple Silicon + Intel) DMG/zip artifacts and GitHub-based **auto-update** via `electron-updater`.
- **Folders** in the sidebar group apps, drag to reorder, rename/delete, collapse; a glowing dot marks collapsed folders that contain a running app.
- **Tags** chip input with autocomplete in app settings; group the sidebar by tag, filter on the dashboard, and search tags in ⌘P.
- **Folders** in the sidebar - group apps, drag to reorder, rename/delete, collapse; a glowing dot marks collapsed folders that contain a running app.
- **Tags** - chip input with autocomplete in app settings; group the sidebar by tag, filter on the dashboard, and search tags in ⌘P.
- **Group-by switcher** (Folder ⇄ Tag) and a **shared sort** (Name · Recently used · Running first) across the sidebar and dashboard.
- **Folder picker** combobox in app settings (select existing or create new).
- Open-source community files (CONTRIBUTING, Code of Conduct, Security policy, issue/PR templates).
Expand All @@ -31,12 +98,12 @@ First public stable release: code-signed, notarized, and auto-updating.
- Detail-page lifecycle buttons read **Start app / Stop app / Restart app**.

### Fixed
- Stopped apps now read **Stopped/Crashed** consistently including after the teardown window and across app restarts (persisted from run history) instead of flickering back to Idle.
- Stopped apps now read **Stopped/Crashed** consistently - including after the teardown window and across app restarts (persisted from run history) - instead of flickering back to Idle.
- Single-instance relaunch always surfaces a window (no more "running but no window" dead state).
- Smaller install: renderer libraries are bundled, not double-shipped as `node_modules` (asar ~40 MB → ~4 MB).
- Numerous correctness fixes from an internal review (atomic app-add, env parsing, stale stats, navigation origin checks, folder-state drift).

## [0.1.0] 2026-05-30
## [0.1.0] - 2026-05-30

First public preview. macOS-only (Apple Silicon).

Expand All @@ -50,7 +117,8 @@ First public preview. macOS-only (Apple Silicon).
- Dashboard control room, folder organization, and a ⌘K command palette.
- Local-only storage (SQLite). No accounts, no telemetry.

[Unreleased]: https://github.com/jainath/devharbor/compare/v1.0.1...HEAD
[Unreleased]: https://github.com/jainath/devharbor/compare/v1.1.0...HEAD
[1.1.0]: https://github.com/jainath/devharbor/compare/v1.0.1...v1.1.0
[1.0.1]: https://github.com/jainath/devharbor/compare/v1.0.0...v1.0.1
[1.0.0]: https://github.com/jainath/devharbor/compare/v0.1.0...v1.0.0
[0.1.0]: https://github.com/jainath/devharbor/releases/tag/v0.1.0
24 changes: 12 additions & 12 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Contributing to DevHarbor

Thanks for your interest in DevHarbor! This is a macOS-first desktop app for managing
local Node.js dev servers. Contributions of all kinds are welcome bug reports, fixes,
local Node.js dev servers. Contributions of all kinds are welcome - bug reports, fixes,
features, docs, and design feedback.

By participating you agree to abide by our [Code of Conduct](CODE_OF_CONDUCT.md).
Expand All @@ -20,11 +20,11 @@ pnpm test # vitest

## Prerequisites

- **macOS** (Apple Silicon or Intel). The app is macOS-only today it relies on
- **macOS** (Apple Silicon or Intel). The app is macOS-only today - it relies on
`lsof`/`pgrep`, `open -a`, and Unix Node-manager paths. Windows/Linux are not supported
yet (see the roadmap).
- **Node 22+** (an `.nvmrc` pins the version) and **pnpm** (`npm i -g pnpm`).
- **Xcode Command Line Tools** (`xcode-select --install`) needed to rebuild the native
- **Xcode Command Line Tools** (`xcode-select --install`) - needed to rebuild the native
modules (`better-sqlite3`, `node-pty`).

If `pnpm install` fails rebuilding native modules, see
Expand All @@ -37,7 +37,7 @@ src/main/ Electron main process (services, IPC, DB, menu)
src/preload/ contextBridge API surface
src/renderer/ React UI (components, store, hooks)
src/shared/ shared types + the typed IPC contract (ipc.ts)
specs/ the source of truth read before non-trivial work
specs/ the source of truth - read before non-trivial work
```

## The specs are the source of truth
Expand All @@ -48,16 +48,16 @@ DevHarbor is **spec-driven**. Before non-trivial work, read the relevant file in
[`specs/PROGRESS.md`](specs/PROGRESS.md) for the current build status.

Quick map:
- [`specs/01-architecture.md`](specs/01-architecture.md) process model, IPC, stack
- [`specs/02-data-model.md`](specs/02-data-model.md) SQLite schema + core types
- [`specs/03-features.md`](specs/03-features.md) every feature with acceptance criteria
- [`specs/04-ui.md`](specs/04-ui.md) screens, layout, interactions
- [`specs/01-architecture.md`](specs/01-architecture.md) - process model, IPC, stack
- [`specs/02-data-model.md`](specs/02-data-model.md) - SQLite schema + core types
- [`specs/03-features.md`](specs/03-features.md) - every feature with acceptance criteria
- [`specs/04-ui.md`](specs/04-ui.md) - screens, layout, interactions

## Development workflow

1. **Fork** the repo and create a branch off `main`:
`git checkout -b fix/short-description`
2. Make your change. Keep it focused one logical change per PR.
2. Make your change. Keep it focused - one logical change per PR.
3. **Match the surrounding code.** Follow existing naming, comment density, and idioms.
4. Run the checks locally:
```bash
Expand All @@ -76,7 +76,7 @@ Quick map:
`sandbox: true`. Don't loosen these.
- **Native modules** (`better-sqlite3`, `node-pty`) must be rebuilt for Electron's ABI
(`pnpm rebuild`), not Node's.
- **Zustand selectors must return stable references** `?? []` / `?? {}` inside a
- **Zustand selectors must return stable references** - `?? []` / `?? {}` inside a
selector causes infinite re-render loops. Use module-level constants.

## Tests
Expand All @@ -95,7 +95,7 @@ Quick map:
## Reporting bugs / requesting features

Use the [issue templates](.github/ISSUE_TEMPLATE/). For security issues, **do not open a
public issue** see [SECURITY.md](SECURITY.md).
public issue** - see [SECURITY.md](SECURITY.md).

## License

Expand All @@ -104,4 +104,4 @@ By contributing, you agree that your contributions will be licensed under the pr
submit the work under that license.

Note the [trademark policy](TRADEMARK.md): the code is AGPL, but the **DevHarbor name and logo
are not** forks must ship under a different name.
are not** - forks must ship under a different name.
Loading