Skip to content

feat(teams): Graph-based ingestion (no @mention required) #492

feat(teams): Graph-based ingestion (no @mention required)

feat(teams): Graph-based ingestion (no @mention required) #492

Workflow file for this run

name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true
# Default to least-privilege at the workflow level. Individual jobs may
# elevate via their own `permissions:` block if a future change needs
# write access (e.g. to comment on PRs or upload SBOMs). CodeQL alerts
# #2, #4, #5, #7 — actions/missing-workflow-permissions.
permissions:
contents: read
jobs:
backend:
name: Backend (Python 3.12)
permissions:
contents: read
runs-on: ubuntu-latest
services:
mongodb:
image: mongo:7@sha256:43fddee7e532a920f3dfdee9e8f4834398c155c26bcb92d790cc1cd3c630fc40
ports:
- 27017:27017
options: >-
--health-cmd "mongosh --eval 'db.runCommand({ ping: 1 })'"
--health-interval 10s
--health-timeout 5s
--health-retries 5
redis:
image: redis:7@sha256:24ea35ee039d7345bef2377611e3b1a615231746de4cfea74c1414575ee56167
ports:
- 6379:6379
options: >-
--health-cmd "redis-cli ping"
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- uses: actions/checkout@v6
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.12"
- name: Install uv
uses: astral-sh/setup-uv@v7
with:
enable-cache: true
- name: Install dependencies
run: uv sync --extra dev --extra test --extra nebula
- name: Ruff format
run: uv run ruff format --check src/ tests/
- name: Ruff
run: uv run ruff check src/ tests/
- name: Pyright
run: uv run pyright
- name: Pytest + coverage
env:
MONGODB_URI: mongodb://localhost:27017/beever_atlas
REDIS_URL: redis://localhost:6379
# 64 hex chars = 32 bytes = AES-256-GCM key. Tests that exercise
# the Endpoint+Assignment credential-encryption path
# (``tests/api/test_assignments_api.py``, ``tests/llm/test_endpoint_store.py``)
# ``monkeypatch.setenv`` this same value, but ``get_settings()`` is
# ``@lru_cache``-decorated and the FIRST call freezes the env state.
# Without this CI-level default, the first import that touches
# settings caches an empty key, and every subsequent
# ``encrypt_endpoint_credential`` 503s with
# ``credential_encryptor_unavailable``.
# Same value the ``smoke.yml`` workflow uses.
CREDENTIAL_MASTER_KEY: ${{ secrets.CI_CREDENTIAL_MASTER_KEY || 'abababababababababababababababababababababababababababababababab' }}
run: uv run pytest --cov=src/beever_atlas --cov-report=term-missing -q
web:
name: Web (Node 20)
permissions:
contents: read
runs-on: ubuntu-latest
defaults:
run:
working-directory: web
steps:
- uses: actions/checkout@v6
- name: Set up Node
uses: actions/setup-node@v6
with:
node-version: "20"
cache: "npm"
cache-dependency-path: web/package-lock.json
- name: Install
run: npm ci
- name: Lint
run: npm run lint
- name: Typecheck
run: npm run typecheck
- name: Test
run: npm test -- --run
- name: Build
run: npm run build
bot:
name: Bot (Node 20)
permissions:
contents: read
runs-on: ubuntu-latest
defaults:
run:
working-directory: bot
steps:
- uses: actions/checkout@v6
- name: Set up Node
uses: actions/setup-node@v6
with:
node-version: "20"
cache: "npm"
cache-dependency-path: bot/package-lock.json
- name: Install
run: npm ci
- name: Lint
run: npm run lint
- name: Build
run: npm run build
- name: Test
run: npm test
supply-chain-lint:
name: Supply Chain (digest pinning)
permissions:
contents: read
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: "Reject unpinned FROM / image: references"
run: |
set -e
violations=0
# Dockerfiles: reject FROM <ref> when ref is a real image (has : or /)
# without an @sha256: digest suffix. Stage aliases (bare words) pass.
for f in Dockerfile web/Dockerfile bot/Dockerfile; do
[ -f "$f" ] || continue
while IFS= read -r line; do
ref="$(echo "$line" | awk '/^FROM /{print $2}')"
[ -z "$ref" ] && continue
[ "$ref" = "scratch" ] && continue
case "$ref" in
*@sha256:*) ;;
*)
case "$ref" in
*[:/]*)
echo "::error file=$f::unpinned FROM: $line"
violations=$((violations+1))
;;
esac
;;
esac
done < "$f"
# Also catch COPY --from=<external> without a digest.
while IFS= read -r line; do
from_ref="$(echo "$line" | sed -nE 's/.*COPY[[:space:]]+--from=([^[:space:]]+).*/\1/p')"
[ -z "$from_ref" ] && continue
# Bare stage alias (no : or /): skip.
case "$from_ref" in
*[:/]*) ;;
*) continue ;;
esac
case "$from_ref" in
*@sha256:*) ;;
*)
echo "::error file=$f::unpinned COPY --from: $line"
violations=$((violations+1))
;;
esac
done < "$f"
done
# Compose files + GitHub Actions workflow service images: reject
# `image:` lines without @sha256:. Issue #42 added the workflows
# scan after the nightly job's `services:` images were caught
# running unpinned. The existing
# `^[[:space:]]*image:[[:space:]]+` regex matches both
# compose-level and `services:` block image references.
targets="docker-compose.yml docker-compose.nebula.yml"
for wf in .github/workflows/*.yml; do
[ -f "$wf" ] && targets="$targets $wf"
done
for f in $targets; do
[ -f "$f" ] || continue
while IFS= read -r line; do
ref="$(echo "$line" | awk '/^[[:space:]]*image:[[:space:]]+/{print $2}')"
[ -z "$ref" ] && continue
case "$ref" in
*@sha256:*) ;;
*)
echo "::error file=$f::unpinned image: $line"
violations=$((violations+1))
;;
esac
done < "$f"
done
if [ "$violations" -gt 0 ]; then
echo "Found $violations unpinned image reference(s). Pin by @sha256:<digest>."
exit 1
fi
echo "OK — every FROM / image: is digest-pinned."
- name: Reject caret/tilde ranges on chat SDK family in bot/package.json
run: |
set -e
# RES-195 (H6): the Vercel `chat` + @chat-adapter/* family must stay
# pinned to exact versions. Caret/tilde ranges reopen the supply-chain
# vector a compromised-maintainer release would exploit.
violations=0
while IFS= read -r line; do
# Extract the specifier string from `"name": "spec",` lines.
spec="$(echo "$line" | sed -nE 's/.*"(chat|@chat-adapter\/[a-z-]+|chat-adapter-mattermost)": "([^"]+)".*/\2/p')"
[ -z "$spec" ] && continue
case "$spec" in
[\^~]*)
echo "::error file=bot/package.json::non-exact chat-family pin: $line"
violations=$((violations+1))
;;
esac
done < bot/package.json
if [ "$violations" -gt 0 ]; then
echo "Found $violations non-exact chat-SDK pin(s). Strip ^ and ~."
exit 1
fi
echo "OK — chat SDK family pinned to exact versions."