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
18 changes: 18 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,13 @@ jobs:
- name: Install dependencies
run: npm ci

# _license-secret.ts is generated (gitignored) — required by
# license-activation.ts before tsc/vitest can run.
- name: Inject license secret
env:
MINGLY_LICENSE_SECRET: ${{ secrets.MINGLY_LICENSE_SECRET }}
run: npx tsx scripts/inject-secret.ts

- name: Type check (main process)
run: npx tsc -p tsconfig.main.json --noEmit

Expand All @@ -48,6 +55,11 @@ jobs:
- name: Install dependencies
run: npm ci

- name: Inject license secret
env:
MINGLY_LICENSE_SECRET: ${{ secrets.MINGLY_LICENSE_SECRET }}
run: npx tsx scripts/inject-secret.ts

- name: Run tests with coverage
run: npx vitest run --coverage

Expand Down Expand Up @@ -96,7 +108,10 @@ jobs:
- name: Install dependencies
run: npm ci

# prebuild:main runs inject-secret automatically (npm lifecycle hook)
- name: Build main process
env:
MINGLY_LICENSE_SECRET: ${{ secrets.MINGLY_LICENSE_SECRET }}
run: npm run build:main

- name: Build renderer
Expand Down Expand Up @@ -176,7 +191,10 @@ jobs:
- name: Install dependencies
run: npm ci

# prebuild:main runs inject-secret automatically (npm lifecycle hook)
- name: Build
env:
MINGLY_LICENSE_SECRET: ${{ secrets.MINGLY_LICENSE_SECRET }}
run: npm run build

- name: Package and publish Electron app
Expand Down
16 changes: 1 addition & 15 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"dev:vite": "vite",
"dev:electron": "npm run build:preload && wait-on http://localhost:5173 && electron .",
"build": "npm run build:main && npm run build:ner-worker && npm run build:preload && npm run build:renderer",
"prebuild:main": "npx tsx scripts/inject-secret.ts",
"build:main": "esbuild src/main/index.ts --bundle --platform=node --outfile=dist/main/index.js --external:electron --external:electron-updater --external:sql.js --external:@huggingface/transformers --format=cjs --target=node18 --sourcemap",
"build:ner-worker": "esbuild src/main/privacy/ner-worker.ts --bundle --platform=node --outfile=dist/main/ner-worker.js --external:@huggingface/transformers --format=cjs --target=node18 --sourcemap",
"build:preload": "esbuild src/preload/index.ts --bundle --platform=node --outfile=dist/preload/index.js --external:electron --format=iife --target=node18",
Expand Down Expand Up @@ -71,6 +72,7 @@
"promptfoo": "^0.121.2",
"tailwindcss": "^3.4.1",
"tailwindcss-animate": "^1.0.7",
"tsx": "^4.22.5",
"typescript": "^5.3.3",
"vite": "^5.0.10",
"vitest": "^4.0.18",
Expand Down
51 changes: 51 additions & 0 deletions scripts/inject-secret.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/**
* Build-time secret injection for license validation.
*
* Reads MINGLY_LICENSE_SECRET from environment (or .env file) and generates
* a TypeScript module that exports the secret. This file is gitignored so the
* secret never leaks into the public repository.
*
* Usage: npx tsx scripts/inject-secret.ts
* Called automatically via `prebuild:main` in package.json (also runs in CI
* before typecheck/tests, since license-activation.ts imports the module).
*/

import { writeFileSync, existsSync, readFileSync } from 'fs'
import { join } from 'path'

const OUTPUT_PATH = join(__dirname, '..', 'src', 'main', 'services', '_license-secret.ts')
const ENV_PATH = join(__dirname, '..', '.env')
const DEV_FALLBACK = 'mingly-dev-secret-not-for-production'

function loadEnvSecret(): string | undefined {
if (process.env.MINGLY_LICENSE_SECRET) {
return process.env.MINGLY_LICENSE_SECRET
}

if (existsSync(ENV_PATH)) {
const content = readFileSync(ENV_PATH, 'utf-8')
const match = content.match(/^MINGLY_LICENSE_SECRET=(.+)$/m)
if (match && match[1] && match[1] !== 'your-secret-here') {
return match[1].trim()
}
}

return undefined
}

const secret = loadEnvSecret()
const isProduction = !!secret

if (!secret) {
console.log('[inject-secret] No MINGLY_LICENSE_SECRET found — using dev fallback')
} else {
console.log('[inject-secret] Production secret injected')
}

const output = `// AUTO-GENERATED — do not edit. Regenerated on every build.
export const LICENSE_HMAC_SECRET = '${secret || DEV_FALLBACK}'
export const IS_PRODUCTION_SECRET = ${isProduction}
`

writeFileSync(OUTPUT_PATH, output, 'utf-8')
console.log(`[inject-secret] Written to ${OUTPUT_PATH}`)
Loading