Skip to content

Commit 8e387e6

Browse files
committed
drop legacy .harmont support entirely (clean break)
Remove the ~/.harmont config/credential forward-migration and the legacy .harmont/ project-dir detection hint — no point keeping the old directory alive. Keeps the HARMONT_* env-var plumbing and the api_url->env move. - hm-util: drop legacy_hm_config_dir() - hm-config: drop creds legacy_path fallback; drop the legacy config layer (collapse load_layered back into load_from_paths, env layering retained) - hm-dsl-engine: detect bails plainly when .hm/ is absent (no .harmont hint)
1 parent b338cdd commit 8e387e6

4 files changed

Lines changed: 14 additions & 150 deletions

File tree

crates/hm-config/src/creds.rs

Lines changed: 7 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -20,30 +20,14 @@ fn path() -> Result<PathBuf> {
2020
Ok(dir.join("credentials.toml"))
2121
}
2222

23-
/// Pre-rename (v0.0.5) credential file at `~/.harmont/credentials.toml`.
24-
///
25-
/// The on-disk format is unchanged across the rename, so we read it through
26-
/// as a fallback when the new location is empty. Writes always target the new
27-
/// [`path`], so the first `hm cloud login`/`set` after upgrade migrates the
28-
/// store forward.
29-
fn legacy_path() -> Option<PathBuf> {
30-
hm_util::dirs::legacy_hm_config_dir().map(|d| d.join("credentials.toml"))
31-
}
32-
3323
fn load() -> CredentialFile {
34-
if let Ok(p) = path()
35-
&& let Ok(contents) = std::fs::read_to_string(&p)
36-
{
37-
return toml::from_str(&contents).unwrap_or_default();
38-
}
39-
// New location absent/unreadable: fall back to the legacy store so tokens
40-
// and the active org written by the pre-rename CLI survive an upgrade.
41-
if let Some(legacy) = legacy_path()
42-
&& let Ok(contents) = std::fs::read_to_string(&legacy)
43-
{
44-
return toml::from_str(&contents).unwrap_or_default();
45-
}
46-
CredentialFile::default()
24+
let Ok(p) = path() else {
25+
return CredentialFile::default();
26+
};
27+
let Ok(contents) = std::fs::read_to_string(&p) else {
28+
return CredentialFile::default();
29+
};
30+
toml::from_str(&contents).unwrap_or_default()
4731
}
4832

