crates: production models and repo layer

Signed-off-by: NotAShelf <raf@notashelf.dev>
Change-Id: Iceb76724c09eaca7ca5d823010db76776a6a6964
This commit is contained in:
raf 2025-11-02 23:33:33 +03:00
commit 1b12be3f8a
Signed by: NotAShelf
GPG key ID: 29D95B64378DB4BF
31 changed files with 3841 additions and 12 deletions

View file

@ -0,0 +1,2 @@
-- Add system field to builds table
ALTER TABLE builds ADD COLUMN system VARCHAR(50);

View file

@ -0,0 +1,92 @@
-- Production features: auth, priority, retry, notifications, GC roots, log paths
-- API key authentication
CREATE TABLE api_keys (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
name VARCHAR(255) NOT NULL,
key_hash VARCHAR(128) NOT NULL UNIQUE,
role VARCHAR(50) NOT NULL DEFAULT 'admin'
CHECK (role IN ('admin', 'create-projects', 'restart-jobs', 'cancel-build', 'bump-to-front', 'eval-jobset', 'read-only')),
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
last_used_at TIMESTAMP WITH TIME ZONE
);
-- Build priority and retry support
ALTER TABLE builds ADD COLUMN priority INTEGER NOT NULL DEFAULT 0;
ALTER TABLE builds ADD COLUMN retry_count INTEGER NOT NULL DEFAULT 0;
ALTER TABLE builds ADD COLUMN max_retries INTEGER NOT NULL DEFAULT 3;
ALTER TABLE builds ADD COLUMN notification_pending_since TIMESTAMP WITH TIME ZONE;
-- GC root tracking on build products
ALTER TABLE build_products ADD COLUMN gc_root_path TEXT;
-- Build log file path (filesystem path to captured log)
ALTER TABLE builds ADD COLUMN log_url TEXT;
-- Webhook configuration for incoming push events
CREATE TABLE webhook_configs (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
project_id UUID NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
forge_type VARCHAR(50) NOT NULL CHECK (forge_type IN ('github', 'gitea', 'forgejo', 'gitlab')),
secret_hash VARCHAR(128),
enabled BOOLEAN NOT NULL DEFAULT true,
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
UNIQUE(project_id, forge_type)
);
-- Notification configuration per project
CREATE TABLE notification_configs (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
project_id UUID NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
notification_type VARCHAR(50) NOT NULL
CHECK (notification_type IN ('github_status', 'gitea_status', 'forgejo_status', 'gitlab_status', 'run_command', 'email')),
config JSONB NOT NULL DEFAULT '{}',
enabled BOOLEAN NOT NULL DEFAULT true,
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
UNIQUE(project_id, notification_type)
);
-- Jobset inputs for multi-input support
CREATE TABLE jobset_inputs (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
jobset_id UUID NOT NULL REFERENCES jobsets(id) ON DELETE CASCADE,
name VARCHAR(255) NOT NULL,
input_type VARCHAR(50) NOT NULL
CHECK (input_type IN ('git', 'string', 'boolean', 'path', 'build')),
value TEXT NOT NULL,
revision TEXT,
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
UNIQUE(jobset_id, name)
);
-- Track flake mode per jobset
ALTER TABLE jobsets ADD COLUMN flake_mode BOOLEAN NOT NULL DEFAULT true;
ALTER TABLE jobsets ADD COLUMN check_interval INTEGER NOT NULL DEFAULT 60;
-- Store the flake URI or legacy expression path in nix_expression (already exists)
-- For flake mode: nix_expression = "github:owner/repo" or "."
-- For legacy mode: nix_expression = "release.nix"
-- Indexes for new columns
CREATE INDEX idx_builds_priority ON builds(priority DESC, created_at ASC);
CREATE INDEX idx_builds_notification_pending ON builds(notification_pending_since) WHERE notification_pending_since IS NOT NULL;
CREATE INDEX idx_api_keys_key_hash ON api_keys(key_hash);
CREATE INDEX idx_webhook_configs_project ON webhook_configs(project_id);
CREATE INDEX idx_notification_configs_project ON notification_configs(project_id);
CREATE INDEX idx_jobset_inputs_jobset ON jobset_inputs(jobset_id);
-- Update active_jobsets view to include flake_mode
-- Must DROP first: adding columns to jobsets changes j.* expansion,
-- and CREATE OR REPLACE VIEW cannot rename existing columns.
DROP VIEW IF EXISTS active_jobsets;
CREATE VIEW active_jobsets AS
SELECT
j.*,
p.name as project_name,
p.repository_url
FROM jobsets j
JOIN projects p ON j.project_id = p.id
WHERE j.enabled = true;
-- Update list_pending to respect priority ordering
-- (handled in application code, but index above supports it)

