172 lines
12 KiB
YAML
172 lines
12 KiB
YAML
# audit:allow test-scenario-coverage reason: 본 매니페스트는 인스톨러 .env 분리 흐름의 시나리오 매트릭스 SSoT 로 기록. 핵심 회귀 가드는 test_files 의 통과 테스트 + Manual 시나리오 검증으로 커버.
|
|
|
|
feature: 인스톨러 .env 분리 흐름 (storage/installer/runtime.php → finalize-env.php)
|
|
|
|
description: |
|
|
설치 진행 중 동적 설정(DB 자격증명, APP_KEY) 을 .env 가 아닌
|
|
storage/installer/runtime.php 에 기록하여 php artisan serve 의 .env mtime
|
|
워처가 SSE/폴링 진행 중 워커를 재시작하지 않도록 한다. 설치 완료 UI 노출
|
|
직후 브라우저가 finalize-env.php 를 fire-and-forget 으로 호출하면 그 시점
|
|
에 .env 1회 머지 + INSTALLER_COMPLETED 추가가 일어난다.
|
|
|
|
배경:
|
|
- php artisan serve 의 ServeCommand 가 .env mtime 을 폴링 → 변경 시 워커 재시작.
|
|
- 기존 인스톨러는 Step 5 SSE 진행 중 .env 를 3회 변경 (env_update / key:generate / complete_flag)
|
|
하여 사용자에게 "Environment modified. Restarting server..." 와 함께 SSE 끊김이 보였다.
|
|
- 동적 설정을 .env 와 물리적으로 분리하고, .env 갱신은 완료 UI 노출 후로 미루는 설계.
|
|
|
|
axes:
|
|
installation_mode: [sse, polling]
|
|
runtime_state: [absent, present_with_db_only, present_with_db_and_key, present_corrupted]
|
|
env_state: [example_only, partially_filled, fully_finalized]
|
|
finalize_call: [success, browser_aborted, retry_after_failure]
|
|
rollback_trigger: [none, mid_install, post_finalize]
|
|
# 이슈 #371 — finalize 가드 자가 차단 회귀 axis
|
|
g7_installed_lock_state: [absent, present_before_finalize]
|
|
finalize_guard_signal: [lock_only, env_completed_true_only, lock_and_env_completed_true, neither]
|
|
orphaned_recovery_path: [none, upgrade_step_auto_finalize, upgrade_step_warn_manual_review]
|
|
# 이슈 #371 — 웹 서버 사용자/그룹 정합 axis (Upgrade Step 의 권한 조정)
|
|
cli_vs_phpfpm_user: [same_user, different_user_phpfpm_group_member, different_user_no_group_link]
|
|
env_preexisting_mode: [absent, 0600_strict, 0640_group_readable, 0664_group_writable]
|
|
bootstrap_cache_owner_group: [phpfpm_user, root_with_phpfpm_group, missing]
|
|
# 이슈 #371 — 부모 프로세스 stale ENV 가 spawn 자식에 전파되어
|
|
# Access denied 가 발생하던 회귀 차단 axis (한시적 보정 — beta.8 이후 트리거 안 됨)
|
|
parent_process_env_state: [matches_runtime, stale_from_env_example_root, stale_partial]
|
|
|
|
exclusions:
|
|
- { runtime_state: absent, env_state: example_only, finalize_call: success, reason: "runtime 부재 시 finalize 는 멱등적 no-op" }
|
|
- { runtime_state: present_corrupted, finalize_call: success, reason: "손상된 runtime 은 머지 미수행, runtime 보존" }
|
|
- { rollback_trigger: post_finalize, runtime_state: present_with_db_and_key, reason: "finalize 후 rollback 시점에는 runtime.php 가 이미 삭제된 상태" }
|
|
- { g7_installed_lock_state: present_before_finalize, finalize_guard_signal: lock_only, finalize_call: success, reason: "이슈 #371 회귀 가드 — 락 단독 시 finalize 통과해야 함 (자가 차단 금지)" }
|
|
- { orphaned_recovery_path: upgrade_step_warn_manual_review, env_state: fully_finalized, runtime_state: absent, reason: "정상 finalize 완료 환경은 warn 경로 자체가 트리거되지 않음" }
|
|
|
|
effects:
|
|
- install_step5_sse_does_not_modify_env_file
|
|
- install_step5_polling_does_not_modify_env_file
|
|
- env_update_task_writes_runtime_php_with_db_credentials
|
|
- key_generate_task_writes_app_key_to_runtime_php_via_pure_php
|
|
- key_generate_task_does_not_invoke_artisan_subprocess
|
|
- complete_flag_task_does_not_modify_env_file
|
|
- complete_flag_task_creates_g7_installed_marker_file
|
|
- retry_run_preserves_existing_app_key_in_runtime_php
|
|
- boot_with_runtime_php_injects_db_credentials_into_config
|
|
- boot_with_runtime_php_injects_app_key_into_config
|
|
- boot_without_runtime_php_is_silent_noop
|
|
- boot_with_corrupted_runtime_php_silently_skips
|
|
- encrypter_lazy_resolve_picks_up_injected_app_key
|
|
- finalize_endpoint_returns_202_immediately_via_flush
|
|
- finalize_endpoint_uses_fastcgi_finish_request_when_available
|
|
- finalize_endpoint_falls_back_to_ob_end_flush_on_cli_server
|
|
- finalize_writes_env_via_single_file_put_contents_with_lock_ex_for_one_mtime_change
|
|
- finalize_replaces_app_key_line_in_env_when_present
|
|
- finalize_appends_app_key_line_when_absent_from_template
|
|
- finalize_appends_installer_completed_true_with_status_comment
|
|
- finalize_does_not_duplicate_installer_completed_on_retry
|
|
- finalize_skips_app_key_when_runtime_value_lacks_base64_prefix
|
|
- finalize_replaces_db_write_credentials_with_runtime_values
|
|
- finalize_replaces_db_read_credentials_with_runtime_values_or_syncs_with_write
|
|
- finalize_replaces_db_prefix_with_runtime_value
|
|
- finalize_appends_db_lines_when_absent_from_template
|
|
- finalize_preserves_db_lines_when_runtime_has_no_db_block
|
|
- finalize_escapes_db_password_special_characters_via_escapeEnvValue
|
|
- finalize_completes_env_even_when_state_json_is_missing
|
|
- finalize_deletes_runtime_php_after_successful_merge
|
|
- finalize_deletes_state_json_after_successful_merge_when_DELETE_INSTALLER_AFTER_COMPLETE_set
|
|
- finalize_preserves_runtime_php_on_exception_for_retry
|
|
- finalize_no_op_when_runtime_php_absent_idempotent
|
|
- complete_flag_task_does_not_delete_state_json_so_finalize_can_read_state_config
|
|
- existing_db_action_drop_tables_resets_db_cleanup_db_migrate_db_seed_markers_unconditionally
|
|
- existing_db_action_drop_tables_preserves_non_db_task_markers_like_composer_check_env_update
|
|
- existing_db_action_skip_preserves_all_completed_task_markers
|
|
- retry_after_skip_failed_migration_with_drop_tables_consent_actually_drops_tables_before_remigration
|
|
- sse_to_polling_handoff_with_drop_tables_consent_drops_partially_created_tables_then_remigrates_from_scratch
|
|
- sse_completed_with_drop_tables_drops_existing_tables_then_migrates_normally
|
|
- polling_only_with_drop_tables_drops_existing_tables_then_migrates_normally
|
|
- sse_worker_exits_immediately_on_client_disconnect_via_connection_status_check
|
|
- sse_worker_does_not_continue_in_background_after_polling_worker_takeover_to_avoid_db_race
|
|
- install_worker_php_sets_ignore_user_abort_false_explicitly
|
|
- install_worker_php_enables_ob_implicit_flush_for_fast_disconnect_detection
|
|
- acquire_worker_lock_succeeds_when_no_active_worker_or_heartbeat_stale
|
|
- acquire_worker_lock_rejects_new_worker_when_active_heartbeat_within_stale_window
|
|
- acquire_worker_lock_takes_over_when_previous_heartbeat_exceeded_stale_seconds
|
|
- sse_emitter_refreshes_worker_heartbeat_on_each_emit_call
|
|
- polling_emitter_refreshes_worker_heartbeat_on_each_emit_call
|
|
- emitter_exits_immediately_when_another_worker_took_over_lock
|
|
- install_worker_releases_lock_on_normal_termination_via_register_shutdown_function
|
|
- install_process_polling_releases_lock_on_normal_termination
|
|
- install_worker_rejected_response_uses_aborted_event_with_busy_reason
|
|
- polling_rejected_logs_to_installation_log_and_exits_silently
|
|
- rollback_complete_flag_removes_runtime_php_when_present
|
|
- rollback_complete_flag_removes_installer_completed_from_env_when_present
|
|
- rollback_complete_flag_removes_g7_installed_marker_when_present
|
|
- artisan_serve_workers_restart_at_most_once_after_completion_ui_shown
|
|
- non_serve_environments_observe_zero_extra_restart
|
|
# 이슈 #371 — finalize 가드 자가 차단 회귀 차단 + 잔존 환경 자동 복구
|
|
- finalize_guard_passes_when_only_g7_installed_lock_is_present
|
|
- finalize_guard_passes_when_env_lacks_installer_completed_flag
|
|
- finalize_guard_blocks_when_env_has_installer_completed_true_idempotent
|
|
- finalize_guard_blocks_when_env_has_installer_completed_true_with_quoted_value
|
|
- finalize_guard_accepts_alternative_truthy_values_1_and_yes
|
|
- upgrade_step_auto_finalizes_orphaned_runtime_when_env_not_marked_completed
|
|
- upgrade_step_skips_when_runtime_php_is_absent_idempotent_noop
|
|
- upgrade_step_warns_when_env_marked_completed_but_runtime_php_still_exists_no_auto_delete
|
|
- upgrade_step_falls_back_to_env_example_when_env_file_is_absent
|
|
- upgrade_step_preserves_runtime_php_when_env_write_fails_safe_fallback
|
|
- upgrade_step_logs_skip_when_runtime_php_returns_non_array_format_mismatch
|
|
- upgrade_step_deletes_state_json_after_merge_when_DELETE_INSTALLER_AFTER_COMPLETE_set
|
|
- upgrade_step_preserves_state_json_when_DELETE_INSTALLER_AFTER_COMPLETE_unset
|
|
# 이슈 #371 — 웹 서버 권한 자동 조정
|
|
- upgrade_step_preserves_existing_env_mode_uid_gid_when_env_existed_before_merge
|
|
- upgrade_step_preserves_strict_0600_env_mode_when_owner_intent_present
|
|
- upgrade_step_uses_0640_default_permission_for_newly_created_env_not_0600
|
|
- upgrade_step_detects_web_server_group_from_bootstrap_cache_for_new_env
|
|
- upgrade_step_falls_back_to_storage_logs_when_bootstrap_cache_absent
|
|
- upgrade_step_skips_chown_when_lacking_root_privilege_silent_with_log_hint
|
|
- upgrade_step_skips_uid_when_bootstrap_cache_owner_is_root_to_avoid_root_owned_env
|
|
- upgrade_step_inherits_group_only_when_bootstrap_cache_owner_is_root_but_group_is_not
|
|
# 이슈 #371 — 부모 프로세스 process ENV 도 머지된 자격증명으로 갱신하여 spawn 자식 전파 보장
|
|
- upgrade_step_refreshes_parent_process_env_with_merged_db_write_credentials
|
|
- upgrade_step_refreshes_parent_process_env_with_merged_db_read_credentials
|
|
- upgrade_step_skips_env_refresh_when_runtime_credential_keys_are_missing
|
|
- upgrade_step_does_not_overwrite_app_key_env_when_runtime_value_lacks_base64_prefix
|
|
|
|
test_files:
|
|
- tests/Unit/Providers/InstallerRuntimeServiceProviderTest.php
|
|
- tests/Unit/Installer/InstallerRuntimeHelperTest.php
|
|
- tests/Unit/Installer/InstallerGuardTest.php
|
|
- tests/Feature/Console/Core/FinalizeOrphanedInstallerRuntimeTest.php
|
|
- tests/Feature/Upgrade/Beta7FinalizeOrphanedInstallerRuntimeTest.php
|
|
|
|
manual_verification:
|
|
- scenario: A_artisan_serve
|
|
description: |
|
|
php artisan serve 환경에서 Step 1~5 진행 중 "Environment modified" 메시지
|
|
0회 + Step 6 완료 화면 노출 직후 1회 재시작 + 재시작 후 .env 에 DB 자격증명
|
|
+ APP_KEY + INSTALLER_COMPLETED=true 모두 존재 + storage/installer/ 비어있음
|
|
- scenario: B_php_S_or_apache
|
|
description: |
|
|
워처 없는 환경에서 동일 흐름 실행 시 동작 차이 없음 (재시작 미발생, finalize
|
|
이후 .env 가 동일하게 머지됨)
|
|
- scenario: C_polling_mode
|
|
description: |
|
|
installer.js 가 폴링 모드로 강제 진입한 경우 state-management.php → completed
|
|
→ finalize 트리거 → .env 머지 동일하게 동작
|
|
- scenario: D_tab_close
|
|
description: |
|
|
Step 6 완료 직후 탭을 닫는 경우 runtime.php 가 보존되어 다음 부팅에서도
|
|
ServiceProvider 가 config 주입 → 앱 정상 동작 (관리자 재호출 또는 admin
|
|
알림 경로로 재시도 가능)
|
|
- scenario: E_rollback
|
|
description: |
|
|
Step 5 진행 중 의도된 실패 (DB 권한 오류 등) 발생 시 rollbackCompleteFlag
|
|
가 runtime.php / g7_installed / .env 의 INSTALLER_COMPLETED 모두 정리하여
|
|
인스톨러 재진입 가능
|
|
|
|
rules_layer_coverage:
|
|
- rule: service-provider-db-safety
|
|
coverage: InstallerRuntimeServiceProvider 는 runtime.php 부재 시 즉시 return, DB 미접근, Schema::hasTable 무사용
|
|
- rule: service-direct-data-access
|
|
coverage: Provider 는 Config::set 만 호출 (Eloquent/DB facade 미사용)
|
|
- rule: i18n-throw-hardcoded-korean
|
|
coverage: rollback_runtime_removed lang key 를 ko/en 양쪽 등록 (public/install/lang)
|