4933
fn save(file: &CredentialFile) -> Result<()> {

crates/hm-config/src/lib.rs

Lines changed: 5 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -96,33 +96,15 @@ impl Config {
9696
/// figment extraction fails (malformed TOML, type mismatches).
9797
pub fn load(project_root: Option<&Path>) -> Result<Self> {
9898
let user_path = Self::user_config_path()?;
99-
let legacy_user_path = hm_util::dirs::legacy_hm_config_dir().map(|d| d.join("config.toml"));
10099
let project_path = project_root.map(Self::project_config_path);
101-
Self::load_layered(
102-
legacy_user_path.as_deref(),
103-
Some(&user_path),
104-
project_path.as_deref(),
105-
)
106-
.context("loading configuration")
100+
Self::load_from_paths(Some(&user_path), project_path.as_deref())
101+
.context("loading configuration")
107102
}
108103

109104
/// Testable core: build a `Config` from explicit file paths.
110105
///
111-
/// # Errors
112-
///
113-
/// Returns an error if figment extraction fails (malformed TOML, type mismatches).
114-
pub fn load_from_paths(user_path: Option<&Path>, project_path: Option<&Path>) -> Result<Self> {
115-
Self::load_layered(None, user_path, project_path)
116-
}
117-
118-
/// Build a `Config` from explicit file paths with an optional legacy
119-
/// (`~/.harmont/`) user layer at the bottom.
120-
///
121-
/// Layering, lowest to highest precedence: defaults -> legacy user file
122-
/// -> user file -> project file -> env. The legacy layer means a custom
123-
/// `[cloud] api_url`/`org` written by the pre-rename CLI survives an
124-
/// upgrade even when the user never re-runs `hm cloud login`; an explicit
125-
/// value in the new `~/.config/hm/config.toml` always wins over it.
106+
/// Layering, lowest to highest precedence: defaults -> user file ->
107+
/// project file -> env.
126108
///
127109
/// Env precedence (highest): both the `HM_`-prefixed split form
128110
/// (`HM_CLOUD__ORG`, `HM_CLOUD__API_URL`) and the documented
@@ -132,16 +114,9 @@ impl Config {
132114
/// # Errors
133115
///
134116
/// Returns an error if figment extraction fails (malformed TOML, type mismatches).
135-
pub fn load_layered(
136-
legacy_user_path: Option<&Path>,
137-
user_path: Option<&Path>,
138-
project_path: Option<&Path>,
139-
) -> Result<Self> {
117+
pub fn load_from_paths(user_path: Option<&Path>, project_path: Option<&Path>) -> Result<Self> {
140118
let mut figment = Figment::new().merge(Serialized::defaults(Self::default()));
141119

142-
if let Some(p) = legacy_user_path {
143-
figment = figment.merge(Toml::file(p));
144-
}
145120
if let Some(p) = user_path {
146121
figment = figment.merge(Toml::file(p));
147122
}
@@ -353,50 +328,6 @@ org = "project-org"
353328
assert_eq!(loaded.preferences.format, "human");
354329
}
355330

356-
#[test]
357-
fn legacy_user_file_carried_forward_when_new_absent() {
358-
let _g = env_guard();
359-
// Pre-rename ~/.harmont/config.toml exists; new ~/.config/hm absent.
360-
let legacy_toml = r#"
361-
[cloud]
362-
org = "legacy-org"
363-
api_url = "https://legacy.api"
364-
"#;
365-
let mut legacy_file = tempfile::NamedTempFile::new().unwrap();
366-
legacy_file.write_all(legacy_toml.as_bytes()).unwrap();
367-
368-
let absent_user = Path::new("/tmp/harmont-test-absent-user-cfg/config.toml");
369-
370-
let cfg = Config::load_layered(Some(legacy_file.path()), Some(absent_user), None).unwrap();
371-
assert_eq!(cfg.cloud.org.as_deref(), Some("legacy-org"));
372-
assert_eq!(cfg.cloud.api_url, "https://legacy.api");
373-
}
374-
375-
#[test]
376-
fn new_user_file_wins_over_legacy() {
377-
let _g = env_guard();
378-
let legacy_toml = r#"
379-
[cloud]
380-
org = "legacy-org"
381-
api_url = "https://legacy.api"
382-
"#;
383-
let new_toml = r#"
384-
[cloud]
385-
org = "new-org"
386-
"#;
387-
let mut legacy_file = tempfile::NamedTempFile::new().unwrap();
388-
legacy_file.write_all(legacy_toml.as_bytes()).unwrap();
389-
let mut new_file = tempfile::NamedTempFile::new().unwrap();
390-
new_file.write_all(new_toml.as_bytes()).unwrap();
391-
392-
let cfg =
393-
Config::load_layered(Some(legacy_file.path()), Some(new_file.path()), None).unwrap();
394-
// Explicit value in the new file overrides the legacy one...
395-
assert_eq!(cfg.cloud.org.as_deref(), Some("new-org"));
396-
// ...while keys absent from the new file still fall through to legacy.
397-
assert_eq!(cfg.cloud.api_url, "https://legacy.api");
398-
}
399-
400331
#[test]
401332
#[allow(clippy::result_large_err)] // figment::Error is the Jail closure's error type.
402333
fn harmont_env_overrides_cloud_keys() {

crates/hm-dsl-engine/src/detect.rs

Lines changed: 2 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use crate::DslLanguage;
1515
pub fn detect_language(repo_root: &Path) -> anyhow::Result<DslLanguage> {
1616
let harmont_dir = repo_root.join(".hm");
1717
if !harmont_dir.is_dir() {
18-
missing_hm_dir(repo_root)?;
18+
bail!("no .hm/ directory found in {}", repo_root.display());
1919
}
2020
match scan_extensions(repo_root)? {
2121
// When both languages are present, prefer TypeScript.
@@ -40,7 +40,7 @@ pub fn detect_language(repo_root: &Path) -> anyhow::Result<DslLanguage> {
4040
pub fn detect_language_python_first(repo_root: &Path) -> anyhow::Result<DslLanguage> {
4141
let harmont_dir = repo_root.join(".hm");
4242
if !harmont_dir.is_dir() {
43-
missing_hm_dir(repo_root)?;
43+
bail!("no .hm/ directory found in {}", repo_root.display());
4444
}
4545
match scan_extensions(repo_root)? {
4646
(true, _) => Ok(DslLanguage::Python),
@@ -49,21 +49,6 @@ pub fn detect_language_python_first(repo_root: &Path) -> anyhow::Result<DslLangu
4949
}
5050
}
5151

52-
/// Bail with a doctrine-shaped error when `.hm/` is absent.
53-
///
54-
/// If the repo still carries a pre-rename `.harmont/` pipeline directory, the
55-
/// message points precisely at the rename and the one-line fix instead of the
56-
/// bare "no .hm/ directory found", which left upgrading users stranded.
57-
fn missing_hm_dir(repo_root: &Path) -> anyhow::Result<()> {
58-
if repo_root.join(".harmont").is_dir() {
59-
bail!(
60-
"found a legacy .harmont/ pipeline directory in {} — it was renamed to .hm/ in this release\n \u{2192} rename it: mv .harmont .hm",
61-
repo_root.display()
62-
);
63-
}
64-
bail!("no .hm/ directory found in {}", repo_root.display());
65-
}
66-
6752
/// True when `.hm/` exists and holds at least one `.py` or `.ts` file.
6853
///
6954
/// The backend fans pipeline discovery out across every repo in an
@@ -148,23 +133,6 @@ mod tests {
148133
assert!(msg.contains("no .hm/ directory"), "unexpected error: {msg}");
149134
}
150135

151-
#[test]
152-
fn legacy_harmont_dir_emits_migration_hint() {
153-
let tmp = TempDir::new().unwrap();
154-
// Pre-rename layout: `.harmont/` present, `.hm/` absent.
155-
fs::create_dir(tmp.path().join(".harmont")).unwrap();
156-
let err = detect_language(tmp.path()).unwrap_err();
157-
let msg = err.to_string();
158-
assert!(msg.contains("legacy .harmont/"), "unexpected error: {msg}");
159-
assert!(msg.contains("mv .harmont .hm"), "unexpected error: {msg}");
160-
// The same hint flows through the python-first path.
161-
let err2 = detect_language_python_first(tmp.path()).unwrap_err();
162-
assert!(
163-
err2.to_string().contains("mv .harmont .hm"),
164-
"unexpected error: {err2}"
165-
);
166-
}
167-
168136
#[test]
169137
fn empty_harmont_dir_is_error() {
170138
let tmp = TempDir::new().unwrap();

crates/hm-util/src/dirs.rs

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,6 @@ pub fn hm_config_dir() -> Option<PathBuf> {
1717
platform::config_dir().map(|c| c.join("hm"))
1818
}
1919

20-
/// `~/.harmont/` — the pre-rename (v0.0.5) user config root.
21-
///
22-
/// The config/credential format is unchanged across the rename, so callers
23-
/// use this as a best-effort fallback/migration source when the new
24-
/// `~/.config/hm/` location is empty. Returns `None` when the home directory
25-
/// cannot be determined.
26-
pub fn legacy_hm_config_dir() -> Option<PathBuf> {
27-
platform::home_dir().map(|h| h.join(".harmont"))
28-
}
29-
3020
/// `~/.cache/hm/` — local build cache root (regenerable).
3121
pub fn hm_cache_dir() -> Option<PathBuf> {
3222
platform::cache_dir().map(|c| c.join("hm"))
@@ -66,15 +56,6 @@ mod tests {
6656
);
6757
}
6858

69-
#[test]
70-
fn legacy_hm_config_dir_is_dot_harmont() {
71-
let p = legacy_hm_config_dir().unwrap();
72-
assert!(
73-
p.ends_with(".harmont"),
74-
"expected path ending in '.harmont', got {p:?}"
75-
);
76-
}
77-
7859
#[test]
7960
fn hm_cache_dir_under_cache() {
8061
let p = hm_cache_dir().unwrap();

0 commit comments

Comments
 (0)