From cd269c83a1750ac727e15efb1e674a9c72a837c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Maritano?= Date: Tue, 9 Jun 2026 11:13:07 -0300 Subject: [PATCH 1/6] fix(release): cut v0.15.1 after Phase 0 DevOps stabilization (#297) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Release v0.15.1 — Phase 0 DevOps stabilization Brings the 7-PR DevOps cleanup chain to main and cuts a clean release. This is the verification gate for the whole Phase 0 effort — if anything breaks at tag, build, or publish, Phase 0 isn't done. ### What landed since v0.15.0 | PR | Phase | Summary | |----|-------|---------| | #290 | **A1** | \`fix(desktop)\`: pin Electron to ^41.7.1 so better-sqlite3 prebuilts apply (closes the v0.15.0 V8 ABI failure on all 3 build platforms) | | #291 | **A2** | \`fix(release)\`: restore version bumping via \`scripts/bump-version.mjs\` + \`@semantic-release/exec\` (closes the "tag at 0.14.0" trap) | | #292 | **B** | \`chore(ci)\`: workflow surface cleanup — actions @v4→@v5 sweep, \`windows-latest\` → \`windows-2025-vs2026\` pin, drop \`FORCE_JAVASCRIPT_ACTIONS_TO_NODE24\`, \`if-no-files-found: error\`, \`permissions:\` blocks, HUSKY: '0' removal | | #293 | **A4** | \`chore(ci)\`: \`release.yml\` pre-flight dry-run gate + post-flight version assertion (closes the "silent no-release" trap) | | #294 | **C1** | \`ci\`: PR-title commitlint as a standalone workflow → required check on develop + main | | #295 | | \`fix(lint)\`: develop lint baseline (preserve-caught-error × 4 in encryptionService + mcp-server tsconfig split for ESLint projectService) | | #296 | | \`chore(ci)\`: unblock CI on develop — ignore CHANGELOG.md in Prettier (semantic-release writes it), \`pnpm install --ignore-scripts\` in setup job (same shape as release.yml + deploy-api.yml) | ### C2 — branch protection updates (already applied via gh api) Both \`develop\` and \`main\`: - **Required status checks**: \`lint\`, \`test\`, \`typecheck\`, \`CodeRabbit\`, \`commitlint\` - Force-pushes blocked - \`strict: true\` (PRs must be up to date) ### Release pipeline guardrails now in place - **Pre-merge**: PR-title commitlint blocks \`release:\`-style non-conventional squash titles upstream. - **Mid-release**: \`release.yml\` dry-run check fails loud if no release would be cut. \`scripts/bump-version.mjs\` mutates both \`package.json\` files. Post-flight assertion verifies both match the dry-run-announced version. - **Post-release**: \`build.yml\` artifact upload uses \`if-no-files-found: error\` (silent zero-asset releases die at upload). - **Native deps**: \`apps/desktop\` pinned to Electron 41.7.1 with prebuilt better-sqlite3. CI \`setup\` skips postinstall so workflow-side install never rebuilds native modules. ### Expected behavior of the Release pipeline after merge 1. Merge this PR → main tip advances. 2. Manually dispatch the **Release** workflow. 3. \`release.yml\` runs: - \`pnpm install --ignore-scripts\` (no native rebuild needed for semantic-release). - **Pre-flight dry-run** → "next release version is 0.15.1" (single \`fix(release):\` commit since v0.15.0). - \`npx semantic-release\`: - \`@semantic-release/exec\` runs \`node scripts/bump-version.mjs 0.15.1\` → both package.json files updated. - \`@semantic-release/git\` commits + pushes tag \`v0.15.1\`. - \`@semantic-release/github\` creates draft Release. - **Post-flight assertion** → both package.jsons read \`0.15.1\`. 4. Tag push triggers \`build.yml\` on macOS-14, windows-2025-vs2026, ubuntu-latest. 5. All 3 platforms succeed → publish job un-drafts the GitHub Release. 6. Auto-sync PR opens to merge main → develop. ### What still needs verification (post-release) - [ ] Tag push actually triggers Build (needs GH_TOKEN with workflow scope — A3 deferred, may need PAT regen) - [ ] Build completes on all 3 platforms with prebuilt better-sqlite3 (smoke-test desktop bundle after publish) - [ ] Auto-sync PR back to develop is created 🤖 This is the Phase 0 verification gate. Mobile + Plugin Marketplace UI remain deferred. ## Summary by CodeRabbit * **New Features** * Added PR title validation workflow for automated commit message compliance checks. * **Bug Fixes** * Enhanced error diagnostics in encryption operations. * Added pre-flight checks to release process to prevent failed deployments. * Stricter artifact validation in builds. * **Chores** * Updated GitHub Actions to latest stable versions. * Improved code formatting configuration and build scripts. * Adjusted Electron dependency version. --------- Signed-off-by: dependabot[bot] Co-authored-by: Claude Opus 4.6 Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build.yml | 22 +++-- .github/workflows/ci.yml | 63 +++++++------ .github/workflows/codeql.yml | 2 +- .github/workflows/deploy-api.yml | 15 ++- .github/workflows/docs.yml | 20 ++-- .github/workflows/pr-title.yml | 54 +++++++++++ .github/workflows/release.yml | 60 +++++++++++- .prettierignore | 11 +++ apps/desktop/package.json | 2 +- .../src/main/services/encryptionService.ts | 12 ++- package.json | 4 +- packages/mcp-server/package.json | 2 +- packages/mcp-server/tsconfig.build.json | 4 + packages/mcp-server/tsconfig.json | 5 +- pnpm-lock.yaml | 45 ++++----- release.config.js | 18 ++-- scripts/bump-version.mjs | 91 +++++++++++++++++++ 17 files changed, 337 insertions(+), 93 deletions(-) create mode 100644 .github/workflows/pr-title.yml create mode 100644 .prettierignore create mode 100644 packages/mcp-server/tsconfig.build.json create mode 100644 scripts/bump-version.mjs diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 55ab9867..b2bdf567 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -13,7 +13,6 @@ env: NODE_VERSION: '22' ELECTRON_CACHE: ~/.cache/electron ELECTRON_BUILDER_CACHE: ~/.cache/electron-builder - FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true jobs: build: @@ -23,7 +22,10 @@ jobs: include: - os: macos-14 platform: mac - - os: windows-latest + # Pin Windows runner explicitly. `windows-latest` migrates to + # `windows-2025-vs2026` on 2026-06-15; pinning ahead avoids a + # surprise toolchain swap mid-release. Revisit on each new GA. + - os: windows-2025-vs2026 platform: win - os: ubuntu-latest platform: linux @@ -32,7 +34,7 @@ jobs: steps: - name: Checkout tag - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: ref: ${{ github.ref }} fetch-depth: 0 @@ -41,20 +43,20 @@ jobs: uses: pnpm/action-setup@v5 - name: Setup Node.js - uses: actions/setup-node@v4 + uses: actions/setup-node@v5 with: node-version: ${{ env.NODE_VERSION }} cache: 'pnpm' - name: Cache pnpm store - uses: actions/cache@v4 + uses: actions/cache@v5 with: path: ~/.pnpm-store key: pnpm-${{ matrix.platform }}-${{ hashFiles('pnpm-lock.yaml') }} restore-keys: pnpm-${{ matrix.platform }}- - name: Cache Electron downloads - uses: actions/cache@v4 + uses: actions/cache@v5 with: path: ${{ env.ELECTRON_CACHE }} key: electron-v3-${{ matrix.platform }}-${{ hashFiles('apps/desktop/package.json') }} @@ -105,7 +107,7 @@ jobs: run: pnpm dist:linux --publish always - name: Upload artifacts (backup) - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 with: name: ${{ matrix.platform }}-build path: | @@ -116,7 +118,11 @@ jobs: apps/desktop/release/*.deb apps/desktop/release/latest*.yml apps/desktop/release/*.blockmap - if-no-files-found: ignore + # `error` (was `ignore`) — if the build silently produced zero + # artefacts (signing failure, electron-builder swallowed an + # error, wrong working-directory, etc.) we want the job to fail + # loud here, not let the release un-draft with no installers. + if-no-files-found: error retention-days: 30 publish: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c469226e..ae7cf3e3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,13 +19,13 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Setup pnpm uses: pnpm/action-setup@v5 - name: Setup Node.js - uses: actions/setup-node@v4 + uses: actions/setup-node@v5 with: node-version: ${{ env.NODE_VERSION }} cache: 'pnpm' @@ -33,11 +33,18 @@ jobs: - name: Force HTTPS for GitHub git dependencies run: git config --global 'url.https://github.com/.insteadOf' 'git@github.com:' + # CI only runs lint/test/typecheck/build — none of those exercise + # better-sqlite3 at runtime. Skipping postinstall avoids apps/desktop's + # electron-builder install-app-deps step, which rebuilds better-sqlite3 + # against the bundled Electron headers — that rebuild can break the + # whole setup job when better-sqlite3 lags an Electron major (see the + # v0.15.0 incident where Electron 42's V8 API broke better-sqlite3 + # 12.10.0). Same pattern used in release.yml and deploy-api.yml. - name: Install dependencies - run: pnpm install --frozen-lockfile + run: pnpm install --frozen-lockfile --ignore-scripts - name: Cache node_modules - uses: actions/cache/save@v4 + uses: actions/cache/save@v5 with: path: | node_modules @@ -50,14 +57,14 @@ jobs: needs: setup runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: pnpm/action-setup@v5 - - uses: actions/setup-node@v4 + - uses: actions/setup-node@v5 with: node-version: ${{ env.NODE_VERSION }} - name: Restore node_modules - uses: actions/cache/restore@v4 + uses: actions/cache/restore@v5 with: path: | node_modules @@ -71,25 +78,23 @@ jobs: - name: Prettier run: pnpm format:check - - name: Commitlint (PR title) - if: github.event_name == 'pull_request' - env: - PR_TITLE: ${{ github.event.pull_request.title }} - run: echo "$PR_TITLE" | pnpm commitlint + # PR title commitlint moved to .github/workflows/pr-title.yml so it + # exposes a stable, standalone status-check name for branch protection + # (squash-merge makes the PR title the release-trigger commit message). # ── Tier 1: Tests + Coverage ─────────────────────── test: needs: setup runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: pnpm/action-setup@v5 - - uses: actions/setup-node@v4 + - uses: actions/setup-node@v5 with: node-version: ${{ env.NODE_VERSION }} - name: Restore node_modules - uses: actions/cache/restore@v4 + uses: actions/cache/restore@v5 with: path: | node_modules @@ -105,7 +110,7 @@ jobs: - name: Upload coverage if: always() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 with: name: coverage-report path: | @@ -118,14 +123,14 @@ jobs: needs: setup runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: pnpm/action-setup@v5 - - uses: actions/setup-node@v4 + - uses: actions/setup-node@v5 with: node-version: ${{ env.NODE_VERSION }} - name: Restore node_modules - uses: actions/cache/restore@v4 + uses: actions/cache/restore@v5 with: path: | node_modules @@ -151,14 +156,14 @@ jobs: runs-on: ubuntu-latest continue-on-error: true steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: pnpm/action-setup@v5 - - uses: actions/setup-node@v4 + - uses: actions/setup-node@v5 with: node-version: ${{ env.NODE_VERSION }} - name: Restore node_modules - uses: actions/cache/restore@v4 + uses: actions/cache/restore@v5 with: path: | node_modules @@ -181,7 +186,7 @@ jobs: - name: Upload Playwright report on failure if: failure() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 with: name: playwright-report path: apps/desktop/playwright-report/ @@ -192,14 +197,14 @@ jobs: needs: setup runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: pnpm/action-setup@v5 - - uses: actions/setup-node@v4 + - uses: actions/setup-node@v5 with: node-version: ${{ env.NODE_VERSION }} - name: Restore node_modules - uses: actions/cache/restore@v4 + uses: actions/cache/restore@v5 with: path: | node_modules @@ -220,14 +225,14 @@ jobs: runs-on: ubuntu-latest if: github.event_name == 'pull_request' steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: pnpm/action-setup@v5 - - uses: actions/setup-node@v4 + - uses: actions/setup-node@v5 with: node-version: ${{ env.NODE_VERSION }} - name: Restore node_modules - uses: actions/cache/restore@v4 + uses: actions/cache/restore@v5 with: path: | node_modules diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index a3c0723d..785c1bc1 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -20,7 +20,7 @@ jobs: security-events: write steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Initialize CodeQL uses: github/codeql-action/init@v4 diff --git a/.github/workflows/deploy-api.yml b/.github/workflows/deploy-api.yml index 69750845..0ea052b4 100644 --- a/.github/workflows/deploy-api.yml +++ b/.github/workflows/deploy-api.yml @@ -17,16 +17,23 @@ on: - staging - production +# Minimum-privilege default. The Cloudflare deploy doesn't push commits +# or create issues; checkout + read of the workflow definition is all +# the GITHUB_TOKEN side needs. Wrangler authenticates via the +# CLOUDFLARE_API_TOKEN secret separately. +permissions: + contents: read + jobs: test: name: Test API runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: pnpm/action-setup@v5 - - uses: actions/setup-node@v4 + - uses: actions/setup-node@v5 with: node-version: '22' cache: 'pnpm' @@ -49,11 +56,11 @@ jobs: needs: test runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: pnpm/action-setup@v5 - - uses: actions/setup-node@v4 + - uses: actions/setup-node@v5 with: node-version: '22' cache: 'pnpm' diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index fde3eb10..a25c865c 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -9,15 +9,18 @@ on: - '.github/workflows/docs.yml' workflow_dispatch: +permissions: + contents: read + jobs: deploy: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: pnpm/action-setup@v5 - - uses: actions/setup-node@v4 + - uses: actions/setup-node@v5 with: node-version: '22' cache: 'pnpm' @@ -25,15 +28,20 @@ jobs: - name: Force HTTPS for GitHub git dependencies run: git config --global 'url.https://github.com/.insteadOf' 'git@github.com:' - - run: pnpm install + # Marketing site only needs @readied/web + product-config's transitive + # deps. Plain `pnpm install` would also run apps/desktop's postinstall + # (electron-builder install-app-deps -> better-sqlite3 native rebuild), + # which fails on the Linux + Node 22 runner. See #287 for the + # deploy-api workflow and #288 for release.yml — same fix shape. + - run: pnpm install --filter '@readied/web...' --ignore-scripts - name: Build web app + working-directory: apps/web + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | - cd apps/web pnpm exec fumadocs-mdx pnpm exec next build - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Deploy to Cloudflare Pages env: diff --git a/.github/workflows/pr-title.yml b/.github/workflows/pr-title.yml new file mode 100644 index 00000000..80317e89 --- /dev/null +++ b/.github/workflows/pr-title.yml @@ -0,0 +1,54 @@ +name: PR title + +# Runs commitlint against the PR title as a standalone status check. +# +# Why standalone (and not a step inside ci.yml `lint`): +# - The repo enforces squash merges (see GitHub Settings > Pull Requests). +# On squash, the PR title becomes the commit message on develop/main. +# - semantic-release then analyses that message with the type-enum from +# commitlint.config.js to decide whether to cut a release. If the type +# isn't recognised (e.g. PR #245's "release: audit..."), semantic-release +# silently exits with "no release". This is the trap that blew up +# v0.15.0. +# - Pulling the check into its own workflow gives it a clear, stable +# status-check name so branch protection on develop and main can require +# it independently of the broader CI lint job. + +on: + pull_request: + types: [opened, edited, reopened, synchronize] + +permissions: + contents: read + +concurrency: + # Re-running on title edits is cheap; cancel any in-flight check for + # the same PR when a new event arrives. + group: pr-title-${{ github.event.pull_request.number }} + cancel-in-progress: true + +jobs: + commitlint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + with: + fetch-depth: 0 + + - uses: pnpm/action-setup@v5 + + - uses: actions/setup-node@v5 + with: + node-version: '22' + cache: 'pnpm' + + # Title check only needs commitlint + its config — no workspace + # native deps, no postinstall. + - name: Install commitlint + run: pnpm install --frozen-lockfile --ignore-scripts + + - name: Lint PR title + env: + PR_TITLE: ${{ github.event.pull_request.title }} + run: | + printf '%s' "$PR_TITLE" | pnpm commitlint diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4bb59b1e..4d7aefb8 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -18,7 +18,7 @@ jobs: if: github.ref == 'refs/heads/main' steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: fetch-depth: 0 token: ${{ secrets.GH_TOKEN }} @@ -27,7 +27,7 @@ jobs: uses: pnpm/action-setup@v5 - name: Setup Node.js - uses: actions/setup-node@v4 + uses: actions/setup-node@v5 with: node-version: '22' cache: 'pnpm' @@ -44,12 +44,66 @@ jobs: - name: Install dependencies run: pnpm install --frozen-lockfile --ignore-scripts + # Dry-run gate: figure out whether a release WOULD be cut from the + # current commit log before we actually try. The trap we're closing: + # if the most recent merge to main used a non-conventional title + # (squash merge eats the PR title), semantic-release silently exits + # with "no release" and the workflow ends green. The user then + # wonders why no tag appeared. Fail loud here instead. + - name: Pre-flight (dry-run) check + env: + GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} + run: | + set -o pipefail + npx semantic-release --dry-run 2>&1 | tee /tmp/sr-dry.log + if grep -qE "There are no relevant changes" /tmp/sr-dry.log; then + echo "::error::semantic-release dry-run: no release will be cut." + echo "::error::Most recent main commit's conventional type is not in releaseRules." + echo "::error::Check release.config.js > releaseRules and the commit message that landed on main." + exit 1 + fi + if ! grep -qE "next release version is" /tmp/sr-dry.log; then + echo "::error::semantic-release dry-run did not announce a next release version." + echo "::error::Aborting before --ci to avoid a silent no-op." + exit 1 + fi + + # HUSKY: '0' was leftover from the husky->lefthook migration in #267 + # (kept hooks from firing inside the workflow's commit step). Lefthook + # only reads .git/hooks if those files exist, and they don't on a + # fresh CI clone, so the env var is no longer needed. - name: Run semantic-release env: GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} - HUSKY: '0' GIT_AUTHOR_NAME: github-actions[bot] GIT_AUTHOR_EMAIL: github-actions[bot]@users.noreply.github.com GIT_COMMITTER_NAME: github-actions[bot] GIT_COMMITTER_EMAIL: github-actions[bot]@users.noreply.github.com run: npx semantic-release + + # Post-flight assertion: after semantic-release runs, + # package.json + apps/desktop/package.json should both match the + # version the dry-run announced. If scripts/bump-version.mjs (via + # @semantic-release/exec) didn't run or didn't touch one of the + # files, we catch it here BEFORE the tag-triggered build downloads + # stale package.json — closes the v0.15.0-style "tag at old + # version" trap. + - name: Verify version bump applied + run: | + expected=$(grep -oE "next release version is [0-9]+\.[0-9]+\.[0-9]+" /tmp/sr-dry.log \ + | tail -n 1 | awk '{print $NF}') + if [ -z "$expected" ]; then + echo "::warning::Could not extract expected version from dry-run log; skipping bump check." + exit 0 + fi + root_v=$(jq -r .version package.json) + desk_v=$(jq -r .version apps/desktop/package.json) + if [ "$root_v" != "$expected" ] || [ "$desk_v" != "$expected" ]; then + echo "::error::Version mismatch after semantic-release." + echo "::error::Expected: $expected" + echo "::error::package.json: $root_v" + echo "::error::apps/desktop/package.json: $desk_v" + echo "::error::scripts/bump-version.mjs did not run or did not update both files." + exit 1 + fi + echo "Version bump verified: both package.json files at $expected" diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 00000000..0289c7d9 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,11 @@ +CHANGELOG.md +dist/ +out/ +release/ +.next/ +.source/ +.astro/ +.wrangler/ +coverage/ +node_modules/ +pnpm-lock.yaml diff --git a/apps/desktop/package.json b/apps/desktop/package.json index 3ca3601f..5c05634d 100644 --- a/apps/desktop/package.json +++ b/apps/desktop/package.json @@ -75,7 +75,7 @@ "@types/turndown": "^5.0.6", "@playwright/test": "^1.49.1", "@vitejs/plugin-react": "^6.0.2", - "electron": "^42.3.3", + "electron": "^41.7.1", "electron-builder": "^26.15.2", "electron-devtools-installer": "^4.0.0", "electron-vite": "^5.0.0", diff --git a/apps/desktop/src/main/services/encryptionService.ts b/apps/desktop/src/main/services/encryptionService.ts index 39287b03..b23cb865 100644 --- a/apps/desktop/src/main/services/encryptionService.ts +++ b/apps/desktop/src/main/services/encryptionService.ts @@ -112,7 +112,8 @@ export class EncryptionService { return false; // No key available — passphrase setup required } catch (error) { throw new Error( - `Failed to initialize encryption: ${error instanceof Error ? error.message : 'Unknown error'}` + `Failed to initialize encryption: ${error instanceof Error ? error.message : 'Unknown error'}`, + { cause: error } ); } } @@ -259,7 +260,8 @@ export class EncryptionService { ); } catch (error) { throw new Error( - `Failed to encrypt content: ${error instanceof Error ? error.message : 'Unknown error'}` + `Failed to encrypt content: ${error instanceof Error ? error.message : 'Unknown error'}`, + { cause: error } ); } } @@ -290,7 +292,8 @@ export class EncryptionService { return decrypted.toString('utf-8'); } catch (error) { throw new Error( - `Failed to decrypt content: ${error instanceof Error ? error.message : 'Unknown error'}` + `Failed to decrypt content: ${error instanceof Error ? error.message : 'Unknown error'}`, + { cause: error } ); } } @@ -349,7 +352,8 @@ export class EncryptionService { await this.cacheCek(this.key); } catch (error) { throw new Error( - `Failed to import key: ${error instanceof Error ? error.message : 'Unknown error'}` + `Failed to import key: ${error instanceof Error ? error.message : 'Unknown error'}`, + { cause: error } ); } } diff --git a/package.json b/package.json index 1c4d7b5a..687d715e 100644 --- a/package.json +++ b/package.json @@ -34,8 +34,8 @@ "test:coverage": "turbo test --filter=!@readied/storage-sqlite -- --coverage", "lint": "eslint packages apps --cache", "lint:fix": "eslint packages apps --fix --cache", - "format": "prettier --write \"**/*.{ts,tsx,js,json,md}\" --ignore-path .gitignore", - "format:check": "prettier --check \"**/*.{ts,tsx,js,json,md}\" --ignore-path .gitignore", + "format": "prettier --write \"**/*.{ts,tsx,js,json,md}\" --ignore-path .gitignore --ignore-path .prettierignore", + "format:check": "prettier --check \"**/*.{ts,tsx,js,json,md}\" --ignore-path .gitignore --ignore-path .prettierignore", "typecheck": "turbo typecheck", "clean": "turbo clean && rm -rf node_modules .eslintcache", "knip": "knip", diff --git a/packages/mcp-server/package.json b/packages/mcp-server/package.json index e2c8819b..a653dbdc 100644 --- a/packages/mcp-server/package.json +++ b/packages/mcp-server/package.json @@ -13,7 +13,7 @@ } }, "scripts": { - "build": "tsc", + "build": "tsc -p tsconfig.build.json", "dev": "tsx src/index.ts", "test": "vitest run" }, diff --git a/packages/mcp-server/tsconfig.build.json b/packages/mcp-server/tsconfig.build.json new file mode 100644 index 00000000..4dc23fb9 --- /dev/null +++ b/packages/mcp-server/tsconfig.build.json @@ -0,0 +1,4 @@ +{ + "extends": "./tsconfig.json", + "exclude": ["src/__tests__"] +} diff --git a/packages/mcp-server/tsconfig.json b/packages/mcp-server/tsconfig.json index 25c386a0..7c03e038 100644 --- a/packages/mcp-server/tsconfig.json +++ b/packages/mcp-server/tsconfig.json @@ -4,8 +4,7 @@ "noEmit": false, "outDir": "dist", "rootDir": "src", - "types": ["node"] + "types": ["node", "vitest/globals"] }, - "include": ["src"], - "exclude": ["src/__tests__"] + "include": ["src"] } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a07a973b..92b03824 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -205,8 +205,8 @@ importers: specifier: ^6.0.2 version: 6.0.2(vite@8.0.16(@types/node@25.9.2)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) electron: - specifier: ^42.3.3 - version: 42.3.3 + specifier: ^41.7.1 + version: 41.7.1 electron-builder: specifier: ^26.15.2 version: 26.15.2(electron-builder-squirrel-windows@26.0.12) @@ -1033,14 +1033,14 @@ packages: resolution: {integrity: sha512-zx0EIq78WlY/lBb1uXlziZmDZI4ubcCXIMJ4uGjXzZW0nS19TjSPeXPAjzzTmKQlJUZm0SbmZhPKP7tuQ1SsEw==} hasBin: true + '@electron/get@2.0.3': + resolution: {integrity: sha512-Qkzpg2s9GnVV2I2BjRksUi43U5e6+zaQMcjoJy0C+C5oxaKl+fmckGDQFtRpZpZV0NQekuZZ+tGz7EA9TVnQtQ==} + engines: {node: '>=12'} + '@electron/get@3.1.0': resolution: {integrity: sha512-F+nKc0xW+kVbBRhFzaMgPy3KwmuNTYX1fx6+FxxoSnNgwYX6LD7AKBTWkU0MQ6IBoe7dz069CNkR673sPAgkCQ==} engines: {node: '>=14'} - '@electron/get@5.0.0': - resolution: {integrity: sha512-pjoBpru1KdEtcExBnuHAP1cAc/5faoedw0hzJkL3o4/IJp7HNF1+fbrdxT3gMYRX2oJfvnA/WXeCTVQpYYxyJA==} - engines: {node: '>=22.12.0'} - '@electron/node-gyp@https://codeload.github.com/electron/node-gyp/tar.gz/06b29aafb7708acef8b3669835c8a7857ebc92d2': resolution: {tarball: https://codeload.github.com/electron/node-gyp/tar.gz/06b29aafb7708acef8b3669835c8a7857ebc92d2} version: 10.2.0-electron.1 @@ -4682,9 +4682,9 @@ packages: resolution: {integrity: sha512-bO3y10YikuUwUuDUQRM4KfwNkKhnpVO7IPdbsrejwN9/AABJzzTQ4GeHwyzNSrVO+tEH3/Np255a3sVZpZDjvg==} engines: {node: '>=8.0.0'} - electron@42.3.3: - resolution: {integrity: sha512-0MwYp9wTb7TrtTalOYqeW+suqd9T/Znstr/nDLKqFGIjHdBZX339guo3mQqTPURRZ/UQmYM4uMpzKpI5wLptfQ==} - engines: {node: '>= 22.12.0'} + electron@41.7.1: + resolution: {integrity: sha512-pdRvNNP99Qfvs1lyIxo/sfIGAwJP0CrJFNCE3goFKc7/fV+kjK3EPxx5Nt6sLTkzqTyeRYylpwPUfpeGojiyyw==} + engines: {node: '>= 12.20.55'} hasBin: true emoji-regex@10.6.0: @@ -7983,10 +7983,6 @@ packages: resolution: {integrity: sha512-xXnp4kTyor2Zq+J1FfPI6Eq3ew5h6Vl0F/8d9XU5zZQf1tX9s2Su1/3PiMmUANFULpmksxkClamIZcaUqryHsQ==} engines: {node: '>=20.18.1'} - undici@7.27.2: - resolution: {integrity: sha512-uZsKNuzQxDMUY6M3pIMvy5tvlGmtq8XJ2oLAkfRKGNu+1VQAIvLy2xIVG5ATZl5wDXl/tddByAWCizRbOme+TA==} - engines: {node: '>=20.18.1'} - unenv@2.0.0-rc.24: resolution: {integrity: sha512-i7qRCmY42zmCwnYlh9H2SvLEypEFGye5iRmEMKjcGi7zk9UquigRjFtTLz0TYqr0ZGLZhaMHl/foy1bZR+Cwlw==} @@ -9096,7 +9092,7 @@ snapshots: fs-extra: 9.1.0 minimist: 1.2.8 - '@electron/get@3.1.0': + '@electron/get@2.0.3': dependencies: debug: 4.4.3 env-paths: 2.2.1 @@ -9110,16 +9106,17 @@ snapshots: transitivePeerDependencies: - supports-color - '@electron/get@5.0.0': + '@electron/get@3.1.0': dependencies: debug: 4.4.3 - env-paths: 3.0.0 - graceful-fs: 4.2.11 + env-paths: 2.2.1 + fs-extra: 8.1.0 + got: 11.8.6 progress: 2.0.3 - semver: 7.8.2 + semver: 6.3.1 sumchecker: 3.0.1 optionalDependencies: - undici: 7.27.2 + global-agent: 3.0.0 transitivePeerDependencies: - supports-color @@ -12693,9 +12690,9 @@ snapshots: transitivePeerDependencies: - supports-color - electron@42.3.3: + electron@41.7.1: dependencies: - '@electron/get': 5.0.0 + '@electron/get': 2.0.3 '@types/node': 24.13.1 extract-zip: 2.0.1 transitivePeerDependencies: @@ -12734,7 +12731,8 @@ snapshots: env-paths@2.2.1: {} - env-paths@3.0.0: {} + env-paths@3.0.0: + optional: true environment@1.1.0: {} @@ -16591,9 +16589,6 @@ snapshots: undici@7.25.0: {} - undici@7.27.2: - optional: true - unenv@2.0.0-rc.24: dependencies: pathe: 2.0.3 diff --git a/release.config.js b/release.config.js index 7b83438f..cdd722cd 100644 --- a/release.config.js +++ b/release.config.js @@ -32,12 +32,18 @@ export default { }, ], '@semantic-release/changelog', - // Note: the previous @semantic-release/exec step ran a custom - // scripts/bump-version.js to sync per-package versions. That script was - // deleted in the knip cleanup (#279) and the desktop release only needs - // the root + apps/desktop package.json versions bumped — which the - // @semantic-release/git plugin below does via the `assets` list. So we - // drop the exec step entirely. + // The @semantic-release/git plugin below only COMMITS files; it does + // not mutate them. Without this exec step, package.json and + // apps/desktop/package.json stay at the previous version even after + // tag/release (the v0.15.0 bug). bump-version.mjs is a pure-ESM, + // zero-dependency script that only touches the `version` field of + // exactly the two files in scope. + [ + '@semantic-release/exec', + { + prepareCmd: 'node scripts/bump-version.mjs ${nextRelease.version}', + }, + ], [ '@semantic-release/git', { diff --git a/scripts/bump-version.mjs b/scripts/bump-version.mjs new file mode 100644 index 00000000..ada877a8 --- /dev/null +++ b/scripts/bump-version.mjs @@ -0,0 +1,91 @@ +#!/usr/bin/env node +/** + * Sync the `version` field across the monorepo's release-relevant + * package.json files. Called by @semantic-release/exec at the + * `prepareCmd` step so the @semantic-release/git plugin actually has + * a diff to commit. + * + * Why this script exists (history): + * + * 1. semantic-release/git's `assets` list is COMMIT-only — it does + * not mutate files. Something else has to bump the versions first. + * 2. The original scripts/bump-version.js was deleted in the knip + * cleanup (#279) under the false assumption nothing referenced it. + * release.config.js still had a prepareCmd pointing at it, + * so v0.15.0 shipped with both package.json files reading 0.14.0 + * (visible mismatch in the v0.15.0 tag). + * 3. PR #289 patched release.config.js by removing the prepareCmd, + * which made semantic-release runnable but kept versions stale. + * 4. This script restores step (1) properly. It's pure ESM, has no + * dependencies, and updates ONLY the version field — no other + * package.json keys are touched. + * + * Usage: + * + * node scripts/bump-version.mjs 0.15.1 + * + * Fails non-zero if: + * - the version arg is missing or empty + * - any target file can't be parsed as JSON + * - a target file has no `version` field to update + */ + +import { readFile, writeFile } from 'node:fs/promises'; +import { resolve, dirname } from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const __dirname = dirname(fileURLToPath(import.meta.url)); +const repoRoot = resolve(__dirname, '..'); + +/** + * Files whose `version` should track the desktop release. + * + * Keep this list explicit (not a glob) so we never accidentally bump + * a workspace package that's intentionally on its own version line + * (e.g. packages/api which deploys on its own cycle to Cloudflare). + */ +const targets = ['package.json', 'apps/desktop/package.json']; + +const newVersion = process.argv[2]; +if (!newVersion || newVersion.trim().length === 0) { + console.error('bump-version: missing version argument'); + console.error('usage: node scripts/bump-version.mjs '); + process.exit(1); +} + +let bumped = 0; +for (const rel of targets) { + const abs = resolve(repoRoot, rel); + const raw = await readFile(abs, 'utf-8'); + + let parsed; + try { + parsed = JSON.parse(raw); + } catch (err) { + console.error(`bump-version: cannot parse ${rel} as JSON: ${err.message}`); + process.exit(1); + } + + if (typeof parsed.version !== 'string') { + console.error(`bump-version: ${rel} has no version field to update`); + process.exit(1); + } + + const old = parsed.version; + if (old === newVersion) { + console.log(`bump-version: ${rel} already at ${newVersion}, skipped`); + continue; + } + + parsed.version = newVersion; + + // Preserve a trailing newline if the original file had one — Prettier and + // most editors expect it. Detecting from `raw` keeps the diff minimal. + const trailingNewline = raw.endsWith('\n') ? '\n' : ''; + await writeFile(abs, JSON.stringify(parsed, null, 2) + trailingNewline, 'utf-8'); + + console.log(`bump-version: ${rel} ${old} -> ${newVersion}`); + bumped++; +} + +console.log(`bump-version: updated ${bumped} of ${targets.length} target(s)`); From 2b5ea9ee2be706136619e8ef477759423a583146 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 9 Jun 2026 14:13:58 +0000 Subject: [PATCH 2/6] chore(release): v0.15.1 [skip ci] --- CHANGELOG.md | 6 ++++++ apps/desktop/package.json | 2 +- package.json | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 40d47af9..dad1e666 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## [0.15.1](https://github.com/tomymaritano/readide/compare/v0.15.0...v0.15.1) (2026-06-09) + +### Bug Fixes + +* **release:** cut v0.15.1 after Phase 0 DevOps stabilization ([#297](https://github.com/tomymaritano/readide/issues/297)) ([cd269c8](https://github.com/tomymaritano/readide/commit/cd269c83a1750ac727e15efb1e674a9c72a837c7)), closes [#290](https://github.com/tomymaritano/readide/issues/290) [#291](https://github.com/tomymaritano/readide/issues/291) [#292](https://github.com/tomymaritano/readide/issues/292) [#293](https://github.com/tomymaritano/readide/issues/293) [#294](https://github.com/tomymaritano/readide/issues/294) [#295](https://github.com/tomymaritano/readide/issues/295) [#296](https://github.com/tomymaritano/readide/issues/296) + ## [0.15.0](https://github.com/tomymaritano/readide/compare/v0.14.0...v0.15.0) (2026-06-09) ### Features diff --git a/apps/desktop/package.json b/apps/desktop/package.json index 5c05634d..5bf52a64 100644 --- a/apps/desktop/package.json +++ b/apps/desktop/package.json @@ -1,6 +1,6 @@ { "name": "@readied/desktop", - "version": "0.14.0", + "version": "0.15.1", "private": true, "description": "Markdown-first, offline-forever note app for developers", "author": { diff --git a/package.json b/package.json index 687d715e..82c7eb74 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "readied", - "version": "0.14.0", + "version": "0.15.1", "private": true, "license": "SEE LICENSE IN LICENSE", "description": "Markdown-first, offline-forever desktop note app", From 33769fc8be341b4787d06260bb1b0e45486fac8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Maritano?= Date: Tue, 9 Jun 2026 11:57:25 -0300 Subject: [PATCH 3/6] fix(release): cut v0.15.2 with linux executableName fix (#301) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Why v0.15.1 Build (run 27212685957) failed on linux because electron-builder rejected the AppImage build: \`executableName contains invalid chars: @readieddesktop\`. publish job (\`needs: build\`) was skipped, no binaries reached the GitHub Release, v0.15.1 has been marked draft. This brings the linux fix from #298 (already on develop) into main as a cherry-pick so semantic-release can cut v0.15.2. ## Why not the original develop → main PR (#299) I created a sync PR (#300) earlier to bring main's release-cycle commits (CHANGELOG + version bump) back to develop. Squash-merging that sync PR collapsed the merge ancestry — develop has main's content but not main's commit history, so #299 stayed permanently BEHIND. Cherry-picking the linux fix straight to main sidesteps that. After this lands and Release cuts v0.15.2, the auto sync-develop job in build.yml will bring the v0.15.2 release commits back to develop with proper ancestry. ## Diff \`apps/desktop/package.json\`: \`"executableName": "readied"\` added to \`build.linux\`. Single line. mac+win already build cleanly because they use \`productName\` ("Readied") and \`appId\` (\`app.readied.desktop\`) respectively. ## Expected Build flow semantic-release reads main's commit log since v0.15.1: - \`fix(desktop): set linux.executableName for AppImage build (#298)\` → patch bump → v0.15.2 cut as draft → Build runs on mac/win/linux → all 3 publish to draft → \`publish\` job undrafts → \`sync-develop\` PRs main → develop. --- apps/desktop/package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/desktop/package.json b/apps/desktop/package.json index 5bf52a64..ea8b2140 100644 --- a/apps/desktop/package.json +++ b/apps/desktop/package.json @@ -151,7 +151,8 @@ ], "icon": "resources/icons", "category": "Office", - "artifactName": "${productName}-${version}-${arch}.${ext}" + "artifactName": "${productName}-${version}-${arch}.${ext}", + "executableName": "readied" }, "nsis": { "oneClick": false, From e471ad8d08fc5f13b68fc9e51933002569eb7f2c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 9 Jun 2026 14:58:21 +0000 Subject: [PATCH 4/6] chore(release): v0.15.2 [skip ci] --- CHANGELOG.md | 6 ++++++ apps/desktop/package.json | 2 +- package.json | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dad1e666..a8ecf331 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## [0.15.2](https://github.com/tomymaritano/readide/compare/v0.15.1...v0.15.2) (2026-06-09) + +### Bug Fixes + +* **release:** cut v0.15.2 with linux executableName fix ([#301](https://github.com/tomymaritano/readide/issues/301)) ([33769fc](https://github.com/tomymaritano/readide/commit/33769fc8be341b4787d06260bb1b0e45486fac8c)), closes [#298](https://github.com/tomymaritano/readide/issues/298) [#299](https://github.com/tomymaritano/readide/issues/299) [#300](https://github.com/tomymaritano/readide/issues/300) [#299](https://github.com/tomymaritano/readide/issues/299) [#298](https://github.com/tomymaritano/readide/issues/298) + ## [0.15.1](https://github.com/tomymaritano/readide/compare/v0.15.0...v0.15.1) (2026-06-09) ### Bug Fixes diff --git a/apps/desktop/package.json b/apps/desktop/package.json index ea8b2140..f16c3215 100644 --- a/apps/desktop/package.json +++ b/apps/desktop/package.json @@ -1,6 +1,6 @@ { "name": "@readied/desktop", - "version": "0.15.1", + "version": "0.15.2", "private": true, "description": "Markdown-first, offline-forever note app for developers", "author": { diff --git a/package.json b/package.json index 82c7eb74..9d2914a8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "readied", - "version": "0.15.1", + "version": "0.15.2", "private": true, "license": "SEE LICENSE IN LICENSE", "description": "Markdown-first, offline-forever desktop note app", From c755a6778f467945060ff407359d482f20605eab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Maritano?= Date: Thu, 11 Jun 2026 08:13:17 -0300 Subject: [PATCH 5/6] fix(release): cut v0.15.3 with window/db lifecycle crash fixes (#304) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary Release PR: `develop` → `main`. Contains one user-facing fix since v0.15.2: - **fix(desktop): prevent 'Object has been destroyed' and 'db not open' crashes** (#303) — guards window and database lifecycle so IPC handlers don't touch a destroyed `BrowserWindow` or a closed SQLite handle during shutdown/reload. semantic-release should cut **v0.15.3** (patch bump from the `fix:` commit). ## Release checklist (after merge) 1. Merge this PR into `main` 2. Click **Run workflow** on the **Release** action (`workflow_dispatch`) 3. semantic-release bumps version, tags, creates draft GitHub Release 4. Tag push triggers Build workflow (mac/win/linux) 5. All builds green → release undrafted → electron-updater picks it up 6. Auto-PR syncs `main` back to `develop` 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Signed-off-by: dependabot[bot] Co-authored-by: Claude Opus 4.6 Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] --- apps/desktop/src/main/index.ts | 50 ++++++++++++++++++-------- apps/desktop/src/main/pluginWatcher.ts | 12 ++++--- 2 files changed, 44 insertions(+), 18 deletions(-) diff --git a/apps/desktop/src/main/index.ts b/apps/desktop/src/main/index.ts index 4f5d88e5..d502e510 100644 --- a/apps/desktop/src/main/index.ts +++ b/apps/desktop/src/main/index.ts @@ -220,6 +220,7 @@ function createWindow(): void { mainWindow.on('move', debouncedSave); mainWindow.on('maximize', () => { + if (mainWindow.isDestroyed()) return; saveWindowState({ ...mainWindow.getBounds(), isMaximized: true, @@ -227,26 +228,30 @@ function createWindow(): void { }); mainWindow.on('unmaximize', () => { - const bounds = mainWindow.getBounds(); + if (mainWindow.isDestroyed()) return; saveWindowState({ - ...bounds, + ...mainWindow.getBounds(), isMaximized: false, }); }); mainWindow.on('ready-to-show', () => { + if (mainWindow.isDestroyed()) return; mainWindow.show(); }); - // Deliver any pending deep link auth token once the renderer is ready + // Deliver any pending deep link auth token once the renderer is ready. + // Guard against window destruction — did-finish-load can fire late during + // app shutdown or auto-update restart, and the captured `mainWindow` ref + // would throw 'Object has been destroyed' on .send/.show/.focus. mainWindow.webContents.on('did-finish-load', () => { - if (pendingAuthToken) { - getLogger().info('Delivering queued auth token to renderer'); - mainWindow.webContents.send('auth:verify-token', pendingAuthToken); - mainWindow.show(); - mainWindow.focus(); - pendingAuthToken = null; - } + if (!pendingAuthToken) return; + if (mainWindow.isDestroyed() || mainWindow.webContents.isDestroyed()) return; + getLogger().info('Delivering queued auth token to renderer'); + mainWindow.webContents.send('auth:verify-token', pendingAuthToken); + mainWindow.show(); + mainWindow.focus(); + pendingAuthToken = null; }); // Load renderer @@ -279,6 +284,7 @@ function createNoteWindow(noteId: string, noteTitle: string): void { }); noteWindow.on('ready-to-show', () => { + if (noteWindow.isDestroyed()) return; noteWindow.show(); if (process.env.NODE_ENV === 'development') { noteWindow.webContents.openDevTools(); @@ -837,12 +843,28 @@ app.on('before-quit', async event => { ); } + // Don't close the DB here — windows are still alive and may issue IPC + // requests (e.g. notebooks:tree on reload) until they fully unmount. + // The DB is closed in `will-quit`, after window-all-closed has fired. + app.quit(); +}); + +// Close the database after all windows are gone but before the process exits. +// This avoids the "database connection is not open" race where the renderer +// fires an IPC request between db.close() and window destruction. +app.on('will-quit', () => { if (db) { - db.close(); - getLogger().info('Database closed'); + try { + db.close(); + getLogger().info('Database closed'); + } catch (err) { + getLogger().error( + { error: err instanceof Error ? err.message : String(err) }, + 'Error closing database during shutdown' + ); + } + db = null; } - - app.quit(); }); // Deep link handler for readied:// protocol (macOS) diff --git a/apps/desktop/src/main/pluginWatcher.ts b/apps/desktop/src/main/pluginWatcher.ts index a124c9a3..8a40c22f 100644 --- a/apps/desktop/src/main/pluginWatcher.ts +++ b/apps/desktop/src/main/pluginWatcher.ts @@ -27,11 +27,15 @@ export function startPluginWatcher(pluginsDir: string): void { let debounceTimer: ReturnType | null = null; const broadcastReload = () => { - BrowserWindow.getAllWindows().forEach(win => { - if (!win.isDestroyed()) { - win.webContents.send('plugins:reload'); + for (const win of BrowserWindow.getAllWindows()) { + try { + if (!win.isDestroyed() && !win.webContents.isDestroyed()) { + win.webContents.send('plugins:reload'); + } + } catch { + // Window destroyed between check and send — ignore } - }); + } }; try { From 1cb164021f35aa114512aa727b2dbb964346c07e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 11 Jun 2026 11:17:46 +0000 Subject: [PATCH 6/6] chore(release): v0.15.3 [skip ci] --- CHANGELOG.md | 6 ++++++ apps/desktop/package.json | 2 +- package.json | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a8ecf331..6f058ebd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## [0.15.3](https://github.com/tomymaritano/readide/compare/v0.15.2...v0.15.3) (2026-06-11) + +### Bug Fixes + +* **release:** cut v0.15.3 with window/db lifecycle crash fixes ([#304](https://github.com/tomymaritano/readide/issues/304)) ([c755a67](https://github.com/tomymaritano/readide/commit/c755a6778f467945060ff407359d482f20605eab)), closes [#303](https://github.com/tomymaritano/readide/issues/303) + ## [0.15.2](https://github.com/tomymaritano/readide/compare/v0.15.1...v0.15.2) (2026-06-09) ### Bug Fixes diff --git a/apps/desktop/package.json b/apps/desktop/package.json index f16c3215..71ea1a1e 100644 --- a/apps/desktop/package.json +++ b/apps/desktop/package.json @@ -1,6 +1,6 @@ { "name": "@readied/desktop", - "version": "0.15.2", + "version": "0.15.3", "private": true, "description": "Markdown-first, offline-forever note app for developers", "author": { diff --git a/package.json b/package.json index 9d2914a8..471ae9ab 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "readied", - "version": "0.15.2", + "version": "0.15.3", "private": true, "license": "SEE LICENSE IN LICENSE", "description": "Markdown-first, offline-forever desktop note app",