@happier-dev/stack 0.1.0-preview.10.1 → 0.1.0-preview.134.1
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/docs/server-flavors.md +6 -6
- package/node_modules/@happier-dev/cli-common/dist/index.d.ts +2 -0
- package/node_modules/@happier-dev/cli-common/dist/index.d.ts.map +1 -1
- package/node_modules/@happier-dev/cli-common/dist/index.js +2 -0
- package/node_modules/@happier-dev/cli-common/dist/index.js.map +1 -1
- package/node_modules/@happier-dev/cli-common/dist/providers/index.d.ts +51 -0
- package/node_modules/@happier-dev/cli-common/dist/providers/index.d.ts.map +1 -0
- package/node_modules/@happier-dev/cli-common/dist/providers/index.js +129 -0
- package/node_modules/@happier-dev/cli-common/dist/providers/index.js.map +1 -0
- package/node_modules/@happier-dev/cli-common/dist/service/index.d.ts +5 -0
- package/node_modules/@happier-dev/cli-common/dist/service/index.d.ts.map +1 -0
- package/node_modules/@happier-dev/cli-common/dist/service/index.js +5 -0
- package/node_modules/@happier-dev/cli-common/dist/service/index.js.map +1 -0
- package/node_modules/@happier-dev/cli-common/dist/service/launchd.d.ts +15 -0
- package/node_modules/@happier-dev/cli-common/dist/service/launchd.d.ts.map +1 -0
- package/node_modules/@happier-dev/cli-common/dist/service/launchd.js +97 -0
- package/node_modules/@happier-dev/cli-common/dist/service/launchd.js.map +1 -0
- package/node_modules/@happier-dev/cli-common/dist/service/manager.d.ts +55 -0
- package/node_modules/@happier-dev/cli-common/dist/service/manager.d.ts.map +1 -0
- package/node_modules/@happier-dev/cli-common/dist/service/manager.js +302 -0
- package/node_modules/@happier-dev/cli-common/dist/service/manager.js.map +1 -0
- package/node_modules/@happier-dev/cli-common/dist/service/systemd.d.ts +12 -0
- package/node_modules/@happier-dev/cli-common/dist/service/systemd.d.ts.map +1 -0
- package/node_modules/@happier-dev/cli-common/dist/service/systemd.js +75 -0
- package/node_modules/@happier-dev/cli-common/dist/service/systemd.js.map +1 -0
- package/node_modules/@happier-dev/cli-common/dist/service/windows.d.ts +8 -0
- package/node_modules/@happier-dev/cli-common/dist/service/windows.d.ts.map +1 -0
- package/node_modules/@happier-dev/cli-common/dist/service/windows.js +29 -0
- package/node_modules/@happier-dev/cli-common/dist/service/windows.js.map +1 -0
- package/node_modules/@happier-dev/cli-common/package.json +11 -0
- package/node_modules/@happier-dev/release-runtime/dist/assets.d.ts +22 -0
- package/node_modules/@happier-dev/release-runtime/dist/assets.d.ts.map +1 -0
- package/node_modules/@happier-dev/release-runtime/dist/assets.js +44 -0
- package/node_modules/@happier-dev/release-runtime/dist/assets.js.map +1 -0
- package/node_modules/@happier-dev/release-runtime/dist/checksums.d.ts +5 -0
- package/node_modules/@happier-dev/release-runtime/dist/checksums.d.ts.map +1 -0
- package/node_modules/@happier-dev/release-runtime/dist/checksums.js +21 -0
- package/node_modules/@happier-dev/release-runtime/dist/checksums.js.map +1 -0
- package/node_modules/@happier-dev/release-runtime/dist/extractPlan.d.ts +14 -0
- package/node_modules/@happier-dev/release-runtime/dist/extractPlan.d.ts.map +1 -0
- package/node_modules/@happier-dev/release-runtime/dist/extractPlan.js +39 -0
- package/node_modules/@happier-dev/release-runtime/dist/extractPlan.js.map +1 -0
- package/node_modules/@happier-dev/release-runtime/dist/github.d.ts +20 -0
- package/node_modules/@happier-dev/release-runtime/dist/github.d.ts.map +1 -0
- package/node_modules/@happier-dev/release-runtime/dist/github.js +60 -0
- package/node_modules/@happier-dev/release-runtime/dist/github.js.map +1 -0
- package/node_modules/@happier-dev/release-runtime/dist/index.d.ts +7 -0
- package/node_modules/@happier-dev/release-runtime/dist/index.d.ts.map +1 -0
- package/node_modules/@happier-dev/release-runtime/dist/index.js +7 -0
- package/node_modules/@happier-dev/release-runtime/dist/index.js.map +1 -0
- package/node_modules/@happier-dev/release-runtime/dist/minisign.d.ts +7 -0
- package/node_modules/@happier-dev/release-runtime/dist/minisign.d.ts.map +1 -0
- package/node_modules/@happier-dev/release-runtime/dist/minisign.js +92 -0
- package/node_modules/@happier-dev/release-runtime/dist/minisign.js.map +1 -0
- package/node_modules/@happier-dev/release-runtime/dist/verifiedDownload.d.ts +26 -0
- package/node_modules/@happier-dev/release-runtime/dist/verifiedDownload.d.ts.map +1 -0
- package/node_modules/@happier-dev/release-runtime/dist/verifiedDownload.js +53 -0
- package/node_modules/@happier-dev/release-runtime/dist/verifiedDownload.js.map +1 -0
- package/node_modules/@happier-dev/release-runtime/package.json +38 -0
- package/package.json +4 -2
- package/scripts/auth.mjs +3 -2
- package/scripts/auth_copy_from_pglite_lock_in_use.integration.test.mjs +1 -0
- package/scripts/auth_copy_from_runCapture.integration.test.mjs +8 -1
- package/scripts/build.mjs +3 -18
- package/scripts/bundleWorkspaceDeps.mjs +5 -1
- package/scripts/bundleWorkspaceDeps.test.mjs +16 -0
- package/scripts/mobile.mjs +32 -1
- package/scripts/mobile_prebuild_happyDir_defined.test.mjs +47 -0
- package/scripts/mobile_prebuild_sets_rct_metro_port.test.mjs +81 -0
- package/scripts/mobile_run_ios_passes_port.integration.test.mjs +101 -0
- package/scripts/providers_cmd.mjs +262 -0
- package/scripts/release_binary_smoke.integration.test.mjs +25 -6
- package/scripts/remote_cmd.mjs +240 -0
- package/scripts/self_host_daemon.real.integration.test.mjs +296 -0
- package/scripts/self_host_launchd.real.integration.test.mjs +211 -0
- package/scripts/self_host_runtime.mjs +1403 -312
- package/scripts/self_host_runtime.test.mjs +361 -1
- package/scripts/self_host_schtasks.real.integration.test.mjs +217 -0
- package/scripts/self_host_service_e2e_harness.mjs +93 -0
- package/scripts/self_host_systemd.real.integration.test.mjs +8 -86
- package/scripts/service.mjs +156 -26
- package/scripts/stack/command_arguments.mjs +1 -0
- package/scripts/stack/help_text.mjs +2 -0
- package/scripts/stack_daemon_cmd.integration.test.mjs +37 -0
- package/scripts/stack_happy_cmd.integration.test.mjs +36 -0
- package/scripts/utils/auth/credentials_paths.mjs +9 -9
- package/scripts/utils/auth/credentials_paths.test.mjs +8 -0
- package/scripts/utils/auth/stable_scope_id.mjs +1 -1
- package/scripts/utils/cli/cli_registry.mjs +18 -0
- package/scripts/utils/cli/progress.mjs +8 -1
- package/scripts/utils/cli/progress.test.mjs +43 -0
- package/scripts/utils/dev/expo_dev.buildEnv.test.mjs +17 -0
- package/scripts/utils/dev/expo_dev.mjs +35 -5
- package/scripts/utils/dev/expo_dev_restart_port_reservation.test.mjs +180 -1
- package/scripts/utils/dev/expo_dev_runtime_metadata.test.mjs +126 -0
- package/scripts/utils/dev/expo_dev_verbose_logs.test.mjs +9 -2
- package/scripts/utils/server/port.mjs +20 -2
- package/scripts/utils/service/service_manager.definition.test.mjs +66 -0
- package/scripts/utils/service/service_manager.mjs +96 -0
- package/scripts/utils/service/service_manager.plan.test.mjs +37 -0
- package/scripts/utils/service/service_manager.test.mjs +20 -0
- package/scripts/utils/service/systemd_service_unit.mjs +1 -0
- package/scripts/utils/service/systemd_service_unit.test.mjs +42 -0
- package/scripts/utils/service/windows_schtasks_wrapper.mjs +1 -0
- package/scripts/utils/service/windows_schtasks_wrapper.test.mjs +25 -0
- package/scripts/utils/ui/ui_export_env.mjs +29 -0
- package/scripts/utils/ui/ui_export_env.test.mjs +25 -0
- package/scripts/worktrees.mjs +3 -0
- package/scripts/worktrees_status_default_target.test.mjs +56 -0
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import test from 'node:test';
|
|
3
|
+
|
|
4
|
+
import { buildServiceDefinition } from './service_manager.mjs';
|
|
5
|
+
|
|
6
|
+
test('buildServiceDefinition renders a systemd user unit definition', () => {
|
|
7
|
+
const def = buildServiceDefinition({
|
|
8
|
+
backend: 'systemd-user',
|
|
9
|
+
homeDir: '/home/me',
|
|
10
|
+
spec: {
|
|
11
|
+
label: 'dev.happier.selfhost',
|
|
12
|
+
description: 'Happier Self-Host',
|
|
13
|
+
programArgs: ['/home/me/.happier/self-host/bin/happier-server'],
|
|
14
|
+
workingDirectory: '/home/me/.happier/self-host',
|
|
15
|
+
env: { PORT: '3005' },
|
|
16
|
+
stdoutPath: '/home/me/.happier/self-host/logs/server.out.log',
|
|
17
|
+
stderrPath: '/home/me/.happier/self-host/logs/server.err.log',
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
assert.equal(def.kind, 'systemd-service');
|
|
22
|
+
assert.equal(def.path, '/home/me/.config/systemd/user/dev.happier.selfhost.service');
|
|
23
|
+
assert.match(def.contents, /ExecStart=\/home\/me\/\.happier\/self-host\/bin\/happier-server/);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test('buildServiceDefinition renders a launchd user plist definition', () => {
|
|
27
|
+
const def = buildServiceDefinition({
|
|
28
|
+
backend: 'launchd-user',
|
|
29
|
+
homeDir: '/Users/me',
|
|
30
|
+
spec: {
|
|
31
|
+
label: 'dev.happier.selfhost',
|
|
32
|
+
description: 'Happier Self-Host',
|
|
33
|
+
programArgs: ['/Users/me/.happier/self-host/bin/happier-server'],
|
|
34
|
+
workingDirectory: '/Users/me/.happier/self-host',
|
|
35
|
+
env: { PORT: '3005' },
|
|
36
|
+
stdoutPath: '/Users/me/.happier/self-host/logs/server.out.log',
|
|
37
|
+
stderrPath: '/Users/me/.happier/self-host/logs/server.err.log',
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
assert.equal(def.kind, 'launchd-plist');
|
|
42
|
+
assert.equal(def.path, '/Users/me/Library/LaunchAgents/dev.happier.selfhost.plist');
|
|
43
|
+
assert.match(def.contents, /<key>Label<\/key>/);
|
|
44
|
+
assert.match(def.contents, /dev\.happier\.selfhost/);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test('buildServiceDefinition renders a windows scheduled-task wrapper', () => {
|
|
48
|
+
const def = buildServiceDefinition({
|
|
49
|
+
backend: 'schtasks-user',
|
|
50
|
+
homeDir: 'C:\\\\Users\\\\me',
|
|
51
|
+
spec: {
|
|
52
|
+
label: 'dev.happier.selfhost',
|
|
53
|
+
description: 'Happier Self-Host',
|
|
54
|
+
programArgs: ['C:\\\\Users\\\\me\\\\.happier\\\\self-host\\\\bin\\\\happier-server.exe'],
|
|
55
|
+
workingDirectory: 'C:\\\\Users\\\\me\\\\.happier\\\\self-host',
|
|
56
|
+
env: { PORT: '3005' },
|
|
57
|
+
stdoutPath: 'C:\\\\Users\\\\me\\\\.happier\\\\self-host\\\\logs\\\\server.out.log',
|
|
58
|
+
stderrPath: 'C:\\\\Users\\\\me\\\\.happier\\\\self-host\\\\logs\\\\server.err.log',
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
assert.equal(def.kind, 'windows-wrapper-ps1');
|
|
63
|
+
assert.match(def.path, /dev\.happier\.selfhost\.ps1$/);
|
|
64
|
+
assert.match(def.contents, /\$env:PORT = "3005"/);
|
|
65
|
+
});
|
|
66
|
+
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { rm } from 'node:fs/promises';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
resolveServiceBackend,
|
|
5
|
+
buildServiceDefinition,
|
|
6
|
+
planServiceAction,
|
|
7
|
+
applyServicePlan,
|
|
8
|
+
} from '@happier-dev/cli-common/service';
|
|
9
|
+
|
|
10
|
+
export { resolveServiceBackend, buildServiceDefinition, planServiceAction };
|
|
11
|
+
|
|
12
|
+
function normalizeLabel(spec) {
|
|
13
|
+
const label = String(spec?.label ?? '').trim();
|
|
14
|
+
if (!label) throw new Error('[service] missing label');
|
|
15
|
+
return label;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function taskNameFor({ backend, label }) {
|
|
19
|
+
if (String(backend).startsWith('schtasks-')) {
|
|
20
|
+
return `Happier\\${label}`;
|
|
21
|
+
}
|
|
22
|
+
return '';
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export async function installService({
|
|
26
|
+
platform = process.platform,
|
|
27
|
+
mode = 'user',
|
|
28
|
+
homeDir = '',
|
|
29
|
+
spec,
|
|
30
|
+
persistent = true,
|
|
31
|
+
uid = null,
|
|
32
|
+
} = {}) {
|
|
33
|
+
const label = normalizeLabel(spec);
|
|
34
|
+
const backend = resolveServiceBackend({ platform, mode });
|
|
35
|
+
const definition = buildServiceDefinition({ backend, homeDir, spec });
|
|
36
|
+
const taskName = taskNameFor({ backend, label });
|
|
37
|
+
|
|
38
|
+
const plan = planServiceAction({
|
|
39
|
+
backend,
|
|
40
|
+
action: 'install',
|
|
41
|
+
label,
|
|
42
|
+
definitionPath: definition.path,
|
|
43
|
+
definitionContents: definition.contents,
|
|
44
|
+
taskName,
|
|
45
|
+
persistent,
|
|
46
|
+
uid,
|
|
47
|
+
});
|
|
48
|
+
await applyServicePlan(plan);
|
|
49
|
+
return { backend, definitionPath: definition.path, taskName: taskName || null };
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export async function uninstallService({
|
|
53
|
+
platform = process.platform,
|
|
54
|
+
mode = 'user',
|
|
55
|
+
homeDir = '',
|
|
56
|
+
spec,
|
|
57
|
+
persistent = true,
|
|
58
|
+
uid = null,
|
|
59
|
+
} = {}) {
|
|
60
|
+
const label = normalizeLabel(spec);
|
|
61
|
+
const backend = resolveServiceBackend({ platform, mode });
|
|
62
|
+
const definition = buildServiceDefinition({ backend, homeDir, spec });
|
|
63
|
+
const taskName = taskNameFor({ backend, label });
|
|
64
|
+
|
|
65
|
+
const plan = planServiceAction({
|
|
66
|
+
backend,
|
|
67
|
+
action: 'uninstall',
|
|
68
|
+
label,
|
|
69
|
+
definitionPath: definition.path,
|
|
70
|
+
definitionContents: definition.contents,
|
|
71
|
+
taskName,
|
|
72
|
+
persistent,
|
|
73
|
+
uid,
|
|
74
|
+
});
|
|
75
|
+
await applyServicePlan(plan);
|
|
76
|
+
await rm(definition.path, { force: true }).catch(() => {});
|
|
77
|
+
return { backend, taskName: taskName || null };
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export async function restartService({ platform = process.platform, mode = 'user', spec, persistent = true, uid = null } = {}) {
|
|
81
|
+
const label = normalizeLabel(spec);
|
|
82
|
+
const backend = resolveServiceBackend({ platform, mode });
|
|
83
|
+
const taskName = taskNameFor({ backend, label });
|
|
84
|
+
|
|
85
|
+
const plan = planServiceAction({
|
|
86
|
+
backend,
|
|
87
|
+
action: 'restart',
|
|
88
|
+
label,
|
|
89
|
+
taskName,
|
|
90
|
+
persistent,
|
|
91
|
+
uid,
|
|
92
|
+
});
|
|
93
|
+
await applyServicePlan(plan);
|
|
94
|
+
return { backend };
|
|
95
|
+
}
|
|
96
|
+
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import test from 'node:test';
|
|
3
|
+
|
|
4
|
+
import { planServiceAction } from './service_manager.mjs';
|
|
5
|
+
|
|
6
|
+
test('planServiceAction plans a systemd user install', () => {
|
|
7
|
+
const plan = planServiceAction({
|
|
8
|
+
backend: 'systemd-user',
|
|
9
|
+
action: 'install',
|
|
10
|
+
label: 'dev.happier.selfhost',
|
|
11
|
+
definitionPath: '/home/me/.config/systemd/user/dev.happier.selfhost.service',
|
|
12
|
+
definitionContents: '[Unit]\nDescription=x\n',
|
|
13
|
+
persistent: true,
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
assert.equal(plan.writes.length, 1);
|
|
17
|
+
assert.equal(plan.writes[0].path, '/home/me/.config/systemd/user/dev.happier.selfhost.service');
|
|
18
|
+
assert.ok(plan.commands.some((c) => c.cmd === 'systemctl' && c.args.includes('--user') && c.args.includes('daemon-reload')));
|
|
19
|
+
assert.ok(plan.commands.some((c) => c.cmd === 'systemctl' && c.args.includes('--user') && c.args.includes('enable')));
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
test('planServiceAction plans a windows task install', () => {
|
|
23
|
+
const plan = planServiceAction({
|
|
24
|
+
backend: 'schtasks-user',
|
|
25
|
+
action: 'install',
|
|
26
|
+
label: 'dev.happier.selfhost',
|
|
27
|
+
taskName: 'Happier\\dev.happier.selfhost',
|
|
28
|
+
definitionPath: 'C:\\\\Users\\\\me\\\\.happier\\\\services\\\\dev.happier.selfhost.ps1',
|
|
29
|
+
definitionContents: 'Set-Location -LiteralPath "C:\\\\Users\\\\me"',
|
|
30
|
+
persistent: true,
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
assert.equal(plan.writes.length, 1);
|
|
34
|
+
assert.ok(plan.commands.some((c) => c.cmd === 'schtasks' && c.args.includes('/Create')));
|
|
35
|
+
assert.ok(plan.commands.some((c) => c.cmd === 'schtasks' && c.args.includes('/Run')));
|
|
36
|
+
});
|
|
37
|
+
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import test from 'node:test';
|
|
3
|
+
|
|
4
|
+
import { resolveServiceBackend } from './service_manager.mjs';
|
|
5
|
+
|
|
6
|
+
test('resolveServiceBackend selects launchd for darwin', () => {
|
|
7
|
+
assert.equal(resolveServiceBackend({ platform: 'darwin', mode: 'user' }), 'launchd-user');
|
|
8
|
+
assert.equal(resolveServiceBackend({ platform: 'darwin', mode: 'system' }), 'launchd-system');
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
test('resolveServiceBackend selects systemd for linux', () => {
|
|
12
|
+
assert.equal(resolveServiceBackend({ platform: 'linux', mode: 'user' }), 'systemd-user');
|
|
13
|
+
assert.equal(resolveServiceBackend({ platform: 'linux', mode: 'system' }), 'systemd-system');
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
test('resolveServiceBackend selects schtasks for windows', () => {
|
|
17
|
+
assert.equal(resolveServiceBackend({ platform: 'win32', mode: 'user' }), 'schtasks-user');
|
|
18
|
+
assert.equal(resolveServiceBackend({ platform: 'win32', mode: 'system' }), 'schtasks-system');
|
|
19
|
+
});
|
|
20
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { renderSystemdServiceUnit } from '@happier-dev/cli-common/service';
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import test from 'node:test';
|
|
3
|
+
|
|
4
|
+
import { renderSystemdServiceUnit } from './systemd_service_unit.mjs';
|
|
5
|
+
|
|
6
|
+
test('renderSystemdServiceUnit emits a minimal systemd unit with env + working dir', () => {
|
|
7
|
+
const unit = renderSystemdServiceUnit({
|
|
8
|
+
description: 'Happier Self-Host',
|
|
9
|
+
execStart: '/home/me/.happier/self-host/bin/happier-server',
|
|
10
|
+
workingDirectory: '/home/me/.happier/self-host',
|
|
11
|
+
env: {
|
|
12
|
+
PORT: '3005',
|
|
13
|
+
HAPPIER_DB_PROVIDER: 'sqlite',
|
|
14
|
+
},
|
|
15
|
+
restart: 'on-failure',
|
|
16
|
+
stdoutPath: '/home/me/.happier/self-host/logs/server.out.log',
|
|
17
|
+
stderrPath: '/home/me/.happier/self-host/logs/server.err.log',
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
assert.match(unit, /^\[Unit\]/m);
|
|
21
|
+
assert.match(unit, /Description=Happier Self-Host/);
|
|
22
|
+
assert.match(unit, /WorkingDirectory=\/home\/me\/\.happier\/self-host/);
|
|
23
|
+
assert.match(unit, /ExecStart=\/home\/me\/\.happier\/self-host\/bin\/happier-server/);
|
|
24
|
+
assert.match(unit, /Environment=PORT=3005/);
|
|
25
|
+
assert.match(unit, /Environment=HAPPIER_DB_PROVIDER=sqlite/);
|
|
26
|
+
assert.match(unit, /Restart=on-failure/);
|
|
27
|
+
assert.match(unit, /StandardOutput=append:\/home\/me\/\.happier\/self-host\/logs\/server\.out\.log/);
|
|
28
|
+
assert.match(unit, /StandardError=append:\/home\/me\/\.happier\/self-host\/logs\/server\.err\.log/);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test('renderSystemdServiceUnit supports system mode User= lines', () => {
|
|
32
|
+
const unit = renderSystemdServiceUnit({
|
|
33
|
+
description: 'Happier Stack',
|
|
34
|
+
execStart: '/opt/happier/bin/hstack start',
|
|
35
|
+
workingDirectory: '%h',
|
|
36
|
+
env: { HAPPIER_STACK_ENV_FILE: '%h/.happier/stacks/main/env' },
|
|
37
|
+
restart: 'always',
|
|
38
|
+
runAsUser: 'happier',
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
assert.match(unit, /\nUser=happier\n/);
|
|
42
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { renderWindowsScheduledTaskWrapperPs1 } from '@happier-dev/cli-common/service';
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import test from 'node:test';
|
|
3
|
+
|
|
4
|
+
import { renderWindowsScheduledTaskWrapperPs1 } from './windows_schtasks_wrapper.mjs';
|
|
5
|
+
|
|
6
|
+
test('renderWindowsScheduledTaskWrapperPs1 emits a wrapper that sets env and runs program args', () => {
|
|
7
|
+
const ps1 = renderWindowsScheduledTaskWrapperPs1({
|
|
8
|
+
workingDirectory: 'C:\\\\Users\\\\me\\\\.happier\\\\self-host',
|
|
9
|
+
programArgs: ['C:\\\\Users\\\\me\\\\.happier\\\\self-host\\\\bin\\\\happier-server.exe'],
|
|
10
|
+
env: {
|
|
11
|
+
PORT: '3005',
|
|
12
|
+
HAPPIER_DB_PROVIDER: 'sqlite',
|
|
13
|
+
},
|
|
14
|
+
stdoutPath: 'C:\\\\Users\\\\me\\\\.happier\\\\self-host\\\\logs\\\\server.out.log',
|
|
15
|
+
stderrPath: 'C:\\\\Users\\\\me\\\\.happier\\\\self-host\\\\logs\\\\server.err.log',
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
assert.match(ps1, /Set-Location -LiteralPath/);
|
|
19
|
+
assert.match(ps1, /\$env:PORT = "3005"/);
|
|
20
|
+
assert.match(ps1, /\$env:HAPPIER_DB_PROVIDER = "sqlite"/);
|
|
21
|
+
assert.match(ps1, /& "C:\\\\Users\\\\me\\\\.happier\\\\self-host\\\\bin\\\\happier-server\.exe"/);
|
|
22
|
+
assert.match(ps1, /1>> "C:\\\\Users\\\\me\\\\.happier\\\\self-host\\\\logs\\\\server\.out\.log"/);
|
|
23
|
+
assert.match(ps1, /2>> "C:\\\\Users\\\\me\\\\.happier\\\\self-host\\\\logs\\\\server\.err\.log"/);
|
|
24
|
+
});
|
|
25
|
+
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export function buildStackWebExportEnv({ baseEnv } = {}) {
|
|
2
|
+
const env = { ...(baseEnv || process.env) };
|
|
3
|
+
|
|
4
|
+
// This is a stack-built bundle (served by the stack server), so ensure the app
|
|
5
|
+
// behaves like stack context at runtime (no Cloud seeding/locking).
|
|
6
|
+
env.NODE_ENV = 'production';
|
|
7
|
+
env.EXPO_PUBLIC_DEBUG = '0';
|
|
8
|
+
env.EXPO_PUBLIC_HAPPY_SERVER_CONTEXT = 'stack';
|
|
9
|
+
|
|
10
|
+
// Leave empty so the web bundle uses window.location.origin at runtime.
|
|
11
|
+
env.EXPO_PUBLIC_HAPPY_SERVER_URL = '';
|
|
12
|
+
|
|
13
|
+
return env;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function buildStackTauriExportEnv({ baseEnv, tauriServerUrl } = {}) {
|
|
17
|
+
const env = { ...(baseEnv || process.env) };
|
|
18
|
+
|
|
19
|
+
env.NODE_ENV = 'production';
|
|
20
|
+
env.EXPO_PUBLIC_DEBUG = '0';
|
|
21
|
+
env.EXPO_PUBLIC_HAPPY_SERVER_CONTEXT = 'stack';
|
|
22
|
+
|
|
23
|
+
// In Tauri, window.location.origin is a tauri:// origin, so hardcode the API base.
|
|
24
|
+
env.EXPO_PUBLIC_HAPPY_SERVER_URL = String(tauriServerUrl || '').trim();
|
|
25
|
+
env.EXPO_PUBLIC_SERVER_URL = env.EXPO_PUBLIC_HAPPY_SERVER_URL;
|
|
26
|
+
|
|
27
|
+
return env;
|
|
28
|
+
}
|
|
29
|
+
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
|
|
4
|
+
import { buildStackTauriExportEnv, buildStackWebExportEnv } from './ui_export_env.mjs';
|
|
5
|
+
|
|
6
|
+
test('buildStackWebExportEnv forces stack server context and leaves server URL empty for runtime origin', () => {
|
|
7
|
+
const env = buildStackWebExportEnv({ baseEnv: { ...process.env } });
|
|
8
|
+
|
|
9
|
+
assert.equal(env.NODE_ENV, 'production');
|
|
10
|
+
assert.equal(env.EXPO_PUBLIC_DEBUG, '0');
|
|
11
|
+
assert.equal(env.EXPO_PUBLIC_HAPPY_SERVER_CONTEXT, 'stack');
|
|
12
|
+
assert.equal(env.EXPO_PUBLIC_HAPPY_SERVER_URL, '');
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
test('buildStackTauriExportEnv forces stack server context and hardcodes API base URL', () => {
|
|
16
|
+
const tauriServerUrl = 'http://127.0.0.1:3013';
|
|
17
|
+
const env = buildStackTauriExportEnv({ baseEnv: { ...process.env }, tauriServerUrl });
|
|
18
|
+
|
|
19
|
+
assert.equal(env.NODE_ENV, 'production');
|
|
20
|
+
assert.equal(env.EXPO_PUBLIC_DEBUG, '0');
|
|
21
|
+
assert.equal(env.EXPO_PUBLIC_HAPPY_SERVER_CONTEXT, 'stack');
|
|
22
|
+
assert.equal(env.EXPO_PUBLIC_HAPPY_SERVER_URL, tauriServerUrl);
|
|
23
|
+
assert.equal(env.EXPO_PUBLIC_SERVER_URL, tauriServerUrl);
|
|
24
|
+
});
|
|
25
|
+
|
package/scripts/worktrees.mjs
CHANGED
|
@@ -1981,6 +1981,9 @@ async function main() {
|
|
|
1981
1981
|
const effectiveArgv = (() => {
|
|
1982
1982
|
if (!commandsNeedingComponent.has(cmd)) return argv;
|
|
1983
1983
|
const pos = argv.filter((a) => !a.startsWith('--'));
|
|
1984
|
+
// Keep no-arg invocations untouched so handlers can apply their own defaults
|
|
1985
|
+
// (for example: `wt status` should target the active repo, not a legacy component token).
|
|
1986
|
+
if (pos.length <= 1) return argv;
|
|
1984
1987
|
const maybeComponent = (pos[1] ?? '').trim();
|
|
1985
1988
|
if (legacyComponents.has(maybeComponent)) return argv;
|
|
1986
1989
|
// Insert the default component right after the command; legacy command handlers keep working.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { mkdtemp, mkdir, rm, writeFile } from 'node:fs/promises';
|
|
4
|
+
import { tmpdir } from 'node:os';
|
|
5
|
+
import { dirname, join } from 'node:path';
|
|
6
|
+
import { fileURLToPath } from 'node:url';
|
|
7
|
+
|
|
8
|
+
import { runCommandCapture, runNodeCapture } from './testkit/stack_script_command_testkit.mjs';
|
|
9
|
+
|
|
10
|
+
async function git(dir, args, env) {
|
|
11
|
+
const res = await runCommandCapture('git', args, { cwd: dir, env });
|
|
12
|
+
assert.equal(res.code, 0, `git ${args.join(' ')} failed\nstdout:\n${res.stdout}\nstderr:\n${res.stderr}`);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
test('hstack wt status defaults to active repo dir when no worktree spec is provided', async (t) => {
|
|
16
|
+
const scriptsDir = dirname(fileURLToPath(import.meta.url));
|
|
17
|
+
const rootDir = dirname(scriptsDir);
|
|
18
|
+
const tmp = await mkdtemp(join(tmpdir(), 'happy-stacks-wt-status-default-'));
|
|
19
|
+
t.after(async () => {
|
|
20
|
+
await rm(tmp, { recursive: true, force: true }).catch(() => {});
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
const workspaceDir = join(tmp, 'workspace');
|
|
24
|
+
const homeDir = join(tmp, 'home');
|
|
25
|
+
const repoDir = join(workspaceDir, 'dev');
|
|
26
|
+
await mkdir(repoDir, { recursive: true });
|
|
27
|
+
|
|
28
|
+
const gitEnv = {
|
|
29
|
+
...process.env,
|
|
30
|
+
GIT_AUTHOR_NAME: 'QA',
|
|
31
|
+
GIT_AUTHOR_EMAIL: 'qa@example.com',
|
|
32
|
+
GIT_COMMITTER_NAME: 'QA',
|
|
33
|
+
GIT_COMMITTER_EMAIL: 'qa@example.com',
|
|
34
|
+
};
|
|
35
|
+
await git(repoDir, ['init'], gitEnv);
|
|
36
|
+
await writeFile(join(repoDir, 'README.md'), '# repo\n', 'utf-8');
|
|
37
|
+
await git(repoDir, ['add', 'README.md'], gitEnv);
|
|
38
|
+
await git(repoDir, ['commit', '-m', 'init'], gitEnv);
|
|
39
|
+
|
|
40
|
+
const env = {
|
|
41
|
+
...process.env,
|
|
42
|
+
HAPPIER_STACK_HOME_DIR: homeDir,
|
|
43
|
+
HAPPIER_STACK_WORKSPACE_DIR: workspaceDir,
|
|
44
|
+
HAPPIER_STACK_REPO_DIR: repoDir,
|
|
45
|
+
HAPPIER_STACK_OWNER: 'test',
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const res = await runNodeCapture([join(rootDir, 'scripts', 'worktrees.mjs'), 'status', '--json'], {
|
|
49
|
+
cwd: rootDir,
|
|
50
|
+
env,
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
assert.equal(res.code, 0, `expected exit 0, got ${res.code}\nstdout:\n${res.stdout}\nstderr:\n${res.stderr}`);
|
|
54
|
+
const parsed = JSON.parse(res.stdout);
|
|
55
|
+
assert.equal(parsed.dir, repoDir);
|
|
56
|
+
});
|