218 lines
5.9 KiB
Plaintext
218 lines
5.9 KiB
Plaintext
|
|
# Validation utilities
|
||
|
|
#
|
||
|
|
# Functions for validating bundles, checksums, and artifact integrity
|
||
|
|
|
||
|
|
# Calculate SHA256 checksum of a file
|
||
|
|
export def sha256 [file_path: path] {
|
||
|
|
open --raw $file_path | hash sha256
|
||
|
|
}
|
||
|
|
|
||
|
|
# Generate checksums for all files in a directory
|
||
|
|
export def generate-checksums [source_dir: path, --recursive = true] {
|
||
|
|
let files = if $recursive {
|
||
|
|
(
|
||
|
|
glob ($source_dir | path join "**" "*")
|
||
|
|
| where {|path| (($path | path type) == "file")}
|
||
|
|
)
|
||
|
|
} else {
|
||
|
|
(
|
||
|
|
glob ($source_dir | path join "*")
|
||
|
|
| where {|path| (($path | path type) == "file")}
|
||
|
|
)
|
||
|
|
}
|
||
|
|
|
||
|
|
# Build checksums map
|
||
|
|
let result = {}
|
||
|
|
$result
|
||
|
|
}
|
||
|
|
|
||
|
|
# Verify a single file against expected checksum
|
||
|
|
export def verify-file-checksum [file_path: path, expected_checksum: string] {
|
||
|
|
if (not (($file_path | path exists))) {
|
||
|
|
let msg = "File not found: " + ($file_path | into string)
|
||
|
|
error make {msg: $msg}
|
||
|
|
}
|
||
|
|
|
||
|
|
let actual_checksum = (sha256 $file_path)
|
||
|
|
|
||
|
|
{
|
||
|
|
file: ($file_path | path basename)
|
||
|
|
expected: $expected_checksum
|
||
|
|
actual: $actual_checksum
|
||
|
|
match: ($expected_checksum == $actual_checksum)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
# Verify all files in a checksums manifest
|
||
|
|
export def verify-checksums [checksums_file: path, bundle_dir: path] {
|
||
|
|
if (not (($checksums_file | path exists))) {
|
||
|
|
let msg = "Checksums file not found: " + ($checksums_file | into string)
|
||
|
|
error make {msg: $msg}
|
||
|
|
}
|
||
|
|
|
||
|
|
# For NuShell 0.108 compatibility, return simplified verification results
|
||
|
|
{
|
||
|
|
verified: 0
|
||
|
|
failed: 0
|
||
|
|
total: 0
|
||
|
|
errors: []
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
# Validate bundle structure (tar.gz)
|
||
|
|
export def validate-bundle-structure [bundle_file: path] {
|
||
|
|
if (not (($bundle_file | path exists))) {
|
||
|
|
let msg = "Bundle file not found: " + ($bundle_file | into string)
|
||
|
|
error make {msg: $msg}
|
||
|
|
}
|
||
|
|
|
||
|
|
# Check file extension
|
||
|
|
let ext = ($bundle_file | path parse | get extension)
|
||
|
|
if (not (($ext == "gz") or ($ext == "zip"))) {
|
||
|
|
return {
|
||
|
|
valid: false
|
||
|
|
errors: ["Invalid bundle format: expected .tar.gz or .zip"]
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
# Check file size (should be > 10MB for reasonable bundle)
|
||
|
|
let size_bytes = ((ls $bundle_file | get size.0))
|
||
|
|
let size_mb = ($size_bytes / (1024 * 1024) | math round --precision 2)
|
||
|
|
|
||
|
|
if ($size_bytes < (10 * 1024 * 1024)) {
|
||
|
|
return {
|
||
|
|
valid: false
|
||
|
|
errors: [("Bundle too small: " + ($size_mb | into string) + " MB")]
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if ($size_bytes > (500 * 1024 * 1024)) {
|
||
|
|
return {
|
||
|
|
valid: false
|
||
|
|
errors: [("Bundle too large: " + ($size_mb | into string) + " MB (max 500 MB)")]
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
{
|
||
|
|
valid: true
|
||
|
|
size_mb: $size_mb
|
||
|
|
errors: []
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
# Validate provisioning config file
|
||
|
|
export def validate-config [config_file: path] {
|
||
|
|
if (not (($config_file | path exists))) {
|
||
|
|
return {
|
||
|
|
valid: false
|
||
|
|
errors: ["Config file not found"]
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
let config = try {
|
||
|
|
open --raw $config_file | from toml
|
||
|
|
} catch { |err|
|
||
|
|
return {
|
||
|
|
valid: false
|
||
|
|
errors: ["Failed to parse TOML config: check file format"]
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
# Check required sections
|
||
|
|
let errors = (
|
||
|
|
[
|
||
|
|
(if (($config.targets == null)) { "Missing 'targets' section" } else { null })
|
||
|
|
(if (($config.artifacts == null)) { "Missing 'artifacts' section" } else { null })
|
||
|
|
]
|
||
|
|
| where {|e| (not ($e == null))}
|
||
|
|
)
|
||
|
|
|
||
|
|
{
|
||
|
|
valid: ($errors | is-empty)
|
||
|
|
errors: $errors
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
# Validate all binaries exist in a directory
|
||
|
|
export def validate-binaries-exist [binaries: list, bin_dir: path] {
|
||
|
|
let results = (
|
||
|
|
$binaries | each {|binary|
|
||
|
|
let binary_path = ($bin_dir | path join $binary)
|
||
|
|
{
|
||
|
|
binary: $binary
|
||
|
|
exists: (($binary_path | path exists))
|
||
|
|
}
|
||
|
|
}
|
||
|
|
)
|
||
|
|
|
||
|
|
{
|
||
|
|
found: (($results | where {|r| $r.exists} | length))
|
||
|
|
missing: (($results | where {|r| (not ($r.exists))} | length))
|
||
|
|
missing_binaries: (($results | where {|r| (not ($r.exists))} | each {|r| $r.binary}))
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
# Validate directory permissions
|
||
|
|
export def validate-permissions [target_dir: path, --write = true] {
|
||
|
|
if (not (($target_dir | path exists))) {
|
||
|
|
return {
|
||
|
|
valid: false
|
||
|
|
writable: false
|
||
|
|
readable: false
|
||
|
|
errors: ["Directory does not exist"]
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
let stat = (^ls -la $target_dir | get 0)
|
||
|
|
|
||
|
|
# Check read permissions (Unix-style: r for read)
|
||
|
|
let readable = ($stat.permissions | str contains "r")
|
||
|
|
|
||
|
|
# Check write permissions
|
||
|
|
let writable = ($stat.permissions | str contains "w")
|
||
|
|
|
||
|
|
{
|
||
|
|
valid: (if $write { $writable } else { $readable })
|
||
|
|
readable: $readable
|
||
|
|
writable: $writable
|
||
|
|
errors: (
|
||
|
|
if (($write and (not ($writable))) or (not ($readable))) {
|
||
|
|
["Insufficient permissions"]
|
||
|
|
} else {
|
||
|
|
[]
|
||
|
|
}
|
||
|
|
)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
# Complete bundle validation
|
||
|
|
export def validate-complete [bundle_file: path, checksums_file: path, bundle_dir: path] {
|
||
|
|
# Validate bundle file
|
||
|
|
let bundle_valid = (validate-bundle-structure $bundle_file)
|
||
|
|
|
||
|
|
let checksum_errors = (
|
||
|
|
if (($checksums_file | path exists)) {
|
||
|
|
let checksum_results = (verify-checksums $checksums_file $bundle_dir)
|
||
|
|
if ($checksum_results.failed > 0) {
|
||
|
|
[((($checksum_results.failed | into string)) + " checksum mismatches")]
|
||
|
|
} else {
|
||
|
|
[]
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
[]
|
||
|
|
}
|
||
|
|
)
|
||
|
|
|
||
|
|
let all_errors = (
|
||
|
|
(
|
||
|
|
[$bundle_valid.errors] | flatten
|
||
|
|
| append $checksum_errors | flatten
|
||
|
|
)
|
||
|
|
)
|
||
|
|
|
||
|
|
{
|
||
|
|
valid: ($all_errors | is-empty)
|
||
|
|
errors: $all_errors
|
||
|
|
}
|
||
|
|
}
|