diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 5a32bd8..230c02d 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -4,6 +4,15 @@ updates: directory: / schedule: interval: weekly + ignore: + # Types must match the Electron-bundled Node runtime (Electron 44 = Node 24) + - dependency-name: "@types/node" + update-types: ["version-update:semver-major"] + # electron-vite 5 supports vite <=7; revisit when electron-vite 6 is stable + - dependency-name: "vite" + update-types: ["version-update:semver-major"] + - dependency-name: "@vitejs/plugin-react" + update-types: ["version-update:semver-major"] - package-ecosystem: github-actions directory: / schedule: diff --git a/CHANGELOG.md b/CHANGELOG.md index 5baaa92..f4cb2a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ All notable changes to GitGood. Versions follow [semver](https://semver.org); release notes for each version are also on the [Releases page](https://github.com/erwin-wee/gitgood/releases). +## 0.4.2 — 2026-10-19 + +- Fixed tailnet access refusing every request when the background server starts at boot before `tailscaled` is up: the allowed Tailscale login is now looked up on demand (retried at most every 30 seconds until it succeeds) instead of once at startup. +- Dev dependencies: vitest 5.0.2. + ## 0.4.1 — 2026-10-02 - Fixed macOS release packaging when signing secrets are not configured: an empty `CSC_LINK` is now removed before running electron-builder. diff --git a/package-lock.json b/package-lock.json index b1ea067..d28a24d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "gitgood", - "version": "0.4.1", + "version": "0.4.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "gitgood", - "version": "0.4.1", + "version": "0.4.2", "hasInstallScript": true, "license": "MIT", "dependencies": { @@ -29,7 +29,10 @@ "eslint-plugin-react-hooks": "^7.1.1", "typescript": "^5.9.0", "vite": "^7.3.0", - "vitest": "^5.0.1" + "vitest": "^5.0.2" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@anthropic-ai/sdk": { @@ -2352,14 +2355,14 @@ } }, "node_modules/@vitest/mocker": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-5.0.1.tgz", - "integrity": "sha512-6K1DoBNAPGvuOcSsGA4D6x+5zEEff/KmOOP3uetT2TrGpVfI+HRHRnJJfKi5ib/g1vx8IYHQD8s0pbJz8WQI7Q==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-5.0.2.tgz", + "integrity": "sha512-Z5FS00Q1SJHkB35xATsmWGdQ5WA1/0MV3CDjqyv7GavHv1OfOj145MNfHOlHk7QLes21dKFDHr8EO2zvL+9WGA==", "dev": true, "license": "MIT", "dependencies": { "@jridgewell/trace-mapping": "0.3.31", - "@vitest/spy": "5.0.1", + "@vitest/spy": "5.0.2", "estree-walker": "^3.0.3", "magic-string": "^1.2.3" }, @@ -2380,9 +2383,9 @@ } }, "node_modules/@vitest/mocker/node_modules/magic-string": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-1.4.1.tgz", - "integrity": "sha512-8lyCu36ErXR0J9uaGKlKQoiLZKmtI63YGLE8G2o9jyRPdr4X47LusSOwgOJOzcVtp81fTAAjxR7BwKz682Jhow==", + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-1.4.3.tgz", + "integrity": "sha512-z12OxmaPGE0F4xlpGdjuAUckipLpQlTAuAmyGhNoD7ODpYUzvDfvtxUbjM/jwznyP178oju/KDdyi220wNJ0RQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2390,9 +2393,9 @@ } }, "node_modules/@vitest/spy": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-5.0.1.tgz", - "integrity": "sha512-rbto/mF/SGERxEgYOek7Xm6B9b+y+mVoo+f4b2LymYO8zM1b7uB5nHuhVMTP2hxdzgxvGiZYGxGIaMvL5y180Q==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-5.0.2.tgz", + "integrity": "sha512-Ijc7T1nT9efNb5LxvjaBrEqw3f/QwUv5EE0nKqZxgqsaV/FxAAZ8baGylA8X/Z2oS4Lp+K74Jr6dTJsDKxJDeg==", "dev": true, "license": "MIT", "funding": { @@ -6065,13 +6068,6 @@ "node": ">=8" } }, - "node_modules/siginfo": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", - "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", - "dev": true, - "license": "ISC" - }, "node_modules/signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", @@ -6144,13 +6140,6 @@ "license": "BSD-3-Clause", "optional": true }, - "node_modules/stackback": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", - "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", - "dev": true, - "license": "MIT" - }, "node_modules/standardwebhooks": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/standardwebhooks/-/standardwebhooks-1.1.1.tgz", @@ -7126,14 +7115,14 @@ } }, "node_modules/vitest": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-5.0.1.tgz", - "integrity": "sha512-iA95lQbKEkvrtTkdAgnWbXfbipWiiWe/hDl2P5tMi6WFwD76G0NxXAGp/M9EOcYupeGJRr6wppMc7CoA41TQjg==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-5.0.2.tgz", + "integrity": "sha512-7MQrx9pDv5aHiUcovIb/70Ys3tgtkUVgCtledvKdCmEO+/1Dicq5ZqoSxOW034m03oqC+oHOKui2dM6qtMLoJg==", "dev": true, "license": "MIT", "dependencies": { "@types/chai": "^5.2.2", - "@vitest/mocker": "5.0.1", + "@vitest/mocker": "5.0.2", "chai": "^6.2.2", "es-module-lexer": "^2.3.2", "expect-type": "^1.4.0", @@ -7141,10 +7130,10 @@ "obug": "^2.1.4", "picomatch": "^4.0.7", "std-env": "^4.2.0", - "tinybench": "6.1.4", - "tinyexec": "1.3.0", + "tinybench": "^6.1.4", + "tinyexec": "^1.3.0", "tinyglobby": "^0.2.17", - "why-is-node-running": "^2.3.0" + "why-is-node-running": "^3.2.1" }, "bin": { "vitest": "vitest.mjs" @@ -7159,12 +7148,12 @@ "@edge-runtime/vm": "*", "@opentelemetry/api": "^1.9.0", "@types/node": "^22.0.0 || >=24.0.0", - "@vitest/browser-playwright": "5.0.1", - "@vitest/browser-preview": "5.0.1", + "@vitest/browser-playwright": "5.0.2", + "@vitest/browser-preview": "5.0.2", "@vitest/browser-webdriverio": "^5.0.0-beta.5 || >=5.0.0", - "@vitest/coverage-istanbul": "5.0.1", - "@vitest/coverage-v8": "5.0.1", - "@vitest/ui": "5.0.1", + "@vitest/coverage-istanbul": "5.0.2", + "@vitest/coverage-v8": "5.0.2", + "@vitest/ui": "5.0.2", "happy-dom": "*", "jsdom": "*", "vite": "^6.4.0 || ^7.0.0 || ^8.0.0" @@ -7249,20 +7238,16 @@ } }, "node_modules/why-is-node-running": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", - "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-3.2.2.tgz", + "integrity": "sha512-NKUzAelcoCXhXL4dJzKIwXeR8iEVqsA0Lq6Vnd0UXvgaKbzVo4ZTHROF2Jidrv+SgxOQ03fMinnNhzZATxOD3A==", "dev": true, "license": "MIT", - "dependencies": { - "siginfo": "^2.0.0", - "stackback": "0.0.2" - }, "bin": { "why-is-node-running": "cli.js" }, "engines": { - "node": ">=8" + "node": ">=20.11" } }, "node_modules/word-wrap": { diff --git a/package.json b/package.json index c06cf35..1447206 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gitgood", - "version": "0.4.1", + "version": "0.4.2", "description": "A GitHub Desktop-style Git client for Windows, macOS and Linux that drives the git and gh CLIs", "main": "./out/main/index.js", "author": "Erwin Wee", @@ -54,6 +54,6 @@ "eslint-plugin-react-hooks": "^7.1.1", "typescript": "^5.9.0", "vite": "^7.3.0", - "vitest": "^5.0.1" + "vitest": "^5.0.2" } } diff --git a/src/server/config.ts b/src/server/config.ts index b12815b..93b37d6 100644 --- a/src/server/config.ts +++ b/src/server/config.ts @@ -40,11 +40,26 @@ function tailnetOwner(): string | null { } } +// Resolved lazily: at boot tailscaled may not be up yet, and a startup-time null would refuse every tailnet request until restart. +// Only a successful lookup is cached; failures are retried at most every 30s (the lookup is a synchronous exec). +function lazyTailnetOwner(): () => string | null { + let owner: string | null = null; + let lastTry = 0; + return () => { + if (owner === null && Date.now() - lastTry > 30_000) { + lastTry = Date.now(); + owner = tailnetOwner(); + } + return owner; + }; +} + export function loadServerConfig(): ServerConfig { const userData = process.env.GITGOOD_USER_DATA || join(homedir(), '.config', 'gitgood-server'); const tokenFile = join(userData, 'server-token'); const existedBefore = existsSync(tokenFile); const token = loadOrCreateToken(tokenFile, userData); + const owner = lazyTailnetOwner(); return { host: '127.0.0.1', port: Number(process.env.GITGOOD_SERVER_PORT) || 4600, @@ -52,7 +67,9 @@ export function loadServerConfig(): ServerConfig { rendererDir: process.env.GITGOOD_RENDERER_DIR || defaultRendererDir(), token, tokenCreated: !existedBefore, - allowedLogin: process.env.GITGOOD_ALLOWED_LOGIN || tailnetOwner(), + get allowedLogin() { + return process.env.GITGOOD_ALLOWED_LOGIN || owner(); + }, // Home is browsable so the folder picker can add repos/watched folders that aren't registered yet. extraRoots: [homedir(), ...(process.env.GITGOOD_ALLOWED_ROOTS || '').split(delimiter).filter(Boolean)], version: process.env.GITGOOD_VERSION || (typeof __GITGOOD_VERSION__ === 'string' ? __GITGOOD_VERSION__ : '0.0.0'),