Bump the memory benchmark runtime pins to the current stable releases: Bun 1.3.11 -> 1.4.0, Node 22.19.0 -> 22.23.2, pi host 0.82.1 -> 0.84.4. Pin the Oh My Pi compatibility job to the same Bun release instead of 'latest' so a required check does not drift on its own.
200 lines
8.3 KiB
YAML
200 lines
8.3 KiB
YAML
name: CI
|
|
|
|
on:
|
|
push:
|
|
branches: [main]
|
|
pull_request:
|
|
branches: [main]
|
|
workflow_dispatch:
|
|
|
|
jobs:
|
|
# ──────────────────────────────────────────────────────────
|
|
# Code correctness
|
|
# ──────────────────────────────────────────────────────────
|
|
typecheck:
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- uses: actions/setup-node@v4
|
|
with:
|
|
node-version: 20
|
|
cache: npm
|
|
- run: npm ci
|
|
- run: npm run typecheck
|
|
- run: npm test
|
|
|
|
format:
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- uses: actions/setup-node@v4
|
|
with:
|
|
node-version: 20
|
|
cache: npm
|
|
- run: npm ci
|
|
- run: npm run format:check
|
|
|
|
# ──────────────────────────────────────────────────────────
|
|
# Oh My Pi host compatibility — real omp binary, mock API
|
|
# ──────────────────────────────────────────────────────────
|
|
omp-compat:
|
|
name: Oh My Pi compatibility
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- uses: actions/setup-node@v4
|
|
with:
|
|
node-version: 20
|
|
cache: npm
|
|
- uses: oven-sh/setup-bun@v2.2.0
|
|
with:
|
|
bun-version: 1.4.0
|
|
- run: npm ci
|
|
- name: Install Oh My Pi
|
|
run: |
|
|
npm install -g @oh-my-pi/pi-coding-agent@latest
|
|
echo "OMP_BIN=$(npm prefix -g)/bin/omp" >> "$GITHUB_ENV"
|
|
- name: Verify omp starts
|
|
run: '"$OMP_BIN" --version'
|
|
- name: Run OMP compatibility suite
|
|
env:
|
|
OMP_COMPAT_REQUIRED: "1"
|
|
run: node tests/test-omp-compat.mjs
|
|
|
|
# ──────────────────────────────────────────────────────────
|
|
# Code-level vulnerability scanning (SAST)
|
|
# ──────────────────────────────────────────────────────────
|
|
codeql:
|
|
name: CodeQL SAST
|
|
runs-on: ubuntu-latest
|
|
permissions:
|
|
actions: read
|
|
contents: read
|
|
security-events: write
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- uses: github/codeql-action/init@v3
|
|
with:
|
|
languages: javascript-typescript
|
|
queries: +security-extended,security-and-quality
|
|
- uses: github/codeql-action/autobuild@v3
|
|
- uses: github/codeql-action/analyze@v3
|
|
|
|
# ──────────────────────────────────────────────────────────
|
|
# Custom static analysis — pi extension attack patterns
|
|
# ──────────────────────────────────────────────────────────
|
|
semgrep:
|
|
name: Semgrep — pi extension audit
|
|
runs-on: ubuntu-latest
|
|
permissions:
|
|
contents: read
|
|
security-events: write
|
|
container:
|
|
image: semgrep/semgrep:latest
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- name: Run Semgrep with custom rules
|
|
run: |
|
|
semgrep --config .semgrep/ --error --output semgrep-report.sarif --sarif .
|
|
- name: Upload SARIF
|
|
uses: github/codeql-action/upload-sarif@v3
|
|
with:
|
|
sarif_file: semgrep-report.sarif
|
|
category: semgrep-pi-audit
|
|
if: always()
|
|
|
|
# ──────────────────────────────────────────────────────────
|
|
# Hardcoded secrets detection
|
|
# ──────────────────────────────────────────────────────────
|
|
gitleaks:
|
|
name: Gitleaks — secrets scan
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
with:
|
|
fetch-depth: 0
|
|
- uses: gitleaks/gitleaks-action@v2
|
|
env:
|
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
GITLEAKS_ENABLE_COMMENTS: "true"
|
|
|
|
# ──────────────────────────────────────────────────────────
|
|
# Dependency supply-chain security
|
|
# ──────────────────────────────────────────────────────────
|
|
deps-review:
|
|
name: Dependency review
|
|
runs-on: ubuntu-latest
|
|
if: github.event_name == 'pull_request'
|
|
permissions:
|
|
contents: read
|
|
pull-requests: write
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- name: Check if dependency graph is enabled
|
|
run: |
|
|
echo "Dependency review requires enabling Dependency graph in repo settings."
|
|
echo "Go to: https://github.com/patlux/pi-commandcode-provider/settings/security_analysis"
|
|
echo "Enable: Dependency graph"
|
|
- uses: actions/dependency-review-action@v4
|
|
with:
|
|
fail-on-severity: high
|
|
allow-licenses: MIT, Apache-2.0, BSD-2-Clause, BSD-3-Clause, ISC, 0BSD
|
|
comment-summary-in-pr: always
|
|
continue-on-error: true
|
|
|
|
deps-audit:
|
|
name: npm audit
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- uses: actions/setup-node@v4
|
|
with:
|
|
node-version: 20
|
|
cache: npm
|
|
- run: npm ci
|
|
- run: npm audit --audit-level=moderate
|
|
- name: Exit gracefully on audit findings
|
|
if: failure()
|
|
run: |
|
|
echo "::warning::npm audit found vulnerabilities. Review and patch before merging."
|
|
|
|
# ──────────────────────────────────────────────────────────
|
|
# Postinstall script check — prevents install-time malware
|
|
# ──────────────────────────────────────────────────────────
|
|
check-scripts:
|
|
name: Check lifecycle scripts
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- name: Check for malicious lifecycle scripts
|
|
run: |
|
|
echo "::group::package.json scripts"
|
|
node -e "
|
|
const pkg = require('./package.json');
|
|
const dangerous = ['preinstall','install','postinstall','prepublish','prepare'];
|
|
const found = dangerous.filter(s => pkg.scripts && pkg.scripts[s]);
|
|
if (found.length) {
|
|
found.forEach(s => console.log('WARNING: package.json has "' + s + '":', pkg.scripts[s]));
|
|
process.exit(1);
|
|
} else {
|
|
console.log('No dangerous lifecycle scripts in package.json');
|
|
}
|
|
"
|
|
echo "::endgroup::"
|
|
echo "::group::dependency scripts (top-level)"
|
|
npm query '.scripts' --all 2>/dev/null | node -e "
|
|
const d = require('fs').readFileSync('/dev/stdin','utf8');
|
|
if (!d.trim()) { console.log('No dependency scripts found'); process.exit(0); }
|
|
let pkgs;
|
|
try { pkgs = JSON.parse(d); } catch(e) { console.log('Could not parse npm query output'); process.exit(0); }
|
|
if (!Array.isArray(pkgs)) pkgs = Object.values(pkgs);
|
|
const withScripts = pkgs.filter(p => p && p.pkgid && p.scripts);
|
|
withScripts.forEach(p => {
|
|
const dangerous = ['preinstall','install','postinstall','prepublish','prepare'];
|
|
const has = Object.keys(p.scripts || {}).filter(s => dangerous.includes(s));
|
|
if (has.length) console.log('⚠', p.pkgid, 'has scripts:', Object.keys(p.scripts));
|
|
});
|
|
if (withScripts.length === 0) console.log('No dependency lifecycle scripts');
|
|
" 2>&1 || true
|
|
echo "::endgroup::"
|