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
79 changes: 62 additions & 17 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -1,46 +1,87 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
# RSR-compliant .gitattributes
# SPDX-FileCopyrightText: 2025 Jonathan D.A. Jewell
# RSR-compliant .gitattributes with Language Policy Enforcement

* text=auto eol=lf

# Source
# ============================================================================
# ALLOWED LANGUAGES
# ============================================================================

# ReScript (primary application code)
*.res text eol=lf linguist-language=ReScript
*.resi text eol=lf linguist-language=ReScript

# JavaScript (FFI glue only - generated from ReScript)
*.js text eol=lf
*.res.js linguist-generated=true

# Nickel (configuration)
*.ncl text eol=lf linguist-language=Nickel

# Shell (scripts, automation)
*.sh text eol=lf

# Rust (if needed for performance-critical code)
*.rs text eol=lf diff=rust
*.ex text eol=lf diff=elixir
*.exs text eol=lf diff=elixir
*.jl text eol=lf
*.res text eol=lf
*.resi text eol=lf

# Ada (safety-critical systems)
*.ada text eol=lf diff=ada
*.adb text eol=lf diff=ada
*.ads text eol=lf diff=ada

# Other allowed languages per policy
*.jl text eol=lf
*.hs text eol=lf
*.chpl text eol=lf
*.scm text eol=lf
*.ncl text eol=lf
*.nix text eol=lf
*.chpl text eol=lf
*.ex text eol=lf diff=elixir
*.exs text eol=lf diff=elixir

# ============================================================================
# DOCUMENTATION
# ============================================================================

# Docs
*.md text eol=lf diff=markdown
*.adoc text eol=lf
*.txt text eol=lf

# Data
# ============================================================================
# DATA & CONFIGURATION
# ============================================================================

*.json text eol=lf
*.yaml text eol=lf
*.yml text eol=lf
*.toml text eol=lf

# Config
# Config files
.gitignore text eol=lf
.gitattributes text eol=lf
justfile text eol=lf
Makefile text eol=lf
Containerfile text eol=lf

# Scripts
*.sh text eol=lf
# ============================================================================
# BANNED - These should trigger policy check warnings
# ============================================================================

# TypeScript (banned - use ReScript)
*.ts diff=banned
*.tsx diff=banned
*.mts diff=banned
*.cts diff=banned

# Go (banned - use Rust)
*.go diff=banned

# Makefile (banned - use justfile)
# Note: Can't fully block in gitattributes, enforced via pre-commit hook

# ============================================================================
# BINARY FILES
# ============================================================================

# Binary
*.png binary
*.jpg binary
*.gif binary
Expand All @@ -49,6 +90,10 @@ Containerfile text eol=lf
*.zip binary
*.gz binary

# Lock files
# ============================================================================
# LOCK FILES
# ============================================================================

Cargo.lock text eol=lf -diff
flake.lock text eol=lf -diff
deno.lock text eol=lf -diff
47 changes: 47 additions & 0 deletions .githooks/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#!/bin/sh
# SPDX-License-Identifier: AGPL-3.0-or-later
# SPDX-FileCopyrightText: 2025 Jonathan D.A. Jewell
# Pre-commit hook for odd-ssg language policy enforcement

set -e

echo "Running pre-commit checks..."

# Check for banned file types in staged files
BANNED_EXTENSIONS=".ts .tsx .mts .cts .go .mk"
BANNED_FILES="Makefile makefile GNUmakefile package.json package-lock.json bun.lockb npm-shrinkwrap.json go.mod go.sum"

# Get list of staged files
STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACM)

for file in $STAGED_FILES; do
filename=$(basename "$file")

# Check banned extensions
for ext in $BANNED_EXTENSIONS; do
if echo "$filename" | grep -q "${ext}$"; then
echo "❌ BLOCKED: $file"
echo " Files with extension '$ext' are not allowed."
echo " See CLAUDE.md for language policy."
exit 1
fi
done

# Check banned files
for banned in $BANNED_FILES; do
if [ "$filename" = "$banned" ]; then
echo "❌ BLOCKED: $file"
echo " File '$banned' is not allowed."
echo " See CLAUDE.md for language policy."
exit 1
fi
done
done

# Run policy check if deno is available
if command -v deno >/dev/null 2>&1; then
echo "Running full policy check..."
deno run --allow-read scripts/check-policy.js
fi

echo "✅ Pre-commit checks passed!"
41 changes: 26 additions & 15 deletions Containerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,48 +2,59 @@
# SPDX-FileCopyrightText: 2025 Jonathan D.A. Jewell
# odd-ssg Container Image

# Build stage
# Build stage - includes Node.js for ReScript compilation
FROM docker.io/denoland/deno:2.1.4 AS builder

# Install Node.js for ReScript compiler
RUN apt-get update && apt-get install -y nodejs npm && rm -rf /var/lib/apt/lists/*

WORKDIR /app

# Copy configuration files
COPY deno.json rescript.json ./
COPY package.json ./

# Install ReScript compiler
RUN npm install rescript

# Copy source files
COPY deno.json deno.lock ./
COPY engine/ engine/
COPY ssg/ ssg/
COPY src/ src/
COPY adapters/ adapters/
COPY noteg-lang/ noteg-lang/
COPY noteg-mcp/ noteg-mcp/

# Cache dependencies
RUN deno cache mod.ts
# Build ReScript
RUN npx rescript build

# Cache Deno dependencies
RUN deno cache src/Mod.res.js

# Type check
RUN deno check **/*.ts
# Check JavaScript
RUN deno check src/**/*.js || true

