Skip to content
Closed
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/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
version: 2
updates:
- package-ecosystem: bun
directory: /
schedule:
interval: weekly
open-pull-requests-limit: 5
groups:
non-major-dependencies:
update-types:
- minor
- patch

- package-ecosystem: github-actions
directory: /
schedule:
interval: weekly
open-pull-requests-limit: 5
36 changes: 27 additions & 9 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ on:
pull_request:
branches: [main]

permissions:
contents: read

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
Expand Down Expand Up @@ -47,20 +50,37 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0 # Required for the schema-vs-migrations check.
persist-credentials: false

- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: latest
# Pin the version from the last successful upstream CI run (PR #70).
# The older 1.2.12 fails to resolve workspace packages in Vitest.
bun-version: "1.4.3"

- name: Install dependencies
run: bun install --frozen-lockfile

- name: Generate Fumadocs types
# Required by apps/web-ui's TypeScript sources; otherwise typecheck
# incorrectly fails on an absent generated @/.source module.
working-directory: apps/web-ui
run: bun -e 'import { postInstall } from "fumadocs-mdx/next"; await postInstall();'

- name: Prepare test database
# migrate deploy, not db push: the HNSW pgvector indexes live in raw SQL
# inside migrations, and db push silently skips them.
run: bunx prisma migrate deploy --schema=./prisma/schema.prisma

- name: Validate migrations and vector indexes
run: bun run check:db

- name: Typecheck
run: bun run typecheck

- name: Unit tests
run: bun run test

Expand All @@ -73,9 +93,10 @@ jobs:
- name: Build
run: bun run build

- name: Install Playwright browsers
run: bunx playwright install --with-deps chromium

# E2E needs an independently verified CI web server. Installing a
# browser with no test execution wastes runner time. See the follow-up
# described in docs/dev/ci-security.md.
#
# e2e is intentionally NOT run here yet.
#
# Playwright's webServer runs `bun run dev:all`, which starts four servers in
Expand All @@ -98,8 +119,5 @@ jobs:
path: apps/web-ui-e2e/playwright-report/
retention-days: 7

# TODO: Add lint and typecheck scripts when ESLint / tsc targets are configured in Nx
# - name: Lint
# run: bun run lint
# - name: Type check
# run: bun run typecheck
# ESLint is not a CI gate yet: the repository does not currently have
# a working Nx-wide lint target/configuration. Enable it once defined.
36 changes: 36 additions & 0 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
name: CodeQL SAST

on:
pull_request:
branches: [main]
push:
branches: [main]
schedule:
- cron: "17 4 * * 1"

permissions:
contents: read

jobs:
analyze:
name: CodeQL (JavaScript/TypeScript)
runs-on: ubuntu-latest
timeout-minutes: 35
permissions:
contents: read
actions: read
security-events: write
steps:
- name: Checkout
uses: actions/checkout@v4
with:
persist-credentials: false
- name: Initialize CodeQL
uses: github/codeql-action/init@v4
with:
languages: javascript-typescript
queries: security-extended
- name: Analyze
uses: github/codeql-action/analyze@v4
with:
category: "/language:javascript-typescript"
91 changes: 76 additions & 15 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,15 @@ name: Build & push web-ui image
# region), so a deploy job here would fail for reasons unrelated to the image.
# Adding it later is a small diff once the infrastructure exists.

# Deploy only a successfully-tested main commit, never untrusted PR code.
on:
push:
workflow_run:
workflows: ["CI"]
types: [completed]
branches: [main]
paths:
- "apps/web-ui/**"
- "apps/sdk/**"
- "libs/**"
- "prisma/**"
- "package.json"
- "bun.lock"
- ".github/workflows/deploy.yml"
workflow_dispatch:

permissions:
contents: read

concurrency:
group: image-${{ github.ref }}
Expand All @@ -29,10 +26,50 @@ env:
AWS_REGION: ${{ vars.AWS_REGION || 'ap-south-2' }}
ECR_REPOSITORY: ${{ vars.ECR_REPOSITORY || 'chatbot-web-ui' }}
AWS_ROLE_ARN: ${{ vars.AWS_ROLE_ARN || 'arn:aws:iam::704554708141:role/agentfork-github-deploy' }}
SOURCE_SHA: ${{ github.event.workflow_run.head_sha }}

