Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
59 changes: 59 additions & 0 deletions .github/workflows/api-inventory-tests.yml
Original file line number Diff line number Diff line change
@@ -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
24 changes: 22 additions & 2 deletions .github/workflows/unit-tests.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# ユニットテスト。
#
# 【設計】本体イメージは「1回だけ」ビルドして GHCR に置き、45個のマトリクス
# 【設計】本体イメージは「1回だけ」ビルドして GHCR に置き、47個のマトリクス
# ジョブはそれを pull するだけにしている。
#
# - イメージの用意は ci-images.yml (UIテストと共通) に委譲している。
Expand All @@ -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) だけ作り、各ジョブがそこからローカルビルドする。

Expand All @@ -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:
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
60 changes: 53 additions & 7 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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**に準拠 (スタイルガイドの遵守)
Expand All @@ -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の説明欄には変更内容と目的、動作確認の方法を簡潔に記述してください
86 changes: 86 additions & 0 deletions README-TEST.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down
28 changes: 28 additions & 0 deletions scripts/ci/compose.local.yml
Original file line number Diff line number Diff line change
@@ -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
Loading
Loading