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
37 changes: 37 additions & 0 deletions .githooks/commit-msg
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#!/bin/bash
# SPDX-License-Identifier: AGPL-3.0-or-later
# Commit message hook - enforces conventional commits

commit_msg_file=$1
commit_msg=$(cat "$commit_msg_file")

# Conventional commit pattern
pattern="^(feat|fix|docs|style|refactor|test|chore|security|perf|build|ci)(\(.+\))?: .{1,72}"

# Allow merge commits
if echo "$commit_msg" | grep -qE "^Merge "; then
exit 0
fi

# Allow revert commits
if echo "$commit_msg" | grep -qE "^Revert "; then
exit 0
fi

if ! echo "$commit_msg" | grep -qE "$pattern"; then
echo "❌ Invalid commit message format"
echo ""
echo "Expected format: type(scope): description"
echo ""
echo "Types: feat, fix, docs, style, refactor, test, chore, security, perf, build, ci"
echo ""
echo "Examples:"
echo " feat(adapters): add new hugo adapter"
echo " fix(zola): correct build path handling"
echo " docs: update README with examples"
echo " security: fix command injection vulnerability"
echo ""
exit 1
fi

echo "✅ Commit message valid"
29 changes: 29 additions & 0 deletions .githooks/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#!/bin/bash
# SPDX-License-Identifier: AGPL-3.0-or-later
# Pre-commit hook for labnote-ssg

set -e

echo "🔍 Running pre-commit checks..."

# Check if deno is available
if ! command -v deno &> /dev/null; then
echo "⚠️ Deno not found, skipping checks"
exit 0
fi

# Format check
echo " Checking formatting..."
if ! deno fmt --check adapters/ 2>/dev/null; then
echo "❌ Formatting issues found. Run: deno fmt adapters/"
exit 1
fi

# Lint check
echo " Running linter..."
if ! deno lint adapters/ 2>/dev/null; then
echo "❌ Linting issues found. Run: deno lint adapters/"
exit 1
fi

echo "✅ Pre-commit checks passed"
38 changes: 38 additions & 0 deletions .githooks/pre-push
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#!/bin/bash
# SPDX-License-Identifier: AGPL-3.0-or-later
# Pre-push hook for labnote-ssg

set -e

echo "🚀 Running pre-push checks..."

# Check if deno is available
if ! command -v deno &> /dev/null; then
echo "⚠️ Deno not found, skipping checks"
exit 0
fi

