7.0.11 인스톨러·코어 업데이트 변경(e60a82d73)에 대해 과거 회귀 22건을 부류별로 대조한
결과, 신규 노출면 1건과 테스트 위생 1건이 나와 인터뷰 결정대로 조치했다.
1. argv 채널의 SAPI 게이트 — CGI/FPM 은 register_argc_argv=On 이면 $_SERVER['argv'] 를
쿼리스트링을 '+' 로 쪼개 채우므로(`GET /?x+core:update` → argv[1]==='core:update', php-cgi
실측) 비인증 웹 요청이 업데이트 트리로 판정되어 bootstrap/app.php 자가 치유가 요청마다
패키지 매니페스트를 지우고 다시 만들었다. CoreUpdateContext 와 bootstrap/app.php 복제본
모두 argv 를 cli·phpdbg 에서만 읽는다. env 플래그 채널은 웹에서 주입할 수 없으므로 그대로
두어 웹 요청 안에서 시작하는 업데이트 흐름(7.1.0)에 영향이 없다. 동형성 테스트에 SAPI 축을
더했다.
2. 매니페스트 삭제 실패 기록 — PackageManifestCacheHelper::clear 가 지우지 못한 파일의
경로를 돌려주고, spawn 직전 호출부가 업그레이드 로그·콘솔에 경고로 남긴다. 권한·소유권
불일치면 자식의 자가 치유도 같은 이유로 실패해 증상은 제보와 같은 「Class not found」 인데,
이 경고가 원인이 권한이라는 유일한 흔적이다.
3. 테스트 격리 — tests/bootstrap.php 가 APP_PACKAGES_CACHE/APP_SERVICES_CACHE 를 테스트
전용 경로로 돌린다. proc_open 으로 자식을 띄우는 기존 테스트 2종의 자식이 개발 클론의
실제 bootstrap/cache 매니페스트를 지우고 다시 쓰던 것(stat 실측)을 부모·자식 함께 막는다.
관리자 [시스템 최적화] 경로(withPreservedContainer 파사드 복원의 미실측 형제 호출처)는
임시 설치본에서 API 로 실측했다 — 200, 설정·라우트 캐시 재생성, 후속 요청 200, 로그 오류 0.
4. 트러블슈팅 사례 ↔ 회귀 테스트 앵커 계약 — 신규 사례는 헤딩에 <!-- case:{영역}-{번호} -->
앵커를 달고 같은 문자열을 그 사례를 잠그는 회귀 테스트에도 남겨야 한다. 사례 번호는
문서마다 1부터 재시작하고 병합으로 중복되므로(이번 리베이스에서도 우리 사례가 develop 과
같은 29 였다가 31 로 밀렸다), 개수만 대조하면 다른 사례를 덮는 테스트도 초록이 된다.
그런데 판정기 check-troubleshooting-test-coverage.cjs 를 부르는 지점이 저장소에 하나도
없었다 — 스크립트 자체 주석에만 실행법이 적혀 있어 아무도 부르지 않으면 영원히 돌지
않았고, 그 사이 위반이 9건 쌓였다(backend 26~31, cache 17~19). 돌지 않는 대조는 아무것도
잠그지 못하므로 위반 해소와 실행 지점 부여를 함께 한다.
9건 전부에 앵커를 부착하고(각 사례가 선언한 회귀 테스트 중 가장 구체적인 파일에 배치,
한 파일이 두 사례에 선언된 경우는 갈라 배치), stop-guard 7.2 에 앵커 계약 + 미커버
baseline ratchet 두 축으로 등록했다. 트러블슈팅 사례 추가 프로토콜에 6단계를
더하고 coverage 에 troubleshooting-case-anchor-contract(manual-only, 전용 판정기 위임)를
등재했다. 판정기 종료코드 1 → 0, 미커버 건수는 전 문서 baseline 그대로다.
80 lines
3.9 KiB
PHP
80 lines
3.9 KiB
PHP
<?php
|
|
|
|
namespace App\Support;
|
|
|
|
/**
|
|
* 현재 PHP 프로세스가 코어 업데이트 프로세스 트리 안에 있는지 판정하는 단일 SSoT.
|
|
*
|
|
* 코어 업데이트는 부모 `core:update` 가 `core:execute-upgrade-steps` 를 `proc_open` 으로
|
|
* spawn 하고, 그 자식이 다시 `config:cache` 등으로 일회용 Application 을 부팅하는 다층 구조다.
|
|
* 이 트리 안에서만 켜져야 하는 예외 동작이 여럿이라(확장 자동 비활성화 스킵, 코어 버전의
|
|
* env 우선 판독, `bootstrap/app.php` 의 패키지 매니페스트 자가 치유) 판정이 흩어지면
|
|
* 한 곳만 어긋나도 그 경로가 조용히 다르게 동작한다 — 판정을 여기 한 곳에 둔다.
|
|
*
|
|
* 판정 채널이 둘인 이유: `G7_UPDATE_IN_PROGRESS` env 는 부모가 세우고 spawn 자식에게
|
|
* `$env` 로 전파되지만, `variables_order` 에 `E` 가 없는 호스팅에서는 `$_ENV` 가 비어 있을 수
|
|
* 있어 argv 보조 판정을 함께 둔다.
|
|
*
|
|
* argv 채널은 명령줄 SAPI 에서만 읽는다. CGI/FPM 은 `register_argc_argv=On` 이면 `$_SERVER['argv']`
|
|
* 를 쿼리스트링을 `+` 로 쪼갠 값으로 채우므로(`GET /?x+core:update` → `argv[1] === 'core:update'`),
|
|
* 그 SAPI 에서 argv 를 믿으면 비인증 웹 요청이 업데이트 트리로 판정되어 `bootstrap/app.php` 의
|
|
* 자가 치유가 요청마다 매니페스트를 지운다. env 플래그 채널은 웹 요청으로 주입할 수 없어 SAPI 와
|
|
* 무관하게 인정한다 — 웹 요청 안에서 시작하는 업데이트 흐름은 그 플래그를 프로세스 안에서 세운다.
|
|
*
|
|
* 주의: `bootstrap/app.php` 의 자가 치유 블록은 부팅 전이라 이 클래스를 참조할 수 없어
|
|
* 같은 판정을 순수 PHP 로 복제한다. 조건을 바꾸면 그쪽도 함께 고친다.
|
|
*/
|
|
final class CoreUpdateContext
|
|
{
|
|
/**
|
|
* 코어 업데이트를 수행하는 artisan 커맨드 이름 (argv 보조 판정용)
|
|
*/
|
|
private const UPDATE_COMMANDS = ['core:update', 'core:execute-upgrade-steps'];
|
|
|
|
/**
|
|
* argv 보조 판정을 신뢰하는 SAPI — 명령줄 프로세스만. 웹 SAPI 의 argv 는 쿼리스트링에서 채워질 수 있다.
|
|
*/
|
|
private const CONSOLE_SAPIS = ['cli', 'phpdbg'];
|
|
|
|
/**
|
|
* 현재 프로세스가 코어 업데이트 트리(부모 core:update 또는 그 spawn 자식) 안에 있는지 판정합니다.
|
|
*
|
|
* 판정 조건 (OR):
|
|
* 1. 환경변수 `G7_UPDATE_IN_PROGRESS=1` — 부모가 시작 시 설정하고 spawn 자식에 전파
|
|
* 2. 명령줄 SAPI 에서 artisan 커맨드 이름이 `core:update` / `core:execute-upgrade-steps` —
|
|
* 1 이 전파되지 않은 극단 상황 대비 보조 판정. 웹 SAPI 에서는 읽지 않는다
|
|
*
|
|
* @param string|null $sapi 판정에 쓸 SAPI 이름. 기본은 `PHP_SAPI` 이며, 테스트가 웹 SAPI 를 주입할 때만 지정
|
|
* @return bool 업데이트 트리 안이면 true
|
|
*/
|
|
public static function isInProgress(?string $sapi = null): bool
|
|
{
|
|
if (self::hasEnvFlag()) {
|
|
return true;
|
|
}
|
|
|
|
if (! in_array($sapi ?? PHP_SAPI, self::CONSOLE_SAPIS, true)) {
|
|
return false;
|
|
}
|
|
|
|
$argv = $_SERVER['argv'] ?? [];
|
|
|
|
return in_array($argv[1] ?? '', self::UPDATE_COMMANDS, true);
|
|
}
|
|
|
|
/**
|
|
* `G7_UPDATE_IN_PROGRESS=1` 환경변수 플래그만 확인합니다 (argv 보조 판정 없음).
|
|
*
|
|
* spawn 자식 여부를 가려야 하는 지점(자식은 사전·사후 단계를 부모에 위임)에서 쓴다.
|
|
* argv 판정을 섞으면 `core:execute-upgrade-steps` 단독 실행이 자식으로 오판된다.
|
|
*
|
|
* @return bool env 플래그가 켜져 있으면 true
|
|
*/
|
|
public static function hasEnvFlag(): bool
|
|
{
|
|
$flag = $_ENV['G7_UPDATE_IN_PROGRESS'] ?? $_SERVER['G7_UPDATE_IN_PROGRESS'] ?? getenv('G7_UPDATE_IN_PROGRESS');
|
|
|
|
return $flag === '1' || $flag === 1 || $flag === true;
|
|
}
|
|
}
|