@happier-dev/stack 0.1.0-preview.5.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/README.md +501 -0
- package/bin/hstack.mjs +348 -0
- package/docs/codex-mcp-resume.md +129 -0
- package/docs/edison.md +74 -0
- package/docs/forking-and-branding.md +189 -0
- package/docs/happy-development.md +22 -0
- package/docs/isolated-linux-vm.md +243 -0
- package/docs/menubar.md +244 -0
- package/docs/mobile-ios.md +322 -0
- package/docs/monorepo-migration.md +20 -0
- package/docs/paths-and-env.md +154 -0
- package/docs/remote-access.md +43 -0
- package/docs/server-flavors.md +147 -0
- package/docs/stacks.md +330 -0
- package/docs/tauri.md +60 -0
- package/docs/worktrees-and-forks.md +133 -0
- package/extras/swiftbar/auth-login.sh +29 -0
- package/extras/swiftbar/git-cache-refresh.sh +122 -0
- package/extras/swiftbar/hstack-term.sh +133 -0
- package/extras/swiftbar/hstack.5s.sh +296 -0
- package/extras/swiftbar/hstack.sh +35 -0
- package/extras/swiftbar/icons/happy-green.png +0 -0
- package/extras/swiftbar/icons/happy-orange.png +0 -0
- package/extras/swiftbar/icons/happy-red.png +0 -0
- package/extras/swiftbar/icons/logo-white.png +0 -0
- package/extras/swiftbar/install.sh +265 -0
- package/extras/swiftbar/lib/git.sh +629 -0
- package/extras/swiftbar/lib/icons.sh +92 -0
- package/extras/swiftbar/lib/render.sh +999 -0
- package/extras/swiftbar/lib/system.sh +244 -0
- package/extras/swiftbar/lib/utils.sh +717 -0
- package/extras/swiftbar/set-interval.sh +65 -0
- package/extras/swiftbar/set-server-flavor.sh +61 -0
- package/extras/swiftbar/wt-pr.sh +140 -0
- package/node_modules/@happier-dev/cli-common/README.md +6 -0
- package/node_modules/@happier-dev/cli-common/dist/index.d.ts +4 -0
- package/node_modules/@happier-dev/cli-common/dist/index.d.ts.map +1 -0
- package/node_modules/@happier-dev/cli-common/dist/index.js +4 -0
- package/node_modules/@happier-dev/cli-common/dist/index.js.map +1 -0
- package/node_modules/@happier-dev/cli-common/dist/links/index.d.ts +18 -0
- package/node_modules/@happier-dev/cli-common/dist/links/index.d.ts.map +1 -0
- package/node_modules/@happier-dev/cli-common/dist/links/index.js +25 -0
- package/node_modules/@happier-dev/cli-common/dist/links/index.js.map +1 -0
- package/node_modules/@happier-dev/cli-common/dist/links.d.ts +2 -0
- package/node_modules/@happier-dev/cli-common/dist/links.d.ts.map +1 -0
- package/node_modules/@happier-dev/cli-common/dist/links.js +2 -0
- package/node_modules/@happier-dev/cli-common/dist/links.js.map +1 -0
- package/node_modules/@happier-dev/cli-common/dist/update/index.d.ts +67 -0
- package/node_modules/@happier-dev/cli-common/dist/update/index.d.ts.map +1 -0
- package/node_modules/@happier-dev/cli-common/dist/update/index.js +259 -0
- package/node_modules/@happier-dev/cli-common/dist/update/index.js.map +1 -0
- package/node_modules/@happier-dev/cli-common/dist/workspaces/index.d.ts +17 -0
- package/node_modules/@happier-dev/cli-common/dist/workspaces/index.d.ts.map +1 -0
- package/node_modules/@happier-dev/cli-common/dist/workspaces/index.js +80 -0
- package/node_modules/@happier-dev/cli-common/dist/workspaces/index.js.map +1 -0
- package/node_modules/@happier-dev/cli-common/package.json +26 -0
- package/package.json +77 -0
- package/scripts/auth.mjs +1829 -0
- package/scripts/auth_copy_from_pglite_lock_in_use.integration.test.mjs +90 -0
- package/scripts/auth_copy_from_runCapture.integration.test.mjs +447 -0
- package/scripts/auth_help_cmd.test.mjs +28 -0
- package/scripts/auth_login_flow_in_tty.test.mjs +100 -0
- package/scripts/auth_login_force_default.test.mjs +66 -0
- package/scripts/auth_login_guided_server_no_expo.test.mjs +126 -0
- package/scripts/auth_login_method_override.test.mjs +67 -0
- package/scripts/auth_login_print_includes_configure_links.test.mjs +99 -0
- package/scripts/auth_status_server_validation.integration.test.mjs +140 -0
- package/scripts/build.mjs +266 -0
- package/scripts/bundleWorkspaceDeps.mjs +38 -0
- package/scripts/bundleWorkspaceDeps.test.mjs +77 -0
- package/scripts/ci.mjs +135 -0
- package/scripts/ci.test.mjs +50 -0
- package/scripts/cli-link.mjs +57 -0
- package/scripts/completion.mjs +395 -0
- package/scripts/contrib.mjs +333 -0
- package/scripts/daemon.mjs +1160 -0
- package/scripts/daemon.status_scope.test.mjs +51 -0
- package/scripts/daemon_cmd.mjs +26 -0
- package/scripts/daemon_dist_guard.test.mjs +171 -0
- package/scripts/daemon_invalid_auth_reseed_stack_name.integration.test.mjs +608 -0
- package/scripts/daemon_server_scoped_state.test.mjs +49 -0
- package/scripts/daemon_start_verification.integration.test.mjs +296 -0
- package/scripts/dev.mjs +545 -0
- package/scripts/doctor.mjs +340 -0
- package/scripts/doctor_cmd.test.mjs +22 -0
- package/scripts/doctor_ui_index_missing.test.mjs +37 -0
- package/scripts/eas.mjs +367 -0
- package/scripts/eas_platform_parsing.test.mjs +63 -0
- package/scripts/edison.mjs +1848 -0
- package/scripts/env.mjs +149 -0
- package/scripts/env_cmd.test.mjs +118 -0
- package/scripts/exit_cleanup_kills_detached_children_on_crash.integration.test.mjs +80 -0
- package/scripts/happier.mjs +82 -0
- package/scripts/import.mjs +1327 -0
- package/scripts/init.mjs +464 -0
- package/scripts/install.mjs +550 -0
- package/scripts/lint.mjs +177 -0
- package/scripts/menubar.mjs +202 -0
- package/scripts/migrate.mjs +318 -0
- package/scripts/mobile.mjs +353 -0
- package/scripts/mobile_dev_client.mjs +87 -0
- package/scripts/monorepo.mjs +2234 -0
- package/scripts/monorepo_port.apply.integration.test.mjs +680 -0
- package/scripts/monorepo_port.conflicts.integration.test.mjs +454 -0
- package/scripts/monorepo_port.validation.integration.test.mjs +486 -0
- package/scripts/orchestrated_stack_auth_flow.test.mjs +134 -0
- package/scripts/orchestrated_stack_auth_flow_resolve_port.test.mjs +98 -0
- package/scripts/orchestrated_stack_auth_flow_webapp_url.test.mjs +119 -0
- package/scripts/pack.mjs +257 -0
- package/scripts/pack.test.mjs +68 -0
- package/scripts/pglite_lock.integration.test.mjs +152 -0
- package/scripts/provision/linux-ubuntu-e2e.sh +132 -0
- package/scripts/provision/linux-ubuntu-review-pr.sh +66 -0
- package/scripts/provision/macos-lima-happy-vm.sh +192 -0
- package/scripts/provision/macos-lima-hstack-e2e.sh +100 -0
- package/scripts/release.mjs +53 -0
- package/scripts/release_binary_smoke.integration.test.mjs +159 -0
- package/scripts/review.mjs +1752 -0
- package/scripts/review_pr.mjs +435 -0
- package/scripts/run.mjs +561 -0
- package/scripts/run_script_with_stack_env.restart_port_reuse.test.mjs +30 -0
- package/scripts/self.mjs +465 -0
- package/scripts/self_host.mjs +9 -0
- package/scripts/self_host_binary_smoke.integration.test.mjs +94 -0
- package/scripts/self_host_runtime.mjs +883 -0
- package/scripts/self_host_runtime.test.mjs +82 -0
- package/scripts/self_host_systemd.real.integration.test.mjs +367 -0
- package/scripts/server_flavor.mjs +148 -0
- package/scripts/service.mjs +868 -0
- package/scripts/service_mode_help.test.mjs +27 -0
- package/scripts/setup.mjs +1324 -0
- package/scripts/setup_non_interactive_flag.test.mjs +60 -0
- package/scripts/setup_pr.mjs +605 -0
- package/scripts/setup_pr_orchestrated_auth_flow_util_import.test.mjs +117 -0
- package/scripts/stack/command_arguments.mjs +91 -0
- package/scripts/stack/copy_auth_from_stack.mjs +111 -0
- package/scripts/stack/delegated_script_commands.mjs +92 -0
- package/scripts/stack/help_text.mjs +110 -0
- package/scripts/stack/port_reservation.mjs +74 -0
- package/scripts/stack/repo_checkout_resolution.mjs +31 -0
- package/scripts/stack/run_script_with_stack_env.mjs +634 -0
- package/scripts/stack/stack_daemon_command.mjs +219 -0
- package/scripts/stack/stack_delegated_help.mjs +81 -0
- package/scripts/stack/stack_environment.mjs +151 -0
- package/scripts/stack/stack_environment.sanitization.test.mjs +75 -0
- package/scripts/stack/stack_happier_passthrough_command.mjs +63 -0
- package/scripts/stack/stack_info_snapshot.mjs +167 -0
- package/scripts/stack/stack_mobile_install_command.mjs +61 -0
- package/scripts/stack/stack_resume_command.mjs +76 -0
- package/scripts/stack/stack_stop_command.mjs +34 -0
- package/scripts/stack/stack_workspace_command.mjs +83 -0
- package/scripts/stack/transient_repo_overrides.mjs +29 -0
- package/scripts/stack.mjs +2388 -0
- package/scripts/stack_archive_cmd.integration.test.mjs +31 -0
- package/scripts/stack_audit_fix_light_env.test.mjs +129 -0
- package/scripts/stack_background_pinned_stack_json.test.mjs +81 -0
- package/scripts/stack_copy_auth_server_scoped.test.mjs +243 -0
- package/scripts/stack_daemon_cmd.integration.test.mjs +484 -0
- package/scripts/stack_eas_help.test.mjs +72 -0
- package/scripts/stack_editor_workspace_monorepo_root.test.mjs +102 -0
- package/scripts/stack_env_cmd.test.mjs +107 -0
- package/scripts/stack_guided_login_bundle_error_parse.test.mjs +20 -0
- package/scripts/stack_guided_login_inner_invocation.test.mjs +46 -0
- package/scripts/stack_happy_cmd.integration.test.mjs +263 -0
- package/scripts/stack_info_snapshot_running_status.test.mjs +186 -0
- package/scripts/stack_interactive_monorepo_group.test.mjs +128 -0
- package/scripts/stack_monorepo_defaults.test.mjs +31 -0
- package/scripts/stack_monorepo_repo_dev_token.test.mjs +32 -0
- package/scripts/stack_monorepo_server_light_from_happy_spec.test.mjs +37 -0
- package/scripts/stack_new_name_normalize_cmd.test.mjs +38 -0
- package/scripts/stack_pr_name_normalize_cmd.test.mjs +84 -0
- package/scripts/stack_resume_cmd.integration.test.mjs +134 -0
- package/scripts/stack_server_flavors_defaults.test.mjs +64 -0
- package/scripts/stack_shorthand_cmd.integration.test.mjs +74 -0
- package/scripts/stack_stop_sweeps_legacy_infra_without_kind.integration.test.mjs +44 -0
- package/scripts/stack_stop_sweeps_when_runtime_missing.integration.test.mjs +42 -0
- package/scripts/stack_stop_sweeps_when_runtime_stale.integration.test.mjs +50 -0
- package/scripts/stack_wt_list.test.mjs +117 -0
- package/scripts/start_ui_required_default.test.mjs +63 -0
- package/scripts/stop.mjs +190 -0
- package/scripts/stopStackWithEnv_no_autosweep_when_runtime_missing.integration.test.mjs +95 -0
- package/scripts/swiftbar_git_monorepo_cmd.test.mjs +75 -0
- package/scripts/swiftbar_render_monorepo_wt_actions.integration.test.mjs +116 -0
- package/scripts/swiftbar_utils_cmd.test.mjs +92 -0
- package/scripts/swiftbar_wt_pr_backcompat.test.mjs +162 -0
- package/scripts/systemd_unit_info.test.mjs +24 -0
- package/scripts/tailscale.mjs +490 -0
- package/scripts/test_ci.mjs +36 -0
- package/scripts/test_cmd.mjs +274 -0
- package/scripts/test_cmd.test.mjs +133 -0
- package/scripts/test_integration.mjs +33 -0
- package/scripts/testkit/auth_testkit.mjs +121 -0
- package/scripts/testkit/doctor_testkit.mjs +68 -0
- package/scripts/testkit/monorepo_port_testkit.mjs +157 -0
- package/scripts/testkit/stack_archive_command_testkit.mjs +55 -0
- package/scripts/testkit/stack_new_monorepo_testkit.mjs +83 -0
- package/scripts/testkit/stack_script_command_testkit.mjs +27 -0
- package/scripts/testkit/stack_stop_sweeps_testkit.mjs +172 -0
- package/scripts/testkit/worktrees_monorepo_testkit.mjs +53 -0
- package/scripts/tools.mjs +70 -0
- package/scripts/tui.mjs +914 -0
- package/scripts/tui_stopStackForTuiExit_no_autosweep.integration.test.mjs +95 -0
- package/scripts/typecheck.mjs +178 -0
- package/scripts/ui_gateway.mjs +247 -0
- package/scripts/uninstall.mjs +179 -0
- package/scripts/utils/auth/credentials_paths.mjs +181 -0
- package/scripts/utils/auth/credentials_paths.test.mjs +187 -0
- package/scripts/utils/auth/daemon_gate.mjs +66 -0
- package/scripts/utils/auth/daemon_gate.test.mjs +116 -0
- package/scripts/utils/auth/decode_jwt_payload_unsafe.mjs +16 -0
- package/scripts/utils/auth/dev_key.mjs +163 -0
- package/scripts/utils/auth/files.mjs +56 -0
- package/scripts/utils/auth/guided_pr_auth.mjs +86 -0
- package/scripts/utils/auth/guided_stack_web_login.mjs +56 -0
- package/scripts/utils/auth/handy_master_secret.mjs +42 -0
- package/scripts/utils/auth/interactive_stack_auth.mjs +70 -0
- package/scripts/utils/auth/login_ux.mjs +105 -0
- package/scripts/utils/auth/orchestrated_stack_auth_flow.mjs +291 -0
- package/scripts/utils/auth/sources.mjs +28 -0
- package/scripts/utils/auth/stable_scope_id.mjs +91 -0
- package/scripts/utils/auth/stable_scope_id.test.mjs +51 -0
- package/scripts/utils/auth/stack_guided_login.mjs +438 -0
- package/scripts/utils/cli/arg_values.mjs +23 -0
- package/scripts/utils/cli/arg_values.test.mjs +43 -0
- package/scripts/utils/cli/args.mjs +17 -0
- package/scripts/utils/cli/cli.mjs +24 -0
- package/scripts/utils/cli/cli_registry.mjs +440 -0
- package/scripts/utils/cli/cwd_scope.mjs +158 -0
- package/scripts/utils/cli/cwd_scope.test.mjs +154 -0
- package/scripts/utils/cli/flags.mjs +17 -0
- package/scripts/utils/cli/log_forwarder.mjs +157 -0
- package/scripts/utils/cli/normalize.mjs +16 -0
- package/scripts/utils/cli/prereqs.mjs +103 -0
- package/scripts/utils/cli/prereqs.test.mjs +33 -0
- package/scripts/utils/cli/progress.mjs +141 -0
- package/scripts/utils/cli/smoke_help.mjs +44 -0
- package/scripts/utils/cli/verbosity.mjs +11 -0
- package/scripts/utils/cli/wizard.mjs +139 -0
- package/scripts/utils/cli/wizard_promptSelect.test.mjs +44 -0
- package/scripts/utils/cli/wizard_prompt_worktree_source_lazy.test.mjs +132 -0
- package/scripts/utils/cli/wizard_worktree_slug.test.mjs +33 -0
- package/scripts/utils/crypto/tokens.mjs +14 -0
- package/scripts/utils/dev/daemon.mjs +232 -0
- package/scripts/utils/dev/daemon_watch_resilience.test.mjs +224 -0
- package/scripts/utils/dev/expo_dev.buildEnv.test.mjs +35 -0
- package/scripts/utils/dev/expo_dev.mjs +478 -0
- package/scripts/utils/dev/expo_dev.test.mjs +89 -0
- package/scripts/utils/dev/expo_dev_restart_port_reservation.test.mjs +120 -0
- package/scripts/utils/dev/expo_dev_verbose_logs.test.mjs +60 -0
- package/scripts/utils/dev/server.mjs +180 -0
- package/scripts/utils/dev_auth_key.mjs +7 -0
- package/scripts/utils/edison/git_roots.mjs +30 -0
- package/scripts/utils/edison/git_roots.test.mjs +49 -0
- package/scripts/utils/env/config.mjs +52 -0
- package/scripts/utils/env/dotenv.mjs +32 -0
- package/scripts/utils/env/dotenv.test.mjs +32 -0
- package/scripts/utils/env/env.mjs +130 -0
- package/scripts/utils/env/env_file.mjs +98 -0
- package/scripts/utils/env/env_file.test.mjs +49 -0
- package/scripts/utils/env/env_local.mjs +25 -0
- package/scripts/utils/env/load_env_file.mjs +34 -0
- package/scripts/utils/env/read.mjs +30 -0
- package/scripts/utils/env/sandbox.mjs +13 -0
- package/scripts/utils/env/scrub_env.mjs +69 -0
- package/scripts/utils/env/scrub_env.test.mjs +102 -0
- package/scripts/utils/env/values.mjs +13 -0
- package/scripts/utils/expo/command.mjs +65 -0
- package/scripts/utils/expo/expo.mjs +139 -0
- package/scripts/utils/expo/expo_state_running.test.mjs +48 -0
- package/scripts/utils/expo/metro_ports.mjs +101 -0
- package/scripts/utils/expo/metro_ports.test.mjs +35 -0
- package/scripts/utils/fs/atomic_dir_swap.mjs +55 -0
- package/scripts/utils/fs/atomic_dir_swap.test.mjs +54 -0
- package/scripts/utils/fs/file_has_content.mjs +10 -0
- package/scripts/utils/fs/fs.mjs +11 -0
- package/scripts/utils/fs/json.mjs +25 -0
- package/scripts/utils/fs/ops.mjs +29 -0
- package/scripts/utils/fs/package_json.mjs +8 -0
- package/scripts/utils/fs/tail.mjs +12 -0
- package/scripts/utils/git/dev_checkout.mjs +127 -0
- package/scripts/utils/git/dev_checkout.test.mjs +115 -0
- package/scripts/utils/git/git.mjs +67 -0
- package/scripts/utils/git/parse_name_status_z.mjs +21 -0
- package/scripts/utils/git/refs.mjs +26 -0
- package/scripts/utils/git/worktrees.mjs +323 -0
- package/scripts/utils/git/worktrees_monorepo.test.mjs +60 -0
- package/scripts/utils/git/worktrees_pathstyle.test.mjs +53 -0
- package/scripts/utils/llm/assist.mjs +260 -0
- package/scripts/utils/llm/codex_exec.mjs +61 -0
- package/scripts/utils/llm/codex_exec.test.mjs +46 -0
- package/scripts/utils/llm/hstack_runner.mjs +59 -0
- package/scripts/utils/llm/tools.mjs +56 -0
- package/scripts/utils/llm/tools.test.mjs +67 -0
- package/scripts/utils/menubar/swiftbar.mjs +121 -0
- package/scripts/utils/menubar/swiftbar.test.mjs +85 -0
- package/scripts/utils/mobile/config.mjs +35 -0
- package/scripts/utils/mobile/dev_client_links.mjs +59 -0
- package/scripts/utils/mobile/identifiers.mjs +46 -0
- package/scripts/utils/mobile/identifiers.test.mjs +41 -0
- package/scripts/utils/mobile/ios_xcodeproj_patch.mjs +128 -0
- package/scripts/utils/mobile/ios_xcodeproj_patch.test.mjs +131 -0
- package/scripts/utils/net/bind_mode.mjs +39 -0
- package/scripts/utils/net/dns.mjs +10 -0
- package/scripts/utils/net/lan_ip.mjs +24 -0
- package/scripts/utils/net/ports.mjs +110 -0
- package/scripts/utils/net/tcp_forward.mjs +162 -0
- package/scripts/utils/net/url.mjs +30 -0
- package/scripts/utils/net/url.test.mjs +29 -0
- package/scripts/utils/paths/canonical_home.mjs +15 -0
- package/scripts/utils/paths/canonical_home.test.mjs +28 -0
- package/scripts/utils/paths/localhost_host.mjs +112 -0
- package/scripts/utils/paths/localhost_host.test.mjs +58 -0
- package/scripts/utils/paths/paths.mjs +302 -0
- package/scripts/utils/paths/paths_env_win32.test.mjs +36 -0
- package/scripts/utils/paths/paths_monorepo.test.mjs +58 -0
- package/scripts/utils/paths/paths_server_flavors.test.mjs +50 -0
- package/scripts/utils/paths/runtime.mjs +41 -0
- package/scripts/utils/pglite_lock.mjs +107 -0
- package/scripts/utils/proc/commands.mjs +33 -0
- package/scripts/utils/proc/exit_cleanup.mjs +57 -0
- package/scripts/utils/proc/happy_monorepo_deps.mjs +37 -0
- package/scripts/utils/proc/happy_monorepo_deps.test.mjs +89 -0
- package/scripts/utils/proc/ownership.mjs +217 -0
- package/scripts/utils/proc/ownership_killProcessGroupOwnedByStack.test.mjs +216 -0
- package/scripts/utils/proc/ownership_listPidsWithEnvNeedles.test.mjs +88 -0
- package/scripts/utils/proc/package_scripts.mjs +38 -0
- package/scripts/utils/proc/package_scripts.test.mjs +58 -0
- package/scripts/utils/proc/parallel.mjs +25 -0
- package/scripts/utils/proc/pids.mjs +11 -0
- package/scripts/utils/proc/pm.mjs +478 -0
- package/scripts/utils/proc/pm_spawn.integration.test.mjs +131 -0
- package/scripts/utils/proc/pm_stack_cache_env.test.mjs +313 -0
- package/scripts/utils/proc/proc.mjs +331 -0
- package/scripts/utils/proc/proc.test.mjs +85 -0
- package/scripts/utils/proc/terminate.mjs +69 -0
- package/scripts/utils/proc/terminate.test.mjs +54 -0
- package/scripts/utils/proc/watch.mjs +63 -0
- package/scripts/utils/review/augment_runner_integration.test.mjs +105 -0
- package/scripts/utils/review/base_ref.mjs +82 -0
- package/scripts/utils/review/base_ref.test.mjs +89 -0
- package/scripts/utils/review/chunks.mjs +55 -0
- package/scripts/utils/review/chunks.test.mjs +107 -0
- package/scripts/utils/review/detached_worktree.mjs +61 -0
- package/scripts/utils/review/detached_worktree.test.mjs +61 -0
- package/scripts/utils/review/findings.mjs +278 -0
- package/scripts/utils/review/findings.test.mjs +203 -0
- package/scripts/utils/review/head_slice.mjs +132 -0
- package/scripts/utils/review/head_slice.test.mjs +117 -0
- package/scripts/utils/review/instructions/deep.md +20 -0
- package/scripts/utils/review/prompts.mjs +279 -0
- package/scripts/utils/review/prompts.test.mjs +77 -0
- package/scripts/utils/review/run_reviewers_safe.mjs +12 -0
- package/scripts/utils/review/run_reviewers_safe.test.mjs +45 -0
- package/scripts/utils/review/runners/augment.mjs +91 -0
- package/scripts/utils/review/runners/augment.test.mjs +64 -0
- package/scripts/utils/review/runners/claude.mjs +92 -0
- package/scripts/utils/review/runners/claude.test.mjs +47 -0
- package/scripts/utils/review/runners/coderabbit.mjs +105 -0
- package/scripts/utils/review/runners/coderabbit.test.mjs +32 -0
- package/scripts/utils/review/runners/codex.mjs +129 -0
- package/scripts/utils/review/runners/codex.test.mjs +115 -0
- package/scripts/utils/review/slice_mode.mjs +20 -0
- package/scripts/utils/review/slice_mode.test.mjs +69 -0
- package/scripts/utils/review/sliced_runner.mjs +39 -0
- package/scripts/utils/review/sliced_runner.test.mjs +57 -0
- package/scripts/utils/review/slices.mjs +140 -0
- package/scripts/utils/review/slices.test.mjs +41 -0
- package/scripts/utils/review/targets.mjs +23 -0
- package/scripts/utils/review/targets.test.mjs +31 -0
- package/scripts/utils/review/tool_home_seed.mjs +106 -0
- package/scripts/utils/review/tool_home_seed.test.mjs +124 -0
- package/scripts/utils/review/uncommitted_ops.mjs +77 -0
- package/scripts/utils/review/uncommitted_ops.test.mjs +117 -0
- package/scripts/utils/sandbox/review_pr_sandbox.mjs +105 -0
- package/scripts/utils/server/apply_server_light_env_defaults.mjs +14 -0
- package/scripts/utils/server/flavor_scripts.mjs +138 -0
- package/scripts/utils/server/flavor_scripts.test.mjs +115 -0
- package/scripts/utils/server/infra/happy_server_infra.mjs +444 -0
- package/scripts/utils/server/mobile_api_url.mjs +60 -0
- package/scripts/utils/server/mobile_api_url.test.mjs +58 -0
- package/scripts/utils/server/port.mjs +55 -0
- package/scripts/utils/server/prisma_import.mjs +36 -0
- package/scripts/utils/server/prisma_import.test.mjs +78 -0
- package/scripts/utils/server/server.mjs +109 -0
- package/scripts/utils/server/ui_build_check.mjs +37 -0
- package/scripts/utils/server/ui_build_check.test.mjs +70 -0
- package/scripts/utils/server/ui_env.mjs +13 -0
- package/scripts/utils/server/ui_env.test.mjs +57 -0
- package/scripts/utils/server/urls.mjs +100 -0
- package/scripts/utils/server/validate.mjs +60 -0
- package/scripts/utils/server/validate.test.mjs +76 -0
- package/scripts/utils/service/autostart_darwin.mjs +198 -0
- package/scripts/utils/service/autostart_darwin.test.mjs +49 -0
- package/scripts/utils/service/autostart_darwin_keepalive.test.mjs +19 -0
- package/scripts/utils/stack/cli_identities.mjs +29 -0
- package/scripts/utils/stack/context.mjs +19 -0
- package/scripts/utils/stack/dirs.mjs +26 -0
- package/scripts/utils/stack/editor_workspace.mjs +126 -0
- package/scripts/utils/stack/interactive_stack_config.mjs +266 -0
- package/scripts/utils/stack/interactive_stack_config.port_validation.test.mjs +93 -0
- package/scripts/utils/stack/interactive_stack_config.remote_validation.test.mjs +122 -0
- package/scripts/utils/stack/interactive_stack_config.stack_name_validation.test.mjs +76 -0
- package/scripts/utils/stack/interactive_stack_config_testkit.mjs +18 -0
- package/scripts/utils/stack/names.mjs +27 -0
- package/scripts/utils/stack/names.test.mjs +26 -0
- package/scripts/utils/stack/pr_stack_name.mjs +16 -0
- package/scripts/utils/stack/runtime_state.mjs +88 -0
- package/scripts/utils/stack/stacks.mjs +40 -0
- package/scripts/utils/stack/startup.mjs +370 -0
- package/scripts/utils/stack/startup_server_light_dirs.test.mjs +119 -0
- package/scripts/utils/stack/startup_server_light_generate.test.mjs +20 -0
- package/scripts/utils/stack/startup_server_light_legacy.test.mjs +79 -0
- package/scripts/utils/stack/startup_server_light_testkit.mjs +106 -0
- package/scripts/utils/stack/stop.mjs +284 -0
- package/scripts/utils/stack_context.mjs +1 -0
- package/scripts/utils/stack_runtime_state.mjs +1 -0
- package/scripts/utils/stacks.mjs +1 -0
- package/scripts/utils/tailscale/ip.mjs +116 -0
- package/scripts/utils/tauri/stack_overrides.mjs +22 -0
- package/scripts/utils/test/collect_test_files.mjs +29 -0
- package/scripts/utils/time/get_today_ymd.mjs +7 -0
- package/scripts/utils/tui/cleanup.mjs +38 -0
- package/scripts/utils/ui/ansi.mjs +47 -0
- package/scripts/utils/ui/browser.mjs +31 -0
- package/scripts/utils/ui/browser.test.mjs +56 -0
- package/scripts/utils/ui/clipboard.mjs +38 -0
- package/scripts/utils/ui/layout.mjs +44 -0
- package/scripts/utils/ui/qr.mjs +17 -0
- package/scripts/utils/ui/terminal_launcher.mjs +129 -0
- package/scripts/utils/ui/text.mjs +16 -0
- package/scripts/utils/update/auto_update_notice.mjs +93 -0
- package/scripts/utils/validate.mjs +5 -0
- package/scripts/where.mjs +138 -0
- package/scripts/worktrees.mjs +2174 -0
- package/scripts/worktrees_archive_cmd.integration.test.mjs +228 -0
- package/scripts/worktrees_cursor_monorepo_root.test.mjs +23 -0
- package/scripts/worktrees_list_specs_no_recurse.test.mjs +32 -0
- package/scripts/worktrees_monorepo_testkit.test.mjs +29 -0
- package/scripts/worktrees_monorepo_use_group.test.mjs +41 -0
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { spawn } from 'node:child_process';
|
|
4
|
+
import { mkdtemp, mkdir, readFile, rm, stat, writeFile } from 'node:fs/promises';
|
|
5
|
+
import { tmpdir } from 'node:os';
|
|
6
|
+
import { dirname, join } from 'node:path';
|
|
7
|
+
import { fileURLToPath } from 'node:url';
|
|
8
|
+
|
|
9
|
+
const scriptsDir = dirname(fileURLToPath(import.meta.url));
|
|
10
|
+
const rootDir = dirname(scriptsDir);
|
|
11
|
+
|
|
12
|
+
function runCmd(cmd, args, { cwd, env }) {
|
|
13
|
+
return new Promise((resolve, reject) => {
|
|
14
|
+
const cleanEnv = {};
|
|
15
|
+
for (const [k, v] of Object.entries(env ?? {})) {
|
|
16
|
+
if (v == null) continue;
|
|
17
|
+
cleanEnv[k] = String(v);
|
|
18
|
+
}
|
|
19
|
+
const proc = spawn(cmd, args, { cwd, env: cleanEnv, stdio: ['ignore', 'pipe', 'pipe'] });
|
|
20
|
+
let stdout = '';
|
|
21
|
+
let stderr = '';
|
|
22
|
+
proc.stdout.on('data', (d) => (stdout += String(d)));
|
|
23
|
+
proc.stderr.on('data', (d) => (stderr += String(d)));
|
|
24
|
+
proc.on('error', reject);
|
|
25
|
+
proc.on('close', (code, signal) => {
|
|
26
|
+
if (code != null) {
|
|
27
|
+
resolve({ code, stdout, stderr });
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
const signalText = signal ? ` (signal: ${signal})` : '';
|
|
31
|
+
resolve({ code: 1, stdout, stderr: `${stderr}Process exited without code${signalText}\n` });
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function runNode(args, { cwd, env }) {
|
|
37
|
+
return runCmd(process.execPath, args, { cwd, env });
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async function runOk(cmd, args, { cwd, env }) {
|
|
41
|
+
const res = await runCmd(cmd, args, { cwd, env });
|
|
42
|
+
assert.equal(res.code, 0, `expected exit 0 for ${cmd} ${args.join(' ')}\nstdout:\n${res.stdout}\nstderr:\n${res.stderr}`);
|
|
43
|
+
return res;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function createBaseEnv({ homeDir, storageDir, workspaceDir }) {
|
|
47
|
+
return {
|
|
48
|
+
...Object.fromEntries(Object.entries(process.env).filter(([k]) => !k.startsWith('HAPPIER_STACK_'))),
|
|
49
|
+
GIT_TERMINAL_PROMPT: '0',
|
|
50
|
+
HAPPIER_STACK_HOME_DIR: homeDir,
|
|
51
|
+
HAPPIER_STACK_STORAGE_DIR: storageDir,
|
|
52
|
+
HAPPIER_STACK_WORKSPACE_DIR: workspaceDir,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async function initMainRepo({ repoDir, baseEnv }) {
|
|
57
|
+
await mkdir(repoDir, { recursive: true });
|
|
58
|
+
await runOk('git', ['init', '-b', 'main'], { cwd: repoDir, env: baseEnv });
|
|
59
|
+
await runOk('git', ['config', 'user.name', 'Test'], { cwd: repoDir, env: baseEnv });
|
|
60
|
+
await runOk('git', ['config', 'user.email', 'test@example.com'], { cwd: repoDir, env: baseEnv });
|
|
61
|
+
await writeFile(join(repoDir, 'README.md'), 'hello\n', 'utf-8');
|
|
62
|
+
await runOk('git', ['add', 'README.md'], { cwd: repoDir, env: baseEnv });
|
|
63
|
+
await runOk('git', ['commit', '-m', 'init'], { cwd: repoDir, env: baseEnv });
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async function addWorktree({ repoDir, worktreeDir, branch, baseEnv }) {
|
|
67
|
+
await mkdir(dirname(worktreeDir), { recursive: true });
|
|
68
|
+
await runOk('git', ['worktree', 'add', '-b', branch, worktreeDir, 'main'], { cwd: repoDir, env: baseEnv });
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async function createArchiveFixture(t, prefix) {
|
|
72
|
+
const tmp = await mkdtemp(join(tmpdir(), prefix));
|
|
73
|
+
t.after(async () => {
|
|
74
|
+
await rm(tmp, { recursive: true, force: true });
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
const storageDir = join(tmp, 'storage');
|
|
78
|
+
const homeDir = join(tmp, 'home');
|
|
79
|
+
const workspaceDir = join(tmp, 'workspace');
|
|
80
|
+
const baseEnv = createBaseEnv({ homeDir, storageDir, workspaceDir });
|
|
81
|
+
const repoDir = join(workspaceDir, 'main');
|
|
82
|
+
|
|
83
|
+
await initMainRepo({ repoDir, baseEnv });
|
|
84
|
+
|
|
85
|
+
return {
|
|
86
|
+
baseEnv,
|
|
87
|
+
homeDir,
|
|
88
|
+
repoDir,
|
|
89
|
+
storageDir,
|
|
90
|
+
tmp,
|
|
91
|
+
workspaceDir,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
test('hstack wt archive detaches and moves a git worktree (preserving uncommitted changes)', async (t) => {
|
|
96
|
+
const { baseEnv, repoDir, workspaceDir } = await createArchiveFixture(t, 'happy-stacks-wt-archive-');
|
|
97
|
+
|
|
98
|
+
const worktreeDir = join(workspaceDir, 'pr', 'test-archive');
|
|
99
|
+
await addWorktree({ repoDir, worktreeDir, branch: 'pr/test-archive', baseEnv });
|
|
100
|
+
|
|
101
|
+
await writeFile(join(worktreeDir, 'staged.txt'), 'staged\n', 'utf-8');
|
|
102
|
+
await runOk('git', ['add', 'staged.txt'], { cwd: worktreeDir, env: baseEnv });
|
|
103
|
+
await writeFile(join(worktreeDir, 'untracked.txt'), 'untracked\n', 'utf-8');
|
|
104
|
+
await writeFile(join(worktreeDir, 'README.md'), 'hello\nchanged\n', 'utf-8');
|
|
105
|
+
|
|
106
|
+
const beforeStatus = await runOk('git', ['status', '--porcelain'], { cwd: worktreeDir, env: baseEnv });
|
|
107
|
+
assert.ok(beforeStatus.stdout.includes('A staged.txt'), `expected staged file in status\n${beforeStatus.stdout}`);
|
|
108
|
+
assert.ok(beforeStatus.stdout.includes(' M README.md'), `expected modified file in status\n${beforeStatus.stdout}`);
|
|
109
|
+
assert.ok(beforeStatus.stdout.includes('?? untracked.txt'), `expected untracked file in status\n${beforeStatus.stdout}`);
|
|
110
|
+
|
|
111
|
+
const date = '2000-01-02';
|
|
112
|
+
// Simulate a minimal PATH environment like launchd/SwiftBar shells.
|
|
113
|
+
const nodeEnv = { ...baseEnv, PATH: '' };
|
|
114
|
+
const res = await runNode([join(rootDir, 'scripts', 'worktrees.mjs'), 'archive', 'pr/test-archive', `--date=${date}`, '--json'], {
|
|
115
|
+
cwd: rootDir,
|
|
116
|
+
env: nodeEnv,
|
|
117
|
+
});
|
|
118
|
+
assert.equal(res.code, 0, `expected archive exit 0\nstdout:\n${res.stdout}\nstderr:\n${res.stderr}`);
|
|
119
|
+
const parsed = JSON.parse(res.stdout);
|
|
120
|
+
assert.equal(parsed.ok, true, `expected ok=true JSON output\n${res.stdout}`);
|
|
121
|
+
|
|
122
|
+
const archivedDir = join(workspaceDir, 'archive', 'worktrees', date, 'pr', 'test-archive');
|
|
123
|
+
assert.equal(parsed.destDir, archivedDir, `expected destDir in JSON output to match archive path\n${res.stdout}`);
|
|
124
|
+
const legacyGitFile = await stat(join(archivedDir, '.git.worktree')).catch(() => null);
|
|
125
|
+
assert.equal(legacyGitFile, null, 'expected .git.worktree to be removed (avoid untracked noise)');
|
|
126
|
+
const gitStat = await stat(join(archivedDir, '.git'));
|
|
127
|
+
assert.ok(gitStat.isDirectory(), 'expected archived .git to be a directory (detached repo)');
|
|
128
|
+
|
|
129
|
+
const meta = await readFile(join(archivedDir, 'ARCHIVE_META.txt'), 'utf-8');
|
|
130
|
+
assert.ok(meta.includes('component=happier-ui'), `expected component in ARCHIVE_META.txt\n${meta}`);
|
|
131
|
+
assert.ok(meta.includes('ref=pr/test-archive'), `expected ref in ARCHIVE_META.txt\n${meta}`);
|
|
132
|
+
|
|
133
|
+
const afterStatus = await runOk('git', ['status', '--porcelain'], { cwd: archivedDir, env: baseEnv });
|
|
134
|
+
assert.ok(afterStatus.stdout.includes('A staged.txt'), `expected staged file preserved\n${afterStatus.stdout}`);
|
|
135
|
+
assert.ok(afterStatus.stdout.includes(' M README.md'), `expected modified file preserved\n${afterStatus.stdout}`);
|
|
136
|
+
assert.ok(afterStatus.stdout.includes('?? untracked.txt'), `expected untracked file preserved\n${afterStatus.stdout}`);
|
|
137
|
+
|
|
138
|
+
const list = await runOk('git', ['worktree', 'list', '--porcelain'], { cwd: repoDir, env: baseEnv });
|
|
139
|
+
assert.ok(!list.stdout.includes(worktreeDir), `expected source repo worktree entry pruned\n${list.stdout}`);
|
|
140
|
+
|
|
141
|
+
const branchExists = await runCmd('git', ['show-ref', '--verify', 'refs/heads/pr/test-archive'], { cwd: repoDir, env: baseEnv });
|
|
142
|
+
assert.notEqual(branchExists.code, 0, 'expected source repo branch deleted');
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
test('hstack wt archive refuses to break stacks unless --detach-stacks is provided', async (t) => {
|
|
146
|
+
const { baseEnv, repoDir, storageDir, workspaceDir } = await createArchiveFixture(t, 'happy-stacks-wt-archive-stacks-');
|
|
147
|
+
|
|
148
|
+
const worktreeDir = join(workspaceDir, 'pr', 'linked-to-stack');
|
|
149
|
+
await addWorktree({ repoDir, worktreeDir, branch: 'pr/linked-to-stack', baseEnv });
|
|
150
|
+
await writeFile(join(worktreeDir, 'untracked.txt'), 'untracked\n', 'utf-8');
|
|
151
|
+
|
|
152
|
+
const stackName = 'exp-test';
|
|
153
|
+
const envPath = join(storageDir, stackName, 'env');
|
|
154
|
+
await mkdir(dirname(envPath), { recursive: true });
|
|
155
|
+
await writeFile(envPath, [`HAPPIER_STACK_STACK=${stackName}`, `HAPPIER_STACK_REPO_DIR=${worktreeDir}`, ''].join('\n'), 'utf-8');
|
|
156
|
+
|
|
157
|
+
const date = '2000-01-03';
|
|
158
|
+
const nodeEnv = { ...baseEnv, PATH: '' };
|
|
159
|
+
|
|
160
|
+
const denied = await runNode(
|
|
161
|
+
[join(rootDir, 'scripts', 'worktrees.mjs'), 'archive', 'pr/linked-to-stack', `--date=${date}`],
|
|
162
|
+
{ cwd: rootDir, env: nodeEnv }
|
|
163
|
+
);
|
|
164
|
+
assert.notEqual(denied.code, 0, `expected archive to refuse without --detach-stacks\nstdout:\n${denied.stdout}\nstderr:\n${denied.stderr}`);
|
|
165
|
+
|
|
166
|
+
const ok = await runNode(
|
|
167
|
+
[
|
|
168
|
+
join(rootDir, 'scripts', 'worktrees.mjs'),
|
|
169
|
+
'archive',
|
|
170
|
+
'pr/linked-to-stack',
|
|
171
|
+
`--date=${date}`,
|
|
172
|
+
'--detach-stacks',
|
|
173
|
+
'--json',
|
|
174
|
+
],
|
|
175
|
+
{ cwd: rootDir, env: nodeEnv }
|
|
176
|
+
);
|
|
177
|
+
assert.equal(ok.code, 0, `expected archive to succeed with --detach-stacks\nstdout:\n${ok.stdout}\nstderr:\n${ok.stderr}`);
|
|
178
|
+
|
|
179
|
+
const nextEnv = await readFile(envPath, 'utf-8');
|
|
180
|
+
assert.ok(!nextEnv.includes('HAPPIER_STACK_REPO_DIR='), `expected stack env to detach from worktree\n${nextEnv}`);
|
|
181
|
+
|
|
182
|
+
const archivedDir = join(workspaceDir, 'archive', 'worktrees', date, 'pr', 'linked-to-stack');
|
|
183
|
+
const gitStat = await stat(join(archivedDir, '.git'));
|
|
184
|
+
assert.ok(gitStat.isDirectory(), 'expected archived .git to be a directory (detached repo)');
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
test('hstack wt archive can archive a broken git worktree (missing .git/worktrees entry)', async (t) => {
|
|
188
|
+
const { baseEnv, repoDir, workspaceDir } = await createArchiveFixture(t, 'happy-stacks-wt-archive-broken-');
|
|
189
|
+
|
|
190
|
+
const worktreeDir = join(workspaceDir, 'pr', 'broken-worktree');
|
|
191
|
+
await addWorktree({ repoDir, worktreeDir, branch: 'pr/broken-worktree', baseEnv });
|
|
192
|
+
|
|
193
|
+
// Create uncommitted changes (no staging; the index will be deleted when we break the worktree).
|
|
194
|
+
await writeFile(join(worktreeDir, 'untracked.txt'), 'untracked\n', 'utf-8');
|
|
195
|
+
await writeFile(join(worktreeDir, 'README.md'), 'hello\nchanged\n', 'utf-8');
|
|
196
|
+
|
|
197
|
+
// Simulate a corrupted linked worktree by removing its gitdir entry from the source repo.
|
|
198
|
+
const gitFile = await readFile(join(worktreeDir, '.git'), 'utf-8');
|
|
199
|
+
const gitdirLine = gitFile
|
|
200
|
+
.split('\n')
|
|
201
|
+
.map((l) => l.trim())
|
|
202
|
+
.find((l) => l.startsWith('gitdir:'));
|
|
203
|
+
assert.ok(gitdirLine, `expected .git file to include gitdir line\n${gitFile}`);
|
|
204
|
+
const gitdir = gitdirLine.slice('gitdir:'.length).trim();
|
|
205
|
+
assert.ok(gitdir, `expected gitdir path\n${gitFile}`);
|
|
206
|
+
// Use an absolute path so we can rm it reliably.
|
|
207
|
+
const gitdirAbs = gitdir.startsWith('/') ? gitdir : join(worktreeDir, gitdir);
|
|
208
|
+
await rm(gitdirAbs, { recursive: true, force: true });
|
|
209
|
+
|
|
210
|
+
const date = '2000-01-05';
|
|
211
|
+
const nodeEnv = { ...baseEnv, PATH: '' };
|
|
212
|
+
const res = await runNode([join(rootDir, 'scripts', 'worktrees.mjs'), 'archive', 'pr/broken-worktree', `--date=${date}`, '--json'], {
|
|
213
|
+
cwd: rootDir,
|
|
214
|
+
env: nodeEnv,
|
|
215
|
+
});
|
|
216
|
+
assert.equal(res.code, 0, `expected archive exit 0\nstdout:\n${res.stdout}\nstderr:\n${res.stderr}`);
|
|
217
|
+
const parsed = JSON.parse(res.stdout);
|
|
218
|
+
assert.equal(parsed.ok, true, `expected ok=true JSON output\n${res.stdout}`);
|
|
219
|
+
assert.equal(parsed.branch, 'pr/broken-worktree', 'expected branch name to be preserved');
|
|
220
|
+
|
|
221
|
+
const archivedDir = join(workspaceDir, 'archive', 'worktrees', date, 'pr', 'broken-worktree');
|
|
222
|
+
const gitStat = await stat(join(archivedDir, '.git'));
|
|
223
|
+
assert.ok(gitStat.isDirectory(), 'expected archived .git to be a directory (detached repo)');
|
|
224
|
+
|
|
225
|
+
const afterStatus = await runOk('git', ['status', '--porcelain'], { cwd: archivedDir, env: baseEnv });
|
|
226
|
+
assert.ok(afterStatus.stdout.includes(' M README.md'), `expected modified file preserved\n${afterStatus.stdout}`);
|
|
227
|
+
assert.ok(afterStatus.stdout.includes('?? untracked.txt'), `expected untracked file preserved\n${afterStatus.stdout}`);
|
|
228
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { dirname, join } from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
|
|
6
|
+
import { createMonorepoWorktreeEnv, createMonorepoWorktreeFixture, runNode } from './testkit/worktrees_monorepo_testkit.mjs';
|
|
7
|
+
|
|
8
|
+
test('hstack wt cursor opens the monorepo root (not a subpackage dir) in monorepo worktrees', async (t) => {
|
|
9
|
+
const scriptsDir = dirname(fileURLToPath(import.meta.url));
|
|
10
|
+
const rootDir = dirname(scriptsDir);
|
|
11
|
+
const { homeDir, monoRoot, sandboxDir, workspaceDir } = await createMonorepoWorktreeFixture(t, {
|
|
12
|
+
prefix: 'happy-stacks-wt-cursor-mono-',
|
|
13
|
+
});
|
|
14
|
+
const env = createMonorepoWorktreeEnv({ homeDir, workspaceDir, sandboxDir });
|
|
15
|
+
|
|
16
|
+
const res = await runNode([join(rootDir, 'scripts', 'worktrees.mjs'), 'cursor', 'tmp/mono-wt', '--json'], {
|
|
17
|
+
cwd: rootDir,
|
|
18
|
+
env,
|
|
19
|
+
});
|
|
20
|
+
assert.equal(res.code, 0, `expected exit 0, got ${res.code}\nstdout:\n${res.stdout}\nstderr:\n${res.stderr}`);
|
|
21
|
+
const parsed = JSON.parse(res.stdout);
|
|
22
|
+
assert.equal(parsed.dir, monoRoot);
|
|
23
|
+
});
|
|
@@ -0,0 +1,32 @@
|
|
|
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 { join } from 'node:path';
|
|
6
|
+
|
|
7
|
+
import { listWorktreeSpecs } from './utils/git/worktrees.mjs';
|
|
8
|
+
|
|
9
|
+
test('listWorktreeSpecs does not recurse into worktree roots', async () => {
|
|
10
|
+
const tmp = await mkdtemp(join(tmpdir(), 'happier-stack-list-wt-specs-'));
|
|
11
|
+
try {
|
|
12
|
+
const workspaceDir = join(tmp, 'workspace');
|
|
13
|
+
const env = { ...process.env, HAPPIER_STACK_WORKSPACE_DIR: workspaceDir, HAPPIER_STACK_OWNER: 'test' };
|
|
14
|
+
const rootDir = tmp;
|
|
15
|
+
|
|
16
|
+
const wtRoot = join(workspaceDir, 'tmp', 'test', 'mono-wt');
|
|
17
|
+
await mkdir(wtRoot, { recursive: true });
|
|
18
|
+
await writeFile(join(wtRoot, '.git'), 'gitdir: dummy\n', 'utf-8');
|
|
19
|
+
|
|
20
|
+
// If listWorktreeSpecs incorrectly recurses into worktree roots, it would discover this nested ".git"
|
|
21
|
+
// and return an extra spec.
|
|
22
|
+
const nested = join(wtRoot, 'nested');
|
|
23
|
+
await mkdir(nested, { recursive: true });
|
|
24
|
+
await writeFile(join(nested, '.git'), 'gitdir: dummy\n', 'utf-8');
|
|
25
|
+
|
|
26
|
+
const specs = await listWorktreeSpecs({ rootDir, component: 'happier-ui', env });
|
|
27
|
+
assert.ok(specs.includes('tmp/mono-wt'), specs.join('\n'));
|
|
28
|
+
assert.ok(!specs.includes('tmp/mono-wt/nested'), specs.join('\n'));
|
|
29
|
+
} finally {
|
|
30
|
+
await rm(tmp, { recursive: true, force: true });
|
|
31
|
+
}
|
|
32
|
+
});
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
|
|
4
|
+
import { runNode } from './testkit/worktrees_monorepo_testkit.mjs';
|
|
5
|
+
|
|
6
|
+
test('runNode reports non-zero exit code for signal-terminated process', async () => {
|
|
7
|
+
const res = await runNode(['-e', 'process.kill(process.pid, "SIGTERM")'], {
|
|
8
|
+
cwd: process.cwd(),
|
|
9
|
+
env: process.env,
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
assert.equal(res.code, 1);
|
|
13
|
+
assert.match(res.stderr, /signal/i);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
test('runNode waits for stdio streams to flush before resolving', async () => {
|
|
17
|
+
const size = 200_000;
|
|
18
|
+
const res = await runNode(
|
|
19
|
+
['-e', `process.stdout.write("A".repeat(${size})); process.stderr.write("B".repeat(${size}));`],
|
|
20
|
+
{
|
|
21
|
+
cwd: process.cwd(),
|
|
22
|
+
env: process.env,
|
|
23
|
+
}
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
assert.equal(res.code, 0);
|
|
27
|
+
assert.equal(res.stdout.length, size);
|
|
28
|
+
assert.equal(res.stderr.length, size);
|
|
29
|
+
});
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { readFile, writeFile } from 'node:fs/promises';
|
|
4
|
+
import { dirname, join } from 'node:path';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
|
|
7
|
+
import { createMonorepoWorktreeEnv, createMonorepoWorktreeFixture, runNode } from './testkit/worktrees_monorepo_testkit.mjs';
|
|
8
|
+
|
|
9
|
+
test('hstack wt use switches all monorepo group components when target is a monorepo worktree', async (t) => {
|
|
10
|
+
const scriptsDir = dirname(fileURLToPath(import.meta.url));
|
|
11
|
+
const rootDir = dirname(scriptsDir);
|
|
12
|
+
const { homeDir, monoRoot, sandboxDir, tmp, workspaceDir } = await createMonorepoWorktreeFixture(t, {
|
|
13
|
+
prefix: 'happy-stacks-wt-use-mono-',
|
|
14
|
+
});
|
|
15
|
+
const envFile = join(tmp, 'env');
|
|
16
|
+
await writeFile(envFile, '', 'utf-8');
|
|
17
|
+
|
|
18
|
+
const env = createMonorepoWorktreeEnv({
|
|
19
|
+
homeDir,
|
|
20
|
+
workspaceDir,
|
|
21
|
+
sandboxDir,
|
|
22
|
+
extraEnv: {
|
|
23
|
+
HAPPIER_STACK_STACK: 'exp',
|
|
24
|
+
HAPPIER_STACK_ENV_FILE: envFile,
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
const res = await runNode(
|
|
29
|
+
[join(rootDir, 'scripts', 'worktrees.mjs'), 'use', 'tmp/mono-wt', '--force', '--json'],
|
|
30
|
+
{ cwd: rootDir, env }
|
|
31
|
+
);
|
|
32
|
+
assert.equal(res.code, 0, `expected exit 0, got ${res.code}\nstdout:\n${res.stdout}\nstderr:\n${res.stderr}`);
|
|
33
|
+
|
|
34
|
+
const parsed = JSON.parse(res.stdout);
|
|
35
|
+
assert.equal(parsed.repoDir, monoRoot);
|
|
36
|
+
assert.equal(parsed.activeDir, monoRoot);
|
|
37
|
+
|
|
38
|
+
const contents = await readFile(envFile, 'utf-8');
|
|
39
|
+
assert.ok(contents.includes(`HAPPIER_STACK_REPO_DIR=${monoRoot}\n`), contents);
|
|
40
|
+
assert.ok(!contents.includes('HAPPIER_STACK_COMPONENT_DIR_'), contents);
|
|
41
|
+
});
|