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
13 changes: 13 additions & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Global default
* @sammargolis

# Security-sensitive paths
/apps/web/src/app/api/ @sammargolis
/apps/web/src/middleware.ts @sammargolis
/packages/storage/src/ @sammargolis
/packages/pipeline/transcribe/src/ @sammargolis
/packages/pipeline/assemble/src/ @sammargolis
/config/ @sammargolis
/infra/ @sammargolis
/.github/workflows/ @sammargolis
/docs/compliance/ @sammargolis
25 changes: 25 additions & 0 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
## Summary
-

## Security Impact
-

## PHI Handling Impact
- Data touched:
- New PHI flows:
- Logging changes:

## Tests Executed
- [ ] `pnpm lint`
- [ ] `pnpm typecheck`
- [ ] `pnpm test`
- [ ] `pnpm test:no-phi-logs`

## Rollback Plan
-

## Checklist
- [ ] Single concern PR (300-600 LOC target)
- [ ] Docs updated
- [ ] No direct push to `main`
- [ ] No PHI added to logs/telemetry
179 changes: 179 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
name: CI

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

jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: pnpm
- run: pnpm install --frozen-lockfile
- run: pnpm lint:structure
- name: Detect lint targets
id: lint-targets
run: |
if [[ "${{ github.event_name }}" == "pull_request" ]]; then
BASE_SHA="${{ github.event.pull_request.base.sha }}"
else
BASE_SHA="${{ github.event.before }}"
fi
CHANGED_FILES="$(git diff --name-only "$BASE_SHA" "${{ github.sha }}" -- \
apps/web/src \
packages/storage/src \
packages/pipeline/transcribe/src \
packages/pipeline/assemble/src \
scripts/check-no-phi-logs.mjs \
config/scripts/check-structure.mjs \
| tr '\n' ' ')"
echo "files=${CHANGED_FILES}" >> "$GITHUB_OUTPUT"
- name: Run ESLint on changed files
if: steps.lint-targets.outputs.files != ''
run: pnpm exec eslint --config config/eslint.config.mjs ${{ steps.lint-targets.outputs.files }}
- name: Skip ESLint when no tracked files changed
if: steps.lint-targets.outputs.files == ''
run: echo "No lint-tracked files changed; skipping eslint."

typecheck:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: pnpm
- run: pnpm install --frozen-lockfile
- run: pnpm build:test

test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: pnpm
- run: pnpm install --frozen-lockfile
- name: Detect test-impacting changes
id: test-scope
run: |
if [[ "${{ github.event_name }}" == "pull_request" ]]; then
BASE_SHA="${{ github.event.pull_request.base.sha }}"
else
BASE_SHA="${{ github.event.before }}"
fi
if git diff --name-only "$BASE_SHA" "${{ github.sha }}" -- apps/web/src packages | grep -q .; then
echo "run_tests=true" >> "$GITHUB_OUTPUT"
else
echo "run_tests=false" >> "$GITHUB_OUTPUT"
fi
- name: Run tests
if: steps.test-scope.outputs.run_tests == 'true'
run: pnpm test
- name: Skip tests when no runtime files changed
if: steps.test-scope.outputs.run_tests != 'true'
run: echo "No runtime code changes; skipping test suite."

no-phi-log-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: pnpm
- run: pnpm install --frozen-lockfile
- name: Detect guarded file changes
id: phi-scope
run: |
if [[ "${{ github.event_name }}" == "pull_request" ]]; then
BASE_SHA="${{ github.event.pull_request.base.sha }}"
else
BASE_SHA="${{ github.event.before }}"
fi
CHANGED_FILES="$(git diff --name-only "$BASE_SHA" "${{ github.sha }}" -- \
apps/web/src/app/api/transcription/final/route.ts \
apps/web/src/app/api/transcription/segment/route.ts \
'apps/web/src/app/api/transcription/stream/[sessionId]/route.ts' \
apps/web/src/app/api/notes/generate/route.ts \
apps/web/src/app/actions.ts \
apps/web/src/lib/auth.ts \
apps/web/src/middleware.ts \
packages/pipeline/assemble/src/session-store.ts \
packages/pipeline/transcribe/src/providers/whisper-transcriber.ts \
packages/pipeline/transcribe/src/providers/whisper-local-transcriber.ts \
packages/pipeline/transcribe/src/providers/medasr-transcriber.ts \
scripts/check-no-phi-logs.mjs \
| tr '\n' ' ')"
echo "files=${CHANGED_FILES}" >> "$GITHUB_OUTPUT"
- name: Run PHI log guard
if: steps.phi-scope.outputs.files != ''
run: pnpm test:no-phi-logs -- ${{ steps.phi-scope.outputs.files }}
- name: Skip PHI log guard when no guarded files changed
if: steps.phi-scope.outputs.files == ''
run: echo "No guarded files changed; skipping PHI log check."

