nip/docs/bootstrap-api.md

856 lines
17 KiB
Markdown

# Bootstrap System API Documentation
## Overview
This document describes the internal API of the NIP Bootstrap System. This is intended for developers who want to understand or extend the bootstrap functionality.
## Architecture
```
┌─────────────────────────────────────────────────────────────┐
│ Bootstrap Coordinator │
│ (bootstrap.nim) │
└────────────────────┬────────────────────────────────────────┘
┌────────────┼────────────┬──────────────┐
│ │ │ │
▼ ▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ Recipe │ │Download │ │Install │ │ Tool │
│ Manager │ │ Manager │ │ Manager │ │ Adapters │
└──────────┘ └──────────┘ └──────────┘ └──────────┘
```
## Module: recipe_parser.nim
Parses and validates KDL recipe files.
### Types
#### `PlatformArch`
```nim
type PlatformArch* = enum
paX86_64 = "x86_64"
paAarch64 = "aarch64"
paArmv7 = "armv7"
```
Supported CPU architectures.
#### `RecipeBinary`
```nim
type RecipeBinary* = object
name*: string
url*: string
checksum*: string # Format: "blake2b-<hash>"
size*: int64 # bytes
executable*: bool
```
Represents a standalone binary file.
#### `RecipeArchive`
```nim
type RecipeArchive* = object
name*: string
url*: string
checksum*: string
size*: int64
extractTo*: string
```
Represents a compressed archive.
#### `RecipePlatform`
```nim
type RecipePlatform* = object
arch*: PlatformArch
os*: string # "linux", "bsd", etc.
binaries*: seq[RecipeBinary]
archives*: seq[RecipeArchive]
```
Platform-specific binaries and archives.
#### `RecipeDependency`
```nim
type RecipeDependency* = object
name*: string
kind*: string # "system", "nip", "optional"
version*: string # Optional version requirement
required*: bool
```
System or package dependency.
#### `RecipeInstall`
```nim
type RecipeInstall* = object
script*: string # Path to installation script
verifyScript*: string # Path to verification script
postInstall*: string # Optional post-install script
```
Installation script configuration.
#### `RecipeMetadata`
```nim
type RecipeMetadata* = object
author*: string
license*: string
updated*: string # ISO date
homepage*: string
issues*: string
```
Recipe metadata.
#### `Recipe`
```nim
type Recipe* = object
name*: string
version*: string
description*: string
toolType*: string # "nix", "pkgsrc", "gentoo"
platforms*: seq[RecipePlatform]
dependencies*: seq[RecipeDependency]
install*: RecipeInstall
metadata*: RecipeMetadata
```
Complete recipe definition.
### Procedures
#### `parseRecipe`
```nim
proc parseRecipe*(kdlContent: string): Option[Recipe]
```
Parse a recipe from KDL string content.
**Parameters:**
- `kdlContent` - KDL recipe content as string
**Returns:**
- `Option[Recipe]` - Parsed recipe or none if parsing fails
**Example:**
```nim
let content = readFile("minimal-nix.kdl")
let recipeOpt = parseRecipe(content)
if recipeOpt.isSome():
let recipe = recipeOpt.get()
echo recipe.name
```
#### `parseRecipeFile`
```nim
proc parseRecipeFile*(filePath: string): Option[Recipe]
```
Parse a recipe from a file.
**Parameters:**
- `filePath` - Path to recipe file
**Returns:**
- `Option[Recipe]` - Parsed recipe or none if parsing fails
**Example:**
```nim
let recipeOpt = parseRecipeFile("recipes/nix/minimal-nix.kdl")
```
#### `validateRecipe`
```nim
proc validateRecipe*(recipe: Recipe): tuple[valid: bool, errors: seq[string]]
```
Validate a recipe structure.
**Parameters:**
- `recipe` - Recipe to validate
**Returns:**
- Tuple of (valid, errors) where valid is true if recipe is valid
**Example:**
```nim
let (valid, errors) = validateRecipe(recipe)
if not valid:
for error in errors:
echo "Error: ", error
```
#### `selectPlatform`
```nim
proc selectPlatform*(recipe: Recipe, targetArch: PlatformArch,
targetOs: string = "linux"): Option[RecipePlatform]
```
Select appropriate platform from recipe.
**Parameters:**
- `recipe` - Recipe to select from
- `targetArch` - Target architecture
- `targetOs` - Target OS (default: "linux")
**Returns:**
- `Option[RecipePlatform]` - Selected platform or none
**Example:**
```nim
let platformOpt = selectPlatform(recipe, paX86_64, "linux")
```
#### `getCurrentPlatform`
```nim
proc getCurrentPlatform*(): tuple[arch: PlatformArch, os: string]
```
Detect current system platform.
**Returns:**
- Tuple of (arch, os)
**Example:**
```nim
let (arch, os) = getCurrentPlatform()
echo "Running on: ", arch, "/", os
```
## Module: recipe_manager.nim
Manages recipe fetching, caching, and loading.
### Types
#### `RecipeManager`
```nim
type RecipeManager* = ref object
repoUrl*: string
localCache*: string
recipes*: Table[string, Recipe]
lastUpdate*: Time
```
Manages recipe repository and caching.
#### `RecipeFetchResult`
```nim
type RecipeFetchResult* = object
success*: bool
message*: string
recipesUpdated*: int
```
Result of recipe fetch operation.
### Procedures
#### `newRecipeManager`
```nim
proc newRecipeManager*(repoUrl: string = DefaultRepoUrl,
cacheDir: string = ""): RecipeManager
```
Create a new RecipeManager.
**Parameters:**
- `repoUrl` - Git repository URL (default: NexusToolKit repo)
- `cacheDir` - Cache directory (default: XDG cache)
**Returns:**
- `RecipeManager` instance
**Example:**
```nim
let manager = newRecipeManager()
```
#### `fetchRecipes`
```nim
proc fetchRecipes*(rm: RecipeManager): RecipeFetchResult
```
Fetch or update recipes from Git repository.
**Parameters:**
- `rm` - RecipeManager instance
**Returns:**
- `RecipeFetchResult` with status and message
**Example:**
```nim
let result = manager.fetchRecipes()
if result.success:
echo "Updated ", result.recipesUpdated, " recipes"
```
#### `loadRecipe`
```nim
proc loadRecipe*(rm: RecipeManager, toolType: string): Option[Recipe]
```
Load and parse recipe for a tool type.
**Parameters:**
- `rm` - RecipeManager instance
- `toolType` - Tool type ("nix", "pkgsrc", "gentoo")
**Returns:**
- `Option[Recipe]` - Loaded recipe or none
**Example:**
```nim
let recipeOpt = manager.loadRecipe("nix")
if recipeOpt.isSome():
let recipe = recipeOpt.get()
echo "Loaded: ", recipe.name
```
#### `hasRecipe`
```nim
proc hasRecipe*(rm: RecipeManager, toolType: string): bool
```
Check if recipe exists for a tool type.
**Parameters:**
- `rm` - RecipeManager instance
- `toolType` - Tool type to check
**Returns:**
- `bool` - True if recipe exists
**Example:**
```nim
if manager.hasRecipe("nix"):
echo "Nix recipe available"
```
#### `listAvailableRecipes`
```nim
proc listAvailableRecipes*(rm: RecipeManager): seq[string]
```
List all available recipe tool types.
**Parameters:**
- `rm` - RecipeManager instance
**Returns:**
- Sequence of tool type strings
**Example:**
```nim
for toolType in manager.listAvailableRecipes():
echo "Available: ", toolType
```
## Module: download_manager.nim
Manages file downloads with verification.
### Types
#### `DownloadResult`
```nim
type DownloadResult* = object
success*: bool
filePath*: string
message*: string
bytesDownloaded*: int64
duration*: Duration
```
Result of download operation.
#### `DownloadManager`
```nim
type DownloadManager* = ref object
cacheDir*: string
maxRetries*: int
timeout*: Duration
client*: HttpClient
```
Manages file downloads.
### Procedures
#### `newDownloadManager`
```nim
proc newDownloadManager*(cacheDir: string = ""): DownloadManager
```
Create a new DownloadManager.
**Parameters:**
- `cacheDir` - Cache directory (default: XDG cache)
**Returns:**
- `DownloadManager` instance
**Example:**
```nim
let manager = newDownloadManager()
defer: manager.close()
```
#### `close`
```nim
proc close*(dm: DownloadManager)
```
Close the download manager and cleanup resources.
**Parameters:**
- `dm` - DownloadManager instance
**Example:**
```nim
manager.close()
```
#### `downloadFile`
```nim
proc downloadFile*(dm: DownloadManager, url: string, destPath: string,
expectedChecksum: string = ""): DownloadResult
```
Download a file with checksum verification.
**Parameters:**
- `dm` - DownloadManager instance
- `url` - Download URL (HTTPS)
- `destPath` - Destination file path
- `expectedChecksum` - Expected Blake2b checksum (optional)
**Returns:**
- `DownloadResult` with status and details
**Example:**
```nim
let result = manager.downloadFile(
"https://example.com/file",
"/tmp/file",
"blake2b-abc123..."
)
if result.success:
echo "Downloaded: ", result.bytesDownloaded, " bytes"
```
#### `verifyChecksum`
```nim
proc verifyChecksum*(filePath: string, expectedChecksum: string): bool
```
Verify file checksum using Blake2b.
**Parameters:**
- `filePath` - Path to file
- `expectedChecksum` - Expected checksum in format "blake2b-<hash>"
**Returns:**
- `bool` - True if checksum matches
**Example:**
```nim
if verifyChecksum("/tmp/file", "blake2b-abc123..."):
echo "Checksum verified"
```
#### `getCachedFile`
```nim
proc getCachedFile*(dm: DownloadManager, filename: string): string
```
Get path to cached file.
**Parameters:**
- `dm` - DownloadManager instance
- `filename` - Filename
**Returns:**
- Full path to cached file
**Example:**
```nim
let path = manager.getCachedFile("nix-build")
```
## Module: installation_manager.nim
Manages tool installation and rollback.
### Types
#### `InstallState`
```nim
type InstallState* = enum
isNotStarted = "not_started"
isFetching = "fetching"
isDownloading = "downloading"
isVerifying = "verifying"
isExtracting = "extracting"
isInstalling = "installing"
isVerifyingInstall = "verifying_install"
isComplete = "complete"
isFailed = "failed"
```
Installation state.
#### `InstallResult`
```nim
type InstallResult* = object
success*: bool
toolPath*: string
version*: string
message*: string
errors*: seq[string]
warnings*: seq[string]
installTime*: Duration
```
Result of installation operation.
#### `InstallationManager`
```nim
type InstallationManager* = ref object
toolsDir*: string
tempDir*: string
backupDir*: string
```
Manages tool installation.
### Procedures
#### `newInstallationManager`
```nim
proc newInstallationManager*(toolsDir: string = ""): InstallationManager
```
Create a new InstallationManager.
**Parameters:**
- `toolsDir` - Tools directory (default: XDG data)
**Returns:**
- `InstallationManager` instance
**Example:**
```nim
let manager = newInstallationManager()
```
#### `extractArchive`
```nim
proc extractArchive*(im: InstallationManager, archivePath: string,
destDir: string): tuple[success: bool, message: string]
```
Extract archive to destination directory.
**Parameters:**
- `im` - InstallationManager instance
- `archivePath` - Path to archive file
- `destDir` - Destination directory
**Returns:**
- Tuple of (success, message)
**Example:**
```nim
let (success, msg) = manager.extractArchive(
"/tmp/archive.tar.xz",
"/opt/tool"
)
```
#### `executeScript`
```nim
proc executeScript*(im: InstallationManager, scriptPath: string,
workDir: string, env: Table[string, string] = initTable[string, string]()):
tuple[success: bool, output: string]
```
Execute installation script.
**Parameters:**
- `im` - InstallationManager instance
- `scriptPath` - Path to script
- `workDir` - Working directory
- `env` - Environment variables
**Returns:**
- Tuple of (success, output)
**Example:**
```nim
let env = {"INSTALL_DIR": "/opt/tool"}.toTable
let (success, output) = manager.executeScript(
"install.sh",
"/opt/tool",
env
)
```
#### `verifyInstallation`
```nim
proc verifyInstallation*(im: InstallationManager, toolType: string,
verifyScript: string, toolPath: string):
tuple[success: bool, message: string]
```
Verify tool installation.
**Parameters:**
- `im` - InstallationManager instance
- `toolType` - Tool type
- `verifyScript` - Path to verification script
- `toolPath` - Tool installation path
**Returns:**
- Tuple of (success, message)
**Example:**
```nim
let (success, msg) = manager.verifyInstallation(
"nix",
"verify.sh",
"/opt/nix"
)
```
#### `backupTool`
```nim
proc backupTool*(im: InstallationManager, toolType: string): bool
```
Backup existing tool installation.
**Parameters:**
- `im` - InstallationManager instance
- `toolType` - Tool type
**Returns:**
- `bool` - True if backup succeeded
**Example:**
```nim
if manager.backupTool("nix"):
echo "Backup created"
```
#### `rollback`
```nim
proc rollback*(im: InstallationManager, toolType: string): bool
```
Rollback failed installation.
**Parameters:**
- `im` - InstallationManager instance
- `toolType` - Tool type
**Returns:**
- `bool` - True if rollback succeeded
**Example:**
```nim
if not installSuccess:
discard manager.rollback("nix")
```
## Module: bootstrap.nim
Main bootstrap coordinator.
### Types
#### `BuildToolType`
```nim
type BuildToolType* = enum
bttNix = "nix"
bttPkgsrc = "pkgsrc"
bttGentoo = "gentoo"
```
Supported build tool types.
#### `BootstrapResult`
```nim
type BootstrapResult* = object
success*: bool
toolPath*: string
message*: string
errors*: seq[string]
warnings*: seq[string]
```
Result of bootstrap operation.
### Procedures
#### `isToolInstalled`
```nim
proc isToolInstalled*(toolType: BuildToolType): bool
```
Check if a build tool is installed in NIP's directory.
**Parameters:**
- `toolType` - Tool type to check
**Returns:**
- `bool` - True if installed
**Example:**
```nim
if isToolInstalled(bttNix):
echo "Nix is installed"
```
#### `isSystemToolAvailable`
```nim
proc isSystemToolAvailable*(toolType: BuildToolType): bool
```
Check if tool is available on the system.
**Parameters:**
- `toolType` - Tool type to check
**Returns:**
- `bool` - True if available
**Example:**
```nim
if isSystemToolAvailable(bttNix):
echo "Nix is available on system"
```
#### `handleMissingTool`
```nim
proc handleMissingTool*(toolType: BuildToolType, autoBootstrap: bool = false): bool
```
Handle missing build tool - prompt user or auto-bootstrap.
**Parameters:**
- `toolType` - Tool type
- `autoBootstrap` - Auto-install without prompting
**Returns:**
- `bool` - True if tool is now available
**Example:**
```nim
if not isToolInstalled(bttNix):
if handleMissingTool(bttNix, autoBootstrap = true):
echo "Nix installed successfully"
```
#### `installMinimalTools`
```nim
proc installMinimalTools*(toolType: BuildToolType): BootstrapResult
```
Install minimal build tools for the specified type.
**Parameters:**
- `toolType` - Tool type to install
**Returns:**
- `BootstrapResult` with status and details
**Example:**
```nim
let result = installMinimalTools(bttNix)
if result.success:
echo "Installed to: ", result.toolPath
```
## Usage Examples
### Complete Installation Flow
```nim
import nimpak/build/[bootstrap, recipe_manager, download_manager, installation_manager]
# Initialize managers
let recipeManager = newRecipeManager()
let downloadManager = newDownloadManager()
let installManager = newInstallationManager()
defer: downloadManager.close()
# Fetch recipes
let fetchResult = recipeManager.fetchRecipes()
if not fetchResult.success:
echo "Failed to fetch recipes"
quit(1)
# Load recipe
let recipeOpt = recipeManager.loadRecipe("nix")
if recipeOpt.isNone():
echo "Recipe not found"
quit(1)
let recipe = recipeOpt.get()
# Select platform
let (arch, os) = getCurrentPlatform()
let platformOpt = selectPlatform(recipe, arch, os)
if platformOpt.isNone():
echo "Platform not supported"
quit(1)
let platform = platformOpt.get()
# Download binaries
for binary in platform.binaries:
let destPath = downloadManager.getCachedFile(binary.name)
let result = downloadManager.downloadFile(binary.url, destPath, binary.checksum)
if not result.success:
echo "Download failed: ", result.message
quit(1)
# Install
let toolDir = "/opt/nix"
let env = {"INSTALL_DIR": toolDir}.toTable
let (success, output) = installManager.executeScript(
recipe.install.script,
toolDir,
env
)
if success:
echo "Installation successful"
else:
echo "Installation failed: ", output
```
## Error Handling
All procedures that can fail return either:
- `Option[T]` - Use `isSome()` and `get()` to check and extract
- `tuple[success: bool, ...]` - Check `success` field
- `Result` object with `success` field
Always check return values and handle errors appropriately.
## Thread Safety
The bootstrap system is not thread-safe. Do not use the same manager instances from multiple threads.
## See Also
- [Bootstrap Guide](bootstrap-guide.md) - User documentation
- [Recipe Authoring Guide](../recipes/AUTHORING-GUIDE.md) - Creating recipes
- [Source Build Guide](source-build-guide.md) - Building from source