# Type check
echo " Type checking adapters..."
if ! deno check adapters/*.js 2>/dev/null; then
echo "❌ Type errors found"
exit 1
fi

# Run tests if they exist
if [ -d "tests" ] && [ "$(ls -A tests/*.js 2>/dev/null)" ]; then
echo " Running tests..."
if ! deno test --allow-run --allow-read tests/; then
echo "❌ Tests failed"
exit 1
fi
fi

# Security check
echo " Running security check..."
if grep -rn 'eval\s*(' adapters/ 2>/dev/null; then
echo "❌ Found eval() usage - potential security risk"
exit 1
fi

echo "✅ Pre-push checks passed"
176 changes: 176 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
# SPDX-FileCopyrightText: 2025 Jonathan D.A. Jewell
# RSR-compliant CI workflow with SHA-pinned actions

name: CI

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

permissions:
contents: read

env:
DENO_VERSION: "v1.40.0"

jobs:
lint:
name: Lint
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2

- name: Setup Deno
uses: denoland/setup-deno@5fae568d37c3b73449009674875529a984555dd1 # v2.0.2
with:
deno-version: ${{ env.DENO_VERSION }}

- name: Lint adapters
run: deno lint adapters/

- name: Check formatting
run: deno fmt --check adapters/

check:
name: Type Check
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2

- name: Setup Deno
uses: denoland/setup-deno@5fae568d37c3b73449009674875529a984555dd1 # v2.0.2
with:
deno-version: ${{ env.DENO_VERSION }}

- name: Check adapters
run: deno check adapters/*.js

test:
name: Test
runs-on: ubuntu-latest
needs: [lint, check]
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2

- name: Setup Deno
uses: denoland/setup-deno@5fae568d37c3b73449009674875529a984555dd1 # v2.0.2
with:
deno-version: ${{ env.DENO_VERSION }}

- name: Run tests
run: |
if [ -d "tests" ] && [ "$(ls -A tests/*.js 2>/dev/null)" ]; then
deno test --allow-run --allow-read tests/
else
echo "No tests found - skipping"
fi

- name: Run coverage
run: |
if [ -d "tests" ] && [ "$(ls -A tests/*.js 2>/dev/null)" ]; then
deno test --allow-run --allow-read --coverage=coverage/ tests/
deno coverage coverage/
fi
continue-on-error: true

security:
name: Security Check
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2

- name: Check for dangerous patterns
run: |
echo "Checking for dangerous code patterns..."
if grep -rn 'eval\s*(' adapters/; then
echo "::error::Found eval() usage - potential security risk"
exit 1
fi
if grep -rn 'new Function' adapters/; then
echo "::error::Found Function constructor - potential security risk"
exit 1
fi
echo "✓ No dangerous patterns found"

- name: Verify safe command execution
run: |
echo "Verifying all adapters use Deno.Command..."
count=$(grep -l 'Deno.Command' adapters/*.js | wc -l)
total=$(ls adapters/*.js | wc -l)
echo "Adapters using Deno.Command: $count/$total"
if [ "$count" -ne "$total" ]; then
echo "::warning::Not all adapters use Deno.Command"
fi

- name: Check for hardcoded secrets
run: |
echo "Checking for hardcoded secrets..."
if grep -rniE '(password|secret|api.?key|token)\s*[:=]\s*["\x27][^"\x27]+["\x27]' adapters/; then
echo "::error::Potential hardcoded secret found"
exit 1
fi
echo "✓ No hardcoded secrets found"

adapter-validation:
name: Validate Adapters
runs-on: ubuntu-latest
needs: [lint, check]
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2

- name: Setup Deno
uses: denoland/setup-deno@5fae568d37c3b73449009674875529a984555dd1 # v2.0.2
with:
deno-version: ${{ env.DENO_VERSION }}

- name: Validate adapter exports
run: |
echo "Validating adapter exports..."
for adapter in adapters/*.js; do
echo "Checking: $adapter"
deno eval "
import * as a from './$adapter';
const required = ['name', 'language', 'description', 'connect', 'disconnect', 'isConnected', 'tools'];
const missing = required.filter(k => !(k in a));
if (missing.length > 0) {
console.error('Missing exports:', missing.join(', '));
Deno.exit(1);
}
console.log(' ✓', a.name, '-', a.tools.length, 'tools');
"
done
echo "✓ All adapters validated"

- name: Count adapters
run: |
count=$(ls adapters/*.js | wc -l)
echo "Total adapters: $count"
if [ "$count" -ne 28 ]; then
echo "::warning::Expected 28 adapters, found $count"
fi

summary:
name: CI Summary
runs-on: ubuntu-latest
needs: [lint, check, test, security, adapter-validation]
if: always()
steps:
- name: Check results
run: |
echo "## CI Summary" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "| Job | Status |" >> $GITHUB_STEP_SUMMARY
echo "|-----|--------|" >> $GITHUB_STEP_SUMMARY
echo "| Lint | ${{ needs.lint.result }} |" >> $GITHUB_STEP_SUMMARY
echo "| Check | ${{ needs.check.result }} |" >> $GITHUB_STEP_SUMMARY
echo "| Test | ${{ needs.test.result }} |" >> $GITHUB_STEP_SUMMARY
echo "| Security | ${{ needs.security.result }} |" >> $GITHUB_STEP_SUMMARY
echo "| Adapter Validation | ${{ needs.adapter-validation.result }} |" >> $GITHUB_STEP_SUMMARY
4 changes: 4 additions & 0 deletions .tool-versions
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# asdf tool versions for labnote-ssg
# Install: asdf install

deno 1.40.0
Loading
Loading