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/2] 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/2] 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",