v7.0.0-beta.6 release
This commit is contained in:
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
|
||||
|
||||
Reference in new issue
Block a user