diff --git a/.github/workflows/api-inventory-tests.yml b/.github/workflows/api-inventory-tests.yml new file mode 100644 index 0000000000..602ee772d4 --- /dev/null +++ b/.github/workflows/api-inventory-tests.yml @@ -0,0 +1,59 @@ +# WEKO3 リポジトリ(RCOSDP/weko)の .github/workflows/ に配置する。 +# +# 台帳ツールの単体テスト。**Docker も実機も台帳も要らない**ので数秒で終わる。 +# api-inventory-drift.yml(実機を起こして突き合わせる。60分枠)とは役割が違う: +# +# このワークフロー … 台帳を作る側(スクリプト・手順書)が壊れていないか +# drift ワークフロー … 台帳の中身が実機とずれていないか +# +# ツールが壊れたまま drift だけ回すと、検知器が黙って死んでいても緑で通る。 +# 先にこちらを通すこと。Secret も不要なので fork からの PR でも動く。 + +name: API Inventory Tests + +# 対象は tools/api-inventory/ だけなので、そこを触ったときだけ回す。 +# push と pull_request でパスの並びを揃えること(片方だけ古びると、 +# 「PR では回るが push では回らない」といった説明のつかない差になる)。 +on: + pull_request: + paths: &paths + - 'tools/api-inventory/**' + - '.github/workflows/api-inventory-tests.yml' + push: + branches: ['**'] + paths: *paths + workflow_dispatch: + +jobs: + unit: + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-python@v5 + with: + python-version: '3.11' + + - name: Install pytest + run: python3 -m pip install --disable-pip-version-check pytest + + - name: Run unit tests + working-directory: tools/api-inventory + run: python3 -m pytest -q + + # 台帳が無くても、ソースからの経路検知そのものは動く。 + # 検知件数が 0 に落ちていれば、検知器が壊れている。 + - name: Smoke check the static detector + run: | + set -o pipefail + python3 tools/api-inventory/scripts/detect_routes.py \ + --weko-root "$PWD" --summary-only | tee /tmp/detect.md + python3 - <<'PY' + import re, sys + text = open('/tmp/detect.md', encoding='utf-8').read() + total = int(re.search(r'\*\*計\*\* \| \*\*(\d+)\*\*', text).group(1)) + print(f'detections={total}') + # 経路が数百ある前提のリポジトリ。2桁に落ちたら検知器の故障を疑う。 + sys.exit(0 if total >= 300 else 1) + PY diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index 4b8dcec6f5..babb3fbeeb 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -1,6 +1,6 @@ # ユニットテスト。 # -# 【設計】本体イメージは「1回だけ」ビルドして GHCR に置き、45個のマトリクス +# 【設計】本体イメージは「1回だけ」ビルドして GHCR に置き、47個のマトリクス # ジョブはそれを pull するだけにしている。 # # - イメージの用意は ci-images.yml (UIテストと共通) に委譲している。 @@ -20,6 +20,11 @@ # - tox が毎回 requirements2.txt (約290パッケージ) を入れ直す分は、 # bind mount 済みの .ci-cache/pip を pip のキャッシュにして共有する。 # +# - 手元で同じことをするには scripts/ci/run-local.sh を使う。同じ compose +# オーバレイ・同じ待ち受けスクリプト・同じ run-module-tests.sh・同じマトリクスを +# 読むので、ローカルと CI で結果が食い違わない。 +# モジュール一覧はこのファイルの matrix が唯一の正 (scripts/ci/matrix.sh が読む)。 +# # fork からの PR は GHCR に push できない。その場合はビルドキャッシュ # (type=gha) だけ作り、各ジョブがそこからローカルビルドする。 @@ -36,6 +41,18 @@ on: default: false jobs: + # マトリクスの列挙漏れを止める。数十行のリストは静かに古びる: + # v2.0.5 までに weko-notifications / weko-signposting / weko-workspace の3つが + # 漏れ、テスト一式(283本)を持ちながら一度も実行されていなかった。 + # ジョブが立たない以上、赤くもならないので誰も気付けない。 + matrix-check: + name: Matrix covers every testable module + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + - uses: actions/checkout@v4 + - run: bash scripts/ci/matrix.sh check + images: uses: ./.github/workflows/ci-images.yml permissions: @@ -95,17 +112,20 @@ jobs: - weko-items-ui - weko-itemtypes-ui - weko-logging + - weko-notifications - weko-plugins - weko-records-ui - weko-records - weko-redis - weko-schema-ui - weko-search-ui + - weko-signposting - weko-sitemap - weko-swordserver - weko-theme - weko-user-profiles - weko-workflow + - weko-workspace steps: - name: Checkout code @@ -199,7 +219,7 @@ jobs: [ -d .ci-cache ] || exit 0 sudo chown -R "$(id -u):$(id -g)" .ci-cache - # 45ジョブが同じキーで保存を試みるが、先着1つだけが保存され残りは + # 47ジョブが同じキーで保存を試みるが、先着1つだけが保存され残りは # 予約に失敗してスキップされる(警告のみ)。モジュール間で # requirements2.txt はほぼ同一なので、1つ保存されれば全体に効く。 - name: Save pip cache diff --git a/AGENTS.md b/AGENTS.md index 3343b8a123..de36007a14 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -14,10 +14,56 @@ - 環境構築後、`https://127.0.0.1/` でサーバにアクセスすることができる。 ## テストの実行方法 / Testing -- ユニットテストを実行: `python manage.py test` - (またはpytest使用時: `pytest`) -- 新機能を追加した際は必ず対応するテストコードを追加してください -- テストが全てパスすることを確認してから変更を確定します + +### 手元で回す — **CI と同じ経路を使うこと** + +```bash +scripts/ci/run-local.sh weko-records # 1モジュール +scripts/ci/run-local.sh --all # マトリクス全部 +scripts/ci/run-local.sh --list # 対象モジュール一覧 +``` + +GitHub Actions の Unit Tests ジョブと同じ compose オーバレイ・同じ待ち受け +スクリプト・同じ `run-module-tests.sh`(= tox)・同じモジュール一覧を使う。 +**別の回し方をしないこと。** 違う回し方をすると、テストは正常なのに落ちる: + +- 手元の無関係な `weko-web` イメージを流用 → イメージに焼き付いた古い egg-info の + entry_point を `invenio_assets` が読みにいって大量の ImportError +- invenio の venv で直接 `pytest` → `pytest-mock` / `mock` が無く + `fixture 'mocker' not found` + +`run-local.sh` は起動前に「別の WEKO スタックとのポート衝突」と +「イメージの egg-info が古くないか」を確認して、この2つを事前に落とす。 + +CI との差は Elasticsearch を `discovery.type=single-node` で起動する1点だけ +(AMD / ARM を問わず同じ。理由は `scripts/ci/compose.local.yml`)。 +**最終的な合否は CI で確認する。** + +詳細は `README-TEST.md`。 + +### 台帳ツール(tools/api-inventory)のテスト + +```bash +cd tools/api-inventory && python3 -m pytest # 数秒。Docker も台帳も不要 +``` + +### 新しいモジュールを足したとき + +`.github/workflows/unit-tests.yml` の `matrix.module` が**モジュール一覧の唯一の正**。 +`tests/` と `tox.ini` を持つのに未登録だと、CI の `matrix-check` ジョブが落とす +(ジョブが立たない = 赤くもならない、という静かな漏れを防ぐため。実際に3モジュール +283本がこの状態で放置されていた)。手元では次で確認する。 + +```bash +scripts/ci/matrix.sh check +``` + +### 変更を確定する前に + +- 新機能を追加した際は必ず対応するテストコードを追加する +- 触ったモジュールを `run-local.sh` で回し、パスすることを確認する +- 既存の失敗と自分の変更による失敗を必ず区別する。develop_v2.0.5 時点で + ベースラインに複数の失敗が残っているため、「赤い = 自分のせい」とは限らない ## コードスタイル / Code Style - コーディング規約: **PEP8**に準拠 (スタイルガイドの遵守) @@ -28,12 +74,12 @@ ## セキュリティ方針 / Security - **秘密情報は厳重に管理**: APIキーやパスワードなど秘密情報は`.env`や環境変数から読み込み、絶対にGitに含めないでください -- **ユーザ入力の検証**: フォームやAPIで受け取る入力はDjangoのバリデーション機構で適切に検証してください -- **デバッグ設定**: 開発中以外では`DEBUG = False`に設定し、エラーページや機密情報が漏洩しないようにします +- **ユーザ入力の検証**: フォームやAPIで受け取る入力は Flask-WTF / marshmallow / JSON Schema など、そのモジュールで既に使われている検証機構で必ず検証してください(本プロジェクトは Django ではありません) +- **デバッグ設定**: 開発中以外では `FLASK_ENV=production` / `DEBUG = False` とし、エラーページや機密情報が漏洩しないようにします - **依存パッケージ**: 新しいパッケージを導入する際はセキュリティ面を確認し、必要に応じてチームの承認を得てください ## プルリクエストガイドライン / PR Guidelines - **タイトル形式**: `feat: 機能概要` のように、プレフィックスと簡潔な説明を書いてください -- **事前チェック**: コードを提出する前に `flake8` や `pytest` を実行し、エラーやテスト失敗がないことを確認しましょう +- **事前チェック**: コードを提出する前に `flake8` と `scripts/ci/run-local.sh <触ったモジュール>` を実行し、エラーやテスト失敗がないことを確認しましょう - **差分の範囲**: 1つのPRは関連する変更に留め、小さくまとまった変更を心がけてください(大規模な変更は分割を検討) - **説明コメント**: PRの説明欄には変更内容と目的、動作確認の方法を簡潔に記述してください \ No newline at end of file diff --git a/README-TEST.md b/README-TEST.md index 14093bc193..4793c8637e 100644 --- a/README-TEST.md +++ b/README-TEST.md @@ -1,5 +1,91 @@ # Running tests locally +## CI と同じ経路で回す(推奨) + +```shell +scripts/ci/run-local.sh weko-records # 1モジュール +scripts/ci/run-local.sh --all # マトリクス全部 +scripts/ci/run-local.sh --list # 対象モジュール一覧 +``` + +GitHub Actions の Unit Tests ジョブと**同じ部品**を呼びます。 + +| | ローカル | CI | +|---|---|---| +| compose | `docker-compose2.yml:docker-compose.ci.yml` | 同左 | +| 起動サービス | postgresql / elasticsearch / redis / rabbitmq のみ | 同左 | +| 起動待ち | `scripts/ci/wait-for-services.sh` | 同左 | +| テスト実行 | `scripts/ci/run-module-tests.sh`(= tox) | 同左 | +| モジュール一覧 | `.github/workflows/unit-tests.yml` の matrix | 同左 | +| イメージ | 同じ入力ファイルのハッシュでタグ付け、無ければビルド | 同じ入力で GHCR から pull | + +分岐しているのはイメージの入手方法だけです。CI と完全に同一のイメージで +確かめたいときは `WEKO_IMAGE` / `WEKO_ES_IMAGE` で明示してください。 + +### ローカルだけで回すと踏む罠 + +**別の回し方をすると、テストは正常なのに落ちます。** 実測した2件: + +- **手元の無関係な `weko-web` イメージを流用した** → イメージに焼き付いた古い + egg-info の entry_point(`weko_theme.bundles:js_preview_widget`。現行の + `setup.py` には無い)を `invenio_assets` が読みにいって **191件が ImportError**。 + CI は `ci-images.yml` が `modules/*/setup.py` を含むハッシュでタグを決めるので、 + `setup.py` が変われば作り直され発生しません。 + `run-local.sh` は起動直後に entry_point の健全性を確認して落とします。 +- **invenio の venv で直接 `pytest` を叩いた** → `pytest-mock` / `mock` が無く + `fixture 'mocker' not found`。CI は tox が `requirements2.txt` から入れます。 + +また、別の WEKO スタックを動かしたままだとポート(29201 / 26301 / 24301)が +衝突し、最悪そちらのサービスを掴みます。`run-local.sh` は起動前に検出します。 + +### CI との唯一の差: Elasticsearch の bootstrap check + +`run-local.sh` は `scripts/ci/compose.local.yml` を重ねて、Elasticsearch を +`discovery.type=single-node` で起動します。**AMD(x86_64)でも ARM でも同じ**で、 +アーキテクチャによる分岐はしません。 + +ES 6.8 は非ループバックアドレスに bind した時点で bootstrap check(本番運用向けの +検査)を強制しますが、これは**ホストのカーネルと sysctl に依存する**ため、 +開発機では環境しだいで落ちます。確認できたものだけでも: + +- **ARM**: seccomp の実装が x86_64 専用で、`seccomp unavailable: + CONFIG_SECCOMP not compiled into kernel` を投げて起動しない +- **`vm.max_map_count` が 262144 未満のホスト**: `max_map_count` の検査で落ちる + +`discovery.type=single-node` にすると bootstrap check 自体が省かれます。ES は +テストが使う単一ノードなので意味は変わりません(リポジトリの +`docker-compose.arm64.yml` も同じ扱いです)。 + +アーキで分岐しないのは、分岐すると「片方の CPU でしか再現しない失敗」を自分で +作ることになり、ローカルと CI を揃えるという目的に反するためです。調整点は +`install.sh` と同じく `COMPOSE_FILE` ひとつに寄せています。 + +CI(GitHub Actions)はこのオーバレイを読みません。**最終的な合否は CI で確認して +ください。** + +なお `Dockerfile.arm64` / `elasticsearch/Dockerfile.arm64` は使いません。 +nodesource の `setup_4.x` が消えており現在はビルドできないためで、 +標準の `Dockerfile` / `elasticsearch/Dockerfile` は aarch64 でもビルドできます。 + +### モジュールを増やしたとき + +`.github/workflows/unit-tests.yml` の `matrix.module` が唯一の正です。 +`tests/` と `tox.ini` を持つのに未登録のモジュールがあると、CI の +`matrix-check` ジョブが落とします(ジョブが立たない=赤くもならない、という +静かな漏れを防ぐため)。手元では次で確認できます。 + +```shell +scripts/ci/matrix.sh check +``` + +--- + +## 以下は旧手順(CI とは別経路。参考) + +> Python 3.5 の venv を自前で組む手順です。**CI とは Python も依存も tox の +> 有無も違う**ため、ここで通っても CI で通る保証はありません。結果を CI と +> 突き合わせたいときは上の `run-local.sh` を使ってください。 + ## Running with venv ### Install python 3.5.x diff --git a/scripts/ci/compose.local.yml b/scripts/ci/compose.local.yml new file mode 100644 index 0000000000..cd9666e8a7 --- /dev/null +++ b/scripts/ci/compose.local.yml @@ -0,0 +1,28 @@ +# 手元でテストを回すときの、CI との唯一の差分。 +# scripts/ci/run-local.sh が**アーキテクチャを問わず**常に重ねる。 +# +# なぜ要るか: +# Elasticsearch 6.8 は非ループバックアドレスに bind した時点で bootstrap check +# (本番運用向けの検査) を強制する。これはホストのカーネルと sysctl に依存するため、 +# 開発機では環境しだいで落ちる。実際に確認できたものだけでも: +# +# - ARM: seccomp の実装が x86_64 専用で +# 「seccomp unavailable: CONFIG_SECCOMP not compiled into kernel」で失敗する +# - vm.max_map_count が 262144 未満のホスト: max_map_count の検査で失敗する +# +# discovery.type=single-node にすると bootstrap check 自体が省かれる。 +# ES はテストが使う単一ノードなので、これで意味が変わることはない。 +# リポジトリの docker-compose.arm64.yml も同じ扱いをしている。 +# +# なぜアーキテクチャで分岐しないか: +# 分岐すると「片方のアーキでしか再現しない失敗」を作ることになり、 +# ローカルとCIを揃えるという目的に反する。開発機が AMD でも ARM でも、 +# 手元では同じ条件で回るようにする。 +# +# CI(GitHub Actions)はこのファイルを読まない。CI は bootstrap check が通る +# 前提の環境なので、素の設定のまま動かす。 +services: + elasticsearch: + environment: + - discovery.type=single-node + - ES_JAVA_OPTS=-Xms2048m -Xmx2048m diff --git a/scripts/ci/matrix.sh b/scripts/ci/matrix.sh new file mode 100755 index 0000000000..af57f453d4 --- /dev/null +++ b/scripts/ci/matrix.sh @@ -0,0 +1,86 @@ +#!/bin/bash +# +# unit-tests.yml のマトリクスを唯一の正として読む。 +# +# モジュール一覧が「ワークフローの中」と「ローカル実行の手順」に二重に書かれると、 +# 必ず片方が古びる。実際 v2.0.5 までの間に weko-notifications / weko-signposting / +# weko-workspace の3モジュールがマトリクスから漏れ、テスト一式(283本)を持ちながら +# 一度も CI で実行されていなかった。 +# +# scripts/ci/matrix.sh list マトリクスのモジュールを1行1件で出す +# scripts/ci/matrix.sh check マトリクスとテスト対象モジュールの食い違いを検出する +# (テストがあるのに未登録 = 失敗 / 逆 = 警告) + +set -uo pipefail + +ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd) +WORKFLOW="$ROOT/.github/workflows/unit-tests.yml" + +list_matrix() { + [ -f "$WORKFLOW" ] || { echo "❌ $WORKFLOW が無い" >&2; return 1; } + # ` module:` の下に続く ` - name` を拾う。 + awk ' + /^[[:space:]]+module:[[:space:]]*$/ { inlist = 1; next } + inlist && /^[[:space:]]+- [A-Za-z0-9_-]+[[:space:]]*$/ { + gsub(/^[[:space:]]+- |[[:space:]]+$/, ""); print; next + } + inlist { inlist = 0 } + ' "$WORKFLOW" +} + +# テスト一式を持つ = tests/ と tox.ini の両方がある。 +# cookiecutter-weko-module(雛形) や resources(証明書置き場) は該当しない。 +list_testable() { + for d in "$ROOT"/modules/*/; do + m=$(basename "$d") + [ -d "$d/tests" ] && [ -f "$d/tox.ini" ] && echo "$m" + done +} + +case "${1:-list}" in + list) + list_matrix + ;; + check) + tmp_m=$(mktemp) tmp_t=$(mktemp) + trap 'rm -f "$tmp_m" "$tmp_t"' EXIT + list_matrix | sort > "$tmp_m" + list_testable | sort > "$tmp_t" + + missing=$(comm -13 "$tmp_m" "$tmp_t") + stale=$(comm -23 "$tmp_m" "$tmp_t") + rc=0 + + # 【失敗させる】テストがあるのにマトリクスに無い = 静かな穴。 + # ジョブが立たないので、赤くならないまま何百本も実行されない状態が続く。 + if [ -n "$missing" ]; then + echo "❌ tests/ と tox.ini を持つのにマトリクスに無いモジュール:" + echo "$missing" | sed 's/^/ - /' + echo " → .github/workflows/unit-tests.yml の matrix.module に追加してください。" + echo " 載せない正当な理由があるなら、その旨をワークフローにコメントで残すこと。" + rc=1 + fi + + # 【警告に留める】マトリクスにあるがテストが無い = ジョブは立って赤くなるので + # 見えている。消すか足すかは人の判断なので、ここでは黙って落とさない。 + # --strict を付けたときだけ失敗させる。 + if [ -n "$stale" ]; then + echo "⚠️ マトリクスにあるが tests/ か tox.ini が無いモジュール:" + echo "$stale" | sed 's/^/ - /' + echo " → ジョブは立つが実行するテストが無く、常に失敗し続ける。" + echo " テストを足すか、マトリクスから外すか、どちらかに決めてください。" + [ "${2:-}" = "--strict" ] && rc=1 + fi + + if [ -z "$missing" ] && [ -z "$stale" ]; then + echo "✓ マトリクス $(wc -l < "$tmp_m") 件がテスト対象モジュールと一致" + elif [ $rc -eq 0 ]; then + echo "✓ 静かな漏れは無し(マトリクス $(wc -l < "$tmp_m") 件)" + fi + exit $rc + ;; + *) + echo "使い方: $0 {list|check}" >&2 + exit 2 + ;; +esac diff --git a/scripts/ci/run-local.sh b/scripts/ci/run-local.sh new file mode 100755 index 0000000000..6c4857d650 --- /dev/null +++ b/scripts/ci/run-local.sh @@ -0,0 +1,221 @@ +#!/bin/bash +# +# GitHub Actions の Unit Tests ジョブを、手元で同じ経路で回す。 +# +# scripts/ci/run-local.sh weko-records +# scripts/ci/run-local.sh --all +# scripts/ci/run-local.sh --list +# +# 【なぜ要るか】 +# ローカルとCIで違う回し方をすると、どちらかでしか出ない失敗が生まれ、 +# 結果を突き合わせられなくなる。実測した2件: +# +# - 手元にあった無関係な weko-web イメージを流用したところ、イメージに +# 焼き付いた古い egg-info の entry_point (weko_theme.bundles:js_preview_widget。 +# 現行の setup.py には無い) を invenio_assets が読みにいって 191件が +# ImportError になった。CI は ci-images.yml が modules/*/setup.py を含む +# ハッシュでタグを決め、変われば作り直すので発生しない。 +# - invenio の venv で直接 pytest を叩いたところ pytest-mock / mock が無く、 +# 「fixture 'mocker' not found」でテストが落ちた。CI は tox が +# requirements2.txt から入れるので発生しない。 +# +# どちらも**テストは正常なのに落ちる**。原因の切り分けに時間を取られるだけなので、 +# このスクリプトは CI と同じ部品をそのまま呼ぶ: +# +# COMPOSE_FILE docker-compose2.yml:docker-compose.ci.yml (CI と同一) +# 起動するサービス postgresql / elasticsearch / redis / rabbitmq のみ (CI と同一) +# 起動待ち scripts/ci/wait-for-services.sh (CI と同一) +# テスト実行 scripts/ci/run-module-tests.sh (CI と同一 = tox) +# モジュール一覧 .github/workflows/unit-tests.yml の matrix (CI と同一) +# +# イメージだけは GHCR から引けないことがあるので、同じ入力ファイル集合の +# ハッシュでローカルタグを作り、無ければビルドする。CI と同じイメージを +# 使いたいときは WEKO_IMAGE / WEKO_ES_IMAGE で明示する。 + +set -uo pipefail + +ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd) +cd "$ROOT" || exit 1 + +KEEP=0 +REBUILD=0 +MODULES=() + +usage() { + cat <<'USAGE' +使い方: scripts/ci/run-local.sh [オプション] <モジュール名>... + + --all マトリクスの全モジュールを回す + --list マトリクスのモジュール一覧を出して終了 + --keep 終了後もサービスを落とさない(続けて回すとき) + --rebuild イメージを作り直す + -h, --help これ + +環境変数: + WEKO_IMAGE 本体イメージを明示する(CI と同一のものを使いたいとき) + WEKO_ES_IMAGE Elasticsearch イメージを明示する +USAGE +} + +while [ $# -gt 0 ]; do + case "$1" in + --all) MODULES=(__ALL__) ;; + --list) exec "$ROOT/scripts/ci/matrix.sh" list ;; + --keep) KEEP=1 ;; + --rebuild) REBUILD=1 ;; + -h|--help) usage; exit 0 ;; + -*) echo "❌ 不明なオプション: $1" >&2; usage >&2; exit 2 ;; + *) MODULES+=("$1") ;; + esac + shift +done + +[ ${#MODULES[@]} -eq 0 ] && { echo "❌ モジュール名か --all が要る" >&2; usage >&2; exit 2; } + +mapfile -t MATRIX < <("$ROOT/scripts/ci/matrix.sh" list) +[ ${#MATRIX[@]} -eq 0 ] && { echo "❌ マトリクスを読めない" >&2; exit 1; } + +if [ "${MODULES[0]}" = "__ALL__" ]; then + MODULES=("${MATRIX[@]}") +else + # CI に無いモジュールを手元だけで回しても、結果を突き合わせられない。 + for m in "${MODULES[@]}"; do + printf '%s\n' "${MATRIX[@]}" | grep -qx "$m" || { + echo "❌ '$m' は unit-tests.yml のマトリクスに無い。" >&2 + echo " CI で回らないモジュールを手元だけで回しても結果を比べられない。" >&2 + echo " 先にマトリクスへ追加すること。一覧は --list。" >&2 + exit 2 + } + done +fi + +export COMPOSE_FILE=docker-compose2.yml:docker-compose.ci.yml + +# --- compose の重ね方 -------------------------------------------------------- +# install.sh と同じく COMPOSE_FILE を唯一の調整点にする(アーキで分岐しない)。 +# +# Dockerfile は CI と同じものを使う。x86_64 用の Dockerfile / +# elasticsearch/Dockerfile は aarch64 でもビルドできる。リポジトリには +# Dockerfile.arm64 もあるが、nodesource の setup_4.x が消えており現在は +# ビルドできないので使わない。 +DOCKERFILE_WEB=Dockerfile +DOCKERFILE_ES=elasticsearch/Dockerfile + +# 手元では Elasticsearch の bootstrap check を外す。**アーキテクチャで分岐しない**: +# 分岐すると「片方のアーキでしか再現しない失敗」を作ることになり、ローカルと CI を +# 揃えるという目的に反する。理由は scripts/ci/compose.local.yml に書いてある。 +export COMPOSE_FILE="$COMPOSE_FILE:scripts/ci/compose.local.yml" +echo "ℹ️ ホスト: $(uname -m)。CI(x86_64)との差は1点だけ:" +echo " Elasticsearch を discovery.type=single-node で起動する" +echo " (bootstrap check はホストのカーネル/sysctl に依存し、開発機では環境しだいで落ちる)" +echo " テストの内容には影響しないが、最終的な合否は CI で確認すること。" + +# --- イメージ --------------------------------------------------------------- +# CI(ci-images.yml)がタグの元にしているのと同じファイル集合。ここが変われば +# 別タグになり、作り直される。egg-info が古いまま使い回される事故を防ぐ要。 +web_hash() { + { sha256sum "$DOCKERFILE_WEB" scripts/provision-web.sh scripts/create-instance.sh \ + scripts/create-instance2.sh scripts/instance.cfg packages.txt \ + packages-invenio.txt requirements-weko-modules.txt requirements-devel.txt \ + package.json 2>/dev/null + sha256sum modules/*/setup.py 2>/dev/null | sort + } | sha256sum | cut -c1-16 +} +es_hash() { + sha256sum "$DOCKERFILE_ES" scripts/provision-elasticsearch.sh \ + elasticsearch/dic/character/kui.txt 2>/dev/null | sha256sum | cut -c1-16 +} + +export WEKO_IMAGE=${WEKO_IMAGE:-weko-ci-web:local-$(web_hash)} +export WEKO_ES_IMAGE=${WEKO_ES_IMAGE:-weko-ci-es:local-$(es_hash)} + + +build_if_missing() { + local ref=$1 file=$2 ctx=$3 + if [ "$REBUILD" = 1 ] || ! docker image inspect "$ref" >/dev/null 2>&1; then + echo "▶ ビルド: $ref ($file)" + docker build -f "$file" -t "$ref" "$ctx" || return 1 + else + echo "▶ 既存イメージを使う: $ref" + fi +} + +build_if_missing "$WEKO_IMAGE" "$DOCKERFILE_WEB" . || exit 1 +build_if_missing "$WEKO_ES_IMAGE" "$DOCKERFILE_ES" . || exit 1 + +# --- 他の WEKO スタックとの衝突 --------------------------------------------- +# docker-compose2.yml は 29201 / 26301 / 24301 を publish する。別の WEKO を +# 動かしたままだと起動に失敗するか、最悪そちらのサービスを掴む。 +for p in 29201 26301 24301; do + if (exec 3<>"/dev/tcp/127.0.0.1/$p") 2>/dev/null; then + exec 3<&- 2>/dev/null + running=$(docker ps --filter "publish=$p" --format '{{.Names}}' | head -1) + echo "❌ ポート $p が既に使われている${running:+ (${running})}。" + echo " 別の WEKO スタックが動いていると、テストがそちらのサービスを掴む。" + echo " 先に止めること: cd <その WEKO> && docker compose stop" + exit 1 + fi +done + +# --- サービス起動 ------------------------------------------------------------ +echo "▶ サービス起動 (postgresql / elasticsearch / redis / rabbitmq)" +docker compose up -d --no-build postgresql elasticsearch redis rabbitmq || exit 1 + +cleanup() { + if [ "$KEEP" = 1 ]; then + echo "▶ --keep のためサービスは起動したまま。止めるとき:" + echo " COMPOSE_FILE='$COMPOSE_FILE' docker compose down -v" + else + echo "▶ 後片付け" + docker compose down -v >/dev/null 2>&1 + fi +} +trap cleanup EXIT + +bash "$ROOT/scripts/ci/wait-for-services.sh" || exit 1 + +# --- 事前確認: 古い egg-info を掴んでいないか -------------------------------- +# WEKO_IMAGE を手で指定したときに効く。ここで落としておかないと、テストの失敗と +# 見分けがつかない ImportError が何百件も出る。 +echo "▶ entry_point の健全性を確認" +docker compose run --rm --no-deps -T web bash -c ' +/home/invenio/.virtualenvs/invenio/bin/python - <