dependency-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: pnpm
- run: pnpm install --frozen-lockfile
- name: Detect dependency manifest changes
id: dep-scope
run: |
if [[ "${{ github.event_name }}" == "pull_request" ]]; then
BASE_SHA="${{ github.event.pull_request.base.sha }}"
else
BASE_SHA="${{ github.event.before }}"
fi
if git diff --name-only "$BASE_SHA" "${{ github.sha }}" -- pnpm-lock.yaml | grep -q .; then
echo "run_audit=true" >> "$GITHUB_OUTPUT"
else
echo "run_audit=false" >> "$GITHUB_OUTPUT"
fi
- name: Run dependency audit
if: steps.dep-scope.outputs.run_audit == 'true'
run: pnpm audit --audit-level=high
- name: Skip dependency audit when manifests are unchanged
if: steps.dep-scope.outputs.run_audit != 'true'
run: echo "No dependency manifest changes; skipping audit."

secret-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Run gitleaks (license-free)
run: |
if [[ "${{ github.event_name }}" == "pull_request" ]]; then
LOG_OPTS="${{ github.event.pull_request.base.sha }}..${{ github.sha }}"
else
LOG_OPTS="${{ github.event.before }}..${{ github.sha }}"
fi
docker run --rm -v "$PWD:/repo" zricethezav/gitleaks:latest \
git --log-opts="$LOG_OPTS" --redact --exit-code 1 /repo
16 changes: 13 additions & 3 deletions config/eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,16 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url))
const tsconfigRootDir = path.resolve(__dirname, "..")
const kebabCasePattern = "^[a-z0-9]+(?:-[a-z0-9]+)*$"

const ignoredPaths = ["build/**", "node_modules/**", "apps/web/public/**", "apps/web/.next/**"]
const nodeFiles = ["config/**/*.{js,mjs,cjs}", "packages/shell/**/*.js"]
const ignoredPaths = [
"build/**",
"node_modules/**",
"**/.venv-*/**",
"**/.venv/**",
"apps/web/public/**",
"apps/web/.next/**",
"output/**",
]
const nodeFiles = ["config/**/*.{js,mjs,cjs}", "packages/shell/**/*.js", "scripts/**/*.{js,mjs,cjs}"]
const nodeGlobals = {
require: "readonly",
module: "readonly",
Expand Down Expand Up @@ -97,7 +105,9 @@ export default tseslint.config(
files: nodeFiles,
languageOptions: {
parserOptions: {
projectService: true,
projectService: {
allowDefaultProject: ["scripts/*.js", "scripts/*.mjs", "scripts/*.cjs"],
},
tsconfigRootDir,
},
globals: nodeGlobals,
Expand Down
22 changes: 21 additions & 1 deletion config/scripts/check-structure.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,29 @@ import path from "node:path"

const root = path.resolve(process.cwd())

const allowedRootDirs = new Set(["apps", "packages", "config", "build", "docker", "node_modules"])
const allowedRootDirs = new Set([
"apps",
"packages",
"config",
"build",
"docker",
"docs",
"infra",
"local-only",
"models",
"output",
"recordings",
"scripts",
"node_modules",
])
const allowedRootFiles = new Set([
"package.json",
"pnpm-lock.yaml",
"tsconfig.json",
"README.md",
"CONTRIBUTING.md",
"LICENSE",
"requirements.txt",
"architecture.md",
".gitignore",
"BUILD_STATUS.md",
Expand Down Expand Up @@ -51,6 +68,9 @@ for (const entry of fs.readdirSync(root, { withFileTypes: true })) {
errors.push(`Unexpected top-level directory: ${name}`)
}
} else {
if (name.endsWith(".tsbuildinfo")) {
continue
}
if (name.startsWith(".env")) {
continue
}
Expand Down
25 changes: 25 additions & 0 deletions docs/BRANCH_PROTECTION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Branch Protection Policy

## Protected branch
- `main`

## Required settings
- Pull request required before merge
- At least 1 approving review
- Dismiss stale approvals when new commits are pushed
- Require conversation resolution before merge
- Require status checks to pass
- Restrict direct pushes to `main`
- Allow squash merge only

## Required status checks
- `lint`
- `typecheck`
- `test`
- `dependency-scan`
- `secret-scan`
- `no-phi-log-check`

## Release policy
- Production deploys are allowed only from signed tags `v*`
- `main` merges may deploy to non-production environments only
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,15 @@
"dev:local:medasr": "concurrently -k \"pnpm medasr:server\" \"pnpm dev\"",
"lint": "pnpm lint:structure && eslint --config config/eslint.config.mjs .",
"lint:structure": "node config/scripts/check-structure.mjs",
"typecheck": "pnpm build:test",
"start": "next start apps/web",
"build:test": "tsc --project config/tsconfig.test.json",
"test": "pnpm build:test && pnpm test:run",
"test:run": "node --test build/tests-dist/**/*.test.js",
"test:e2e": "pnpm build:test && node --test build/tests-dist/pipeline/eval/src/tests/e2e-basic.test.js",
"test:e2e:real": "pnpm build:test && node --test build/tests-dist/pipeline/eval/src/tests/e2e-real-api.test.js",
"test:api": "pnpm build:test && node --test build/tests-dist/pipeline/eval/src/tests/api-simple.test.js",
"test:no-phi-logs": "node scripts/check-no-phi-logs.mjs",
"test:llm": "pnpm build:test && node --test build/tests-dist/llm/src/__tests__/*.test.js",
"test:note": "pnpm build:test && node --test build/tests-dist/pipeline/note-core/src/__tests__/*.test.js",
"build:desktop": "pnpm build && pnpm build:backend && node packages/shell/scripts/prepare-next.js && electron-builder --mac",
Expand Down
Loading