## 概要 ロードマップ #14 の Phase 2 を実装する umbrella issue。`cao-holidays` を monorepo 化し、Python パッケージを新設する。 - 設計: [`docs/monorepo-structure.md`](../blob/main/docs/monorepo-structure.md) - リリース手順: [`docs/release-runbook.md`](../blob/main/docs/release-runbook.md) - ロードマップ: #14 - 進行管理: `tmp/tasks.md` の Phase 13(gitignored、ローカル専用) ## 着手前提(merge 前にやる) - [ ] **PyPI Trusted Publishing の Pending Publisher 事前登録**: PyPI で `aromarious/cao-holidays` + workflow `release-python.yml` + environment `pypi` を登録(プロジェクト作成前でも Pending Publisher として登録可能) - [ ] **main protection ruleset を緩める**: CI workflow リネームで status check 名が変わるため、merge 前に Required status checks を一旦空にする(merge 後に新しい check 名で再設定) - [ ] **#21 の動作検証準備**: monorepo 移行で立つ最初の Release PR が、`RELEASE_TOKEN` PAT 経由で required CI / CodeQL を**空コミット push なしで自動起動するか**を確認する。起動すれば #21 close ## 作業計画(PR 分割案) レビュー負荷を抑えるため、以下の4 PR に分割することを推奨。各 PR の status check は `Phase 2-X` プレフィックスで識別。 ### PR 1: monorepo 化(既存 JS の移動 + 共有資産確立) - [ ] `packages/js/` 作成。既存の `src/`、`tests/`、`bin/`、`package.json`、`tsconfig.json`、`biome.json`、`vitest.config.ts`、`tsup.config.ts`、`pnpm-lock.yaml`、`.changeset/`、`dist/`(gitignore) を `git mv` - [ ] `fixtures/` を root に新設。`packages/js/tests/fixtures/syukujitsu.csv` を `fixtures/syukujitsu.csv` に移動 - [ ] 期待出力 fixture を生成: 既存 CLI で `--all --format json/csv/ics`、`2026 --format json/csv/ics`、`2025..2027`、`2026-04-01..2026-05-31` を叩いて期待値を `fixtures/*.{json,csv,ics}` に保存 - [ ] `packages/js/tests/` の fixture 参照パスを `../../../fixtures/syukujitsu.csv` 等に修正 - [ ] `scripts/sync-fixture.mjs` を root に移動。出力先を新 `fixtures/` に変更 - [ ] CI workflow を `ci.yml` → `ci-js.yml` にリネーム。`paths:` filter を `packages/js/**`、`fixtures/**`、`scripts/sync-fixture.mjs`、`.github/workflows/ci-js.yml` に - [ ] `release.yml` → `release-js.yml` にリネーム、`changesets/action` の `cwd: packages/js` を設定 - [ ] `healthcheck.yml` の参照パス見直し - [ ] `dependabot.yml` 更新: npm ecosystem の directory を `/packages/js` に変更 - [ ] トップ `README.md` / `README.en.md` を全面書き換え: 「multi-language monorepo」を1行目で宣言、各 package へのリンク - [ ] `packages/js/README.md` 新設: 既存 README 内容をベースに、冒頭で monorepo 宣言と相互リンク - [ ] `LICENSE` を `packages/js/LICENSE` にもコピー(npm publish 同梱用) - [ ] root に `Makefile` 新設(`make lint-js` / `make test-js` 等) - [ ] root に `.pre-commit-config.yaml` 新設(biome フックのみ、Phase 2 で ruff/mypy も追加) - [ ] root に `.vscode/extensions.json` 新設(推奨拡張) - [ ] **動作確認**: `cd packages/js && pnpm install && pnpm test && pnpm typecheck && pnpm lint` 全緑、`publint` 緑、`@arethetypeswrong/cli` 緑 - [ ] **#21 検証**: PR merge 後の Release PR で空コミット push なしで required check が起動するか確認 ### PR 2: Python skeleton + CI 配管 - [ ] `packages/python/` ディレクトリ作成 - [ ] `pyproject.toml` 新設: `[project]` (name = `cao-holidays`)、build backend は `hatchling`、`[tool.hatch.version]` で `src/cao_holidays/__init__.py` の `__version__` を真とする、`[tool.ruff]`、`[tool.mypy]` セクション - [ ] `src/cao_holidays/__init__.py` 新設: 公開 API は `fetch_all_holidays` / `fetch_holidays_by_year` / `fetch_holidays_between`(NotImplementedError でスタブ)、`Holiday` (TypedDict)、`FetchOptions` (TypedDict)、`CaoHolidaysError` (Exception)、`__version__ = "0.0.0"` - [ ] `tests/` 空ディレクトリ + `tests/conftest.py`(fixture path 解決用) - [ ] `packages/python/README.md` 新設(PyPI upload 用、相互リンク含む) - [ ] `packages/python/LICENSE` コピー - [ ] `.github/workflows/ci-python.yml` 新設: `paths: packages/python/**`、`fixtures/**`、ruff check + ruff format --check + mypy + pytest を Python 3.11/3.12/3.13 matrix で実行 - [ ] `.github/workflows/codeql.yml` の matrix に Python を追加 - [ ] `.github/dependabot.yml` に pip ecosystem (directory: `/packages/python`) を追加 - [ ] `.pre-commit-config.yaml` に ruff + ruff-format + mypy フックを追加 - [ ] root `Makefile` に `lint-python` / `format-python` / `test-python` / `typecheck-python` / `install` ターゲット追加 - [ ] **動作確認**: `cd packages/python && uv sync && uv run pytest && uv run mypy src && uv run ruff check . && uv run ruff format --check .` 全緑 ### PR 3: Python 実装本体(fixture 一致テスト含む) - [ ] `src/cao_holidays/parse.py`: `decode_sjis()`、`parse_csv()` 実装。JS 版と同等のロジック(ヘッダー行スキップ、`YYYY/M/D` → `YYYY-MM-DD` 正規化、昇順ソート、PARSE_FAILED 相当の例外) - [ ] `src/cao_holidays/source.py`: `CKAN_URL` 定数、`resolve_csv_url()`、`fetch_csv_text()`。User-Agent (`cao-holidays-py/<version>`)、`FetchOptions.fetch` DI 対応、`signal` の Python 版相当(`asyncio.timeout` or 同期版なら `urllib3` Retry の活用検討) - [ ] `src/cao_holidays/format.py`: CSV (RFC 4180)、JSON (1 行)、ICS (RFC 5545) フォーマッタ。JS 版と**バイト単位で一致**する出力 - [ ] `src/cao_holidays/__init__.py` の3関数を実装(INVALID_INPUT / OUT_OF_RANGE / FETCH_FAILED / PARSE_FAILED 例外コード対応) - [ ] `tests/test_parse.py`、`test_source.py`、`test_format.py`、`test_main.py` 実装 - [ ] **fixture 一致テスト**: `tests/test_fixture_parity.py` で `fixtures/syukujitsu.csv` を食わせて `fixtures/all.json` 等と一致することを検証(JS 版と Python 版が同じ出力を返すことの担保) - [ ] **動作確認**: `pytest` 全緑、`mypy --strict src` 緑、JS 版と CSV/JSON/ICS 出力がバイト単位で一致 ### PR 4: Python リリース基盤 + 初版 publish - [ ] `.github/workflows/release-python.yml` 新設: tag pattern `python-v*` を trigger に `hatch build` + `pypa/gh-action-pypi-publish`(OIDC、environment `pypi`) - [ ] `packages/python/CHANGELOG.md` 新設(Keep a Changelog 形式) - [ ] `hatch version` で `0.0.0` → `0.1.0` に bump、CHANGELOG エントリ追加 - [ ] PR merge 後、`git tag python-v0.1.0` を push - [ ] release-python.yml が PyPI publish を実行 - [ ] **動作確認**: `pip install --no-cache-dir cao-holidays==0.1.0` で fresh install、`python -c "from cao_holidays import fetch_holidays_by_year; print(len(fetch_holidays_by_year(2026)))"` で実通信動作確認 - [ ] ロードマップ #14 の Phase 2 チェックボックスを更新 ## 完了条件 - npm `cao-holidays`(既存)が monorepo 配下から問題なく publish されること - PyPI `cao-holidays` 0.1.0 が公開されること - JS 版と Python 版が **同じ fixture を食わせて同じバイト列を出力**すること(CI で自動検証) - main protection / Dependabot / CodeQL / pre-commit が両言語で機能すること - #21(Release PR の required check 起動)が解決済みであること ## Scope 外(このIssueでは扱わない) - 多言語展開の Phase 3(Go, Ruby)以降 - Python パッケージへの CLI 提供(library only と決定済、ロードマップ #14 参照) - 判定ヘルパーパッケージ(`cao-holidays-utils` 等) ## 関連 - ロードマップ: #14 - 既知の運用課題: #21(Release PR の required check 起動) - 設計ドキュメント: [`docs/monorepo-structure.md`](../blob/main/docs/monorepo-structure.md) - リリース手順: [`docs/release-runbook.md`](../blob/main/docs/release-runbook.md)
概要
ロードマップ #14 の Phase 2 を実装する umbrella issue。
cao-holidaysを monorepo 化し、Python パッケージを新設する。docs/monorepo-structure.mddocs/release-runbook.mdtmp/tasks.mdの Phase 13(gitignored、ローカル専用)着手前提(merge 前にやる)
aromarious/cao-holidays+ workflowrelease-python.yml+ environmentpypiを登録(プロジェクト作成前でも Pending Publisher として登録可能)RELEASE_TOKENPAT 経由で required CI / CodeQL を空コミット push なしで自動起動するかを確認する。起動すれば Release PR で required check が走らない問題を PAT 化で解決する #21 close作業計画(PR 分割案)
レビュー負荷を抑えるため、以下の4 PR に分割することを推奨。各 PR の status check は
Phase 2-Xプレフィックスで識別。PR 1: monorepo 化(既存 JS の移動 + 共有資産確立)
packages/js/作成。既存のsrc/、tests/、bin/、package.json、tsconfig.json、biome.json、vitest.config.ts、tsup.config.ts、pnpm-lock.yaml、.changeset/、dist/(gitignore) をgit mvfixtures/を root に新設。packages/js/tests/fixtures/syukujitsu.csvをfixtures/syukujitsu.csvに移動--all --format json/csv/ics、2026 --format json/csv/ics、2025..2027、2026-04-01..2026-05-31を叩いて期待値をfixtures/*.{json,csv,ics}に保存packages/js/tests/の fixture 参照パスを../../../fixtures/syukujitsu.csv等に修正scripts/sync-fixture.mjsを root に移動。出力先を新fixtures/に変更ci.yml→ci-js.ymlにリネーム。paths:filter をpackages/js/**、fixtures/**、scripts/sync-fixture.mjs、.github/workflows/ci-js.ymlにrelease.yml→release-js.ymlにリネーム、changesets/actionのcwd: packages/jsを設定healthcheck.ymlの参照パス見直しdependabot.yml更新: npm ecosystem の directory を/packages/jsに変更README.md/README.en.mdを全面書き換え: 「multi-language monorepo」を1行目で宣言、各 package へのリンクpackages/js/README.md新設: 既存 README 内容をベースに、冒頭で monorepo 宣言と相互リンクLICENSEをpackages/js/LICENSEにもコピー(npm publish 同梱用)Makefile新設(make lint-js/make test-js等).pre-commit-config.yaml新設(biome フックのみ、Phase 2 で ruff/mypy も追加).vscode/extensions.json新設(推奨拡張)cd packages/js && pnpm install && pnpm test && pnpm typecheck && pnpm lint全緑、publint緑、@arethetypeswrong/cli緑PR 2: Python skeleton + CI 配管
packages/python/ディレクトリ作成pyproject.toml新設:[project](name =cao-holidays)、build backend はhatchling、[tool.hatch.version]でsrc/cao_holidays/__init__.pyの__version__を真とする、[tool.ruff]、[tool.mypy]セクションsrc/cao_holidays/__init__.py新設: 公開 API はfetch_all_holidays/fetch_holidays_by_year/fetch_holidays_between(NotImplementedError でスタブ)、Holiday(TypedDict)、FetchOptions(TypedDict)、CaoHolidaysError(Exception)、__version__ = "0.0.0"tests/空ディレクトリ +tests/conftest.py(fixture path 解決用)packages/python/README.md新設(PyPI upload 用、相互リンク含む)packages/python/LICENSEコピー.github/workflows/ci-python.yml新設:paths: packages/python/**、fixtures/**、ruff check + ruff format --check + mypy + pytest を Python 3.11/3.12/3.13 matrix で実行.github/workflows/codeql.ymlの matrix に Python を追加.github/dependabot.ymlに pip ecosystem (directory:/packages/python) を追加.pre-commit-config.yamlに ruff + ruff-format + mypy フックを追加Makefileにlint-python/format-python/test-python/typecheck-python/installターゲット追加cd packages/python && uv sync && uv run pytest && uv run mypy src && uv run ruff check . && uv run ruff format --check .全緑PR 3: Python 実装本体(fixture 一致テスト含む)
src/cao_holidays/parse.py:decode_sjis()、parse_csv()実装。JS 版と同等のロジック(ヘッダー行スキップ、YYYY/M/D→YYYY-MM-DD正規化、昇順ソート、PARSE_FAILED 相当の例外)src/cao_holidays/source.py:CKAN_URL定数、resolve_csv_url()、fetch_csv_text()。User-Agent (cao-holidays-py/<version>)、FetchOptions.fetchDI 対応、signalの Python 版相当(asyncio.timeoutor 同期版ならurllib3Retry の活用検討)src/cao_holidays/format.py: CSV (RFC 4180)、JSON (1 行)、ICS (RFC 5545) フォーマッタ。JS 版とバイト単位で一致する出力src/cao_holidays/__init__.pyの3関数を実装(INVALID_INPUT / OUT_OF_RANGE / FETCH_FAILED / PARSE_FAILED 例外コード対応)tests/test_parse.py、test_source.py、test_format.py、test_main.py実装tests/test_fixture_parity.pyでfixtures/syukujitsu.csvを食わせてfixtures/all.json等と一致することを検証(JS 版と Python 版が同じ出力を返すことの担保)pytest全緑、mypy --strict src緑、JS 版と CSV/JSON/ICS 出力がバイト単位で一致PR 4: Python リリース基盤 + 初版 publish
.github/workflows/release-python.yml新設: tag patternpython-v*を trigger にhatch build+pypa/gh-action-pypi-publish(OIDC、environmentpypi)packages/python/CHANGELOG.md新設(Keep a Changelog 形式)hatch versionで0.0.0→0.1.0に bump、CHANGELOG エントリ追加git tag python-v0.1.0を pushpip install --no-cache-dir cao-holidays==0.1.0で fresh install、python -c "from cao_holidays import fetch_holidays_by_year; print(len(fetch_holidays_by_year(2026)))"で実通信動作確認完了条件
cao-holidays(既存)が monorepo 配下から問題なく publish されることcao-holidays0.1.0 が公開されることScope 外(このIssueでは扱わない)
cao-holidays-utils等)関連
docs/monorepo-structure.mddocs/release-runbook.md