# コントリビューション

> GitHub で Nodaro にコードをコントリビュートする方法です。開発環境を準備し、dev からブランチを作成して、テストを実行し、changeset を追加して、コントリビューター契約に署名します。

Source: https://nodaro.ai/ja/docs/self-hosting/contributing

Nodaro への**コントリビューション**とは、公開リポジトリ [github.com/nodaroai/app.nodaro.ai](https://github.com/nodaroai/app.nodaro.ai) に、プルリクエストとしてコードを送ることです。Nodaro は、Nodaro Sustainable Use License のもとでソースコードを公開しています（ソースアベイラブル）。このページでは、開発環境の準備、ブランチ、プルリクエスト、テスト、コントリビューター契約について説明します。

自分のサーバーで Nodaro を動かすだけなら、[クイックスタート](https://nodaro.ai/docs/self-hosting/quickstart)から始めてください。API を使って開発する場合は、[SDK](https://nodaro.ai/docs/developers/sdk) を参照してください。

## リポジトリ
リポジトリは、npm workspaces を使った 1 つのモノレポで、次のフォルダーで構成されています。

| フォルダー | 内容 |
| --- | --- |
| `backend/` | Fastify の API、BullMQ のワーカー、オーケストレーターです。Node.js 22、TypeScript で書かれています。 |
| `frontend/` | Vite のアプリです。エディター、アプリランナー、管理パネルが含まれます。React 19、React Router 7、React Flow を使っています。 |
| `packages/` | スタック全体で共有する純粋なロジックの `@nodaro/shared`、型付きの REST クライアントの `@nodaro/sdk`、`@nodaro/cli`、プロンプト層の `@nodaro/prompts`、そして Remotion の動画コンポジションです。 |
| `supabase/` | データベースのスキーマです。前方向にだけ適用する SQL マイグレーションとして管理しています。 |
| `docs/` | リポジトリ内のドキュメントです。主要な機能の背景にある理由を説明する設計メモも含まれます。 |
| `scripts/` | リポジトリ用のユーティリティです。アーキテクチャのグラフの生成スクリプトや、監査などがあります。 |
| `.changeset/` | 公開パッケージの、保留中のバージョンアップです。 |

プロジェクトのルールは、リポジトリのルートにある `CLAUDE.md` にまとめています。コーディング規約、モデルプロバイダーを追加するときのチェックリスト、ノードを追加するときのチェックリストです。軽微でないプルリクエストを出す前に、読んでおいてください。各部分がどのように組み合わさっているかは、[アーキテクチャ](https://nodaro.ai/docs/self-hosting/architecture)で説明しています。

## 開発環境を準備する
Node.js 22 以降と npm が必要です。

### クローンしてインストールする
```bash
git clone https://github.com/nodaroai/app.nodaro.ai
cd app.nodaro.ai
npm install        # installs every workspace
```

### 環境を設定する
```bash
cp .env.example .env
```

少なくとも、`SUPABASE_URL`、`SUPABASE_SERVICE_ROLE_KEY`、`SUPABASE_ANON_KEY`、`INTERNAL_ORCHESTRATOR_SECRET` と、`KIE_API_KEY`、`REPLICATE_API_TOKEN`、`ANTHROPIC_API_KEY` などのプロバイダーキーを 1 つ設定します。シークレットは、次のように生成します。

```bash
echo "INTERNAL_ORCHESTRATOR_SECRET=$(openssl rand -hex 32)" >> .env
echo "NODARO_ENCRYPTION_KEY=$(openssl rand -hex 32)" >> .env
```

データベースは、supabase.com の無料プロジェクトを使うのが最も簡単です。Supabase CLI の `supabase start` による完全にローカルなスタックも使えますが、準備に手間がかかります。ワークフローの実行には Redis も必要です。接続先は `REDIS_URL` で、デフォルトは `redis://localhost:6379` です。`.env.example` にはサポートしているすべての変数が載っており、[設定](https://nodaro.ai/docs/self-hosting/configuration)でそれぞれを説明しています。

### 共有パッケージを一度ビルドする
フロントエンドは `@nodaro/shared` をビルド出力から読み込むので、最初に起動する前にビルドしておきます。それ以降は、共有コードを編集すると、パッケージ自身の dev スクリプトがビルドし直します。

```bash
npm -w @nodaro/shared run build
```

### 開発サーバーを起動する
2 つのターミナルで、次を実行します。

```bash
# Terminal 1: the API on port 9000
cd backend
npm run dev

# Terminal 2: the frontend on port 3000, which forwards /v1/* to port 9000
cd frontend
npm run dev
```

## コーディング規約
`CLAUDE.md` のうち、特に重要なルールは次のとおりです。

- **ファイルのサイズ**：200〜400 行が目安で、800 行が絶対的な上限です。それより大きくなったファイルは分割してください。
- **本番コードに `console.log` を使わない**：既存のロガーのパターンを使ってください。
- **Conventional Commits**：`feat:`、`fix:`、`refactor:`、`docs:`、`chore:`、`test:` を使い、件名は具体的に書きます。
- **コミットの前には毎回型チェックを行う**：`backend/` と `frontend/` の両方で `npx tsc --noEmit` を実行します。型チェックに通らないプルリクエストは、CI でブロックされます。
- **Express のルーターではなく、Fastify のプラグインを使う**：どのルートファイルも、Fastify のインスタンスを受け取る async 関数をエクスポートします。
- **API のすべてのエンドポイントに Zod スキーマを用意する**：例外はありません。スキーマはリクエストを検証し、OpenAPI ドキュメントの生成にも使われます。
- **フロントエンドの状態**：サーバーの状態には React Query、UI の状態には Zustand、キャンバスの状態には React Flow を使います。これらを混在させないでください。
- **オブジェクトや配列を変更しない**：常に新しいコピーを作成してください。Zustand と React Flow は、どちらも参照の等価性に依存しています。

## ブランチとプルリクエスト
- リポジトリには、長期間使うブランチが 2 つあります。ステージング用の `dev` と、本番用の `main` です。
- リポジトリをフォークし、**`dev` からブランチを作成**して、`dev` に向けてプルリクエストを作成します。`main` からブランチを作成したり、`main` に直接コミットしたりしないでください。
- ブランチ名は、種類に応じて `feat/`、`fix/`、`refactor/`、`docs/`、`chore/`、`test/` のいずれかで始めます。たとえば `feat/whisper-tts-node` です。
- マージされた変更は、まずステージング環境の `next.nodaro.ai` で動きます。約 24 時間の検証期間を経て、メンテナーが `dev` を `main` に昇格させます。

プルリクエストでは、次のことを行ってください。

- 関連する GitHub の issue をリンクします。
- UI を変更する場合は、スクリーンショットか GIF を添付します。
- ノードを追加または変更する場合は、`CLAUDE.md` のノードのチェックリストに従います。
- 公開パッケージを変更する場合は、changeset を追加します。[Changesets](#changesets) を参照してください。

## テスト
各ワークスペースには、それぞれの Vitest スイートがあります。リポジトリのルートからすべてを実行することも、ワークスペースを 1 つずつ実行することもできます。

```bash
npm test                       # every workspace's test script

npm -w @nodaro/shared test     # pure-logic unit tests
npm -w @nodaro/sdk test        # SDK contract tests against a mocked API
cd backend && npm test         # route and service tests
cd frontend && npm test        # component and hook tests
```

テストする内容は次のとおりです。

- **API ルート**：主要な経路と、スキーマに挙げたエッジケースをテストします。Supabase と AI プロバイダーはモックにしてください。単体テストでは、実際のプロバイダーの API を決して呼び出しません。既存のテストの多くに、そのまま流用できるセットアップがあります。
- **フロントエンドのコンポーネント**：React Testing Library によるスモークテストです。重いロジックは、単独で単体テストできるフックやヘルパーに置いてください。React Flow や Zustand のストアの内部はテストしないでください。
- **共有パッケージ**：純粋関数の単体テストです。エクスポートするものは、フロントエンドとバックエンドの間でシリアライズできる状態を保つ必要があります。

CI では、`tsc --noEmit`、Vitest、簡単な lint を実行します。テストがローカルでは失敗するのに CI では通る場合や、その逆の場合は、再現手順を添えて issue を作成してください。

### ffmpeg の出力チェック
通常の単体テストがチェックするのは ffmpeg に渡す引数で、ffmpeg がレンダリングする結果ではありません。別の特性テスト（characterization test）のスイートが、ffmpeg を使うすべての処理で、テスト用のフィクスチャをレンダリングします。そして、デコードした出力の特性を測定し、コミット済みの基準値と比較します。比較するのは、エネルギー、スペクトル、減衰、長さ、フレームごとの明るさです。

このスイートは `npm test` には含まれていません。その数値が有効なのは、本番イメージで固定している、まったく同じ ffmpeg のビルドに対してだけだからです。このスイートは、そのイメージの中で実行します。

```bash
backend/scripts/characterize-in-image.sh check   # compare with the reference values
backend/scripts/characterize-in-image.sh bless   # rewrite the reference values, on purpose only
cd backend && npm run characterize:report -- --against ffmpeg-X.json
```

ffmpeg を使う処理を変更した場合は、プルリクエストを作成する前に `check` を実行してください。CI でも実行されます。レンダリング結果を意図的に変える変更の場合は、イメージの中で bless を実行し、新しい基準値をコミットします。その際、値が変わったすべての測定項目について説明を添えてください。基準値を手で編集したり、ローカルの ffmpeg に対して bless を実行したりしないでください。スイートのバージョンガードが、それを拒否します。

## ノードやモデルプロバイダーを追加する
ノードを追加すると、多くのファイルに手が入ります。API ルート、フロントエンドのコンポーネント、実行エンジン、いくつかのレジストリです。1 つでも漏れると、ピッカーの 1 つにノードが表示されない、**実行**ボタンを押しても何も起きないといった、わかりにくい結果になります。`CLAUDE.md` の **New Node Registration** チェックリストに、1 ステップずつ従ってください。

既存のノードにモデルを追加する作業はもっと短く、`CLAUDE.md` に専用の **Provider Enum Sync** チェックリストがあります。最も忘れやすいステップは、API ルートのスキーマです。スキーマを更新しないと、API が `400` で拒否するオプションを、エディターが表示してしまいます。

## Changesets
npm に公開しているパッケージは、`@nodaro/shared`、`@nodaro/sdk`、`@nodaro/cli` の 3 つです。リリースには Changesets を使い、自動化されています。これらのいずれかに手を加える変更では、次を実行します。

```bash
npx changeset
```

パッケージとバージョンアップの種類（patch、minor、major）を選び、1 行の要約を入力します。`.changeset/` に書き出されたファイルを、プルリクエストに含めてコミットします。公開パッケージを変更しているのに changeset がないプルリクエストは、**Changeset Guard** チェックで失敗します。リリースノートが不要な変更には、`npx changeset --empty` を使います。

それ以外は、すべて自動で行われます。`dev` では、**Version Packages** プルリクエストが、保留中の changeset を集めます。`dev` が `main` に昇格すると、リリースのワークフローが新しいバージョンを npm に公開し、タグを付け、GitHub のリリースを作成して、スタンドアロンの CLI バイナリをビルドし直します。`backend`、`frontend`、Remotion の各ワークスペースは公開されないので、changeset は不要です。

## 行動規範
親切に、敬意を持って接し、相手の善意を前提にしてください。このプロジェクトは、[CODE_OF_CONDUCT.md](https://github.com/nodaroai/app.nodaro.ai/blob/main/CODE_OF_CONDUCT.md) にある Contributor Covenant 2.1 に従っています。いかなる種類のハラスメントも、プロジェクトから除外する理由になります。

- 批判するのはコードであって、人ではありません。
- 反対意見は、失礼にならないように伝えてください。「Y なので、ここはパターン X のほうがすっきりすると思います」は良い伝え方ですが、「これはダメ」は良くありません。
- 初めてのコントリビューターをレビューするメンテナーは、辛抱強く対応してください。

## 質問する場所
- **GitHub Discussions**：自由な質問、アイデア、Nodaro で作ったものの共有に使います。バグ以外のことは、Issues ではなくこちらを使ってください。
- **GitHub Issues**：バグの報告と機能のリクエストに使います。
- **セキュリティの問題**：GitHub の非公開のセキュリティアドバイザリーで報告してください。

プロジェクトについて、メンテナーに個別にメールを送るのは避けてください。公開の場での回答は、みんなの役に立ちます。セキュリティの報告や利益相反に関することは、個別に連絡してもかまいません。

## ライセンスとコントリビューター契約
リポジトリには、4 段階のライセンスがあります。ほとんどのコードは、Nodaro Sustainable Use License のもとにあります。`ee` フォルダー内のコードと、名前に `.ee.` を含むファイルのコードは、Nodaro Enterprise License のもとにあります。`@nodaro/prompts` は FSL-1.1-Apache-2.0、`@nodaro/sdk`、`@nodaro/shared`、`@nodaro/cli` は Apache 2.0 のもとにあります。[ライセンス](https://nodaro.ai/docs/self-hosting/license)を参照してください。

**新しいコードの置き場所**：Apache のパッケージは、公開したバージョンごとに、取り消せない形で権利を許諾することになります。新しいプロンプトエンジニアリング、カタログ、プリセットは、`@nodaro/prompts` か `backend/` に置きます。`@nodaro/shared` に追加するのは、公開 API と SDK のコントラクトに必要なもの（型、通信で使う列挙型、API の利用者と共有する検証など）か、再利用のために意図して公開するものだけにしてください。どちらに当たるかを、プルリクエストに書いてください。

**コントリビューター契約**：コントリビューションを提出すると、[Nodaro Contributor License Agreement](https://github.com/nodaroai/app.nodaro.ai/blob/main/CLA.md) に同意したことになります。個人と企業のどちらのコントリビューションも、同じ契約の対象です。雇用主の許可については、契約の第 2 条で扱っています。最初のプルリクエストで、cla-assistant ボットが署名を求めます。雇用主の代理としてコントリビュートする場合は、雇用主の知的財産ポリシーで認められていることを確認してください。この契約により、Nodaro は、あなたのコントリビューションを Nodaro のどのライセンスのもとでも提供できます。Sustainable Use License と Enterprise License の間で移すことも含まれます。

## Frequently asked questions

### プルリクエストは、どのブランチに向けて作成すればよいですか？

dev からブランチを作成し、プルリクエストは dev に向けて作成します。main からブランチを作成したり、main に直接コミットしたりしないでください。変更は、ステージング環境での一定期間の検証を経て main に反映されます。

### コントリビューターライセンス契約への署名は必要ですか？

はい。コントリビューションを提出すると、Nodaro Contributor License Agreement に同意したことになります。最初のプルリクエストで cla-assistant ボットが署名を求めます。個人と企業のどちらのコントリビューションも、同じ契約の対象です。

### テストは、どのように実行しますか？

リポジトリのルートで npm test を実行すると、すべてのワークスペースの Vitest スイートが実行されます。npm -w @nodaro/shared test のように、1 つのワークスペースだけを実行することもできます。コミットの前には毎回、npx tsc --noEmit で backend と frontend の型チェックを行ってください。

### changeset が必要になるのは、どのような場合ですか？

公開パッケージ（@nodaro/shared、@nodaro/sdk、@nodaro/cli）に手を加える場合です。npx changeset を実行し、書き出されたファイルをコミットします。リリースノートが不要な変更には、npx changeset --empty を使います。

### コントリビューションについての質問は、どこですればよいですか？

自由な質問やアイデアには GitHub Discussions を、バグや機能のリクエストには GitHub Issues を使ってください。セキュリティの問題は、非公開のセキュリティアドバイザリーで報告してください。
