Files
cell/verify.ce
2026-02-20 15:00:08 -06:00

258 lines
6.5 KiB
Plaintext

// cell verify [<scope>] - Verify integrity and consistency
//
// Usage:
// cell verify Verify current directory package
// cell verify . Verify current directory package
// cell verify <locator> Verify specific package
// cell verify shop Verify entire shop
// cell verify world Verify all world roots
//
// Options:
// --deep Traverse full dependency closure
// --target <triple> Verify builds for specific target
var shop = use('internal/shop')
var pkg = use('package')
var link = use('link')
var build = use('build')
var fd = use('fd')
var scope = null
var deep = false
var target_triple = null
var i = 0
var run = function() {
for (i = 0; i < length(args); i++) {
if (args[i] == '--deep') {
deep = true
} else if (args[i] == '--target' || args[i] == '-t') {
if (i + 1 < length(args)) {
target_triple = args[++i]
} else {
log.error('--target requires a triple')
return
}
} else if (args[i] == '--help' || args[i] == '-h') {
log.console("Usage: cell verify [<scope>] [options]")
log.console("")
log.console("Verify integrity and consistency.")
log.console("")
log.console("Scopes:")
log.console(" <locator> Verify specific package")
log.console(" shop Verify entire shop")
log.console(" world Verify all world roots")
log.console("")
log.console("Options:")
log.console(" --deep Traverse full dependency closure")
log.console(" --target <triple> Verify builds for specific target")
return
} else if (!starts_with(args[i], '-')) {
scope = args[i]
}
}
// Default to current directory
if (!scope) {
scope = '.'
}
// Detect target if not specified
if (!target_triple) {
target_triple = build.detect_host_target()
}
var errors = []
var warnings = []
var checked = 0
function add_error(msg) {
push(errors, msg)
}
function add_warning(msg) {
push(warnings, msg)
}
// Verify a single package
function verify_package(locator) {
var lock = null
var lock_entry = null
var links = null
var link_target = null
var pkg_dir = null
var dir_exists = false
var current_target = null
var expected_target = null
var target_dir = null
var c_files = null
checked++
lock = shop.load_lock()
lock_entry = lock[locator]
links = link.load()
link_target = links[locator]
// Check lock entry exists
if (!lock_entry) {
add_error(locator + ": not in lock")
}
// Check package directory exists
pkg_dir = shop.get_package_dir(locator)
dir_exists = fd.is_dir(pkg_dir) || fd.is_link(pkg_dir)
if (!dir_exists) {
add_error(locator + ": package directory missing at " + pkg_dir)
return
}
// Check cell.toml exists
if (!fd.is_file(pkg_dir + '/cell.toml')) {
add_error(locator + ": missing cell.toml")
return
}
// For linked packages, verify link target
if (link_target) {
if (starts_with(link_target, '/')) {
// Local path target
if (!fd.is_dir(link_target)) {
add_error(locator + ": link target does not exist: " + link_target)
} else if (!fd.is_file(link_target + '/cell.toml')) {
add_error(locator + ": link target is not a valid package: " + link_target)
}
} else {
// Package target
target_dir = shop.get_package_dir(link_target)
if (!fd.is_dir(target_dir) && !fd.is_link(target_dir)) {
add_error(locator + ": link target package not found: " + link_target)
}
}
// Check symlink is correct
if (fd.is_link(pkg_dir)) {
current_target = fd.readlink(pkg_dir)
expected_target = starts_with(link_target, '/') ? link_target : shop.get_package_dir(link_target)
if (current_target != expected_target) {
add_warning(locator + ": symlink target mismatch (expected " + expected_target + ", got " + current_target + ")")
}
} else {
add_warning(locator + ": linked but directory is not a symlink")
}
}
// Check if package has C files (builds happen lazily on first use)
var _check_build = function() {
c_files = pkg.get_c_files(locator, target_triple, true)
} disruption {
// Skip build check if can't determine C files
}
_check_build()
}
// Check for link cycles
function check_link_cycles() {
var links = link.load()
function follow_chain(origin, visited) {
var target = null
if (visited[origin]) {
return origin // cycle detected
}
visited[origin] = true
target = links[origin]
if (target && links[target]) {
return follow_chain(target, visited)
}
return null
}
arrfor(array(links), function(origin) {
var cycle_start = follow_chain(origin, {})
if (cycle_start) {
add_error("Link cycle detected starting from: " + origin)
}
})
}
// Check for dangling links
function check_dangling_links() {
var links = link.load()
arrfor(array(links), function(origin) {
var target = links[origin]
if (starts_with(target, '/')) {
if (!fd.is_dir(target)) {
add_error("Dangling link: " + origin + " -> " + target + " (target does not exist)")
}
}
})
}
// Gather packages to verify
var packages_to_verify = []
var locator = null
var all_deps = null
if (scope == 'shop') {
packages_to_verify = shop.list_packages()
} else if (scope == 'world') {
// For now, world is the same as shop
packages_to_verify = shop.list_packages()
} else {
// Single package
locator = scope
locator = shop.resolve_locator(locator)
if (deep) {
// Gather all dependencies
all_deps = pkg.gather_dependencies(locator)
push(packages_to_verify, locator)
arrfor(all_deps, function(dep) {
push(packages_to_verify, dep)
})
} else {
push(packages_to_verify, locator)
}
}
log.console("Verifying " + text(length(packages_to_verify)) + " package(s)...")
log.console("")
// Run verification
check_link_cycles()
check_dangling_links()
arrfor(packages_to_verify, function(p) {
if (p == 'core') return
verify_package(p)
})
// Print results
if (length(warnings) > 0) {
log.console("Warnings:")
arrfor(warnings, function(w) {
log.console(" " + w)
})
log.console("")
}
if (length(errors) > 0) {
log.console("Errors:")
arrfor(errors, function(e) {
log.console(" " + e)
})
log.console("")
log.console("Verification FAILED: " + text(length(errors)) + " error(s), " + text(length(warnings)) + " warning(s)")
} else {
log.console("Verification PASSED: " + text(checked) + " package(s) checked, " + text(length(warnings)) + " warning(s)")
}
}
run()
$stop()