jobs:
changes:
name: Detect image-related changes
if: >-
${{ github.event.workflow_run.event == 'push' &&
github.event.workflow_run.conclusion == 'success' }}
runs-on: ubuntu-latest
outputs:
build: ${{ steps.paths.outputs.build }}
steps:
- name: Checkout tested commit
uses: actions/checkout@v4
with:
ref: ${{ github.event.workflow_run.head_sha }}
fetch-depth: 2
persist-credentials: false
- name: Compare with first parent
id: paths
shell: bash
run: |
set -euo pipefail
if git rev-parse HEAD^ >/dev/null 2>&1; then
git diff --name-only HEAD^ HEAD > /tmp/image-changes.txt
else
git ls-tree -r --name-only HEAD > /tmp/image-changes.txt
fi
build=false
while IFS= read -r file; do
case "$file" in
apps/web-ui/*|apps/sdk/*|libs/*|prisma/*|package.json|bun.lock|.github/workflows/deploy.yml)
build=true
break
;;
esac
done < /tmp/image-changes.txt
echo "build=$build" >> "$GITHUB_OUTPUT"
echo "Image-related changes: $build" >> "$GITHUB_STEP_SUMMARY"

build-and-push:
name: Build & push image
name: Scan & push image
needs: [changes]
if: needs.changes.outputs.build == 'true'
# Native arm64 runner. The image targets linux/arm64 (Fargate Graviton), and
# this repo is public, so arm64 runners are free — building natively avoids
# QEMU, which is slow enough on a Next.js build to hit the job timeout.
Expand All @@ -42,8 +79,11 @@ jobs:
id-token: write

steps:
- name: Checkout
- name: Checkout tested commit
uses: actions/checkout@v4
with:
ref: ${{ github.event.workflow_run.head_sha }}
persist-credentials: false

- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
Expand All @@ -63,20 +103,21 @@ jobs:
id: meta
run: |
IMAGE="${{ steps.ecr.outputs.registry }}/${ECR_REPOSITORY}"
SHORT_SHA="${GITHUB_SHA:0:12}"
SHORT_SHA="${SOURCE_SHA:0:12}"
{
echo "image=${IMAGE}:${SHORT_SHA}"
echo "latest=${IMAGE}:latest"
} >> "$GITHUB_OUTPUT"
echo "Building ${IMAGE}:${SHORT_SHA}"

- name: Build and push
- name: Build local image for vulnerability scanning
uses: docker/build-push-action@v6
with:
context: .
file: apps/web-ui/Dockerfile
platforms: linux/arm64
push: true
load: true
push: false
# Tagged by commit SHA (immutable, traceable) plus :latest for convenience.
tags: |
${{ steps.meta.outputs.image }}
Expand All @@ -93,6 +134,26 @@ jobs:
# silently bake a localhost URL into a production image's client bundle.
NEXT_PUBLIC_MISSION_CONTROL_URL=${{ vars.NEXT_PUBLIC_MISSION_CONTROL_URL }}

- name: Scan local image before publication
uses: aquasecurity/trivy-action@v0.36.0
with:
scan-type: image
image-ref: ${{ steps.meta.outputs.image }}
scanners: vuln
severity: HIGH,CRITICAL
ignore-unfixed: true
format: table
exit-code: "0" # Advisory until baseline findings are triaged.

- name: Push scanned image
env:
IMAGE: ${{ steps.meta.outputs.image }}
LATEST: ${{ steps.meta.outputs.latest }}
run: |
set -euo pipefail
docker push "$IMAGE"
docker push "$LATEST"

- name: Summary
run: |
{
Expand Down
14 changes: 14 additions & 0 deletions .github/workflows/osv-scanner-pr.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
name: OSV-Scanner PR

on:
pull_request:
branches: [main]

permissions:
actions: read
contents: read
security-events: write

jobs:
scan:
uses: google/osv-scanner-action/.github/workflows/osv-scanner-reusable-pr.yml@v2.7.0
15 changes: 15 additions & 0 deletions .github/workflows/osv-scanner-scheduled.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
name: OSV-Scanner full scan

on:
schedule:
- cron: "11 6 * * 1"
workflow_dispatch:

permissions:
actions: read
contents: read
security-events: write

jobs:
scan:
uses: google/osv-scanner-action/.github/workflows/osv-scanner-reusable.yml@v2.7.0
66 changes: 66 additions & 0 deletions .github/workflows/security-checks.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
name: Security checks

on:
pull_request:
branches: [main]
push:
branches: [main]
schedule:
- cron: "45 5 * * 1"

permissions:
contents: read

jobs:
secrets:
name: TruffleHog (verified secrets)
if: github.event_name != 'schedule'
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Checkout full history for secret diff
uses: actions/checkout@v4
with:
fetch-depth: 0
persist-credentials: false
- name: Scan changed commits
uses: trufflesecurity/trufflehog@v3.92.5
with:
extra_args: --results=verified

workflow-audit:
name: Zizmor (GitHub Actions security)
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout
uses: actions/checkout@v4
with:
persist-credentials: false
- name: Audit GitHub Actions workflows
uses: zizmorcore/zizmor-action@v0.6.4
continue-on-error: true # Advisory until existing findings are triaged.
with:
advanced-security: false
min-severity: medium

trivy:
name: Trivy (filesystem / IaC baseline)
if: github.event_name == 'schedule'
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- name: Checkout
uses: actions/checkout@v4
with:
persist-credentials: false
- name: Scan dependencies and configuration
uses: aquasecurity/trivy-action@v0.36.0
with:
scan-type: fs
scan-ref: .
scanners: vuln,misconfig
severity: HIGH,CRITICAL
ignore-unfixed: true
format: table
exit-code: "0" # Initial baseline report; do not hide historical findings.
Loading
Loading