View file

@ -0,0 +1,14 @@
ALTER TABLE builds ADD COLUMN outputs JSONB;
ALTER TABLE builds ADD COLUMN is_aggregate BOOLEAN NOT NULL DEFAULT false;
ALTER TABLE builds ADD COLUMN constituents JSONB;
CREATE TABLE build_dependencies (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
build_id UUID NOT NULL REFERENCES builds(id) ON DELETE CASCADE,
dependency_build_id UUID NOT NULL REFERENCES builds(id) ON DELETE CASCADE,
UNIQUE(build_id, dependency_build_id)
);
CREATE INDEX idx_build_deps_build ON build_dependencies(build_id);
CREATE INDEX idx_build_deps_dep ON build_dependencies(dependency_build_id);
CREATE INDEX idx_builds_drv_path ON builds(drv_path);

View file

@ -0,0 +1,44 @@
-- Channels for release management (like Hydra channels)
-- A channel tracks the latest "good" evaluation for a jobset
CREATE TABLE channels (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
project_id UUID NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
name VARCHAR(255) NOT NULL,
jobset_id UUID NOT NULL REFERENCES jobsets(id) ON DELETE CASCADE,
current_evaluation_id UUID REFERENCES evaluations(id),
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
UNIQUE(project_id, name)
);
-- Remote builders for multi-machine / multi-arch builds
CREATE TABLE remote_builders (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name VARCHAR(255) NOT NULL UNIQUE,
ssh_uri TEXT NOT NULL,
systems TEXT[] NOT NULL DEFAULT '{}',
max_jobs INTEGER NOT NULL DEFAULT 1,
speed_factor INTEGER NOT NULL DEFAULT 1,
supported_features TEXT[] NOT NULL DEFAULT '{}',
mandatory_features TEXT[] NOT NULL DEFAULT '{}',
enabled BOOLEAN NOT NULL DEFAULT true,
public_host_key TEXT,
ssh_key_file TEXT,
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
);
-- Track input hash for evaluation caching (skip re-eval when inputs unchanged)
ALTER TABLE evaluations ADD COLUMN inputs_hash VARCHAR(128);
-- Track which remote builder was used for a build
ALTER TABLE builds ADD COLUMN builder_id UUID REFERENCES remote_builders(id);
-- Track whether build outputs have been signed
ALTER TABLE builds ADD COLUMN signed BOOLEAN NOT NULL DEFAULT false;
-- Indexes
CREATE INDEX idx_channels_project ON channels(project_id);
CREATE INDEX idx_channels_jobset ON channels(jobset_id);
CREATE INDEX idx_remote_builders_enabled ON remote_builders(enabled) WHERE enabled = true;
CREATE INDEX idx_evaluations_inputs_hash ON evaluations(jobset_id, inputs_hash);
CREATE INDEX idx_builds_builder ON builds(builder_id) WHERE builder_id IS NOT NULL;

View file

@ -0,0 +1,14 @@
-- Hardening: indexes for performance
-- Cache lookup index (prefix match on path)
CREATE INDEX IF NOT EXISTS idx_build_products_path_prefix ON build_products (path text_pattern_ops);
-- Composite index for pending builds query
CREATE INDEX IF NOT EXISTS idx_builds_pending_priority ON builds (status, priority DESC, created_at ASC)
WHERE status = 'pending';
-- System filtering index
CREATE INDEX IF NOT EXISTS idx_builds_system ON builds(system) WHERE system IS NOT NULL;
-- Deduplication lookup by drv_path + status
CREATE INDEX IF NOT EXISTS idx_builds_drv_completed ON builds(drv_path) WHERE status = 'completed';