grokking simplicity and refactoring
This commit is contained in:
parent
89a7fe100d
commit
fb4361d938
67 changed files with 6275 additions and 56 deletions
846
packages/lab-tool/research/guile.md
Normal file
846
packages/lab-tool/research/guile.md
Normal file
|
@ -0,0 +1,846 @@
|
|||
# Guile Scheme Coding Instructions for Home Lab Tool
|
||||
|
||||
## Functional Programming Principles
|
||||
|
||||
**Core Philosophy**: Functional programming is about actions, data, and computation - compose small, pure functions to build complex behaviors.
|
||||
|
||||
### 1. Pure Functions First
|
||||
- Functions should be deterministic and side-effect free when possible
|
||||
- Separate pure computation from I/O operations
|
||||
- Use immutable data structures as default
|
||||
|
||||
```scheme
|
||||
;; Good: Pure function
|
||||
(define (calculate-deployment-hash config)
|
||||
(sha256 (scm->json-string config)))
|
||||
|
||||
;; Better: Separate pure logic from I/O
|
||||
(define (deployment-ready? machine-config current-state)
|
||||
(and (eq? (assoc-ref machine-config 'status) 'configured)
|
||||
(eq? (assoc-ref current-state 'connectivity) 'online)))
|
||||
|
||||
;; I/O operations separate
|
||||
(define (check-machine-deployment machine)
|
||||
(let ((config (load-machine-config machine))
|
||||
(state (probe-machine-state machine)))
|
||||
(deployment-ready? config state)))
|
||||
```
|
||||
|
||||
### 2. Data-Driven Design
|
||||
- Represent configurations and state as data structures
|
||||
- Use association lists (alists) and vectors for structured data
|
||||
- Leverage Guile's homoiconicity (code as data)
|
||||
|
||||
```scheme
|
||||
;; Machine configuration as data
|
||||
(define machine-specs
|
||||
`((grey-area
|
||||
(services (ollama jellyfin forgejo))
|
||||
(deployment-method deploy-rs)
|
||||
(backup-schedule weekly))
|
||||
(sleeper-service
|
||||
(services (nfs zfs monitoring))
|
||||
(deployment-method hybrid-update)
|
||||
(backup-schedule daily))))
|
||||
|
||||
;; Operations on data
|
||||
(define (get-machine-services machine)
|
||||
(assoc-ref (assoc-ref machine-specs machine) 'services))
|
||||
|
||||
(define (machines-with-service service)
|
||||
(filter (lambda (machine-spec)
|
||||
(member service (get-machine-services (car machine-spec))))
|
||||
machine-specs))
|
||||
```
|
||||
|
||||
## Guile-Specific Idioms
|
||||
|
||||
### 3. Module Organization
|
||||
- Use meaningful module hierarchies
|
||||
- Export only necessary public interfaces
|
||||
- Group related functionality together
|
||||
|
||||
```scheme
|
||||
;; File: modules/lab/machines.scm
|
||||
(define-module (lab machines)
|
||||
#:use-module (srfi srfi-1) ; List processing
|
||||
#:use-module (srfi srfi-26) ; Cut/cute
|
||||
#:use-module (ice-9 match) ; Pattern matching
|
||||
#:use-module (ssh session)
|
||||
#:export (machine-status
|
||||
deploy-machine
|
||||
list-machines
|
||||
machine-services))
|
||||
|
||||
;; File: modules/lab/deployment.scm
|
||||
(define-module (lab deployment)
|
||||
#:use-module (lab machines)
|
||||
#:use-module (json)
|
||||
#:export (deploy-rs
|
||||
hybrid-update
|
||||
rollback-deployment))
|
||||
```
|
||||
|
||||
### 4. Error Handling the Scheme Way
|
||||
- Use exceptions for exceptional conditions
|
||||
- Return #f or special values for expected failures
|
||||
- Provide meaningful error context
|
||||
|
||||
```scheme
|
||||
;; Use exceptions for programming errors
|
||||
(define (deploy-machine machine method)
|
||||
(unless (member machine (list-machines))
|
||||
(throw 'invalid-machine "Unknown machine" machine))
|
||||
(unless (member method '(deploy-rs hybrid-update legacy))
|
||||
(throw 'invalid-method "Unknown deployment method" method))
|
||||
;; ... deployment logic)
|
||||
|
||||
;; Return #f for expected failures
|
||||
(define (machine-reachable? machine)
|
||||
(catch #t
|
||||
(lambda ()
|
||||
(ssh-connect machine)
|
||||
#t)
|
||||
(lambda (key . args)
|
||||
#f)))
|
||||
|
||||
;; Provide context with failure info
|
||||
(define (deployment-result success? machine method details)
|
||||
`((success . ,success?)
|
||||
(machine . ,machine)
|
||||
(method . ,method)
|
||||
(timestamp . ,(current-time))
|
||||
(details . ,details)))
|
||||
```
|
||||
|
||||
### 5. Higher-Order Functions and Composition
|
||||
- Use map, filter, fold for list processing
|
||||
- Compose functions to build complex operations
|
||||
- Leverage SRFI-1 for advanced list operations
|
||||
|
||||
```scheme
|
||||
(use-modules (srfi srfi-1))
|
||||
|
||||
;; Functional composition
|
||||
(define (healthy-machines machines)
|
||||
(filter machine-reachable?
|
||||
(filter (lambda (m) (not (maintenance-mode? m)))
|
||||
machines)))
|
||||
|
||||
;; Map operations across machines
|
||||
(define (update-all-machines)
|
||||
(map (lambda (machine)
|
||||
(cons machine (update-machine machine)))
|
||||
(healthy-machines (list-machines))))
|
||||
|
||||
;; Fold for aggregation
|
||||
(define (deployment-summary results)
|
||||
(fold (lambda (result acc)
|
||||
(if (assoc-ref result 'success)
|
||||
(cons 'successful (1+ (assoc-ref acc 'successful)))
|
||||
(cons 'failed (1+ (assoc-ref acc 'failed)))))
|
||||
'((successful . 0) (failed . 0))
|
||||
results))
|
||||
```
|
||||
|
||||
### 6. Pattern Matching for Control Flow
|
||||
- Use `match` for destructuring and dispatch
|
||||
- Pattern match on data structures
|
||||
- Cleaner than nested if/cond statements
|
||||
|
||||
```scheme
|
||||
(use-modules (ice-9 match))
|
||||
|
||||
(define (handle-deployment-event event)
|
||||
(match event
|
||||
(('start machine method)
|
||||
(log-info "Starting deployment of ~a using ~a" machine method))
|
||||
|
||||
(('progress machine percent)
|
||||
(update-progress-bar machine percent))
|
||||
|
||||
(('success machine result)
|
||||
(log-success "Deployment completed: ~a" machine)
|
||||
(notify-success machine result))
|
||||
|
||||
(('error machine error-msg)
|
||||
(log-error "Deployment failed: ~a - ~a" machine error-msg)
|
||||
(initiate-rollback machine))
|
||||
|
||||
(_ (log-warning "Unknown event: ~a" event))))
|
||||
|
||||
;; Pattern matching for configuration parsing
|
||||
(define (parse-machine-config config-sexp)
|
||||
(match config-sexp
|
||||
(('machine name ('services services ...) ('options options ...))
|
||||
`((name . ,name)
|
||||
(services . ,services)
|
||||
(options . ,(alist->hash-table options))))
|
||||
|
||||
(_ (throw 'invalid-config "Malformed machine config" config-sexp))))
|
||||
```
|
||||
|
||||
### 7. REPL-Driven Development
|
||||
- Design for interactive development
|
||||
- Provide introspection functions
|
||||
- Make state queryable and modifiable
|
||||
|
||||
```scheme
|
||||
;; REPL helpers for development
|
||||
(define (debug-machine-state machine)
|
||||
"Display comprehensive machine state for debugging"
|
||||
(format #t "Machine: ~a~%" machine)
|
||||
(format #t "Status: ~a~%" (machine-status machine))
|
||||
(format #t "Services: ~a~%" (machine-services machine))
|
||||
(format #t "Last deployment: ~a~%" (last-deployment machine))
|
||||
(format #t "Reachable: ~a~%" (machine-reachable? machine)))
|
||||
|
||||
;; Interactive deployment with confirmation
|
||||
(define (interactive-deploy machine)
|
||||
(let ((current-config (get-machine-config machine)))
|
||||
(display-config current-config)
|
||||
(when (yes-or-no? "Proceed with deployment?")
|
||||
(deploy-machine machine 'deploy-rs))))
|
||||
|
||||
;; State introspection
|
||||
(define (lab-status)
|
||||
`((total-machines . ,(length (list-machines)))
|
||||
(reachable . ,(length (filter machine-reachable? (list-machines))))
|
||||
(services-running . ,(total-running-services))
|
||||
(pending-deployments . ,(length (pending-deployments)))))
|
||||
```
|
||||
|
||||
### 8. Concurrency with Fibers
|
||||
- Use fibers for concurrent operations
|
||||
- Non-blocking I/O for better performance
|
||||
- Coordinate parallel deployments safely
|
||||
|
||||
```scheme
|
||||
(use-modules (fibers) (fibers channels))
|
||||
|
||||
;; Concurrent machine checking
|
||||
(define (check-all-machines-concurrent machines)
|
||||
(run-fibers
|
||||
(lambda ()
|
||||
(let ((results-channel (make-channel)))
|
||||
;; Spawn fiber for each machine
|
||||
(for-each (lambda (machine)
|
||||
(spawn-fiber
|
||||
(lambda ()
|
||||
(let ((status (check-machine-status machine)))
|
||||
(put-message results-channel
|
||||
(cons machine status))))))
|
||||
machines)
|
||||
|
||||
;; Collect results
|
||||
(let loop ((remaining (length machines))
|
||||
(results '()))
|
||||
(if (zero? remaining)
|
||||
results
|
||||
(loop (1- remaining)
|
||||
(cons (get-message results-channel) results))))))))
|
||||
|
||||
;; Parallel deployment with coordination
|
||||
(define (deploy-machines-parallel machines)
|
||||
(run-fibers
|
||||
(lambda ()
|
||||
(let ((deployment-channel (make-channel))
|
||||
(coordinator (spawn-fiber (deployment-coordinator deployment-channel))))
|
||||
(par-map (lambda (machine)
|
||||
(deploy-with-coordination machine deployment-channel))
|
||||
machines)))))
|
||||
```
|
||||
|
||||
### 9. MCP Server Implementation Patterns
|
||||
- Structured message handling
|
||||
- Capability-based tool organization
|
||||
- Resource management with caching
|
||||
|
||||
```scheme
|
||||
;; MCP message dispatch
|
||||
(define (handle-mcp-request request)
|
||||
(match (json-ref request "method")
|
||||
("tools/list"
|
||||
(mcp-tools-list))
|
||||
|
||||
("tools/call"
|
||||
(let ((tool (json-ref request "params" "name"))
|
||||
(args (json-ref request "params" "arguments")))
|
||||
(call-lab-tool tool args)))
|
||||
|
||||
("resources/list"
|
||||
(mcp-resources-list))
|
||||
|
||||
("resources/read"
|
||||
(let ((uri (json-ref request "params" "uri")))
|
||||
(read-lab-resource uri)))
|
||||
|
||||
(method
|
||||
(mcp-error -32601 "Method not found" method))))
|
||||
|
||||
;; Tool capability definition
|
||||
(define lab-tools
|
||||
`((deploy-machine
|
||||
(description . "Deploy configuration to a specific machine")
|
||||
(inputSchema . ,(json-schema
|
||||
`((type . "object")
|
||||
(properties . ((machine (type . "string"))
|
||||
(method (type . "string")
|
||||
(enum . ("deploy-rs" "hybrid-update")))))
|
||||
(required . ("machine")))))
|
||||
(handler . ,deploy-machine-tool))
|
||||
|
||||
(check-status
|
||||
(description . "Check machine status and connectivity")
|
||||
(inputSchema . ,(json-schema
|
||||
`((type . "object")
|
||||
(properties . ((machines (type . "array")
|
||||
(items (type . "string"))))))))
|
||||
(handler . ,check-status-tool))))
|
||||
```
|
||||
|
||||
### 10. Configuration and Environment
|
||||
- Use parameters for configuration
|
||||
- Environment-aware defaults
|
||||
- Validate configuration on startup
|
||||
|
||||
```scheme
|
||||
;; Configuration parameters
|
||||
(define lab-config-dir
|
||||
(make-parameter (or (getenv "LAB_CONFIG_DIR")
|
||||
"/etc/lab-tool")))
|
||||
|
||||
(define deployment-timeout
|
||||
(make-parameter (string->number (or (getenv "DEPLOYMENT_TIMEOUT") "300"))))
|
||||
|
||||
(define ssh-key-path
|
||||
(make-parameter (or (getenv "LAB_SSH_KEY")
|
||||
(string-append (getenv "HOME") "/.ssh/lab_key"))))
|
||||
|
||||
;; Configuration validation
|
||||
(define (validate-lab-config)
|
||||
(unless (file-exists? (lab-config-dir))
|
||||
(throw 'config-error "Lab config directory not found" (lab-config-dir)))
|
||||
|
||||
(unless (file-exists? (ssh-key-path))
|
||||
(throw 'config-error "SSH key not found" (ssh-key-path)))
|
||||
|
||||
(unless (> (deployment-timeout) 0)
|
||||
(throw 'config-error "Invalid deployment timeout" (deployment-timeout))))
|
||||
|
||||
;; Initialize with validation
|
||||
(define (init-lab-tool)
|
||||
(validate-lab-config)
|
||||
(load-machine-configurations)
|
||||
(initialize-ssh-agent)
|
||||
(setup-logging))
|
||||
```
|
||||
|
||||
## Code Style Guidelines
|
||||
|
||||
### 11. Naming Conventions
|
||||
- Use kebab-case for variables and functions
|
||||
- Predicates end with `?`
|
||||
- Mutating procedures end with `!`
|
||||
- Constants in ALL-CAPS with hyphens
|
||||
|
||||
```scheme
|
||||
;; Good naming
|
||||
(define DEFAULT-SSH-PORT 22)
|
||||
(define machine-deployment-status ...)
|
||||
(define (machine-reachable? machine) ...)
|
||||
(define (update-machine-config! machine config) ...)
|
||||
|
||||
;; Avoid
|
||||
(define defaultSSHPort 22) ; camelCase
|
||||
(define machine_status ...) ; snake_case
|
||||
(define (is-machine-reachable ...) ; unnecessary 'is-'
|
||||
```
|
||||
|
||||
### 12. Documentation and Comments
|
||||
- Document module purposes and exports
|
||||
- Use docstrings for complex functions
|
||||
- Comment the "why", not the "what"
|
||||
|
||||
```scheme
|
||||
(define (deploy-machine machine method)
|
||||
"Deploy configuration to MACHINE using METHOD.
|
||||
|
||||
Returns a deployment result alist with success status, timing,
|
||||
and any error messages. May throw exceptions for invalid inputs."
|
||||
|
||||
;; Validate inputs early to fail fast
|
||||
(validate-machine machine)
|
||||
(validate-deployment-method method)
|
||||
|
||||
;; Use atomic operations to prevent partial deployments
|
||||
(call-with-deployment-lock machine
|
||||
(lambda ()
|
||||
(let ((start-time (current-time)))
|
||||
;; ... deployment logic
|
||||
))))
|
||||
```
|
||||
|
||||
### 13. Testing Approach
|
||||
- Write tests for pure functions first
|
||||
- Mock I/O operations
|
||||
- Use SRFI-64 testing framework
|
||||
|
||||
```scheme
|
||||
(use-modules (srfi srfi-64))
|
||||
|
||||
(test-begin "machine-configuration")
|
||||
|
||||
(test-equal "machine services extraction"
|
||||
'(ollama jellyfin forgejo)
|
||||
(get-machine-services 'grey-area))
|
||||
|
||||
(test-assert "deployment readiness check"
|
||||
(deployment-ready?
|
||||
'((status . configured) (health . good))
|
||||
'((connectivity . online) (load . normal))))
|
||||
|
||||
(test-error "invalid machine throws exception"
|
||||
'invalid-machine
|
||||
(deploy-machine 'non-existent-machine 'deploy-rs))
|
||||
|
||||
(test-end "machine-configuration")
|
||||
```
|
||||
|
||||
## Project Structure Best Practices
|
||||
|
||||
### 14. Module Organization
|
||||
```
|
||||
modules/
|
||||
├── lab/
|
||||
│ ├── core.scm ; Core data structures and utilities
|
||||
│ ├── machines.scm ; Machine management
|
||||
│ ├── deployment.scm ; Deployment strategies
|
||||
│ ├── monitoring.scm ; Status checking and metrics
|
||||
│ └── config.scm ; Configuration handling
|
||||
├── mcp/
|
||||
│ ├── server.scm ; MCP server implementation
|
||||
│ ├── tools.scm ; MCP tool definitions
|
||||
│ └── resources.scm ; MCP resource handlers
|
||||
└── utils/
|
||||
├── ssh.scm ; SSH utilities
|
||||
├── json.scm ; JSON helpers
|
||||
└── logging.scm ; Logging facilities
|
||||
```
|
||||
|
||||
### 15. Build and Development Workflow
|
||||
- Use Guile's module compilation
|
||||
- Leverage REPL for iterative development
|
||||
- Provide development/production configurations
|
||||
|
||||
```scheme
|
||||
;; Development helpers in separate module
|
||||
(define-module (lab dev)
|
||||
#:use-module (lab core)
|
||||
#:export (reload-config
|
||||
reset-state
|
||||
dev-deploy))
|
||||
|
||||
;; Hot-reload for development
|
||||
(define (reload-config)
|
||||
(reload-module (resolve-module '(lab config)))
|
||||
(init-lab-tool))
|
||||
|
||||
;; Safe deployment for development
|
||||
(define (dev-deploy machine)
|
||||
(if (eq? (current-environment) 'development)
|
||||
(deploy-machine machine 'deploy-rs)
|
||||
(error "dev-deploy only available in development mode")))
|
||||
```
|
||||
|
||||
## VS Code and GitHub Copilot Integration
|
||||
|
||||
### 16. MCP Client Integration with VS Code
|
||||
- Implement MCP client in VS Code extension
|
||||
- Bridge home lab context to Copilot
|
||||
- Provide real-time infrastructure state
|
||||
|
||||
```typescript
|
||||
// VS Code extension structure for MCP integration
|
||||
// File: vscode-extension/src/extension.ts
|
||||
import * as vscode from 'vscode';
|
||||
import { MCPClient } from './mcp-client';
|
||||
|
||||
export function activate(context: vscode.ExtensionContext) {
|
||||
const mcpClient = new MCPClient('stdio', {
|
||||
command: 'guile',
|
||||
args: ['-c', '(use-modules (mcp server)) (run-mcp-server)']
|
||||
});
|
||||
|
||||
// Register commands for home lab operations
|
||||
const deployCommand = vscode.commands.registerCommand(
|
||||
'homelab.deploy',
|
||||
async (machine: string) => {
|
||||
const result = await mcpClient.callTool('deploy-machine', {
|
||||
machine: machine,
|
||||
method: 'deploy-rs'
|
||||
});
|
||||
vscode.window.showInformationMessage(
|
||||
`Deployment ${result.success ? 'succeeded' : 'failed'}`
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
// Provide context to Copilot through workspace state
|
||||
const statusProvider = new HomeLab StatusProvider(mcpClient);
|
||||
context.subscriptions.push(
|
||||
vscode.workspace.registerTextDocumentContentProvider(
|
||||
'homelab', statusProvider
|
||||
)
|
||||
);
|
||||
|
||||
context.subscriptions.push(deployCommand);
|
||||
}
|
||||
|
||||
class HomeLabStatusProvider implements vscode.TextDocumentContentProvider {
|
||||
constructor(private mcpClient: MCPClient) {}
|
||||
|
||||
async provideTextDocumentContent(uri: vscode.Uri): Promise<string> {
|
||||
// Fetch current lab state for Copilot context
|
||||
const resources = await this.mcpClient.listResources();
|
||||
const status = await this.mcpClient.readResource('machines://status/all');
|
||||
|
||||
return `# Home Lab Status
|
||||
Current Infrastructure State:
|
||||
${JSON.stringify(status, null, 2)}
|
||||
|
||||
Available Resources:
|
||||
${resources.map(r => `- ${r.uri}: ${r.description}`).join('\n')}
|
||||
`;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 17. MCP Server Configuration for IDE Integration
|
||||
- Provide IDE-specific tools and resources
|
||||
- Format responses for developer consumption
|
||||
- Include code suggestions and snippets
|
||||
|
||||
```scheme
|
||||
;; IDE-specific MCP tools
|
||||
(define ide-tools
|
||||
`((generate-nix-config
|
||||
(description . "Generate NixOS configuration for new machine")
|
||||
(inputSchema . ,(json-schema
|
||||
`((type . "object")
|
||||
(properties . ((machine-name (type . "string"))
|
||||
(services (type . "array")
|
||||
(items (type . "string")))
|
||||
(hardware-profile (type . "string"))))
|
||||
(required . ("machine-name")))))
|
||||
(handler . ,generate-nix-config-tool))
|
||||
|
||||
(suggest-deployment-strategy
|
||||
(description . "Suggest optimal deployment strategy for changes")
|
||||
(inputSchema . ,(json-schema
|
||||
`((type . "object")
|
||||
(properties . ((changed-files (type . "array")
|
||||
(items (type . "string")))
|
||||
(target-machines (type . "array")
|
||||
(items (type . "string")))))
|
||||
(required . ("changed-files")))))
|
||||
(handler . ,suggest-deployment-strategy-tool))
|
||||
|
||||
(validate-config
|
||||
(description . "Validate NixOS configuration syntax and dependencies")
|
||||
(inputSchema . ,(json-schema
|
||||
`((type . "object")
|
||||
(properties . ((config-path (type . "string"))
|
||||
(machine (type . "string"))))
|
||||
(required . ("config-path")))))
|
||||
(handler . ,validate-config-tool))))
|
||||
|
||||
;; IDE-specific resources
|
||||
(define ide-resources
|
||||
`(("homelab://templates/machine-config"
|
||||
(description . "Template for new machine configuration")
|
||||
(mimeType . "application/x-nix"))
|
||||
|
||||
("homelab://examples/service-configs"
|
||||
(description . "Example service configurations")
|
||||
(mimeType . "application/x-nix"))
|
||||
|
||||
("homelab://docs/deployment-guide"
|
||||
(description . "Step-by-step deployment procedures")
|
||||
(mimeType . "text/markdown"))
|
||||
|
||||
("homelab://status/real-time"
|
||||
(description . "Real-time infrastructure status for context")
|
||||
(mimeType . "application/json"))))
|
||||
|
||||
;; Generate contextual code suggestions
|
||||
(define (generate-nix-config-tool args)
|
||||
(let ((machine-name (assoc-ref args "machine-name"))
|
||||
(services (assoc-ref args "services"))
|
||||
(hardware-profile (assoc-ref args "hardware-profile")))
|
||||
|
||||
`((content . ,(format #f "# Generated configuration for ~a
|
||||
{ config, pkgs, ... }:
|
||||
|
||||
{
|
||||
imports = [
|
||||
./hardware-configuration.nix
|
||||
~/args
|
||||
];
|
||||
|
||||
# Machine-specific configuration
|
||||
networking.hostName = \"~a\";
|
||||
|
||||
# Services configuration
|
||||
~a
|
||||
|
||||
# System packages
|
||||
environment.systemPackages = with pkgs; [
|
||||
# Add your packages here
|
||||
];
|
||||
|
||||
system.stateVersion = \"24.05\";
|
||||
}"
|
||||
machine-name
|
||||
machine-name
|
||||
(if services
|
||||
(string-join
|
||||
(map (lambda (service)
|
||||
(format #f " services.~a.enable = true;" service))
|
||||
services)
|
||||
"\n")
|
||||
" # No services specified")))
|
||||
(isError . #f))))
|
||||
```
|
||||
|
||||
### 18. Copilot Context Enhancement
|
||||
- Provide infrastructure context to improve suggestions
|
||||
- Include deployment patterns and best practices
|
||||
- Real-time system state for informed recommendations
|
||||
|
||||
```scheme
|
||||
;; Context provider for Copilot integration
|
||||
(define (provide-copilot-context)
|
||||
`((infrastructure-state . ,(get-current-infrastructure-state))
|
||||
(deployment-patterns . ,(get-common-deployment-patterns))
|
||||
(service-configurations . ,(get-service-config-templates))
|
||||
(best-practices . ,(get-deployment-best-practices))
|
||||
(current-issues . ,(get-active-alerts))))
|
||||
|
||||
(define (get-current-infrastructure-state)
|
||||
`((machines . ,(map (lambda (machine)
|
||||
`((name . ,machine)
|
||||
(status . ,(machine-status machine))
|
||||
(services . ,(machine-services machine))
|
||||
(last-deployment . ,(last-deployment-time machine))))
|
||||
(list-machines)))
|
||||
(network-topology . ,(get-network-topology))
|
||||
(resource-usage . ,(get-resource-utilization))))
|
||||
|
||||
(define (get-common-deployment-patterns)
|
||||
`((safe-deployment . "Use deploy-rs for production, hybrid-update for development")
|
||||
(rollback-strategy . "Always test deployments in staging first")
|
||||
(service-dependencies . "Ensure database services start before applications")
|
||||
(backup-before-deploy . "Create snapshots before major configuration changes")))
|
||||
|
||||
;; Format context for IDE consumption
|
||||
(define (format-ide-context context)
|
||||
(scm->json-string context #:pretty #t))
|
||||
```
|
||||
|
||||
### 19. VS Code Extension Development
|
||||
- Create extension for seamless MCP integration
|
||||
- Provide commands, views, and context
|
||||
- Enable real-time collaboration with infrastructure
|
||||
|
||||
```typescript
|
||||
// package.json for VS Code extension
|
||||
{
|
||||
"name": "homelab-mcp-integration",
|
||||
"displayName": "Home Lab MCP Integration",
|
||||
"description": "Integrate home lab infrastructure with VS Code through MCP",
|
||||
"version": "0.1.0",
|
||||
"engines": {
|
||||
"vscode": "^1.74.0"
|
||||
},
|
||||
"categories": ["Other"],
|
||||
"activationEvents": [
|
||||
"onCommand:homelab.connect",
|
||||
"workspaceContains:**/flake.nix"
|
||||
],
|
||||
"main": "./out/extension.js",
|
||||
"contributes": {
|
||||
"commands": [
|
||||
{
|
||||
"command": "homelab.deploy",
|
||||
"title": "Deploy Machine",
|
||||
"category": "Home Lab"
|
||||
},
|
||||
{
|
||||
"command": "homelab.status",
|
||||
"title": "Check Status",
|
||||
"category": "Home Lab"
|
||||
},
|
||||
{
|
||||
"command": "homelab.generateConfig",
|
||||
"title": "Generate Config",
|
||||
"category": "Home Lab"
|
||||
}
|
||||
],
|
||||
"views": {
|
||||
"explorer": [
|
||||
{
|
||||
"id": "homelabStatus",
|
||||
"name": "Home Lab Status",
|
||||
"when": "homelab:connected"
|
||||
}
|
||||
]
|
||||
},
|
||||
"viewsContainers": {
|
||||
"activitybar": [
|
||||
{
|
||||
"id": "homelab",
|
||||
"title": "Home Lab",
|
||||
"icon": "$(server-environment)"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MCP Client implementation
|
||||
class MCPClient {
|
||||
private transport: MCPTransport;
|
||||
private capabilities: MCPCapabilities;
|
||||
|
||||
constructor(transportType: 'stdio' | 'websocket', config: any) {
|
||||
this.transport = this.createTransport(transportType, config);
|
||||
this.initialize();
|
||||
}
|
||||
|
||||
async callTool(name: string, arguments: any): Promise<any> {
|
||||
return this.transport.request('tools/call', {
|
||||
name: name,
|
||||
arguments: arguments
|
||||
});
|
||||
}
|
||||
|
||||
async listResources(): Promise<MCPResource[]> {
|
||||
const response = await this.transport.request('resources/list', {});
|
||||
return response.resources;
|
||||
}
|
||||
|
||||
async readResource(uri: string): Promise<any> {
|
||||
return this.transport.request('resources/read', { uri });
|
||||
}
|
||||
|
||||
// Integration with Copilot context
|
||||
async getCopilotContext(): Promise<string> {
|
||||
const context = await this.readResource('homelab://context/copilot');
|
||||
return context.content;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 20. GitHub Copilot Workspace Integration
|
||||
- Configure workspace for optimal Copilot suggestions
|
||||
- Provide infrastructure context files
|
||||
- Set up context patterns for deployment scenarios
|
||||
|
||||
```json
|
||||
// .vscode/settings.json
|
||||
{
|
||||
"github.copilot.enable": {
|
||||
"*": true,
|
||||
"yaml": true,
|
||||
"nix": true,
|
||||
"scheme": true
|
||||
},
|
||||
"github.copilot.advanced": {
|
||||
"length": 500,
|
||||
"temperature": 0.2
|
||||
},
|
||||
"homelab.mcpServer": {
|
||||
"command": "guile",
|
||||
"args": ["-L", "modules", "-c", "(use-modules (mcp server)) (run-mcp-server)"],
|
||||
"autoStart": true
|
||||
},
|
||||
"files.associations": {
|
||||
"*.scm": "scheme",
|
||||
"flake.lock": "json"
|
||||
}
|
||||
}
|
||||
|
||||
// .copilot/context.md for workspace context
|
||||
```markdown
|
||||
# Home Lab Infrastructure Context
|
||||
|
||||
## Current Architecture
|
||||
- NixOS-based infrastructure with multiple machines
|
||||
- Deploy-rs for safe deployments
|
||||
- Services: Ollama, Jellyfin, Forgejo, NFS, ZFS
|
||||
- Network topology: reverse-proxy, grey-area, sleeper-service, congenital-optimist
|
||||
|
||||
## Common Patterns
|
||||
- Use `deploy-rs` for production deployments
|
||||
- Test with `hybrid-update` in development
|
||||
- Always backup before major changes
|
||||
- Follow NixOS module structure in `/modules/`
|
||||
|
||||
## Configuration Standards
|
||||
- Machine configs in `/machines/{hostname}/`
|
||||
- Shared modules in `/modules/`
|
||||
- Service-specific configs in `services/` subdirectories
|
||||
```
|
||||
|
||||
### 21. Real-time Context Updates
|
||||
- Stream infrastructure changes to VS Code
|
||||
- Update Copilot context automatically
|
||||
- Provide deployment feedback in editor
|
||||
|
||||
```scheme
|
||||
;; Real-time context streaming
|
||||
(define (start-context-stream port)
|
||||
"Stream infrastructure changes to connected IDE clients"
|
||||
(let ((clients (make-hash-table)))
|
||||
(spawn-fiber
|
||||
(lambda ()
|
||||
(let loop ()
|
||||
(let ((update (get-infrastructure-update)))
|
||||
(hash-for-each
|
||||
(lambda (client-id websocket)
|
||||
(catch #t
|
||||
(lambda ()
|
||||
(websocket-send websocket
|
||||
(scm->json-string update)))
|
||||
(lambda (key . args)
|
||||
(hash-remove! clients client-id))))
|
||||
clients)
|
||||
(sleep 5)
|
||||
(loop)))))
|
||||
|
||||
;; WebSocket server for IDE connections
|
||||
(run-websocket-server
|
||||
(lambda (ws)
|
||||
(let ((client-id (generate-client-id)))
|
||||
(hash-set! clients client-id ws)
|
||||
(websocket-send ws
|
||||
(scm->json-string
|
||||
`((type . "welcome")
|
||||
(context . ,(get-current-context)))))
|
||||
(handle-client-messages ws client-id clients)))
|
||||
#:port port)))
|
||||
|
||||
;; Integration with file watchers
|
||||
(define (watch-config-changes)
|
||||
"Watch for configuration file changes and update context"
|
||||
(file-system-watcher
|
||||
(list "/home/geir/Home-lab/machines"
|
||||
"/home/geir/Home-lab/modules")
|
||||
(lambda (event)
|
||||
(match event
|
||||
(('modify path)
|
||||
(when (string-suffix? ".nix" path)
|
||||
(update-copilot-context path)))
|
||||
(_ #f)))))
|
||||
```
|
Loading…
Add table
Add a link
Reference in a new issue