Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
cabf3bf
feat: キャッシュ生成を CLI へ一本化し, 実行時に書き込むキャッシュを分離する
nanasess Sep 1, 2026
e17a53a
fix: 翻訳と HTMLPurifier のキャッシュをビルド生成物として扱う
nanasess Sep 3, 2026
4ad0c19
fix: cache:clear でランタイムディレクトリの cache pool を削除する
nanasess Sep 3, 2026
779f266
Merge remote-tracking branch 'origin/feature/permission-doctor' into …
nanasess Sep 3, 2026
6e1ad04
Merge remote-tracking branch 'origin/feature/permission-doctor' into …
nanasess Sep 4, 2026
d10a176
docs: .env.dist に ECCUBE_UMASK の設定例を追加
nanasess Sep 4, 2026
b24f4bb
fix: 権限を分離した構成で CLI が動作するようにする
nanasess Sep 4, 2026
1e8d284
fix: プラグインの一時展開先の切り替えを CLI 実行時のみにする
nanasess Sep 4, 2026
3ece0e6
fix: pre-push フックが参照する dev コンテナ XML のパスを build ディレクトリへ追従させる
nanasess Sep 4, 2026
3a9e28d
fix: 権限を分離した docker 環境を prod 固定にし, DB サーバーを必須にする
nanasess Sep 4, 2026
c183c78
fix: 削除できない実行時キャッシュで例外を投げないようにする
nanasess Sep 4, 2026
8d57995
fix: レーンの期待値を実態に合わせ, 書き込めない実行時キャッシュへの保存を諦める
nanasess Sep 4, 2026
a1caef3
docs: .env.dist に ECCUBE_CLI_LOG_TO_FILE の設定例を追加
nanasess Sep 4, 2026
50124a2
Merge branch '4.4' into feature/cache-build-dir
nanasess Sep 7, 2026
203a063
fix(cache): cache:clear が実行時 cache pool の削除失敗を伝えるようにする
nanasess Sep 7, 2026
4e4e62c
fix(cache): cache:clear --no-warmup でコンテナが失われたことを案内する
nanasess Sep 7, 2026
5778151
fix(cache): 実行時 twig キャッシュの案内で cache:pool:clear を示さない
nanasess Sep 7, 2026
dedcdf2
fix(docker): 権限を分離した構成で起動時にビルドキャッシュを生成する
nanasess Sep 7, 2026
b542e27
fix: レビュー指摘 (CodeRabbit) の修正
nanasess Sep 7, 2026
ba2e003
fix: レビュー指摘 (CodeRabbit #7105) のうち Phase 2 の範囲を修正
nanasess Sep 7, 2026
7118cc5
test: setUp でスキップしたときに tearDown が未初期化プロパティを参照しないようにする
nanasess Sep 7, 2026
b1482e0
test: キャッシュ管理画面のテストが何も検証していなかったのを修正
nanasess Sep 8, 2026
c7c6c57
test: 案内が欠落しても順序の検証が通ってしまう空振りを修正
nanasess Sep 8, 2026
ad7763d
docs: 分離モードで cache:clear が使えないという記述を実測に合わせる
nanasess Sep 8, 2026
11c59b0
Merge remote-tracking branch 'upstream/4.4' into feature/cache-build-dir
nanasess Sep 9, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .env.dist
Original file line number Diff line number Diff line change
Expand Up @@ -57,5 +57,16 @@ ECCUBE_AUTH_MAGIC=<change.me>
#ECCUBE_2FA_COOKIE_NAME=eccube_2fa
#ECCUBE_2FA_EXPIRE=14
#ECCUBE_RESTRICT_FILE_UPLOAD=0
# umask for files and directories created by the application (octal notation).
# Leave it unset to follow the OS / PHP-FPM default (recommended).
# Set 0000 to restore the 4.3 behavior (directories 0777 / files 0666) when the web server
# and the CLI run as different users and both need to write the same files.
# Note that it also makes them writable by any other local user on the same server.
#ECCUBE_UMASK=

## CLI 実行時にログをファイル (var/log) へ書き込むかどうか。 未設定なら書き込む。
## Web サーバーと CLI で var/log の書き込み権限を分離した構成では 0 を設定する。
## CLI のログはコンソール出力のみになるため、 cron 等で記録が必要な場合はリダイレクトする。
#ECCUBE_CLI_LOG_TO_FILE=1

###< APPLICATION CONFIG ###
2 changes: 1 addition & 1 deletion .github/workflows/unit-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ jobs:
run: |
rm -r app/Plugin/*
git checkout app/Plugin
rm -r var/cache
rm -rf var/cache var/build var/runtime
echo "session.save_path=$PWD/var/sessions/test" > php.ini
echo "memory_limit=512M" >> php.ini
php -c php.ini vendor/bin/phpunit --group plugin-service
Expand Down
11 changes: 6 additions & 5 deletions .husky/pre-push
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,15 @@ case "${ECCUBE_HOOK_RUNNER:-auto}" in
esac
[ -n "$RUN" ] && echo "pre-push: running in Docker ($SVC)" || echo "pre-push: running on host"

# rector.php は dev の Symfony コンテナ XML (var/cache/dev/Eccube_KernelDevDebugContainer.xml) を
# rector.php は dev の Symfony コンテナ XML (var/build/dev/Eccube_KernelDevDebugContainer.xml) を
# 参照する。fresh clone / cache:clear 直後など XML が無い cold 状態だと rector が全ファイルで
# read error → exit 非ゼロになり push がブロックされる。無ければ dev キャッシュを生成しておく。
# (cache:warmup では XML が再生成されないため cache:clear を使う)
CONTAINER_XML="var/cache/dev/Eccube_KernelDevDebugContainer.xml"
# XML (debug.container.dump) はデバッグ用コンテナのコンパイル時にのみ出力されるため、
# .env で APP_DEBUG=0 の環境でも生成されるよう APP_DEBUG=1 を明示する。
CONTAINER_XML="var/build/dev/Eccube_KernelDevDebugContainer.xml"
if ! $RUN test -f "$CONTAINER_XML"; then
echo "pre-push: dev container XML 不在のため cache:clear --env=dev で生成"
if ! $RUN bin/console cache:clear --env=dev; then
echo "pre-push: dev container XML 不在のため cache:clear (dev/debug) で生成"
if ! $RUN env APP_ENV=dev APP_DEBUG=1 bin/console cache:clear; then
echo "FAIL: cache:clear(dev コンテナ XML を生成できませんでした)"
exit 1
fi
Expand Down
44 changes: 36 additions & 8 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,26 +112,48 @@ Web サーバー(`www-data`)と CLI(SSH ログインユーザー相当)
`docker-compose.permission-lanes.yml` を重ねる。`eccube:doctor:permissions` の動作確認に使う。

```bash
docker compose -f docker-compose.yml -f docker-compose.dev.yml -f docker-compose.permission-lanes.yml up -d --wait
# --build は必須。 公開イメージ (ghcr) には dockerbuild/docker-php-entrypoint のレーン分離が
# 含まれないため、 pull されたイメージのままだと www-data がホストユーザーへリマップされ分離されない。
# DB は SQLite だと var/eccube.db を CLI から書けないため、 DB サーバーを重ねる。
docker compose -f docker-compose.yml -f docker-compose.dev.yml -f docker-compose.pgsql.yml \
-f docker-compose.permission-lanes.yml up -d --build --wait
curl -s -o /dev/null http://127.0.0.1:8080/ # セッションを生成し Web サーバーの uid を判定可能にする
docker compose exec -u eccube ec-cube bin/console eccube:doctor:permissions
```

レーン W(`var`、`html/upload/**`、`app/keystore`)は `www-data` 所有とし、共有グループは作らない。
CLI からレーン W を触る操作は Web サーバーのユーザーで実行する。本番の `sudo -u www-data` に相当する。
レーン W(`var/runtime`、`var/sessions`、`var/log`、`html/upload/**`)は `www-data` 所有とし、
共有グループは作らない。CLI からレーン W を触る操作は Web サーバーのユーザーで実行する。
本番の `sudo -u www-data` に相当する。
`app/keystore` はレーン S。秘密鍵はデプロイ成果物で、Web サーバーから書き込めると署名鍵の
差し替えを許すため読み取りのみとする(実行時に鍵を生成する機能を使う場合は事前に配置しておく)。

```bash
docker compose exec -u eccube ec-cube bin/console eccube:page:apply ... # レーン S を触る操作
docker compose exec -u www-data ec-cube bin/console cache:clear # レーン W を触る操作
docker compose exec -u eccube ec-cube bin/console eccube:cache:build # レーン S を触る操作
docker compose exec -u www-data ec-cube bin/console cache:pool:clear --all # レーン W を触る操作
```

`cache:clear` は `var/build` と `var/cache` の双方へ書き込む。3 分割ではどちらもレーン S のため
CLI ユーザーなら成功する(Web サーバーのユーザーでは失敗する)。ただし `--no-warmup` を付けると
コンパイル済みコンテナが再生成されず、次のリクエストで Web サーバーが 500 になる。
コンパイル済みコンテナとテンプレートの再生成は `eccube:cache:build` を使う。

`var/log` はレーン W のため、CLI からはログファイルへ書き込めない。ログの出力に失敗すると本来のエラーが
ログ書き込みエラーへすり替わるため、分離した構成では `ECCUBE_CLI_LOG_TO_FILE=0` を設定し、CLI のログを
コンソール出力に寄せる(記録が必要な場合はリダイレクトする)。未設定なら従来どおりファイルへ書く。
`docker-compose.permission-lanes.yml` では既に設定済み。

アプリケーションが作成するファイルの umask は環境変数 `ECCUBE_UMASK`(8 進数表記)で設定する。
未設定なら OS / PHP-FPM の既定に従う(推奨)。Web サーバーと CLI が別ユーザーで、かつ双方が同じ
ファイルへ書き込む必要がある環境では `0000` を設定すると 4.3 以前と同じ挙動(ディレクトリ 0777 /
ファイル 0666)に戻せるが、同一サーバーの他ユーザーからも書き換え可能になる。

分離すると、`app/template` や `html/user_data` へ書き込む管理画面の機能(プラグイン導入・
ページ/ブロック/メールテンプレート編集・CSS/JS 編集・ファイル管理)は動作しなくなる。
CLI 側の代替導線は整備中のため、**日常の開発では重ねない**こと。

既定モードと分離モードを切り替えるときはレーン W のボリュームを作り直す。切り替え前の `www-data` の
uid で作成されたディレクトリが残り、切り替え後の Web サーバーから書き込めなくなる
(例: `var/cache/{env}/mcp-sessions`)。
(例: `var/runtime/{env}/mcp-sessions`)。

```bash
docker compose ... down -v
Expand Down Expand Up @@ -179,9 +201,15 @@ docker compose -f docker-compose.yml -f docker-compose.dev.yml -f docker-compose

### キャッシュ / データベース

キャッシュは 3 つのディレクトリに分かれる。`var/build/{env}`(コンパイル済みコンテナ・ルーティング・
メタデータ・prod の twig)と `var/cache/{env}`(翻訳カタログ・htmlpurifier)は CLI が生成し、
リクエスト処理中は読み取りのみ。`var/runtime/{env}`(cache pool・mcp-sessions・事前コンパイル漏れの
twig のフォールバック等)はリクエスト処理中に生成される。

```bash
bin/console cache:clear
bin/console cache:warmup
bin/console eccube:cache:build # var/build を再生成(テンプレートの事前コンパイルを含む)
bin/console cache:pool:clear --all # 実行時キャッシュ(cache pool)を削除
bin/console cache:clear # 従来どおり全体を削除(build と cache の双方に書き込み権限が必要)

# スキーマは Entity 属性が源泉。アップデートは 2 段構え:
bin/console doctrine:schema:update --dump-sql # 属性差分の SQL プレビュー
Expand Down
2 changes: 2 additions & 0 deletions app/config/eccube/packages/dev/web_profiler.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,5 @@ framework:
profiler:
only_exceptions: false
collect_serializer_data: true
# プロファイラはリクエスト処理中に書き込まれるため, ランタイムディレクトリへ置く.
dsn: 'file:%eccube_runtime_dir%/profiler'
15 changes: 14 additions & 1 deletion app/config/eccube/packages/eccube.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@ parameters:
env(ECCUBE_2FA_COOKIE_NAME): 'eccube_2fa'
env(ECCUBE_2FA_EXPIRE): '14'
env(ECCUBE_RESTRICT_FILE_UPLOAD): '0'
# アプリケーションが作成するファイル・ディレクトリの umask (8 進数表記).
# 空の場合は OS / PHP-FPM の既定に従う (推奨).
# Web サーバーと CLI が別ユーザーで, かつ双方が同じファイルへ書き込む必要がある環境では
# '0000' を設定すると 4.3 以前と同じ挙動 (ディレクトリ 0777 / ファイル 0666) になるが,
# 同一サーバーの他ユーザーからも書き換え可能になる.
env(ECCUBE_UMASK): ''

# EC-CUBE parameter
eccube_database_url: '%env(DATABASE_URL)%'
Expand All @@ -38,6 +44,13 @@ parameters:
eccube_auth_magic: '%env(ECCUBE_AUTH_MAGIC)%'
eccube_auth_type: HMAC
eccube_password_hash_algos: SHA256
# リクエスト処理中に Web サーバーが書き込むディレクトリ.
# kernel.cache_dir / kernel.build_dir は eccube:cache:build (CLI) が生成するため,
# 実行時に生成されるキャッシュ・一時ファイルはすべてここへ集約する (Eccube\Kernel::getRuntimeDir()).
eccube_runtime_dir: '%kernel.project_dir%/var/runtime/%kernel.environment%'
# 実際の適用は index.php / bin/console の apply_umask() が行う (コンテナ生成より前のため).
# ここでは診断・参照用にパラメータとして公開する.
eccube_umask: '%env(ECCUBE_UMASK)%'
eccube_theme_app_dir: '%kernel.project_dir%/app/template'
eccube_theme_src_dir: '%kernel.project_dir%/src/Eccube/Resource/template'
eccube_theme_user_data_dir: '%eccube_theme_app_dir%/user_data'
Expand All @@ -60,7 +73,7 @@ parameters:
# 本保存領域(eccube_save_refund_request_file_dir)の配下に置かない。配信検証のプレフィックス一致で一時ファイルが到達可能になるのを防ぐため、兄弟ディレクトリ(temp)に分離する
eccube_temp_refund_request_file_dir: '%kernel.project_dir%/html/upload/refund_request/temp'
eccube_csv_size: 5 # post_max_size, upload_max_filesize に任せればよい?
eccube_csv_temp_realdir: '%kernel.cache_dir%/%kernel.environment%/eccube' # upload_tmp_dir に任せればよい?
eccube_csv_temp_realdir: '%eccube_runtime_dir%/eccube' # upload_tmp_dir に任せればよい?
eccube_csv_split_lines: 100
eccube_default_password: 'abc*********123'
eccube_deliv_addr_max: 20
Expand Down
3 changes: 3 additions & 0 deletions app/config/eccube/packages/framework.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ framework:
# to avoid collisions when multiple apps share the same cache backend (e.g. a Redis server)
# See https://symfony.com/doc/current/reference/configuration/framework.html#prefix-seed
prefix_seed: ec-cube
# cache pool はリクエスト処理中に書き込まれるため, ランタイムディレクトリへ置く.
# cache.system 側は設定できないため RuntimeCacheDirPass で差し替える.
directory: '%eccube_runtime_dir%/pools/app'
# Lock factory. flock store works on shared hosting without external infra.
# Used by Agent Commerce (UCP Catalog) cache stampede prevention.
lock: flock
Expand Down
5 changes: 4 additions & 1 deletion app/config/eccube/packages/install/web_profiler.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,7 @@ web_profiler:
intercept_redirects: false

framework:
profiler: { only_exceptions: false }
profiler:
only_exceptions: false
# プロファイラはリクエスト処理中に書き込まれるため, ランタイムディレクトリへ置く.
dsn: 'file:%eccube_runtime_dir%/profiler'
3 changes: 3 additions & 0 deletions app/config/eccube/packages/mcp.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ mcp:
path: '/%eccube_admin_route%/mcp'
session:
store: file
# MCP セッションはリクエスト処理中に書き込まれるため, ランタイムディレクトリへ置く.
# (既定は %kernel.cache_dir%/mcp-sessions/<サーバ名>)
directory: '%eccube_runtime_dir%/mcp-sessions'
# 公開するのは Tool だけ。 scope 検査 (ScopeEnforcingReferenceHandler / ScopeFilteringRegistry) は
# Tool にしか掛からないため、 prompts / resources / resource_templates / apps はこのサーバに登録しない
registry:
Expand Down
17 changes: 16 additions & 1 deletion app/config/eccube/services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ parameters:
env(ECCUBE_MCP_ALLOWED_ORIGINS): ''
# MCP 監査ログ (mcp.log) の保管日数 (rotating_file の世代数)。 設計 §4.2
env(ECCUBE_MCP_LOG_RETENTION_DAYS): '90'
# CLI 実行時もログをファイルへ書くか。 Web サーバーと CLI で var/log の書き込み権限を
# 分離した構成では 0 を設定し、 CLI のログはコンソール出力 (必要ならリダイレクト) に寄せる。
env(ECCUBE_CLI_LOG_TO_FILE): '1'
locale: '%env(ECCUBE_LOCALE)%'
timezone: '%env(ECCUBE_TIMEZONE)%'
currency: '%env(ECCUBE_CURRENCY)%'
Expand Down Expand Up @@ -304,6 +307,18 @@ services:
arguments:
$container: !tagged_locator { tag: 'cache.pool.clearer' }

# cache:clear で %eccube_runtime_dir%/pools を削除する (kernel.cache_clearer は autoconfigure で付与).
Eccube\Util\RuntimeCachePoolClearer:
arguments:
$runtimeDir: '%eccube_runtime_dir%'

# cache:clear の後始末 (コンパイル済みコンテナの再生成・実行時 cache pool の削除) を案内する
Eccube\EventListener\RuntimeCachePoolClearListener:
arguments:
$buildDir: '%kernel.build_dir%'
$cacheDir: '%kernel.cache_dir%'
$containerClass: '%kernel.container_class%'

Eccube\Asset\FilemtimeVersionStrategy:
arguments:
- '%eccube_html_dir%/user_data'
Expand Down Expand Up @@ -405,7 +420,7 @@ services:
# Agent Commerce: UCP Catalog (pull / REST) (#6794)
Eccube\Service\AgentCommerce\Catalog\Ucp\UcpCatalogCache:
arguments:
$cacheDir: '%kernel.cache_dir%'
$cacheDir: '%eccube_runtime_dir%'

# Agent Commerce: UCP Discovery (/.well-known/ucp) (#6794 / #6777)
# payment_handlers は決済ハンドラプラグインが tagged service で寄与する (既定は空 {}).
Expand Down
4 changes: 3 additions & 1 deletion bin/console
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ use Symfony\Bundle\FrameworkBundle\Console\Application;
use Symfony\Component\Console\Input\ArgvInput;
use Symfony\Component\ErrorHandler\Debug;

umask(0000);
set_time_limit(0);

require __DIR__.'/../vendor/autoload.php';
Expand Down Expand Up @@ -36,6 +35,9 @@ if (!isset($_SERVER['APP_ENV'])) {
}
}

// umask はコンテナ生成より前に決める必要があるため, 環境変数 ECCUBE_UMASK から読み込む.
apply_umask();

$input = new ArgvInput();
$env = $input->getParameterOption(['--env', '-e'], $_SERVER['APP_ENV'] ?? 'dev');
$debug = ($_SERVER['APP_DEBUG'] ?? true) !== '0' && !$input->hasParameterOption(['--no-debug', '']);
Expand Down
Loading
Loading