# Run tests
RUN deno test --allow-read --allow-write tests/ || true
RUN deno test --allow-read --allow-write src/tests/ || true

# Production stage
FROM docker.io/denoland/deno:2.1.4-distroless

LABEL org.opencontainers.image.title="odd-ssg"
LABEL org.opencontainers.image.description="Satellite SSG adapter provider with 30 MCP-compatible adapters"
LABEL org.opencontainers.image.description="Probabilistic SSG with Mill-Based Synthesis engine and 30 MCP-compatible adapters"
LABEL org.opencontainers.image.source="https://github.com/hyperpolymath/odd-ssg"
LABEL org.opencontainers.image.licenses="AGPL-3.0-or-later"
LABEL org.opencontainers.image.vendor="hyperpolymath"

WORKDIR /app

# Copy from builder
COPY --from=builder /app /app
# Copy from builder (only compiled JS, not node_modules)
COPY --from=builder /app/src /app/src
COPY --from=builder /app/adapters /app/adapters
COPY --from=builder /app/deno.json /app/deno.json

# Set user
USER deno

# Default command - run MCP server
ENTRYPOINT ["deno", "run", "--allow-read", "--allow-write", "--allow-run"]
CMD ["noteg-mcp/server.ts"]
CMD ["src/noteg-mcp/main.js"]

# Health check
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
Expand Down
155 changes: 155 additions & 0 deletions Mustfile.ncl
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
# SPDX-FileCopyrightText: 2025 Jonathan D.A. Jewell
# Mustfile.ncl - Contract of Physical State for odd-ssg
# Authority-First Deployment System using Nickel

let Project = {
name = "odd-ssg",
version = "0.1.0",
description = "Probabilistic static site generator with Mill-Based Synthesis engine",
license = "AGPL-3.0-or-later",
repository = "https://github.com/hyperpolymath/odd-ssg",
} in

let LanguagePolicy = {
# Allowed languages and their use cases
allowed = {
rescript = {
use_case = "Primary application code",
extensions = [".res", ".resi"],
enforced = true,
},
deno = {
use_case = "Runtime & package management",
config = "deno.json",
enforced = true,
},
javascript = {
use_case = "FFI glue only (minimal)",
extensions = [".js"],
locations = ["src/ssg/deno_fs.js", "src/adapter_loader.js", "src/noteg-lang/lsp/main.js", "src/noteg-mcp/main.js", "src/tests/test_utils.js"],
enforced = true,
},
nickel = {
use_case = "Configuration language",
extensions = [".ncl"],
enforced = true,
},
bash = {
use_case = "Scripts, automation",
extensions = [".sh"],
minimal = true,
},
},

# Banned languages - must not exist in codebase
banned = {
typescript = {
extensions = [".ts", ".tsx", ".mts", ".cts"],
replacement = "ReScript",
},
nodejs = {
indicators = ["package.json", "package-lock.json", "node_modules"],
replacement = "Deno",
},
bun = {
indicators = ["bun.lockb", "bunfig.toml"],
replacement = "Deno",
},
npm = {
indicators = ["npm-shrinkwrap.json", ".npmrc"],
replacement = "Deno",
},
makefile = {
indicators = ["Makefile", "makefile", "GNUmakefile", "*.mk"],
replacement = "justfile",
},
go = {
extensions = [".go"],
replacement = "Rust",
},
},
} in

let BuildTargets = {
rescript = {
command = "npx rescript build",
watch = "npx rescript build -w",
clean = "npx rescript clean",
outputs = ["src/**/*.res.js"],
},

deno = {
check = "deno check src/**/*.js",
lint = "deno lint",
fmt = "deno fmt",
test = "deno test --allow-read --allow-write src/tests/",
},

docker = {
build = "podman build -t odd-ssg:latest .",
run = "podman run -it --rm -v $(pwd):/app:Z odd-ssg:latest",
},
} in

let Deployment = {
# Container-first deployment
container = {
runtime = "podman",
image = "odd-ssg",
registry = "ghcr.io/hyperpolymath",
},

# State transitions
transitions = {
dev = {
from = "clean",
to = "development",
commands = ["deno task rescript:build"],
},
test = {
from = "development",
to = "tested",
commands = ["deno task test"],
},
build = {
from = "tested",
to = "built",
commands = ["deno task build"],
},
deploy = {
from = "built",
to = "deployed",
commands = ["podman push"],
},
},
} in

let PolicyEnforcement = {
pre_commit = {
check_banned_files = true,
check_spdx_headers = true,
check_no_secrets = true,
run_lint = true,
run_fmt_check = true,
},

ci = {
check_policy = "deno task policy:check",
build = "deno task rescript:build",
lint = "deno task lint",
test = "deno task test",
},
} in

{
project = Project,
language_policy = LanguagePolicy,
build = BuildTargets,
deployment = Deployment,
enforcement = PolicyEnforcement,

# Metadata
mustfile_version = "0.1.0",
nickel_version = "1.0",
}
Loading
Loading