From 2d351a0e75ab33bb0b1a36fa5d6954d32c0c6b48 Mon Sep 17 00:00:00 2001 From: Tibor Rogulja Date: Wed, 1 Apr 2026 22:49:36 +0200 Subject: [PATCH 1/2] refactor(docker): remove RVM/nvm wrappers from Procfile generation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit mise shims resolve Ruby/Node versions per-directory from .ruby-version and .node-version files — no explicit version switching needed. Co-Authored-By: Claude Opus 4.6 (1M context) --- crates/tb-devctl/src/docker.rs | 66 +++++----------------------------- 1 file changed, 8 insertions(+), 58 deletions(-) diff --git a/crates/tb-devctl/src/docker.rs b/crates/tb-devctl/src/docker.rs index 63c854a..0e85bae 100644 --- a/crates/tb-devctl/src/docker.rs +++ b/crates/tb-devctl/src/docker.rs @@ -4,10 +4,6 @@ use std::process::Command; use crate::config::{Config, ServiceConfig}; use crate::error::{Error, Result}; -/// Default runtime versions — must match Dockerfile.base ARGs. -const DEFAULT_RUBY: &str = "3.4.7"; -const DEFAULT_NODE: &str = "22.16.0"; - /// Generate a Procfile for overmind from the selected services. /// Writes to `.docker-sessions/.dev/Procfile.dev`. pub fn generate_procfile(config: &Config, services: &[String], project_root: &Path) -> Result<()> { @@ -23,14 +19,14 @@ pub fn generate_procfile(config: &Config, services: &[String], project_root: &Pa .get(svc_name) .ok_or_else(|| Error::Config(format!("Unknown service: {}", svc_name)))?; - if let Some(entry) = procfile_entry(svc_name, svc, project_root) { + if let Some(entry) = procfile_entry(svc_name, svc) { lines.push(entry); } // Add companion (e.g., sidekiq for api) if let Some(companion) = &svc.companion && let Some(comp_svc) = config.services.get(companion) - && let Some(entry) = procfile_entry(companion, comp_svc, project_root) + && let Some(entry) = procfile_entry(companion, comp_svc) { lines.push(entry); } @@ -40,59 +36,13 @@ pub fn generate_procfile(config: &Config, services: &[String], project_root: &Pa Ok(()) } -/// Build a single Procfile entry, with runtime version wrappers if needed. -fn procfile_entry(name: &str, svc: &ServiceConfig, project_root: &Path) -> Option { +/// Build a single Procfile entry. +/// mise shims resolve Ruby/Node versions from .ruby-version/.node-version +/// in each repo's working directory — no version wrappers needed. +fn procfile_entry(name: &str, svc: &ServiceConfig) -> Option { let repo = svc.repo.as_deref()?; let cmd = svc.cmd.as_deref()?; - - let repos_dir = project_root.join("repos"); - let mut wrapper = String::new(); - - // Check if repo needs a different Ruby version - let ruby_version_file = repos_dir.join(repo).join(".ruby-version"); - if ruby_version_file.exists() - && let Ok(version) = std::fs::read_to_string(&ruby_version_file) - { - let version = version.trim(); - if version != DEFAULT_RUBY { - wrapper.push_str(&format!("rvm use {} && ", version)); - } - } - - // Check if repo needs a different Node version - let node_version = read_node_version(&repos_dir.join(repo)); - if let Some(version) = node_version - && version != DEFAULT_NODE - { - wrapper.push_str(&format!( - ". /usr/local/nvm/nvm.sh && nvm use {} && ", - version - )); - } - - let full_cmd = if wrapper.is_empty() { - format!("{}: cd /workspace/{} && {}", name, repo, cmd) - } else { - format!( - "{}: bash -lc '{} cd /workspace/{} && {}'", - name, wrapper, repo, cmd - ) - }; - - Some(full_cmd) -} - -/// Read Node version from .node-version or .nvmrc -fn read_node_version(repo_path: &Path) -> Option { - for filename in &[".node-version", ".nvmrc"] { - let path = repo_path.join(filename); - if path.exists() - && let Ok(version) = std::fs::read_to_string(&path) - { - return Some(version.trim().to_string()); - } - } - None + Some(format!("{}: cd /workspace/{} && {}", name, repo, cmd)) } /// Query overmind inside the container to get running service names and their status. @@ -381,7 +331,7 @@ pub fn start_container(config: &Config, project_root: &Path, services: &[String] } /// Wait for the container healthcheck to pass. -/// Timeout: 10 minutes (first-time setup may compile Ruby/Node from source). +/// Timeout: 10 minutes (first-time setup installs dependencies and runs migrations). pub fn wait_for_healthy(config: &Config) -> Result<()> { let container = &config.docker.container; for i in 0..300 { From a3b22e02f32c4b161a13743184d839d12e9c3a1f Mon Sep 17 00:00:00 2001 From: Tibor Rogulja Date: Wed, 1 Apr 2026 23:38:02 +0200 Subject: [PATCH 2/2] tb-devctl: bump version to 0.2.0 --- Cargo.lock | 2 +- crates/tb-devctl/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index eecc55f..80faf91 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2035,7 +2035,7 @@ dependencies = [ [[package]] name = "tb-devctl" -version = "0.1.1" +version = "0.2.0" dependencies = [ "chrono", "clap", diff --git a/crates/tb-devctl/Cargo.toml b/crates/tb-devctl/Cargo.toml index 3e89d54..e1232ef 100644 --- a/crates/tb-devctl/Cargo.toml +++ b/crates/tb-devctl/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tb-devctl" -version = "0.1.1" +version = "0.2.0" edition = "2024" description = "Local dev environment orchestrator for Productive services" authors.workspace = true