Class: Rubino::CLI::SetupCommand
- Inherits:
-
Object
- Object
- Rubino::CLI::SetupCommand
- Defined in:
- lib/rubino/cli/setup_command.rb
Overview
Handles initial setup: creates config directory, default config, initializes the database, and runs migrations.
Instance Method Summary collapse
Instance Method Details
#execute ⇒ Object
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 |
# File 'lib/rubino/cli/setup_command.rb', line 10 def execute ui = Rubino.ui ui.info("Setting up rubino...") ui.blank_line # Create home directory (0700 — only the owner sees stored secrets) # and subdirectories. ensure_directories! owns the mkdir + chmod so # every entry point that materializes the home agrees on 0700 (#65). home = Rubino.home_path Rubino.ensure_directories! ui.success("Home directory: #{home}") ui.success("Subdirectories created") # Create config file if it doesn't exist loader = Config::Loader.new if loader.config_exists? ui.warning("Config already exists: #{loader.config_path}") else loader.create_default_config! File.chmod(0o600, loader.config_path) ui.success("Config created: #{loader.config_path}") end # Create .env template if it doesn't exist (0600 — contains api keys) env_path = File.join(home, ".env") unless File.exist?(env_path) File.write(env_path, env_template) File.chmod(0o600, env_path) ui.success("Env template created: #{env_path}") end # Initialize database. `setup` is the documented remedy for a broken # install, so it must SELF-HEAL a corrupt/truncated DB instead of # crashing with a raw SQLite3::CorruptException backtrace (HIGH-2): if # the file is present but unopenable, quarantine it aside (preserving the # bytes for forensics) and recreate a fresh one. ui.status("Initializing database...") connection = recover_corrupt_database(ui) migrator = Database::Migrator.new(connection) # `setup` is the documented repair path. The schema is a single, # fully-idempotent baseline migration (`create_table?` / `IF NOT EXISTS` # throughout) applied under the cross-process flock, so this is safe to # run on a fresh, partial, or already-migrated home alike — a healthy DB # just no-ops, a partial one finishes, and a re-run never collides. migrator.migrate!(lock_path: Rubino.migration_lock_path) ui.success("Database initialized: #{connection.db_path}") # First-run onboarding: if no usable key is configured yet AND we're on # a real TTY, guide the user to a working model (provider/model/key) # right here so `setup` ends in a usable config — not a dead-end that # still needs hand-editing config.yml (#93). Non-interactive setup keeps # the old behaviour (files created, no prompts). maybe_run_onboarding(ui) # Non-interactive provider auto-detect (#392a): a headless `setup` can't # prompt, so the seeded default (openai/gpt-4.1 → OPENAI_API_KEY) is a # dead end when the only key in the env is, say, MINIMAX_API_KEY — doctor # then fails "No credentials found for provider 'openai'". When EXACTLY # ONE provider's key is present in the env, point model.provider / # model.default (and any required providers.<name> block) at it so a # CI/container `setup` lands on a usable config. Ambiguous (>1 key) or # none keeps the seeded default untouched. maybe_autodetect_provider(ui) ui.blank_line # Tell the truth about the end state (#31). A green "Setup complete!" is # only honest when a usable credential is actually configured — printing # it after a skipped/abandoned onboarding (no provider, no key) directly # contradicts the state. Re-check the credential after onboarding so the # final line reflects reality on both the interactive and the # non-interactive (files-only) paths. if LLM::CredentialCheck.usable? ui.success("Setup complete! Run 'rubino doctor' to verify.") elsif (model = Rubino.configuration.model_default.to_s).empty? ui.warning("Setup files created, but no model is configured yet.") ui.status("Run 'rubino setup' again or add an API key, then 'rubino doctor' to verify.") else # A model IS configured (#31) — what's missing is its CREDENTIAL (only a # different provider's key is present), so the old "no model configured" # copy was wrong. Name the model and point at the guided setup. provider = LLM::CredentialCheck.resolved_provider ui.warning("Setup files created, but the API key for #{model} (provider '#{provider}') is missing.") ui.status("Run 'rubino setup' to add it, then 'rubino doctor' to verify.") end end |