@happier-dev/stack 0.2.0 → 0.2.1-preview.1775586714.26562
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/node_modules/@happier-dev/agents/dist/.tsbuildinfo +1 -1
- package/node_modules/@happier-dev/agents/dist/index.d.ts +1 -1
- package/node_modules/@happier-dev/agents/dist/index.d.ts.map +1 -1
- package/node_modules/@happier-dev/agents/dist/index.js.map +1 -1
- package/node_modules/@happier-dev/agents/dist/providerSettings/definitions/claudeRemote.d.ts +9 -9
- package/node_modules/@happier-dev/agents/dist/providerSettings/definitions/codex.d.ts +5 -5
- package/node_modules/@happier-dev/agents/dist/providers/providerCliRuntime.d.ts +14 -1
- package/node_modules/@happier-dev/agents/dist/providers/providerCliRuntime.d.ts.map +1 -1
- package/node_modules/@happier-dev/agents/dist/providers/providerCliRuntime.js +25 -12
- package/node_modules/@happier-dev/agents/dist/providers/providerCliRuntime.js.map +1 -1
- package/node_modules/@happier-dev/cli-common/dist/.tsbuildinfo +1 -1
- package/node_modules/@happier-dev/cli-common/dist/componentArtifacts/buildCliBinaryArtifactPayload.js +1 -1
- package/node_modules/@happier-dev/cli-common/dist/componentArtifacts/buildCliBinaryArtifactPayload.js.map +1 -1
- package/node_modules/@happier-dev/cli-common/dist/componentArtifacts/commands.d.ts.map +1 -1
- package/node_modules/@happier-dev/cli-common/dist/componentArtifacts/commands.js +2 -1
- package/node_modules/@happier-dev/cli-common/dist/componentArtifacts/commands.js.map +1 -1
- package/node_modules/@happier-dev/cli-common/dist/componentArtifacts/commands.test.js +22 -0
- package/node_modules/@happier-dev/cli-common/dist/componentArtifacts/commands.test.js.map +1 -1
- package/node_modules/@happier-dev/cli-common/dist/firstPartyRuntime/index.d.ts +2 -2
- package/node_modules/@happier-dev/cli-common/dist/firstPartyRuntime/index.d.ts.map +1 -1
- package/node_modules/@happier-dev/cli-common/dist/firstPartyRuntime/index.js +1 -1
- package/node_modules/@happier-dev/cli-common/dist/firstPartyRuntime/index.js.map +1 -1
- package/node_modules/@happier-dev/cli-common/dist/firstPartyRuntime/relayRuntime.d.ts +12 -0
- package/node_modules/@happier-dev/cli-common/dist/firstPartyRuntime/relayRuntime.d.ts.map +1 -1
- package/node_modules/@happier-dev/cli-common/dist/firstPartyRuntime/relayRuntime.js +18 -0
- package/node_modules/@happier-dev/cli-common/dist/firstPartyRuntime/relayRuntime.js.map +1 -1
- package/node_modules/@happier-dev/cli-common/dist/firstPartyRuntime/relayRuntime.test.js +39 -0
- package/node_modules/@happier-dev/cli-common/dist/firstPartyRuntime/relayRuntime.test.js.map +1 -1
- package/node_modules/@happier-dev/cli-common/dist/process/index.d.ts +1 -1
- package/node_modules/@happier-dev/cli-common/dist/process/index.d.ts.map +1 -1
- package/node_modules/@happier-dev/cli-common/dist/process/index.js +1 -1
- package/node_modules/@happier-dev/cli-common/dist/process/index.js.map +1 -1
- package/node_modules/@happier-dev/cli-common/dist/process/windows/resolveWindowsCommandInvocation.d.ts +1 -0
- package/node_modules/@happier-dev/cli-common/dist/process/windows/resolveWindowsCommandInvocation.d.ts.map +1 -1
- package/node_modules/@happier-dev/cli-common/dist/process/windows/resolveWindowsCommandInvocation.js +23 -5
- package/node_modules/@happier-dev/cli-common/dist/process/windows/resolveWindowsCommandInvocation.js.map +1 -1
- package/node_modules/@happier-dev/cli-common/dist/process/windows/resolveWindowsCommandInvocation.test.d.ts +2 -0
- package/node_modules/@happier-dev/cli-common/dist/process/windows/resolveWindowsCommandInvocation.test.d.ts.map +1 -0
- package/node_modules/@happier-dev/cli-common/dist/process/windows/resolveWindowsCommandInvocation.test.js +64 -0
- package/node_modules/@happier-dev/cli-common/dist/process/windows/resolveWindowsCommandInvocation.test.js.map +1 -0
- package/node_modules/@happier-dev/cli-common/dist/providers/index.d.ts +1 -1
- package/node_modules/@happier-dev/cli-common/dist/providers/index.d.ts.map +1 -1
- package/node_modules/@happier-dev/cli-common/dist/providers/index.js +1 -1
- package/node_modules/@happier-dev/cli-common/dist/providers/index.js.map +1 -1
- package/node_modules/@happier-dev/cli-common/dist/providers/install.d.ts.map +1 -1
- package/node_modules/@happier-dev/cli-common/dist/providers/install.js +3 -1
- package/node_modules/@happier-dev/cli-common/dist/providers/install.js.map +1 -1
- package/node_modules/@happier-dev/cli-common/dist/providers/managedJavaScriptRuntime.d.ts.map +1 -1
- package/node_modules/@happier-dev/cli-common/dist/providers/managedJavaScriptRuntime.js +10 -1
- package/node_modules/@happier-dev/cli-common/dist/providers/managedJavaScriptRuntime.js.map +1 -1
- package/node_modules/@happier-dev/cli-common/dist/providers/managedJavaScriptRuntime.systemNodeFallback.test.js +58 -1
- package/node_modules/@happier-dev/cli-common/dist/providers/managedJavaScriptRuntime.systemNodeFallback.test.js.map +1 -1
- package/node_modules/@happier-dev/cli-common/dist/providers/resolution.d.ts +2 -0
- package/node_modules/@happier-dev/cli-common/dist/providers/resolution.d.ts.map +1 -1
- package/node_modules/@happier-dev/cli-common/dist/providers/resolution.js +120 -32
- package/node_modules/@happier-dev/cli-common/dist/providers/resolution.js.map +1 -1
- package/node_modules/@happier-dev/cli-common/dist/providers/resolveHappyHomeDir.d.ts.map +1 -1
- package/node_modules/@happier-dev/cli-common/dist/providers/resolveHappyHomeDir.js +10 -1
- package/node_modules/@happier-dev/cli-common/dist/providers/resolveHappyHomeDir.js.map +1 -1
- package/node_modules/@happier-dev/cli-common/dist/providers/resolveHappyHomeDir.test.js +6 -0
- package/node_modules/@happier-dev/cli-common/dist/providers/resolveHappyHomeDir.test.js.map +1 -1
- package/node_modules/@happier-dev/protocol/dist/.tsbuildinfo +1 -1
- package/node_modules/@happier-dev/protocol/dist/e2e/providerSpec.d.ts +2 -2
- package/node_modules/@happier-dev/protocol/dist/features/decision.d.ts +2 -2
- package/node_modules/@happier-dev/protocol/dist/features/payload/capabilities/authCapabilities.d.ts +1 -1
- package/node_modules/@happier-dev/protocol/dist/features/payload/capabilities/capabilitiesSchema.d.ts +1 -1
- package/node_modules/@happier-dev/protocol/dist/features/payload/featuresResponseSchema.d.ts +1 -1
- package/node_modules/@happier-dev/protocol/dist/structuredMessages/reviewFindingsV1.d.ts +3 -3
- package/node_modules/@happier-dev/protocol/dist/structuredMessages/reviewFindingsV2.d.ts +1 -1
- package/node_modules/@happier-dev/release-runtime/dist/.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/scripts/doctor.mjs +10 -2
- package/scripts/env.mjs +2 -5
- package/scripts/env_cmd.test.mjs +32 -0
- package/scripts/ghops.mjs +7 -4
- package/scripts/ghops.test.mjs +42 -1
- package/scripts/happier.mjs +2 -2
- package/scripts/menubar.mjs +2 -2
- package/scripts/run.mjs +2 -2
- package/scripts/self_host_runtime.mjs +10 -7
- package/scripts/self_host_runtime.test.mjs +47 -0
- package/scripts/service.mjs +10 -3
- package/scripts/stack.mjs +3 -2
- package/scripts/tailscale.mjs +2 -2
- package/scripts/utils/env/config.mjs +2 -7
- package/scripts/utils/env/env.mjs +9 -5
- package/scripts/utils/env/env_local.mjs +2 -1
- package/scripts/utils/paths/canonical_home.mjs +11 -3
- package/scripts/utils/paths/canonical_home.test.mjs +11 -3
- package/scripts/utils/paths/paths.mjs +17 -5
- package/scripts/utils/paths/paths_home_env.test.mjs +43 -0
- package/scripts/utils/proc/pm.mjs +9 -4
- package/scripts/utils/proc/pm_stack_cache_env.test.mjs +28 -0
- package/scripts/utils/review/targets.mjs +2 -2
- package/scripts/utils/server/apply_server_light_env_defaults.mjs +4 -3
- package/scripts/utils/server/apply_server_light_env_defaults.test.mjs +25 -0
- package/scripts/utils/server/urls.mjs +3 -5
- package/scripts/utils/server/urls.test.mjs +22 -1
- package/scripts/utils/service/stack_autostart_resolution.mjs +6 -2
- package/scripts/utils/service/stack_autostart_resolution.test.mjs +13 -1
- package/scripts/utils/stack/context.mjs +2 -3
- package/scripts/utils/stack/context.test.mjs +18 -0
- package/scripts/utils/stack/dirs.mjs +4 -4
- package/scripts/utils/stack/dirs.test.mjs +33 -0
- package/scripts/utils/stack/stop.mjs +2 -2
- package/scripts/where.mjs +15 -4
- package/scripts/worktrees.mjs +2 -1
package/scripts/ghops.test.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import assert from 'node:assert/strict';
|
|
2
2
|
import { spawnSync } from 'node:child_process';
|
|
3
|
-
import { chmodSync, mkdtempSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { chmodSync, mkdirSync, mkdtempSync, writeFileSync } from 'node:fs';
|
|
4
4
|
import { tmpdir } from 'node:os';
|
|
5
5
|
import { join, resolve } from 'node:path';
|
|
6
6
|
import test from 'node:test';
|
|
@@ -76,3 +76,44 @@ process.stdout.write(JSON.stringify(payload));
|
|
|
76
76
|
assert.equal(out.env.GH_PROMPT_DISABLED, '1');
|
|
77
77
|
assert.equal(out.env.GH_CONFIG_DIR, configDir);
|
|
78
78
|
});
|
|
79
|
+
|
|
80
|
+
test('expands ~/ overrides for gh binary and config dir against HOME', () => {
|
|
81
|
+
const dir = mkdtempSync(join(tmpdir(), 'ghops-home-test-'));
|
|
82
|
+
const homeDir = join(dir, 'home');
|
|
83
|
+
const fakeGh = join(homeDir, 'bin', 'fake-gh');
|
|
84
|
+
const configDir = join(homeDir, 'gh-config');
|
|
85
|
+
mkdirSync(join(homeDir, 'bin'), { recursive: true });
|
|
86
|
+
|
|
87
|
+
writeFileSync(
|
|
88
|
+
fakeGh,
|
|
89
|
+
`#!/usr/bin/env node
|
|
90
|
+
const payload = {
|
|
91
|
+
argv: process.argv.slice(2),
|
|
92
|
+
env: {
|
|
93
|
+
GH_TOKEN: process.env.GH_TOKEN ?? null,
|
|
94
|
+
GH_PROMPT_DISABLED: process.env.GH_PROMPT_DISABLED ?? null,
|
|
95
|
+
GH_CONFIG_DIR: process.env.GH_CONFIG_DIR ?? null,
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
process.stdout.write(JSON.stringify(payload));
|
|
99
|
+
`,
|
|
100
|
+
'utf8',
|
|
101
|
+
);
|
|
102
|
+
chmodSync(fakeGh, 0o755);
|
|
103
|
+
|
|
104
|
+
const token = 'test-bot-token';
|
|
105
|
+
const res = runGhop(['api', 'user'], {
|
|
106
|
+
HOME: homeDir,
|
|
107
|
+
USERPROFILE: homeDir,
|
|
108
|
+
HAPPIER_GITHUB_BOT_TOKEN: token,
|
|
109
|
+
HAPPIER_GHOPS_GH_PATH: '~/bin/fake-gh',
|
|
110
|
+
HAPPIER_GHOPS_CONFIG_DIR: '~/gh-config',
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
assert.equal(res.status, 0, res.stderr);
|
|
114
|
+
const out = JSON.parse(res.stdout);
|
|
115
|
+
assert.deepEqual(out.argv, ['api', 'user']);
|
|
116
|
+
assert.equal(out.env.GH_TOKEN, token);
|
|
117
|
+
assert.equal(out.env.GH_PROMPT_DISABLED, '1');
|
|
118
|
+
assert.equal(out.env.GH_CONFIG_DIR, configDir);
|
|
119
|
+
});
|
package/scripts/happier.mjs
CHANGED
|
@@ -5,7 +5,7 @@ import { createRequire } from 'node:module';
|
|
|
5
5
|
import { dirname, join } from 'node:path';
|
|
6
6
|
import { parseArgs } from './utils/cli/args.mjs';
|
|
7
7
|
import { printResult, wantsHelp, wantsJson } from './utils/cli/cli.mjs';
|
|
8
|
-
import { getComponentDir, getRootDir, getStackName } from './utils/paths/paths.mjs';
|
|
8
|
+
import { getComponentDir, getRootDir, getStackName, resolveExplicitStackEnvFilePath } from './utils/paths/paths.mjs';
|
|
9
9
|
import { resolveCliHomeDir } from './utils/stack/dirs.mjs';
|
|
10
10
|
import { getPublicServerUrlEnvOverride, resolveServerPortFromEnv } from './utils/server/urls.mjs';
|
|
11
11
|
import { resolveLocalServerPortForStack } from './utils/server/resolve_stack_server_port.mjs';
|
|
@@ -328,7 +328,7 @@ async function main() {
|
|
|
328
328
|
// We treat an invocation as "stack-scoped" only when the stack env file actually exists (or when the
|
|
329
329
|
// CLI home dir is explicitly overridden by the stack). This keeps plain `hstack happier` able to
|
|
330
330
|
// reuse the user's CLI settings even when stack helper env vars are present in test/dev harnesses.
|
|
331
|
-
const stackEnvFilePath =
|
|
331
|
+
const stackEnvFilePath = resolveExplicitStackEnvFilePath(env);
|
|
332
332
|
const isStackScopedInvocation =
|
|
333
333
|
Boolean(String(env.HAPPIER_STACK_CLI_HOME_DIR ?? '').trim()) ||
|
|
334
334
|
Boolean(stackEnvFilePath && existsSync(stackEnvFilePath));
|
package/scripts/menubar.mjs
CHANGED
|
@@ -4,7 +4,7 @@ import { existsSync } from 'node:fs';
|
|
|
4
4
|
import { join } from 'node:path';
|
|
5
5
|
import { spawnSync } from 'node:child_process';
|
|
6
6
|
import { createHash } from 'node:crypto';
|
|
7
|
-
import { getHappyStacksHomeDir, getRootDir, resolveStackEnvPath } from './utils/paths/paths.mjs';
|
|
7
|
+
import { getHappyStacksHomeDir, getRootDir, resolveExplicitStackEnvFilePath, resolveStackEnvPath } from './utils/paths/paths.mjs';
|
|
8
8
|
import { parseArgs } from './utils/cli/args.mjs';
|
|
9
9
|
import { printResult, wantsHelp, wantsJson } from './utils/cli/cli.mjs';
|
|
10
10
|
import { ensureEnvLocalUpdated } from './utils/env/env_local.mjs';
|
|
@@ -208,7 +208,7 @@ async function main() {
|
|
|
208
208
|
const pluginBasename = String(process.env.HAPPIER_STACK_SWIFTBAR_PLUGIN_BASENAME ?? '').trim() || defaultBasename;
|
|
209
209
|
const pluginFile = `${pluginBasename}.${interval}.sh`;
|
|
210
210
|
|
|
211
|
-
const defaultEnvFile = (process.env
|
|
211
|
+
const defaultEnvFile = resolveExplicitStackEnvFilePath(process.env);
|
|
212
212
|
const resolvedEnvFile = explicitStack
|
|
213
213
|
? resolveStackEnvPath(normalizedStack, process.env).envPath
|
|
214
214
|
: (defaultEnvFile || resolveStackEnvPath(normalizedStack, process.env).envPath);
|
package/scripts/run.mjs
CHANGED
|
@@ -2,7 +2,7 @@ import './utils/env/env.mjs';
|
|
|
2
2
|
import { parseArgs } from './utils/cli/args.mjs';
|
|
3
3
|
import { pathExists } from './utils/fs/fs.mjs';
|
|
4
4
|
import { killProcessTree, runCapture, spawnProc } from './utils/proc/proc.mjs';
|
|
5
|
-
import { getComponentDir, getDefaultAutostartPaths, getRootDir } from './utils/paths/paths.mjs';
|
|
5
|
+
import { getComponentDir, getDefaultAutostartPaths, getRootDir, resolveExplicitStackEnvFilePath } from './utils/paths/paths.mjs';
|
|
6
6
|
import { killPortListeners } from './utils/net/ports.mjs';
|
|
7
7
|
import { getServerComponentName, isHappierServerRunning, waitForServerReady } from './utils/server/server.mjs';
|
|
8
8
|
import { ensureCliBuilt, ensureDepsInstalled, pmExecBin, pmSpawnScript, requireDir } from './utils/proc/pm.mjs';
|
|
@@ -387,7 +387,7 @@ async function main() {
|
|
|
387
387
|
if (serverComponentName === 'happier-server') {
|
|
388
388
|
const managed = (baseEnv.HAPPIER_STACK_MANAGED_INFRA ?? '1') !== '0';
|
|
389
389
|
if (managed) {
|
|
390
|
-
const envPath = baseEnv
|
|
390
|
+
const envPath = resolveExplicitStackEnvFilePath(baseEnv);
|
|
391
391
|
const infra = await ensureHappyServerManagedInfra({
|
|
392
392
|
stackName: autostart.stackName,
|
|
393
393
|
baseDir: autostart.baseDir,
|
|
@@ -35,6 +35,8 @@ import {
|
|
|
35
35
|
} from '@happier-dev/cli-common/service';
|
|
36
36
|
import {
|
|
37
37
|
parseEnvText as parseEnvTextShared,
|
|
38
|
+
resolveConfiguredRelayRuntimeBinaryOverride,
|
|
39
|
+
resolveConfiguredRelayRuntimePaths,
|
|
38
40
|
renderSelfHostServerEnvText as renderSelfHostServerEnvTextShared,
|
|
39
41
|
} from '@happier-dev/cli-common/firstPartyRuntime';
|
|
40
42
|
import { DEFAULT_MINISIGN_PUBLIC_KEY } from '@happier-dev/release-runtime/minisign';
|
|
@@ -469,13 +471,14 @@ async function applySelfHostSqliteMigrationsAtInstallTime({ env }) {
|
|
|
469
471
|
return { applied: appliedNow, skipped: false, reason: 'ok' };
|
|
470
472
|
}
|
|
471
473
|
|
|
472
|
-
function resolveConfig({ channel, mode = 'user', platform = process.platform } = {}) {
|
|
474
|
+
export function resolveConfig({ channel, mode = 'user', platform = process.platform } = {}) {
|
|
473
475
|
const defaults = resolveSelfHostDefaults({ platform, mode, channel, homeDir: homedir() });
|
|
474
|
-
const
|
|
475
|
-
const
|
|
476
|
-
const
|
|
477
|
-
const
|
|
478
|
-
const
|
|
476
|
+
const configuredPaths = resolveConfiguredRelayRuntimePaths({ defaults, env: process.env });
|
|
477
|
+
const installRoot = configuredPaths.installRoot;
|
|
478
|
+
const binDir = configuredPaths.binDir;
|
|
479
|
+
const configDir = configuredPaths.configDir;
|
|
480
|
+
const dataDir = configuredPaths.dataDir;
|
|
481
|
+
const logDir = configuredPaths.logDir;
|
|
479
482
|
const serviceName = String(process.env.HAPPIER_SELF_HOST_SERVICE_NAME ?? DEFAULTS.serviceName).trim();
|
|
480
483
|
const serverHost = String(process.env.HAPPIER_SERVER_HOST ?? DEFAULTS.serverHost).trim();
|
|
481
484
|
const serverPort = parsePort(process.env.HAPPIER_SERVER_PORT, DEFAULTS.serverPort);
|
|
@@ -1914,7 +1917,7 @@ async function cmdInstall({ channel, mode, argv, json }) {
|
|
|
1914
1917
|
|| parseBoolean(process.env.HAPPIER_WITH_UI, true) === false
|
|
1915
1918
|
|| parseBoolean(process.env.HAPPIER_SELF_HOST_WITH_UI, true) === false);
|
|
1916
1919
|
const nonInteractive = argvSansEnv.includes('--non-interactive') || parseBoolean(process.env.HAPPIER_NONINTERACTIVE, false);
|
|
1917
|
-
const serverBinaryOverride =
|
|
1920
|
+
const serverBinaryOverride = resolveConfiguredRelayRuntimeBinaryOverride(process.env);
|
|
1918
1921
|
|
|
1919
1922
|
if (normalizeOs(config.platform) !== 'windows' && !commandExists('tar')) {
|
|
1920
1923
|
throw new Error('[self-host] tar is required to extract release artifacts');
|
|
@@ -983,6 +983,53 @@ test('resolveSelfHostDefaults isolates publicdev into a side-by-side self-host r
|
|
|
983
983
|
assert.equal(cfg.serviceName, 'happier-server-dev');
|
|
984
984
|
});
|
|
985
985
|
|
|
986
|
+
test('resolveConfig expands ~/ self-host path overrides against HOME', async () => {
|
|
987
|
+
const mod = await import('./self_host_runtime.mjs');
|
|
988
|
+
assert.equal(typeof mod.resolveConfig, 'function');
|
|
989
|
+
|
|
990
|
+
const previous = {
|
|
991
|
+
HOME: process.env.HOME,
|
|
992
|
+
USERPROFILE: process.env.USERPROFILE,
|
|
993
|
+
HAPPIER_SELF_HOST_INSTALL_ROOT: process.env.HAPPIER_SELF_HOST_INSTALL_ROOT,
|
|
994
|
+
HAPPIER_SELF_HOST_BIN_DIR: process.env.HAPPIER_SELF_HOST_BIN_DIR,
|
|
995
|
+
HAPPIER_SELF_HOST_CONFIG_DIR: process.env.HAPPIER_SELF_HOST_CONFIG_DIR,
|
|
996
|
+
HAPPIER_SELF_HOST_DATA_DIR: process.env.HAPPIER_SELF_HOST_DATA_DIR,
|
|
997
|
+
HAPPIER_SELF_HOST_LOG_DIR: process.env.HAPPIER_SELF_HOST_LOG_DIR,
|
|
998
|
+
};
|
|
999
|
+
|
|
1000
|
+
process.env.HOME = '/scoped/home';
|
|
1001
|
+
process.env.USERPROFILE = '/scoped/home';
|
|
1002
|
+
process.env.HAPPIER_SELF_HOST_INSTALL_ROOT = '~/relay/install';
|
|
1003
|
+
process.env.HAPPIER_SELF_HOST_BIN_DIR = '~/relay/bin';
|
|
1004
|
+
process.env.HAPPIER_SELF_HOST_CONFIG_DIR = '~/relay/config';
|
|
1005
|
+
process.env.HAPPIER_SELF_HOST_DATA_DIR = '~/relay/data';
|
|
1006
|
+
process.env.HAPPIER_SELF_HOST_LOG_DIR = '~/relay/logs';
|
|
1007
|
+
|
|
1008
|
+
try {
|
|
1009
|
+
const config = mod.resolveConfig({ platform: 'linux', mode: 'user', channel: 'stable' });
|
|
1010
|
+
assert.equal(config.installRoot, '/scoped/home/relay/install');
|
|
1011
|
+
assert.equal(config.binDir, '/scoped/home/relay/bin');
|
|
1012
|
+
assert.equal(config.configDir, '/scoped/home/relay/config');
|
|
1013
|
+
assert.equal(config.dataDir, '/scoped/home/relay/data');
|
|
1014
|
+
assert.equal(config.logDir, '/scoped/home/relay/logs');
|
|
1015
|
+
} finally {
|
|
1016
|
+
if (previous.HOME === undefined) delete process.env.HOME;
|
|
1017
|
+
else process.env.HOME = previous.HOME;
|
|
1018
|
+
if (previous.USERPROFILE === undefined) delete process.env.USERPROFILE;
|
|
1019
|
+
else process.env.USERPROFILE = previous.USERPROFILE;
|
|
1020
|
+
if (previous.HAPPIER_SELF_HOST_INSTALL_ROOT === undefined) delete process.env.HAPPIER_SELF_HOST_INSTALL_ROOT;
|
|
1021
|
+
else process.env.HAPPIER_SELF_HOST_INSTALL_ROOT = previous.HAPPIER_SELF_HOST_INSTALL_ROOT;
|
|
1022
|
+
if (previous.HAPPIER_SELF_HOST_BIN_DIR === undefined) delete process.env.HAPPIER_SELF_HOST_BIN_DIR;
|
|
1023
|
+
else process.env.HAPPIER_SELF_HOST_BIN_DIR = previous.HAPPIER_SELF_HOST_BIN_DIR;
|
|
1024
|
+
if (previous.HAPPIER_SELF_HOST_CONFIG_DIR === undefined) delete process.env.HAPPIER_SELF_HOST_CONFIG_DIR;
|
|
1025
|
+
else process.env.HAPPIER_SELF_HOST_CONFIG_DIR = previous.HAPPIER_SELF_HOST_CONFIG_DIR;
|
|
1026
|
+
if (previous.HAPPIER_SELF_HOST_DATA_DIR === undefined) delete process.env.HAPPIER_SELF_HOST_DATA_DIR;
|
|
1027
|
+
else process.env.HAPPIER_SELF_HOST_DATA_DIR = previous.HAPPIER_SELF_HOST_DATA_DIR;
|
|
1028
|
+
if (previous.HAPPIER_SELF_HOST_LOG_DIR === undefined) delete process.env.HAPPIER_SELF_HOST_LOG_DIR;
|
|
1029
|
+
else process.env.HAPPIER_SELF_HOST_LOG_DIR = previous.HAPPIER_SELF_HOST_LOG_DIR;
|
|
1030
|
+
}
|
|
1031
|
+
});
|
|
1032
|
+
|
|
986
1033
|
test('resolveMinisignPublicKeyText prefers inline override and otherwise returns bundled key', () => {
|
|
987
1034
|
const bundled = resolveMinisignPublicKeyText({});
|
|
988
1035
|
assert.match(bundled, /minisign public key/i);
|
package/scripts/service.mjs
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
import './utils/env/env.mjs';
|
|
2
2
|
import { run, runCapture } from './utils/proc/proc.mjs';
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
getComponentDir,
|
|
5
|
+
getDefaultAutostartPaths,
|
|
6
|
+
getRootDir,
|
|
7
|
+
getSystemdUnitInfo,
|
|
8
|
+
resolveExplicitStackEnvFilePath,
|
|
9
|
+
resolveStackEnvPath,
|
|
10
|
+
} from './utils/paths/paths.mjs';
|
|
4
11
|
import { getInternalServerUrl, getPublicServerUrlEnvOverride } from './utils/server/urls.mjs';
|
|
5
12
|
import { resolveServerUrls } from './utils/server/urls.mjs';
|
|
6
13
|
import { installService as installManagedService, uninstallService as uninstallManagedService } from './utils/service/service_manager.mjs';
|
|
@@ -67,7 +74,7 @@ function getAutostartEnv({ mode, systemUserHomeDir } = {}) {
|
|
|
67
74
|
// Main installs:
|
|
68
75
|
// - default to the main stack env (outside the repo): ~/.happier/stacks/main/env
|
|
69
76
|
|
|
70
|
-
const explicitEnvFilePath =
|
|
77
|
+
const explicitEnvFilePath = resolveExplicitStackEnvFilePath(process.env);
|
|
71
78
|
const defaultMainEnvFilePath = resolveStackEnvPath('main').envPath;
|
|
72
79
|
const envFile = resolveAutostartEnvFilePath({
|
|
73
80
|
mode,
|
|
@@ -546,7 +553,7 @@ async function stopLaunchAgent({ persistent, requestedBy = 'service stop', reaso
|
|
|
546
553
|
|
|
547
554
|
const envFile = resolveAutostartEnvFilePath({
|
|
548
555
|
mode: 'user',
|
|
549
|
-
explicitEnvFilePath:
|
|
556
|
+
explicitEnvFilePath: resolveExplicitStackEnvFilePath(process.env),
|
|
550
557
|
defaultMainEnvFilePath: resolveStackEnvPath('main').envPath,
|
|
551
558
|
systemUserHomeDir: null,
|
|
552
559
|
});
|
package/scripts/stack.mjs
CHANGED
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
getRootDir,
|
|
16
16
|
getWorkspaceDir,
|
|
17
17
|
happyMonorepoSubdirForComponent,
|
|
18
|
+
resolveExplicitStackEnvFilePath,
|
|
18
19
|
resolveStackEnvPath,
|
|
19
20
|
} from './utils/paths/paths.mjs';
|
|
20
21
|
import { isTcpPortFree, pickNextFreeTcpPort } from './utils/net/ports.mjs';
|
|
@@ -1115,7 +1116,7 @@ async function cmdCreateDevAuthSeed({ rootDir, argv }) {
|
|
|
1115
1116
|
serverPort,
|
|
1116
1117
|
internalServerUrl,
|
|
1117
1118
|
publicServerUrl,
|
|
1118
|
-
envPath: env
|
|
1119
|
+
envPath: resolveExplicitStackEnvFilePath(env),
|
|
1119
1120
|
stackMode: true,
|
|
1120
1121
|
runtimeStatePath: null,
|
|
1121
1122
|
serverAlreadyRunning: false,
|
|
@@ -1141,7 +1142,7 @@ async function cmdCreateDevAuthSeed({ rootDir, argv }) {
|
|
|
1141
1142
|
stackMode: true,
|
|
1142
1143
|
runtimeStatePath: null,
|
|
1143
1144
|
stackName: name,
|
|
1144
|
-
envPath: env
|
|
1145
|
+
envPath: resolveExplicitStackEnvFilePath(env),
|
|
1145
1146
|
children,
|
|
1146
1147
|
spawnOptions: quietAuthFlow ? { silent: true, teeFile: expoLogPath, teeLabel: 'expo' } : {},
|
|
1147
1148
|
quiet: quietAuthFlow,
|
package/scripts/tailscale.mjs
CHANGED
|
@@ -4,7 +4,7 @@ import { run } from './utils/proc/proc.mjs';
|
|
|
4
4
|
import { printResult, wantsHelp, wantsJson } from './utils/cli/cli.mjs';
|
|
5
5
|
import { isSandboxed, sandboxAllowsGlobalSideEffects } from './utils/env/sandbox.mjs';
|
|
6
6
|
import { getInternalServerUrl } from './utils/server/urls.mjs';
|
|
7
|
-
import { getStackName, resolveStackEnvPath } from './utils/paths/paths.mjs';
|
|
7
|
+
import { getStackName, resolveExplicitStackEnvFilePath, resolveStackEnvPath } from './utils/paths/paths.mjs';
|
|
8
8
|
import { banner, bullets, cmd as cmdFmt, kv, ok, sectionTitle } from './utils/ui/layout.mjs';
|
|
9
9
|
import { cyan, dim, green } from './utils/ui/ansi.mjs';
|
|
10
10
|
import {
|
|
@@ -352,7 +352,7 @@ async function main() {
|
|
|
352
352
|
);
|
|
353
353
|
}
|
|
354
354
|
const stackName = getStackName(process.env);
|
|
355
|
-
const envPath = (process.env
|
|
355
|
+
const envPath = resolveExplicitStackEnvFilePath(process.env) || resolveStackEnvPath(stackName, process.env).envPath;
|
|
356
356
|
const { upstream } = getServeConfig(internalServerUrl);
|
|
357
357
|
const res = await tailscaleServeEnable({ internalServerUrl });
|
|
358
358
|
if (res?.enableUrl && !res?.httpsUrl) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { join } from 'node:path';
|
|
2
2
|
import { ensureEnvFileUpdated } from './env_file.mjs';
|
|
3
|
-
import { getHappyStacksHomeDir,
|
|
3
|
+
import { getHappyStacksHomeDir, resolveActiveStackEnvFilePath } from '../paths/paths.mjs';
|
|
4
4
|
import { getCanonicalHomeDirFromEnv } from '../paths/canonical_home.mjs';
|
|
5
5
|
|
|
6
6
|
export function getHomeEnvPath() {
|
|
@@ -20,17 +20,12 @@ export function getHomeEnvLocalPath() {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
export function resolveUserConfigEnvPath({ cliRootDir }) {
|
|
23
|
-
const explicit = (process.env.HAPPIER_STACK_ENV_FILE ?? '').trim();
|
|
24
|
-
if (explicit) {
|
|
25
|
-
return explicit;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
23
|
// By default, persist configuration to the main stack env file so config is
|
|
29
24
|
// outside the repo and consistent across install modes.
|
|
30
25
|
//
|
|
31
26
|
// This also matches the stack env precedence in scripts/utils/env.mjs.
|
|
32
27
|
void cliRootDir;
|
|
33
|
-
return
|
|
28
|
+
return resolveActiveStackEnvFilePath('main', process.env);
|
|
34
29
|
}
|
|
35
30
|
|
|
36
31
|
export async function ensureHomeEnvUpdated({ updates }) {
|
|
@@ -3,6 +3,7 @@ import { homedir } from 'node:os';
|
|
|
3
3
|
import { dirname, join } from 'node:path';
|
|
4
4
|
import { fileURLToPath } from 'node:url';
|
|
5
5
|
import { expandHome, getCanonicalHomeEnvPathFromEnv } from '../paths/canonical_home.mjs';
|
|
6
|
+
import { getStacksStorageRoot, resolveExplicitStackEnvFilePath } from '../paths/paths.mjs';
|
|
6
7
|
import { isSandboxed, sandboxAllowsGlobalSideEffects } from './sandbox.mjs';
|
|
7
8
|
import { loadEnvFile, loadEnvFileIgnoringPrefixes } from './load_env_file.mjs';
|
|
8
9
|
|
|
@@ -16,7 +17,7 @@ const __cliRootDir = dirname(__scriptsDir);
|
|
|
16
17
|
function resolveHomeDir() {
|
|
17
18
|
const fromEnv = (process.env.HAPPIER_STACK_HOME_DIR ?? '').trim();
|
|
18
19
|
if (fromEnv) {
|
|
19
|
-
return expandHome(fromEnv);
|
|
20
|
+
return expandHome(fromEnv, process.env);
|
|
20
21
|
}
|
|
21
22
|
return join(homedir(), '.happier-stack');
|
|
22
23
|
}
|
|
@@ -76,13 +77,13 @@ if (hasHomeConfig) {
|
|
|
76
77
|
if ((process.env.HAPPIER_STACK_DISABLE_STACK_ENV_AUTOLOAD ?? '').toString().trim() === '1') {
|
|
77
78
|
return;
|
|
78
79
|
}
|
|
79
|
-
const stacksEnv = (process.env
|
|
80
|
+
const stacksEnv = resolveExplicitStackEnvFilePath(process.env);
|
|
80
81
|
if (stacksEnv) {
|
|
82
|
+
process.env.HAPPIER_STACK_ENV_FILE = stacksEnv;
|
|
81
83
|
return;
|
|
82
84
|
}
|
|
83
85
|
const stackName = (process.env.HAPPIER_STACK_STACK ?? '').trim() || 'main';
|
|
84
|
-
const
|
|
85
|
-
const stacksStorageRoot = stacksStorageRootRaw ? expandHome(stacksStorageRootRaw) : join(homedir(), '.happier', 'stacks');
|
|
86
|
+
const stacksStorageRoot = getStacksStorageRoot(process.env);
|
|
86
87
|
|
|
87
88
|
const candidates = [
|
|
88
89
|
join(stacksStorageRoot, stackName, 'env'),
|
|
@@ -98,7 +99,10 @@ if (hasHomeConfig) {
|
|
|
98
99
|
// Stack env files intentionally include some non-prefixed keys (e.g. DATABASE_URL, HAPPIER_SERVER_LIGHT_DATA_DIR)
|
|
99
100
|
// that must apply for true per-stack isolation. Do not filter by prefix here.
|
|
100
101
|
{
|
|
101
|
-
const stacksEnv =
|
|
102
|
+
const stacksEnv = resolveExplicitStackEnvFilePath(process.env);
|
|
103
|
+
if (stacksEnv) {
|
|
104
|
+
process.env.HAPPIER_STACK_ENV_FILE = stacksEnv;
|
|
105
|
+
}
|
|
102
106
|
const unique = Array.from(new Set([stacksEnv].filter(Boolean)));
|
|
103
107
|
for (const p of unique) {
|
|
104
108
|
// eslint-disable-next-line no-await-in-loop
|
|
@@ -3,13 +3,14 @@ import { join } from 'node:path';
|
|
|
3
3
|
|
|
4
4
|
import { ensureEnvFileUpdated } from './env_file.mjs';
|
|
5
5
|
import { ensureUserConfigEnvUpdated, getHomeEnvLocalPath, getHomeEnvPath } from './config.mjs';
|
|
6
|
+
import { resolveExplicitStackEnvFilePath } from '../paths/paths.mjs';
|
|
6
7
|
|
|
7
8
|
export async function ensureEnvLocalUpdated({ rootDir, updates }) {
|
|
8
9
|
// Behavior:
|
|
9
10
|
// - If a stack env file is explicitly set, write there (stack-scoped).
|
|
10
11
|
// - If the user has run `hstack init` (home config exists), write to the main stack env file (user config).
|
|
11
12
|
// - If no home config exists (legacy cloned-repo usage), write to <repo>/env.local for repo-local behavior.
|
|
12
|
-
const explicit = (process.env
|
|
13
|
+
const explicit = resolveExplicitStackEnvFilePath(process.env);
|
|
13
14
|
if (explicit) {
|
|
14
15
|
await ensureEnvFileUpdated({ envPath: explicit, updates });
|
|
15
16
|
return;
|
|
@@ -1,13 +1,21 @@
|
|
|
1
1
|
import { homedir } from 'node:os';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
function resolveHomeDirFromEnv(env = process.env) {
|
|
5
|
+
const candidate = process.platform === 'win32'
|
|
6
|
+
? (env?.USERPROFILE ?? env?.HOME)
|
|
7
|
+
: env?.HOME;
|
|
8
|
+
const trimmed = String(candidate ?? '').trim();
|
|
9
|
+
return trimmed || homedir();
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function expandHome(p, env = process.env) {
|
|
13
|
+
return String(p ?? '').replace(/^~(?=[/\\])/, resolveHomeDirFromEnv(env));
|
|
6
14
|
}
|
|
7
15
|
|
|
8
16
|
export function getCanonicalHomeDirFromEnv(env = process.env) {
|
|
9
17
|
const fromEnv = (env.HAPPIER_STACK_CANONICAL_HOME_DIR ?? '').trim();
|
|
10
|
-
return fromEnv ? expandHome(fromEnv) : join(
|
|
18
|
+
return fromEnv ? expandHome(fromEnv, env) : join(resolveHomeDirFromEnv(env), '.happier-stack');
|
|
11
19
|
}
|
|
12
20
|
|
|
13
21
|
export function getCanonicalHomeEnvPathFromEnv(env = process.env) {
|
|
@@ -5,10 +5,12 @@ import { homedir } from 'node:os';
|
|
|
5
5
|
import { expandHome, getCanonicalHomeDirFromEnv, getCanonicalHomeEnvPathFromEnv } from './canonical_home.mjs';
|
|
6
6
|
|
|
7
7
|
test('expandHome expands ~/ paths', () => {
|
|
8
|
+
assert.equal(expandHome('~/x', { HOME: '/scoped/home' }), '/scoped/home/x');
|
|
8
9
|
assert.equal(expandHome('~/x'), `${homedir()}/x`);
|
|
9
10
|
});
|
|
10
11
|
|
|
11
12
|
test('expandHome expands ~\\ paths (Windows)', () => {
|
|
13
|
+
assert.equal(expandHome('~\\x', { HOME: '/scoped/home', USERPROFILE: '/scoped/home' }), '/scoped/home\\x');
|
|
12
14
|
assert.equal(expandHome('~\\x'), `${homedir()}\\x`);
|
|
13
15
|
});
|
|
14
16
|
|
|
@@ -18,11 +20,17 @@ test('expandHome leaves non-home-prefixed paths unchanged', () => {
|
|
|
18
20
|
});
|
|
19
21
|
|
|
20
22
|
test('getCanonicalHomeDirFromEnv expands override and defaults to ~/.happier-stack', () => {
|
|
21
|
-
assert.equal(
|
|
23
|
+
assert.equal(
|
|
24
|
+
getCanonicalHomeDirFromEnv({ HOME: '/scoped/home', HAPPIER_STACK_CANONICAL_HOME_DIR: '~/custom-home' }),
|
|
25
|
+
'/scoped/home/custom-home'
|
|
26
|
+
);
|
|
22
27
|
assert.equal(getCanonicalHomeDirFromEnv({}), `${homedir()}/.happier-stack`);
|
|
23
28
|
});
|
|
24
29
|
|
|
25
30
|
test('getCanonicalHomeEnvPathFromEnv resolves .env under canonical home', () => {
|
|
26
|
-
const envPath = getCanonicalHomeEnvPathFromEnv({
|
|
27
|
-
|
|
31
|
+
const envPath = getCanonicalHomeEnvPathFromEnv({
|
|
32
|
+
HOME: '/scoped/home',
|
|
33
|
+
HAPPIER_STACK_CANONICAL_HOME_DIR: '~/custom-home',
|
|
34
|
+
});
|
|
35
|
+
assert.equal(envPath, '/scoped/home/custom-home/.env');
|
|
28
36
|
});
|
|
@@ -108,7 +108,7 @@ export function getRootDir(importMetaUrl) {
|
|
|
108
108
|
export function getHappyStacksHomeDir(env = process.env) {
|
|
109
109
|
const fromEnv = (env.HAPPIER_STACK_HOME_DIR ?? '').trim();
|
|
110
110
|
if (fromEnv) {
|
|
111
|
-
return expandHome(fromEnv);
|
|
111
|
+
return expandHome(fromEnv, env);
|
|
112
112
|
}
|
|
113
113
|
return PRIMARY_HOME_DIR;
|
|
114
114
|
}
|
|
@@ -116,9 +116,9 @@ export function getHappyStacksHomeDir(env = process.env) {
|
|
|
116
116
|
export function getWorkspaceDir(cliRootDir = null, env = process.env) {
|
|
117
117
|
const fromEnv = (env.HAPPIER_STACK_WORKSPACE_DIR ?? '').trim();
|
|
118
118
|
if (fromEnv) {
|
|
119
|
-
return expandHome(fromEnv);
|
|
119
|
+
return expandHome(fromEnv, env);
|
|
120
120
|
}
|
|
121
|
-
const homeDir = getHappyStacksHomeDir();
|
|
121
|
+
const homeDir = getHappyStacksHomeDir(env);
|
|
122
122
|
return join(homeDir, 'workspace');
|
|
123
123
|
}
|
|
124
124
|
|
|
@@ -159,7 +159,7 @@ function normalizePathForEnv(rootDir, raw, env = process.env) {
|
|
|
159
159
|
if (!trimmed) {
|
|
160
160
|
return '';
|
|
161
161
|
}
|
|
162
|
-
const expanded = expandHome(trimmed);
|
|
162
|
+
const expanded = expandHome(trimmed, env);
|
|
163
163
|
// If the path is relative, treat it as relative to the workspace root (default: repo root).
|
|
164
164
|
const workspaceDir = getWorkspaceDir(rootDir, env);
|
|
165
165
|
const abs = isAbsolute(expanded) || isWin32ShapedAbsolutePath(expanded);
|
|
@@ -244,11 +244,19 @@ export function getStackLabel(stackName = null, env = process.env) {
|
|
|
244
244
|
export function getStacksStorageRoot(env = process.env) {
|
|
245
245
|
const fromEnv = (env.HAPPIER_STACK_STORAGE_DIR ?? '').trim();
|
|
246
246
|
if (fromEnv) {
|
|
247
|
-
return expandHome(fromEnv);
|
|
247
|
+
return expandHome(fromEnv, env);
|
|
248
248
|
}
|
|
249
249
|
return PRIMARY_STORAGE_ROOT;
|
|
250
250
|
}
|
|
251
251
|
|
|
252
|
+
export function resolveExplicitStackEnvFilePath(env = process.env) {
|
|
253
|
+
const explicit = (env.HAPPIER_STACK_ENV_FILE ?? '').toString().trim();
|
|
254
|
+
if (!explicit) {
|
|
255
|
+
return '';
|
|
256
|
+
}
|
|
257
|
+
return expandHome(explicit, env);
|
|
258
|
+
}
|
|
259
|
+
|
|
252
260
|
export function resolveStackBaseDir(stackName = null, env = process.env) {
|
|
253
261
|
const name = (stackName ?? '').toString().trim() || getStackName(env);
|
|
254
262
|
return { baseDir: join(getStacksStorageRoot(env), name), isLegacy: false };
|
|
@@ -260,6 +268,10 @@ export function resolveStackEnvPath(stackName = null, env = process.env) {
|
|
|
260
268
|
return { envPath: join(baseDir, 'env'), isLegacy: false, baseDir };
|
|
261
269
|
}
|
|
262
270
|
|
|
271
|
+
export function resolveActiveStackEnvFilePath(stackName = null, env = process.env) {
|
|
272
|
+
return resolveExplicitStackEnvFilePath(env) || resolveStackEnvPath(stackName, env).envPath;
|
|
273
|
+
}
|
|
274
|
+
|
|
263
275
|
export function getDefaultAutostartPaths(env = process.env) {
|
|
264
276
|
const stackName = getStackName(env);
|
|
265
277
|
const { baseDir, isLegacy } = resolveStackBaseDir(stackName, env);
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
getHappyStacksHomeDir,
|
|
7
|
+
getStacksStorageRoot,
|
|
8
|
+
getWorkspaceDir,
|
|
9
|
+
resolveActiveStackEnvFilePath,
|
|
10
|
+
} from './paths.mjs';
|
|
11
|
+
|
|
12
|
+
test('stack path overrides expand ~/ against the provided HOME env', () => {
|
|
13
|
+
const env = {
|
|
14
|
+
HOME: '/scoped/home',
|
|
15
|
+
HAPPIER_STACK_HOME_DIR: '~/.happier-stack-custom',
|
|
16
|
+
HAPPIER_STACK_STORAGE_DIR: '~/.happier/stacks-custom',
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
assert.equal(getHappyStacksHomeDir(env), '/scoped/home/.happier-stack-custom');
|
|
20
|
+
assert.equal(getWorkspaceDir('/tmp/root', env), '/scoped/home/.happier-stack-custom/workspace');
|
|
21
|
+
assert.equal(getStacksStorageRoot(env), '/scoped/home/.happier/stacks-custom');
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test('resolveActiveStackEnvFilePath expands ~/ explicit overrides against the provided HOME env', () => {
|
|
25
|
+
const env = {
|
|
26
|
+
HOME: '/scoped/home',
|
|
27
|
+
HAPPIER_STACK_ENV_FILE: '~/.happier/stacks/dev/env',
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
assert.equal(resolveActiveStackEnvFilePath('dev', env), '/scoped/home/.happier/stacks/dev/env');
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test('resolveActiveStackEnvFilePath falls back to the resolved stack env file when no explicit override is set', () => {
|
|
34
|
+
const env = {
|
|
35
|
+
HOME: '/scoped/home',
|
|
36
|
+
HAPPIER_STACK_STORAGE_DIR: '~/.happier/stacks-custom',
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
assert.equal(
|
|
40
|
+
resolveActiveStackEnvFilePath('dev', env),
|
|
41
|
+
join('/scoped/home/.happier/stacks-custom', 'dev', 'env')
|
|
42
|
+
);
|
|
43
|
+
});
|
|
@@ -8,7 +8,12 @@ import { pathExists } from '../fs/fs.mjs';
|
|
|
8
8
|
import { readJsonIfExists, writeJsonAtomic } from '../fs/json.mjs';
|
|
9
9
|
import { run, runCapture, spawnProc } from './proc.mjs';
|
|
10
10
|
import { commandExists } from './commands.mjs';
|
|
11
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
coerceHappyMonorepoRootFromPath,
|
|
13
|
+
getDefaultAutostartPaths,
|
|
14
|
+
getHappyStacksHomeDir,
|
|
15
|
+
resolveExplicitStackEnvFilePath,
|
|
16
|
+
} from '../paths/paths.mjs';
|
|
12
17
|
import { resolveInstalledPath, resolveInstalledCliRoot } from '../paths/runtime.mjs';
|
|
13
18
|
import { expandHome } from '../paths/canonical_home.mjs';
|
|
14
19
|
import { withCliDistBuildLock } from './cliDistBuildLock.mjs';
|
|
@@ -325,12 +330,12 @@ function resolveStackCacheBaseDirFromEnv(env) {
|
|
|
325
330
|
const explicit = (env.HAPPIER_STACK_PM_CACHE_BASE_DIR ?? '').toString().trim();
|
|
326
331
|
if (explicit) {
|
|
327
332
|
try {
|
|
328
|
-
return resolve(expandHome(explicit));
|
|
333
|
+
return resolve(expandHome(explicit, env));
|
|
329
334
|
} catch {
|
|
330
335
|
return null;
|
|
331
336
|
}
|
|
332
337
|
}
|
|
333
|
-
const envFile = (env
|
|
338
|
+
const envFile = resolveExplicitStackEnvFilePath(env);
|
|
334
339
|
if (!envFile) return null;
|
|
335
340
|
try {
|
|
336
341
|
return join(dirname(envFile), 'cache');
|
|
@@ -367,7 +372,7 @@ export async function applyStackCacheEnv(baseEnv) {
|
|
|
367
372
|
env.NPM_CONFIG_PRODUCTION = 'false';
|
|
368
373
|
}
|
|
369
374
|
|
|
370
|
-
const envFile = (env
|
|
375
|
+
const envFile = resolveExplicitStackEnvFilePath(env);
|
|
371
376
|
const stackCacheBase = resolveStackCacheBaseDirFromEnv(env);
|
|
372
377
|
if (!stackCacheBase) return env;
|
|
373
378
|
|
|
@@ -452,6 +452,34 @@ test('ensureDepsInstalled honors HAPPIER_STACK_PM_CACHE_BASE_DIR when no stack e
|
|
|
452
452
|
assert.equal(parsed.HOME, join(cacheBase, 'home'));
|
|
453
453
|
});
|
|
454
454
|
|
|
455
|
+
test('ensureDepsInstalled derives stack cache roots from ~/ env file overrides against HOME', async (t) => {
|
|
456
|
+
const fixture = await createStackCacheFixture(t, 'hs-pm-tilde-stack-env-file-');
|
|
457
|
+
const { root, componentDir, binDir } = fixture;
|
|
458
|
+
const outputPath = join(root, 'env.json');
|
|
459
|
+
const homeDir = join(root, 'home');
|
|
460
|
+
const expectedCacheBase = join(homeDir, '.happier', 'stacks', 'dev', 'cache');
|
|
461
|
+
const expectedStackHome = join(homeDir, '.happier', 'stacks', 'dev', 'home');
|
|
462
|
+
await writeYarnEnvDumpStub({ binDir, outputPath });
|
|
463
|
+
|
|
464
|
+
applyEnvOverrides(t, {
|
|
465
|
+
PATH: `${binDir}:${process.env.PATH ?? ''}`,
|
|
466
|
+
OUTPUT_PATH: outputPath,
|
|
467
|
+
HOME: homeDir,
|
|
468
|
+
USERPROFILE: homeDir,
|
|
469
|
+
HAPPIER_STACK_ENV_FILE: '~/.happier/stacks/dev/env',
|
|
470
|
+
XDG_CACHE_HOME: null,
|
|
471
|
+
YARN_CACHE_FOLDER: null,
|
|
472
|
+
npm_config_cache: null,
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
await ensureDepsInstalled(componentDir, 'test-component', { quiet: true });
|
|
476
|
+
const parsed = JSON.parse(await readFile(outputPath, 'utf-8'));
|
|
477
|
+
assert.equal(parsed.XDG_CACHE_HOME, join(expectedCacheBase, 'xdg'));
|
|
478
|
+
assert.equal(parsed.YARN_CACHE_FOLDER, join(expectedCacheBase, 'yarn'));
|
|
479
|
+
assert.equal(parsed.npm_config_cache, join(expectedCacheBase, 'npm'));
|
|
480
|
+
assert.equal(parsed.HOME, expectedStackHome);
|
|
481
|
+
});
|
|
482
|
+
|
|
455
483
|
test('ensureDepsInstalled prefers the .nvmrc node runtime for yarn shebangs when available', async (t) => {
|
|
456
484
|
const fixture = await createStackCacheFixture(t, 'hs-pm-nvm-node-runtime-');
|
|
457
485
|
const { root, componentDir, binDir } = fixture;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { getRepoDir } from '../paths/paths.mjs';
|
|
1
|
+
import { getRepoDir, resolveExplicitStackEnvFilePath } from '../paths/paths.mjs';
|
|
2
2
|
|
|
3
3
|
export function isStackMode(env = process.env) {
|
|
4
4
|
const stack = String(env.HAPPIER_STACK_STACK ?? '').trim();
|
|
5
|
-
const envFile =
|
|
5
|
+
const envFile = resolveExplicitStackEnvFilePath(env);
|
|
6
6
|
return Boolean(stack && envFile);
|
|
7
7
|
}
|
|
8
8
|
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import { join } from 'node:path';
|
|
2
|
+
import { expandHome } from '../paths/canonical_home.mjs';
|
|
2
3
|
|
|
3
4
|
export function applyServerLightEnvDefaults({ baseEnv, serverEnv, baseDir }) {
|
|
4
5
|
const dataDir = baseEnv.HAPPIER_SERVER_LIGHT_DATA_DIR?.trim()
|
|
5
|
-
? baseEnv.HAPPIER_SERVER_LIGHT_DATA_DIR.trim()
|
|
6
|
+
? expandHome(baseEnv.HAPPIER_SERVER_LIGHT_DATA_DIR.trim(), baseEnv)
|
|
6
7
|
: join(baseDir, 'server-light');
|
|
7
8
|
serverEnv.HAPPIER_SERVER_LIGHT_DATA_DIR = dataDir;
|
|
8
9
|
serverEnv.HAPPIER_SERVER_LIGHT_FILES_DIR = baseEnv.HAPPIER_SERVER_LIGHT_FILES_DIR?.trim()
|
|
9
|
-
? baseEnv.HAPPIER_SERVER_LIGHT_FILES_DIR.trim()
|
|
10
|
+
? expandHome(baseEnv.HAPPIER_SERVER_LIGHT_FILES_DIR.trim(), baseEnv)
|
|
10
11
|
: join(dataDir, 'files');
|
|
11
12
|
serverEnv.HAPPIER_SERVER_LIGHT_DB_DIR = baseEnv.HAPPIER_SERVER_LIGHT_DB_DIR?.trim()
|
|
12
|
-
? baseEnv.HAPPIER_SERVER_LIGHT_DB_DIR.trim()
|
|
13
|
+
? expandHome(baseEnv.HAPPIER_SERVER_LIGHT_DB_DIR.trim(), baseEnv)
|
|
13
14
|
: join(dataDir, 'pglite');
|
|
14
15
|
}
|