Skip to content
Merged
2 changes: 1 addition & 1 deletion src/util_paths.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ def _private_machine_dir() -> Path | None:
if not root:
return None
expanded = os.path.expanduser(root)
host = socket.gethostname().split(".")[0]
host = os.environ.get("SUTANDO_HOST_LABEL") or socket.gethostname().split(".")[0]
return Path(expanded) / f"machine-{host}"


Expand Down
4 changes: 2 additions & 2 deletions src/util_paths.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ export function personalPath(filename: string, workspace?: string): string {
const privateRoot = memoryDirEnv();
if (privateRoot) {
const root = expandHome(privateRoot);
const host = hostname().split('.')[0];
const host = (process.env.SUTANDO_HOST_LABEL || hostname()).split('.')[0];
const candidate = join(root, `machine-${host}`, filename);
if (existsSync(candidate)) return candidate;
}
Expand All @@ -83,7 +83,7 @@ export function personalPath(filename: string, workspace?: string): string {
// check fails gracefully.
if (privateRoot) {
const root = expandHome(privateRoot);
const host = hostname().split('.')[0];
const host = (process.env.SUTANDO_HOST_LABEL || hostname()).split('.')[0];
return join(root, `machine-${host}`, filename);
}
if (filename === 'stand-avatar.png') return join(ws, 'assets', filename);
Expand Down
34 changes: 33 additions & 1 deletion tests/util-paths-memory-dir.test.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@


def clear_env():
for k in ("SUTANDO_MEMORY_DIR", "SUTANDO_PRIVATE_DIR"):
for k in ("SUTANDO_MEMORY_DIR", "SUTANDO_PRIVATE_DIR", "SUTANDO_HOST_LABEL"):
os.environ.pop(k, None)


Expand Down Expand Up @@ -110,6 +110,38 @@ def test_legacy_var_drives_machine_dir(self):
self.assertEqual(p, Path(f"/tmp/legacy-dir/machine-{host}"))


class HostLabelTests(unittest.TestCase):
"""SUTANDO_HOST_LABEL overrides hostname for machine-<host> dir (#871)."""

def setUp(self):
clear_env()

def tearDown(self):
clear_env()

def test_host_label_used_when_set(self):
os.environ["SUTANDO_MEMORY_DIR"] = "/tmp/memdir"
os.environ["SUTANDO_HOST_LABEL"] = "my-stable-mac"
with redirect_stderr(io.StringIO()):
p = _private_machine_dir()
self.assertEqual(p, Path("/tmp/memdir/machine-my-stable-mac"))

def test_hostname_used_when_label_unset(self):
os.environ["SUTANDO_MEMORY_DIR"] = "/tmp/memdir"
host = socket.gethostname().split(".")[0]
with redirect_stderr(io.StringIO()):
p = _private_machine_dir()
self.assertEqual(p, Path(f"/tmp/memdir/machine-{host}"))

def test_empty_label_falls_back_to_hostname(self):
os.environ["SUTANDO_MEMORY_DIR"] = "/tmp/memdir"
os.environ["SUTANDO_HOST_LABEL"] = ""
host = socket.gethostname().split(".")[0]
with redirect_stderr(io.StringIO()):
p = _private_machine_dir()
self.assertEqual(p, Path(f"/tmp/memdir/machine-{host}"))


class SharedPersonalPathTests(unittest.TestCase):
def setUp(self):
clear_env()
Expand Down
35 changes: 33 additions & 2 deletions tests/util-paths-memory-dir.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,12 @@ import assert from 'node:assert/strict';
* 5. Neither set -> undefined (no warning).
*/

import { memoryDirEnv } from '../src/util_paths.js';
import { memoryDirEnv, personalPath } from '../src/util_paths.js';

function clearEnv() {
delete process.env.SUTANDO_MEMORY_DIR;
delete process.env.SUTANDO_PRIVATE_DIR;
delete process.env.SUTANDO_HOST_LABEL;
}

function captureWarn<T>(fn: () => T): { value: T; warnings: string[] } {
Expand Down Expand Up @@ -74,7 +75,7 @@ describe('memoryDirEnv (#870 rename)', () => {
clearEnv();
});

it('emits the deprecation warning on EVERY read, not just first', () => {
it('emits the deprecation warning on EVERY read, not just first (#870 regression guard)', () => {
// Regression guard: cron / launchd environments miss startup-only
// warnings, so the alias must warn loudly every time it's resolved.
clearEnv();
Expand All @@ -89,3 +90,33 @@ describe('memoryDirEnv (#870 rename)', () => {
clearEnv();
});
});

describe('personalPath SUTANDO_HOST_LABEL (#871)', () => {
it('uses SUTANDO_HOST_LABEL when set', () => {
clearEnv();
process.env.SUTANDO_MEMORY_DIR = '/tmp/nonexistent-memdir-871';
process.env.SUTANDO_HOST_LABEL = 'my-stable-mac';
const p = personalPath('stand-identity.json', '/tmp/nonexistent-ws-871');
assert.match(p, /machine-my-stable-mac/);
clearEnv();
});

it('falls back to hostname when SUTANDO_HOST_LABEL is unset', () => {
clearEnv();
process.env.SUTANDO_MEMORY_DIR = '/tmp/nonexistent-memdir-871';
const p = personalPath('stand-identity.json', '/tmp/nonexistent-ws-871');
// Hostname cannot contain "my-stable-mac" — just verify the label is absent.
assert.doesNotMatch(p, /machine-my-stable-mac/);
assert.match(p, /machine-/);
clearEnv();
});

it('falls back to hostname when SUTANDO_HOST_LABEL is empty string', () => {
clearEnv();
process.env.SUTANDO_MEMORY_DIR = '/tmp/nonexistent-memdir-871';
process.env.SUTANDO_HOST_LABEL = '';
const p = personalPath('stand-identity.json', '/tmp/nonexistent-ws-871');
assert.doesNotMatch(p, /machine-$/); // not machine- with empty suffix
clearEnv();
});
});
Loading