feat: project structure and nix build setup
- Add CLI entry point with urfave/cli/v2 (serve, index, list, search commands) - Add database interface and implementations for PostgreSQL and SQLite - Add schema versioning with automatic recreation on version mismatch - Add MCP protocol types and server scaffold - Add NixOS option types - Configure flake.nix with devShell and buildGoModule package Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
102
internal/database/schema.go
Normal file
102
internal/database/schema.go
Normal file
@@ -0,0 +1,102 @@
|
||||
package database
|
||||
|
||||
// SchemaVersion is the current database schema version.
|
||||
// When this changes, the database will be dropped and recreated.
|
||||
const SchemaVersion = 1
|
||||
|
||||
// Common SQL statements shared between implementations.
|
||||
const (
|
||||
// SchemaInfoTable creates the schema version tracking table.
|
||||
SchemaInfoTable = `
|
||||
CREATE TABLE IF NOT EXISTS schema_info (
|
||||
version INTEGER NOT NULL
|
||||
)`
|
||||
|
||||
// RevisionsTable creates the revisions table.
|
||||
RevisionsTable = `
|
||||
CREATE TABLE IF NOT EXISTS revisions (
|
||||
id INTEGER PRIMARY KEY,
|
||||
git_hash TEXT NOT NULL UNIQUE,
|
||||
channel_name TEXT,
|
||||
commit_date TIMESTAMP,
|
||||
indexed_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
option_count INTEGER NOT NULL DEFAULT 0
|
||||
)`
|
||||
|
||||
// OptionsTable creates the options table.
|
||||
OptionsTable = `
|
||||
CREATE TABLE IF NOT EXISTS options (
|
||||
id INTEGER PRIMARY KEY,
|
||||
revision_id INTEGER NOT NULL REFERENCES revisions(id) ON DELETE CASCADE,
|
||||
name TEXT NOT NULL,
|
||||
parent_path TEXT NOT NULL,
|
||||
type TEXT,
|
||||
default_value TEXT,
|
||||
example TEXT,
|
||||
description TEXT,
|
||||
read_only BOOLEAN NOT NULL DEFAULT FALSE
|
||||
)`
|
||||
|
||||
// DeclarationsTable creates the declarations table.
|
||||
DeclarationsTable = `
|
||||
CREATE TABLE IF NOT EXISTS declarations (
|
||||
id INTEGER PRIMARY KEY,
|
||||
option_id INTEGER NOT NULL REFERENCES options(id) ON DELETE CASCADE,
|
||||
file_path TEXT NOT NULL,
|
||||
line INTEGER
|
||||
)`
|
||||
|
||||
// FilesTable creates the files table.
|
||||
FilesTable = `
|
||||
CREATE TABLE IF NOT EXISTS files (
|
||||
id INTEGER PRIMARY KEY,
|
||||
revision_id INTEGER NOT NULL REFERENCES revisions(id) ON DELETE CASCADE,
|
||||
file_path TEXT NOT NULL,
|
||||
extension TEXT,
|
||||
content TEXT NOT NULL
|
||||
)`
|
||||
)
|
||||
|
||||
// Index creation statements.
|
||||
const (
|
||||
// IndexOptionsRevisionName creates an index on options(revision_id, name).
|
||||
IndexOptionsRevisionName = `
|
||||
CREATE INDEX IF NOT EXISTS idx_options_revision_name
|
||||
ON options(revision_id, name)`
|
||||
|
||||
// IndexOptionsRevisionParent creates an index on options(revision_id, parent_path).
|
||||
IndexOptionsRevisionParent = `
|
||||
CREATE INDEX IF NOT EXISTS idx_options_revision_parent
|
||||
ON options(revision_id, parent_path)`
|
||||
|
||||
// IndexFilesRevisionPath creates an index on files(revision_id, file_path).
|
||||
IndexFilesRevisionPath = `
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_files_revision_path
|
||||
ON files(revision_id, file_path)`
|
||||
|
||||
// IndexDeclarationsOption creates an index on declarations(option_id).
|
||||
IndexDeclarationsOption = `
|
||||
CREATE INDEX IF NOT EXISTS idx_declarations_option
|
||||
ON declarations(option_id)`
|
||||
)
|
||||
|
||||
// Drop statements for schema recreation.
|
||||
const (
|
||||
DropSchemaInfo = `DROP TABLE IF EXISTS schema_info`
|
||||
DropDeclarations = `DROP TABLE IF EXISTS declarations`
|
||||
DropOptions = `DROP TABLE IF EXISTS options`
|
||||
DropFiles = `DROP TABLE IF EXISTS files`
|
||||
DropRevisions = `DROP TABLE IF EXISTS revisions`
|
||||
)
|
||||
|
||||
// ParentPath extracts the parent path from an option name.
|
||||
// For example, "services.nginx.enable" returns "services.nginx".
|
||||
// Top-level options return an empty string.
|
||||
func ParentPath(name string) string {
|
||||
for i := len(name) - 1; i >= 0; i-- {
|
||||
if name[i] == '.' {
|
||||
return name[:i]
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
Reference in New Issue
Block a user