v7.0.0-beta.6 release

This commit is contained in:
HeuJung committed 2026-05-14 12:24:08 +09:00
1 parent 61f5874fd2
commit c186d9a9c2
3 files changed
+139 -11

No files matched your search

@@ -109,20 +109,35 @@ class ExecuteUpgradeStepsCommand extends Command
]);
}
// 코어 업데이트 spawn 자식 감지.
//
// CoreUpdateCommand::spawnUpgradeStepsProcess 가 자식에 `G7_UPDATE_IN_PROGRESS=1` env 를
// 전달한다 (beta.3 부터 존재). 이 시그널은 자식이 부모의 `core:update` 컨텍스트에서
// 실행 중임을 의미한다. 부모는 본인이 사전(Step 9 migration + resync) + 사후
// (Step 11 version-env + cache-clear + bundled prompt) 단계를 모두 책임지므로 자식은
// 5단계 모두 스킵해야 한다.
//
// 구버전 부모(예: beta.5)는 신버전 자식이 도입한 `--skip-*` 옵션을 모른다. 옵션 부재
// 시 자식이 사후 단계를 실행하면 (특히 bundled prompt) non-interactive 환경에서
// `Aborted.` exit=1 회귀 발생 (882deb9b0 사각지대). env 시그널이 옵션 부재를 보완.
//
// 단독 호출 (운영자 수동) 시엔 env 미설정 → 모든 단계 정상 진행.
$isSpawnChild = getenv('G7_UPDATE_IN_PROGRESS') === '1';
// 사전 단계 (단독 실행 안전성 보장).
//
// CoreUpdateCommand 가 spawn 호출 시엔 부모 Step 9 (CoreUpdateCommand.php:408-409) 에서
// 이미 동일 단계를 실행했으므로 --skip-migrations / --skip-resync 로 중복 회피.
// 운영자 단독 호출 시 옵션 미전달 → 기본값으로 두 단계 자동 수행 →
// migration / permission / menu / seeder 누락 차단.
if (! $this->option('skip-migrations')) {
if (! $this->option('skip-migrations') && ! $isSpawnChild) {
$this->info('마이그레이션 실행');
$service->runMigrations();
} else {
$this->info('[spawn] 마이그레이션 스킵 — 부모가 이미 실행');
}
if (! $this->option('skip-resync')) {
if (! $this->option('skip-resync') && ! $isSpawnChild) {
$this->info('코어 config 재로드 및 권한/메뉴/시더 동기화');
$service->reloadCoreConfigAndResync();
} else {
@@ -194,21 +209,21 @@ class ExecuteUpgradeStepsCommand extends Command
// --skip-version-env / --skip-cache-clear / --skip-bundled-updates 로 중복 회피.
// 단독 실행 시엔 옵션 미전달 → 기본값으로 3단계 자동 수행 → gnuboard/g7#34 의 운영자 수동 절차
// (sed APP_VERSION + cache:clear + module/plugin/template:update --force --source=bundled) 통합.
if (! $this->option('skip-version-env')) {
if (! $this->option('skip-version-env') && ! $isSpawnChild) {
$this->info(".env APP_VERSION={$to} 갱신");
$service->updateVersionInEnv($to);
} else {
$this->info('[spawn] .env APP_VERSION 갱신 스킵 — 부모가 처리');
}
if (! $this->option('skip-cache-clear')) {
if (! $this->option('skip-cache-clear') && ! $isSpawnChild) {
$this->info('캐시 정리 (config/route/view/services/packages)');
$service->clearAllCaches();
} else {
$this->info('[spawn] 캐시 정리 스킵 — 부모가 처리');
}
if (! $this->option('skip-bundled-updates')) {
if (! $this->option('skip-bundled-updates') && ! $isSpawnChild) {
$this->info('번들 확장 일괄 업데이트 (모듈/플러그인/템플릿/언어팩)');
$this->runBundledExtensionUpdatePrompt(
app(ModuleManager::class),
@@ -181,6 +181,97 @@ class ExecuteUpgradeStepsStandaloneTest extends TestCase
$this->assertSame(0, $exitCode);
}
public function test_spawn_child_env_bypasses_all_pre_and_post_steps_without_skip_options(): void
{
// 구버전 부모(beta.5) 가 신버전 자식(beta.6+) 을 spawn 할 때 `--skip-*` 옵션을 모르므로
// 전달하지 않지만, `G7_UPDATE_IN_PROGRESS=1` env 는 beta.3+ 부모부터 항상 설정한다.
// 자식은 env 시그널만으로 본인이 spawn 컨텍스트임을 감지하여 5단계 모두 자동 스킵해야
// 한다 (옵션 부재 + non-interactive 환경에서 prompt 호출로 인한 Aborted exit=1 회귀 차단).
[$service, $module, $plugin, $template, $langPack] = $this->bindMocks();
$service->shouldNotReceive('runMigrations');
$service->shouldNotReceive('reloadCoreConfigAndResync');
$service->shouldReceive('runUpgradeSteps')->once();
$service->shouldNotReceive('updateVersionInEnv');
$service->shouldNotReceive('clearAllCaches');
$service->shouldNotReceive('collectBundledExtensionUpdates');
$langPack->shouldNotReceive('collectBundledLangPackUpdates');
$originalEnv = getenv('G7_UPDATE_IN_PROGRESS');
putenv('G7_UPDATE_IN_PROGRESS=1');
try {
$exitCode = $this->runCommand([]);
$this->assertSame(0, $exitCode);
} finally {
if ($originalEnv === false) {
putenv('G7_UPDATE_IN_PROGRESS');
} else {
putenv('G7_UPDATE_IN_PROGRESS='.$originalEnv);
}
}
}
public function test_spawn_child_env_skips_even_when_force_is_set(): void
{
// env=1 + --force 동시 적용 시에도 사후 단계 모두 스킵. force 는 자식이 실행하는 step
// 자체의 동일 버전 강제 플래그로만 사용되며, env 시그널이 우선한다 (부모가 force 든
// 아니든 부모가 5단계를 책임진다는 계약 보존).
[$service, $module, $plugin, $template, $langPack] = $this->bindMocks();
$service->shouldNotReceive('runMigrations');
$service->shouldNotReceive('reloadCoreConfigAndResync');
$service->shouldReceive('runUpgradeSteps')->once();
$service->shouldNotReceive('updateVersionInEnv');
$service->shouldNotReceive('clearAllCaches');
$service->shouldNotReceive('collectBundledExtensionUpdates');
$langPack->shouldNotReceive('collectBundledLangPackUpdates');
$originalEnv = getenv('G7_UPDATE_IN_PROGRESS');
putenv('G7_UPDATE_IN_PROGRESS=1');
try {
$exitCode = $this->runCommand(['--force' => true]);
$this->assertSame(0, $exitCode);
} finally {
if ($originalEnv === false) {
putenv('G7_UPDATE_IN_PROGRESS');
} else {
putenv('G7_UPDATE_IN_PROGRESS='.$originalEnv);
}
}
}
public function test_standalone_invocation_without_env_still_runs_all_steps(): void
{
// env 미설정 (운영자 수동 호출) 시 기존 단독 실행 계약 보존 — 5단계 모두 실행.
// env 가드 도입이 단독 실행 경로에 회귀를 일으키지 않음을 확인.
[$service, $module, $plugin, $template, $langPack] = $this->bindMocks();
$service->shouldReceive('runMigrations')->once();
$service->shouldReceive('reloadCoreConfigAndResync')->once();
$service->shouldReceive('runUpgradeSteps')->once();
$service->shouldReceive('updateVersionInEnv')->once();
$service->shouldReceive('clearAllCaches')->once();
$service->shouldReceive('collectBundledExtensionUpdates')->once()->andReturn([
'modules' => [], 'plugins' => [], 'templates' => [],
]);
$langPack->shouldReceive('collectBundledLangPackUpdates')->once()->andReturn([]);
// env 가 설정되지 않은 상태임을 명시적으로 보장.
$originalEnv = getenv('G7_UPDATE_IN_PROGRESS');
putenv('G7_UPDATE_IN_PROGRESS');
try {
$exitCode = $this->runCommand([]);
$this->assertSame(0, $exitCode);
} finally {
if ($originalEnv !== false) {
putenv('G7_UPDATE_IN_PROGRESS='.$originalEnv);
}
}
}
public function test_spawn_passes_all_five_skip_options_to_child(): void
{
// 부모 CoreUpdateCommand::spawnUpgradeStepsProcess 가 자식 command 배열에
@@ -14,30 +14,44 @@ description: |
본 매니페스트는 5개 누락 단계를 자식 기본값에 포함시키고, 부모 spawn 호출 시엔
5개 `--skip-*` 옵션으로 중복 회피하는 cross product 의 회귀 가드.
추가 가드 (2026-05-14): 구버전 부모(beta.5 이하) 가 신버전 자식(beta.6+) 을 spawn 할 때
`--skip-*` 5개 옵션은 모르지만 `G7_UPDATE_IN_PROGRESS=1` env 는 beta.3+ 부모부터
항상 설정한다. 자식은 env 시그널만으로도 spawn 컨텍스트를 감지하여 5단계 모두 스킵해야
한다 (옵션 부재 + non-interactive 환경에서 prompt 호출로 인한 Aborted exit=1 회귀 차단).
구성:
1. 5개 사전·사후 단계 각각의 단독 호출 / `--skip-*` 옵션 분기 검증.
2. 부모 `CoreUpdateCommand::spawnUpgradeStepsProcess` 가 자식 command 배열에
5개 옵션을 모두 전달하는지 (escapeshellarg 후 commandLine 문자열 검증).
3. `runUpgradeSteps` 본 임무는 항상 호출 (옵션 무관).
4. 5개 옵션을 모두 전달한 경우 CoreUpdateCommand spawn 시나리오 등가 — 사전·사후 미수행.
5. `G7_UPDATE_IN_PROGRESS=1` env 만으로도 5단계 모두 스킵 (구버전 부모 V-1 안전 가드).
axes:
invocation_origin: [standalone_operator, parent_spawn] # 누가 호출했는가
invocation_origin: [standalone_operator, parent_spawn_with_skip_options, parent_spawn_env_only] # 누가 호출했는가
skip_migrations: [present, absent]
skip_resync: [present, absent]
skip_version_env: [present, absent]
skip_cache_clear: [present, absent]
skip_bundled_updates: [present, absent]
spawn_env_flag: [present, absent] # G7_UPDATE_IN_PROGRESS=1 시그널
force_flag: [present, absent] # bundled prompt 우회 여부
bundled_updates_count: [zero, modules_only, plugins_only, templates_only, lang_packs_only, mixed]
from_to_relation: [from_lt_to, from_eq_to_forced] # version_compare 분기
exclusions:
- { invocation_origin: parent_spawn, skip_migrations: absent, reason: "부모 spawn 시엔 5개 옵션 모두 전달 — 일부 절단 시뮬은 인위적 비현실 분기" }
- { invocation_origin: parent_spawn, skip_resync: absent, reason: "동일 — 부모 spawn 시 5개 옵션 일괄 전달" }
- { invocation_origin: parent_spawn, skip_version_env: absent, reason: "동일" }
- { invocation_origin: parent_spawn, skip_cache_clear: absent, reason: "동일" }
- { invocation_origin: parent_spawn, skip_bundled_updates: absent, reason: "동일" }
- { invocation_origin: parent_spawn_with_skip_options, skip_migrations: absent, reason: "부모(beta.6+) spawn 시엔 5개 옵션 모두 전달 — 일부 절단 시뮬은 인위적 비현실 분기" }
- { invocation_origin: parent_spawn_with_skip_options, skip_resync: absent, reason: "동일 — 부모 spawn 시 5개 옵션 일괄 전달" }
- { invocation_origin: parent_spawn_with_skip_options, skip_version_env: absent, reason: "동일" }
- { invocation_origin: parent_spawn_with_skip_options, skip_cache_clear: absent, reason: "동일" }
- { invocation_origin: parent_spawn_with_skip_options, skip_bundled_updates: absent, reason: "동일" }
- { invocation_origin: parent_spawn_env_only, skip_migrations: present, reason: "구버전 부모(beta.5) 는 5개 옵션을 모르므로 전달하지 않음 — 본 분기는 env 가드 단독 효과 검증" }
- { invocation_origin: parent_spawn_env_only, skip_resync: present, reason: "동일" }
- { invocation_origin: parent_spawn_env_only, skip_version_env: present, reason: "동일" }
- { invocation_origin: parent_spawn_env_only, skip_cache_clear: present, reason: "동일" }
- { invocation_origin: parent_spawn_env_only, skip_bundled_updates: present, reason: "동일" }
- { invocation_origin: parent_spawn_env_only, spawn_env_flag: absent, reason: "정의상 env_only 분기는 env=1 필수" }
- { invocation_origin: standalone_operator, spawn_env_flag: present, reason: "운영자 수동 호출은 env 미설정 — env 설정 시는 parent_spawn 분기로 분류" }
- { invocation_origin: standalone_operator, bundled_updates_count: zero, skip_bundled_updates: absent, force_flag: absent, reason: "0건 + 옵션 미사용 + force 미사용 시 prompt 발동되나 본 매트릭스의 회귀 대상은 옵션 분기뿐" }
effects:
@@ -63,6 +77,14 @@ effects:
- CoreUpdateCommand_spawnUpgradeStepsProcess_appends_skip_bundled_updates_to_command_array
# §5 운영자 안내문 (resumeCommand) 호환성 — 옵션 없이도 기본값으로 안전 실행
- resume_command_without_skip_options_invokes_all_five_pre_and_post_steps_safely
# §6 구버전 부모 V-1 안전 가드 (env 시그널 단독)
- ExecuteUpgradeStepsCommand_skips_runMigrations_when_spawn_env_flag_present_without_skip_options
- ExecuteUpgradeStepsCommand_skips_reloadCoreConfigAndResync_when_spawn_env_flag_present_without_skip_options
- ExecuteUpgradeStepsCommand_skips_updateVersionInEnv_when_spawn_env_flag_present_without_skip_options
- ExecuteUpgradeStepsCommand_skips_clearAllCaches_when_spawn_env_flag_present_without_skip_options
- ExecuteUpgradeStepsCommand_skips_bundled_updates_when_spawn_env_flag_present_without_skip_options
- ExecuteUpgradeStepsCommand_env_guard_dominates_over_force_flag_for_post_steps
- ExecuteUpgradeStepsCommand_standalone_invocation_without_env_runs_all_five_steps_intact
test_files:
- tests/Feature/Console/Commands/ExecuteUpgradeStepsStandaloneTest.php