Compare commits
25 Commits
297299c3f8
...
chore/harn
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
acf0fd34d8 | ||
|
|
c00ce0a622 | ||
|
|
64f817e8f6 | ||
|
|
c50082771d | ||
|
|
7102f0f553 | ||
|
|
da2466462e | ||
|
|
77f3326868 | ||
|
|
2dbe8c8a74 | ||
|
|
c163d00f51 | ||
|
|
5e92ea62ef | ||
|
|
f80c2a2518 | ||
|
|
6de184968d | ||
|
|
be710b920e | ||
|
|
d59e55a54e | ||
|
|
53f84e2647 | ||
|
|
86c0180c3f | ||
|
|
f8e9c95403 | ||
|
|
793846e7f5 | ||
|
|
29d2dbbb57 | ||
|
|
0acac8799d | ||
|
|
39a850b064 | ||
|
|
90e805a360 | ||
|
|
f700a3454e | ||
|
|
0d1278b9ac | ||
|
|
639ac23efa |
32
.claude/settings.local.json
Normal file
32
.claude/settings.local.json
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
{
|
||||||
|
"permissions": {
|
||||||
|
"allow": [
|
||||||
|
"Bash(curl -m 10 -s -o /dev/null -w \"%{http_code}\" -H \"Connection: Upgrade\" -H \"Upgrade: websocket\" https://windmill.keinafarm.net/ws/)",
|
||||||
|
"Bash(ssh -i \"/c/Users/akira/.ssh/ssh-key-20241206.pem\" -t claude@keinafarm.net \"cat /home/windmill/windmill/docker-compose.yml\")",
|
||||||
|
"Bash(ssh -i \"/c/Users/akira/.ssh/ssh-key-20241206.pem\" akira@keinafarm.net \"docker exec windmill_server cat /workspace/docker-compose.yml\")",
|
||||||
|
"Bash(ssh -i \"/c/Users/akira/.ssh/ssh-key-20241206.pem\" akira@keinafarm.net \"docker compose --project-directory /home/windmill/windmill version 2>&1\")",
|
||||||
|
"Bash(ssh -i \"/c/Users/akira/.ssh/ssh-key-20241206.pem\" akira@keinafarm.net \"docker compose --project-directory /home/windmill/windmill ps 2>&1\")",
|
||||||
|
"Bash(ssh -i \"/c/Users/akira/.ssh/ssh-key-20241206.pem\" akira@keinafarm.net \"docker exec windmill_server cat /workspace/.env\")",
|
||||||
|
"Bash(ssh -i \"/c/Users/akira/.ssh/ssh-key-20241206.pem\" akira@keinafarm.net \"docker network inspect traefik-net --format ''{{json .Name}}'' && docker network connect traefik-net windmill-windmill_extra-1 && echo ''Connected successfully''\")",
|
||||||
|
"Bash(ssh -i \"/c/Users/akira/.ssh/ssh-key-20241206.pem\" akira@keinafarm.net \"docker exec windmill_server cat /workspace/docker-compose.yml > /tmp/windmill-compose.yml && cat /tmp/windmill-compose.yml | wc -l\")",
|
||||||
|
"Bash(ssh -i \"/c/Users/akira/.ssh/ssh-key-20241206.pem\" akira@keinafarm.net \"grep -A 20 ''windmill_extra:'' /tmp/windmill-compose-fixed.yml | grep -E ''\\(traefik-net|windmill-lsp.service|windmill-debug.service\\)''\")",
|
||||||
|
"Bash(ssh -i \"/c/Users/akira/.ssh/ssh-key-20241206.pem\" akira@keinafarm.net \"grep -n ''windmill-lsp\\\\|windmill-debug\\\\|traefik-net'' /tmp/windmill-compose-fixed.yml\")",
|
||||||
|
"Bash(ssh -i:*)",
|
||||||
|
"WebFetch(domain:github.com)",
|
||||||
|
"Bash(python -c \"from mcp.server.fastmcp import FastMCP; print\\(''mcp OK''\\)\")",
|
||||||
|
"Bash(pip install mcp httpx)",
|
||||||
|
"Bash(WINDMILL_TOKEN=qLJ3VPZ61kTDiIwaUPUu1dXszGrsN1Dh python -c \":*)",
|
||||||
|
"Read(//c/Users/akira/.claude/**)",
|
||||||
|
"Bash(git add .gitignore mcp/)",
|
||||||
|
"Bash(git commit:*)",
|
||||||
|
"mcp__windmill__windmill_list_flows"
|
||||||
|
],
|
||||||
|
"additionalDirectories": [
|
||||||
|
"C:\\Users\\akira\\Develop\\windmill",
|
||||||
|
"C:\\Users\\akira\\.claude\\projects\\c--Users-akira-Develop-windmill\\memory\\"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"enabledMcpjsonServers": [
|
||||||
|
"windmill"
|
||||||
|
]
|
||||||
|
}
|
||||||
9
.env
9
.env
@@ -1,9 +1,14 @@
|
|||||||
DATABASE_URL=postgres://postgres:changeme@db/windmill?sslmode=disable
|
DATABASE_PASSWORD=DbForWindMillPassword
|
||||||
|
WM_VERSION=1.638.0
|
||||||
|
|
||||||
|
DATABASE_URL=postgres://postgres:${DATABASE_PASSWORD}@db/windmill?sslmode=disable
|
||||||
|
|
||||||
# For Enterprise Edition, use:
|
# For Enterprise Edition, use:
|
||||||
# WM_IMAGE=ghcr.io/windmill-labs/windmill-ee:main
|
# WM_IMAGE=ghcr.io/windmill-labs/windmill-ee:main
|
||||||
WM_IMAGE=ghcr.io/windmill-labs/windmill:main
|
WM_IMAGE=ghcr.io/windmill-labs/windmill:${WM_VERSION}
|
||||||
|
|
||||||
|
GOOGLE_OAUTH_CLIENT_ID=976427934311-6oj0l38ptn6ui2hoj37qbs137lcnu6kg.apps.googleusercontent.com
|
||||||
|
GOOGLE_OAUTH_CLIENT_SECRET=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
|
||||||
|
|
||||||
# To use another port than :80, setup the Caddyfile and the caddy section of the docker-compose to your needs: https://caddyserver.com/docs/getting-started
|
# To use another port than :80, setup the Caddyfile and the caddy section of the docker-compose to your needs: https://caddyserver.com/docs/getting-started
|
||||||
# To have caddy take care of automatic TLS
|
# To have caddy take care of automatic TLS
|
||||||
|
|||||||
11
.gitignore
vendored
11
.gitignore
vendored
@@ -29,6 +29,9 @@ Thumbs.db
|
|||||||
*.bak
|
*.bak
|
||||||
*~
|
*~
|
||||||
|
|
||||||
|
# MCP server config (contains API tokens)
|
||||||
|
.mcp.json
|
||||||
|
|
||||||
# Resolved markdown files (generated by editor)
|
# Resolved markdown files (generated by editor)
|
||||||
*.resolved
|
*.resolved
|
||||||
*.resolved.*
|
*.resolved.*
|
||||||
@@ -46,3 +49,11 @@ workflows/.wmill/tmp/
|
|||||||
!workflows/g/
|
!workflows/g/
|
||||||
!workflows/wmill.yaml
|
!workflows/wmill.yaml
|
||||||
!workflows/wmill-lock.yaml
|
!workflows/wmill-lock.yaml
|
||||||
|
__pycache__/
|
||||||
|
|
||||||
|
# Windmill workflow definitions (managed by git_sync on sync branch)
|
||||||
|
# Local working directory should not track these on main
|
||||||
|
u/
|
||||||
|
workflows/f/
|
||||||
|
workflows/u/
|
||||||
|
workflows/g/
|
||||||
|
|||||||
31
AGENTS.md
Normal file
31
AGENTS.md
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
# Project AGENTS (windmill)
|
||||||
|
|
||||||
|
## Startup
|
||||||
|
- Load shared global rules from:
|
||||||
|
- `C:/Users/akira/.codex/shared/GLOBAL_RULES.md`
|
||||||
|
- Apply project-local rules in this file after shared rules.
|
||||||
|
|
||||||
|
## Required Read Order
|
||||||
|
1. `HARNESS.md`
|
||||||
|
2. `INDEX.md`
|
||||||
|
3. `HANDOFF.md`
|
||||||
|
4. `SERVER_SETUP.md`
|
||||||
|
|
||||||
|
## Working Mode (mandatory)
|
||||||
|
- This repository uses harness workflow.
|
||||||
|
- Every request must be framed in 3 lines:
|
||||||
|
- Purpose
|
||||||
|
- Target
|
||||||
|
- Done
|
||||||
|
- Follow `INDEX.md` work sequence and `HARNESS.md` validation policy.
|
||||||
|
|
||||||
|
## Safety Rules (mandatory)
|
||||||
|
- Work locally first.
|
||||||
|
- Before edits, ensure backup branch exists: `backup/2026-03-05-before-harness`.
|
||||||
|
- Implement on working branch (current default: `chore/harness-introduce`).
|
||||||
|
- If unexpected unrelated changes are detected, stop and ask.
|
||||||
|
|
||||||
|
## Recording Rules
|
||||||
|
- Record each task in `state/tasks/<task_id>.md`.
|
||||||
|
- Accumulate regression cases in `tests/golden/`.
|
||||||
|
|
||||||
12
Caddyfile.local
Normal file
12
Caddyfile.local
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
:80 {
|
||||||
|
# LSP - Language Server Protocol for code intelligence
|
||||||
|
reverse_proxy /ws/* http://windmill_extra:3001
|
||||||
|
|
||||||
|
# Debugger - Interactive debugging via DAP WebSocket
|
||||||
|
handle_path /ws_debug/* {
|
||||||
|
reverse_proxy http://windmill_extra:3003
|
||||||
|
}
|
||||||
|
|
||||||
|
# Default: Windmill server
|
||||||
|
reverse_proxy /* http://windmill_server:8000
|
||||||
|
}
|
||||||
112
HANDOFF.md
Normal file
112
HANDOFF.md
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
# Windmill 作業引き継ぎメモ
|
||||||
|
|
||||||
|
> 作成: 2026-02-25(keinasystem_t02 セッションから移管)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 現在の状態
|
||||||
|
|
||||||
|
### git_sync フロー(修正済み ✅)
|
||||||
|
|
||||||
|
**問題**: `u/antigravity/git_sync` フローが `wmill sync pull` で認証エラー
|
||||||
|
**原因**: `$WM_TOKEN`(ジョブトークン)はワークスペーススコープのため、
|
||||||
|
wmill CLIが内部で呼ぶ `/api/users/whoami`(グローバルAPI)で401
|
||||||
|
**解決**:
|
||||||
|
- `/home/windmill/windmill/wmill_config/remotes.ndjson` を作成(永続設定)
|
||||||
|
- グローバルスコープトークン `CQKYm1bUwszHCT4Ww6TGyQX97XMs8qg8`(ラベル: `git-sync`)を使用
|
||||||
|
- フロースクリプトを `--config-dir /workspace/wmill_config` を使う形に修正
|
||||||
|
- 動作確認: Success: True (2秒) ✅
|
||||||
|
|
||||||
|
### windmill.keinafarm.net 外部アクセス(504 未修正 ❌)
|
||||||
|
|
||||||
|
- 外部からアクセスすると 30秒後に 504 Gateway Timeout
|
||||||
|
- サーバー内部からは `http://localhost:8000` で正常にアクセスできる
|
||||||
|
- **Caddyfile がこのディレクトリにある** → 原因調査・修正が必要
|
||||||
|
|
||||||
|
### Windmill MCP サーバー(未着手 ⬜)
|
||||||
|
|
||||||
|
- LLM(Claude)が直接 Windmill を操作できるようにする
|
||||||
|
- 外部アクセスが直れば `https://windmill.keinafarm.net` に接続
|
||||||
|
- 直らない場合は SSH トンネル経由
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 重要な情報
|
||||||
|
|
||||||
|
### サーバー接続
|
||||||
|
|
||||||
|
```
|
||||||
|
SSH: root@keinafarm.net
|
||||||
|
Windmill内部URL: http://localhost:8000
|
||||||
|
Windmillサーバー上のパス: /home/windmill/windmill/
|
||||||
|
```
|
||||||
|
|
||||||
|
### API トークン
|
||||||
|
|
||||||
|
| トークン | スコープ | 用途 |
|
||||||
|
|---------|---------|------|
|
||||||
|
| `qLJ3VPZ61kTDiIwaUPUu1dXszGrsN1Dh` | ワークスペース(admins) | 通常のAPI操作 |
|
||||||
|
| `CQKYm1bUwszHCT4Ww6TGyQX97XMs8qg8` | グローバル | git-sync用(wmill CLI) |
|
||||||
|
|
||||||
|
### 2つの Git リポジトリ(別物!)
|
||||||
|
|
||||||
|
| リポジトリ | パス | 用途 |
|
||||||
|
|-----------|------|------|
|
||||||
|
| `windmill.git` (Gitea) | サーバー `/home/windmill/windmill/` | wmill CLI で自動同期 |
|
||||||
|
| `windmill_workflow.git` (Gitea) | ローカル `C:\Users\akira\Develop\windmill` | このディレクトリ |
|
||||||
|
|
||||||
|
### wmill_config(永続設定)
|
||||||
|
|
||||||
|
```
|
||||||
|
サーバーパス: /home/windmill/windmill/wmill_config/
|
||||||
|
コンテナ内パス: /workspace/wmill_config/
|
||||||
|
remotes.ndjson: {"remote":"http://windmill_server:8000/","workspaceId":"admins","name":"admins","token":"CQKYm1bUwszHCT4Ww6TGyQX97XMs8qg8"}
|
||||||
|
activeWorkspace: admins
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 次にやること(優先順)
|
||||||
|
|
||||||
|
### 1. windmill.keinafarm.net 504 を修正
|
||||||
|
|
||||||
|
`Caddyfile` を確認して、windmill へのリバースプロキシ設定を見直す。
|
||||||
|
タイムアウト設定が足りない可能性が高い。
|
||||||
|
|
||||||
|
```
|
||||||
|
# 確認コマンド(サーバー内部では正常)
|
||||||
|
ssh root@keinafarm.net "curl -s http://localhost:8000/api/version"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Windmill MCP サーバーを実装
|
||||||
|
|
||||||
|
**方針**: カスタム軽量 Python MCP サーバー(6〜8 tools)
|
||||||
|
|
||||||
|
実装する tools:
|
||||||
|
- `windmill_list_flows` — フロー一覧
|
||||||
|
- `windmill_get_flow` — フローのスクリプト取得
|
||||||
|
- `windmill_run_flow` — フローをトリガー
|
||||||
|
- `windmill_list_recent_jobs` — 最近のジョブ一覧(成功/失敗)
|
||||||
|
- `windmill_get_job_logs` — ジョブの詳細ログ
|
||||||
|
- `windmill_list_scripts` — スクリプト一覧
|
||||||
|
- `windmill_get_script` — スクリプト取得
|
||||||
|
|
||||||
|
既存実装: `rothnic/windmill-mcp`(GitHub, スター0, 更新停止)→ 使わず自作
|
||||||
|
|
||||||
|
### 3. mail_filter.flow.json をコミット
|
||||||
|
|
||||||
|
ローカルの `windmill_workflow` に `f/mail/mail_filter.flow.json` が未コミット状態。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 参考: wmill 設定ファイル形式
|
||||||
|
|
||||||
|
```
|
||||||
|
~/.config/windmill/remotes.ndjson (1行1ワークスペース)
|
||||||
|
{"remote":"http://...../","workspaceId":"...","name":"local_alias","token":"..."}
|
||||||
|
|
||||||
|
~/.config/windmill/activeWorkspace (プレーンテキスト)
|
||||||
|
local_alias
|
||||||
|
```
|
||||||
|
|
||||||
|
`wmill sync pull --config-dir /path/to/config` で任意ディレクトリを指定可能。
|
||||||
70
HARNESS.md
Normal file
70
HARNESS.md
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
# HARNESS.md
|
||||||
|
|
||||||
|
## 目的
|
||||||
|
- このリポジトリ(Windmill運用)で、Codex / Claude Code / ローカルLLMの協調作業を安全かつ再現可能にする。
|
||||||
|
- 大きい指示文より、コンテキスト設計・制約・検証・記録を優先する。
|
||||||
|
|
||||||
|
## 役割分担
|
||||||
|
- Codex: 要件分解、変更方針、レビュー観点、ドキュメント整備。
|
||||||
|
- Claude Code: 実装、コマンド実行、検証、差分反映。
|
||||||
|
- ローカルLLM: 下書き生成、軽量リファクタ提案、代替案提示。
|
||||||
|
|
||||||
|
## 依頼フォーマット(必須3行)
|
||||||
|
1. Purpose: 何を達成するか
|
||||||
|
2. Target: どのファイル/機能/環境か
|
||||||
|
3. Done: 何を確認できたら完了か
|
||||||
|
|
||||||
|
## 作業スコープ
|
||||||
|
- 主対象: `workflows/`, `mcp/`, `docker-compose-dev.yml`, `docker-compose.yml`, `sync_to_git.sh`, `HANDOFF.md`, `SERVER_SETUP.md`
|
||||||
|
- 原則: 1タスク1責務。仕様変更と大規模改修を同時に進めない。
|
||||||
|
- 仕様/挙動変更時は、同タスクでドキュメント更新まで行う。
|
||||||
|
|
||||||
|
## コンテキスト方針
|
||||||
|
- 長い会話ログは渡さない。
|
||||||
|
- 毎回渡す情報は最小化する:
|
||||||
|
- 依頼3行(Purpose/Target/Done)
|
||||||
|
- 関連ファイルのパス
|
||||||
|
- 失敗コマンド + ログ先頭5行 + 末尾5行
|
||||||
|
- 現在の前提・制約
|
||||||
|
- トークン圧迫時は要約スナップショットへ圧縮する。
|
||||||
|
|
||||||
|
## 安全レール
|
||||||
|
- 破壊的操作(削除、履歴改変、本番反映)は事前承認必須。
|
||||||
|
- シークレットをプロンプト/ログ/コミットに含めない。
|
||||||
|
- タスクに無関係なパスは編集しない。
|
||||||
|
- 想定外の差分を検知したら作業停止して確認する。
|
||||||
|
|
||||||
|
## 検証ポリシー(Windmill向け固定)
|
||||||
|
- L1 構成検証:
|
||||||
|
- `docker compose -f docker-compose-dev.yml config`
|
||||||
|
- L2 サービス検証(必要時):
|
||||||
|
- `docker compose -f docker-compose-dev.yml up -d db windmill_server`
|
||||||
|
- `docker compose -f docker-compose-dev.yml ps`
|
||||||
|
- `docker compose -f docker-compose-dev.yml exec -T windmill_server curl -fsS http://localhost:8000/api/version`
|
||||||
|
- L3 変更対象別検証:
|
||||||
|
- `mcp/` 変更時: `docker compose build windmill_mcp` と `docker compose up -d windmill_mcp`
|
||||||
|
- `workflows/` 変更時: `wmill sync` 関連手順を `SERVER_SETUP.md` / `HANDOFF.md` に沿って確認
|
||||||
|
- 失敗時は必ずログを残す:
|
||||||
|
- `docker compose -f docker-compose-dev.yml logs --tail=200 windmill_server`
|
||||||
|
|
||||||
|
## 品質ゲート
|
||||||
|
- 変更内容に対応するL1/L2/L3結果を記録して完了とする。
|
||||||
|
- サイレントな挙動変更は禁止(テスト/手順書更新を伴うこと)。
|
||||||
|
- 運用手順に影響する変更は `HANDOFF.md` または `SERVER_SETUP.md` を同時更新する。
|
||||||
|
|
||||||
|
## 記録ルール
|
||||||
|
- タスクごとに `state/tasks/<task_id>.md` を作成。
|
||||||
|
- 最低記録項目:
|
||||||
|
- task_id
|
||||||
|
- 依頼3行
|
||||||
|
- 変更ファイル
|
||||||
|
- 実行コマンドと結果
|
||||||
|
- 未解決リスク/次アクション
|
||||||
|
|
||||||
|
## ゴールデンデータセット
|
||||||
|
- 再発した不具合は `tests/golden/` に再現手順/入出力を追加する。
|
||||||
|
- 修正時は可能な限り先に再現ケースを追加してから直す。
|
||||||
|
|
||||||
|
## 復旧方針
|
||||||
|
- 作業前にバックアップブランチを作る。
|
||||||
|
- 緊急時はバックアップブランチに即時復帰できる状態を維持する。
|
||||||
60
INDEX.md
Normal file
60
INDEX.md
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
# INDEX.md
|
||||||
|
|
||||||
|
## Start Here
|
||||||
|
1. `AGENTS.md`(存在する場合)
|
||||||
|
2. `HARNESS.md`
|
||||||
|
3. `HANDOFF.md`
|
||||||
|
4. `SERVER_SETUP.md`
|
||||||
|
5. 今回タスクの3行(Purpose/Target/Done)
|
||||||
|
|
||||||
|
## このリポジトリで優先的に見る場所
|
||||||
|
- 運用メモ:
|
||||||
|
- `HANDOFF.md`
|
||||||
|
- `SERVER_SETUP.md`
|
||||||
|
- 実装・設定:
|
||||||
|
- `workflows/`
|
||||||
|
- `mcp/`
|
||||||
|
- `docker-compose-dev.yml`
|
||||||
|
- `docker-compose.yml`
|
||||||
|
- `sync_to_git.sh`
|
||||||
|
- 実行時状態:
|
||||||
|
- `.env`(読み取りのみ・値の露出禁止)
|
||||||
|
- `.mcp.json`(読み取りのみ・値の露出禁止)
|
||||||
|
- 記録:
|
||||||
|
- `state/tasks/`
|
||||||
|
- `tests/golden/`
|
||||||
|
|
||||||
|
## 作業手順(固定)
|
||||||
|
1. 依頼3行を確定する。
|
||||||
|
2. 変更対象を最小ファイルに絞る。
|
||||||
|
3. 小さい差分で実装する。
|
||||||
|
4. 検証を実行する(HARNESSのL1/L2/L3)。
|
||||||
|
5. `state/tasks/<task_id>.md` に記録する。
|
||||||
|
6. 必要なら `HANDOFF.md` / `SERVER_SETUP.md` を更新する。
|
||||||
|
|
||||||
|
## Agent入力テンプレート
|
||||||
|
- Purpose: ...
|
||||||
|
- Target: ...
|
||||||
|
- Done: ...
|
||||||
|
- Constraints: ...
|
||||||
|
- Relevant files:
|
||||||
|
- path1
|
||||||
|
- path2
|
||||||
|
- Verification commands:
|
||||||
|
- cmd1
|
||||||
|
- cmd2
|
||||||
|
|
||||||
|
## 検証コマンド(このrepo既定)
|
||||||
|
```powershell
|
||||||
|
docker compose -f docker-compose-dev.yml config
|
||||||
|
docker compose -f docker-compose-dev.yml up -d db windmill_server
|
||||||
|
docker compose -f docker-compose-dev.yml ps
|
||||||
|
docker compose -f docker-compose-dev.yml exec -T windmill_server curl -fsS http://localhost:8000/api/version
|
||||||
|
docker compose -f docker-compose-dev.yml logs --tail=200 windmill_server
|
||||||
|
```
|
||||||
|
|
||||||
|
## 完了条件(DoD)
|
||||||
|
- 要求された挙動を満たす。
|
||||||
|
- 検証結果が記録されている。
|
||||||
|
- 運用手順影響がある場合、関連ドキュメントが更新されている。
|
||||||
|
- 未解決リスクと次アクションが明示されている。
|
||||||
316
SERVER_SETUP.md
316
SERVER_SETUP.md
@@ -1,233 +1,155 @@
|
|||||||
# Windmill サーバー設定手順
|
# Windmill サーバー設定手順 (VPS移行版)
|
||||||
|
|
||||||
現在の状態:
|
本番環境(VPS)へのデプロイ手順です。
|
||||||
- ディレクトリ: `/home/windmill/windmill`
|
既にTraefikが稼働している環境(`traefik-net` ネットワークが存在する環境)を前提としています。
|
||||||
- Giteaから正常にpull完了
|
|
||||||
|
## 前提条件
|
||||||
|
|
||||||
|
- サーバー上でTraefikが稼働しており、`traefik-net` ネットワークが存在すること。
|
||||||
|
- ドメイン `windmill.keinafarm.net` がサーバーのIPに向けられていること。
|
||||||
|
|
||||||
|
## ステップ1: リポジトリの準備
|
||||||
|
|
||||||
|
サーバー上の任意の場所(例: `/home/windmill/windmill`)にリポジトリをクローンします。
|
||||||
|
**重要**: WindmillのGit同期機能を使用するため、このディレクトリパスは重要です。
|
||||||
|
|
||||||
## ステップ1: docker-compose.yml の置き換え
|
|
||||||
```bash
|
```bash
|
||||||
cd /home/windmill/windmill
|
mkdir -p /home/windmill
|
||||||
|
cd /home/windmill
|
||||||
# 現在のファイルをバックアップ
|
git clone https://gitea.keinafarm.net/akira/windmill.git windmill
|
||||||
cp docker-compose.yml docker-compose.yml.local.backup
|
cd windmill
|
||||||
|
|
||||||
# 新しいdocker-compose.ymlを作成
|
|
||||||
# (ダウンロードしたファイルの内容をコピー)
|
|
||||||
nano docker-compose.yml
|
|
||||||
```
|
```
|
||||||
|
|
||||||
または、ローカルで修正してgit pushする方法:
|
## ステップ2: 環境変数の設定
|
||||||
|
|
||||||
|
`.env` ファイルを作成し、本番用の設定を行います。
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# ローカルで
|
cp .env .env.production
|
||||||
cd /home/akira/develop/windmill
|
|
||||||
cp /path/to/downloaded/docker-compose.yml .
|
|
||||||
git add docker-compose.yml
|
|
||||||
git commit -m "Update docker-compose.yml for server deployment"
|
|
||||||
git push gitea main
|
|
||||||
|
|
||||||
# サーバーで
|
|
||||||
cd /home/windmill/windmill
|
|
||||||
git pull origin main
|
|
||||||
```
|
|
||||||
|
|
||||||
## ステップ2: .env ファイルの確認
|
|
||||||
```bash
|
|
||||||
cd /home/windmill/windmill
|
|
||||||
|
|
||||||
# .envファイルを編集
|
|
||||||
nano .env
|
nano .env
|
||||||
```
|
```
|
||||||
|
|
||||||
以下の内容を確認・修正:
|
以下の内容を確認・修正してください:
|
||||||
|
- `DATABASE_URL`: `postgres://postgres:あなたの強力なパスワード@db/windmill?sslmode=disable`
|
||||||
|
- `POSTGRES_PASSWORD`: 上記と同じパスワード
|
||||||
|
- `WM_IMAGE`: `ghcr.io/windmill-labs/windmill:main`
|
||||||
|
|
||||||
|
## ステップ3: 起動
|
||||||
|
|
||||||
|
`docker-compose.yml` は本番用に構成されています(Traefik連携済み)。
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
WM_IMAGE=ghcr.io/windmill-labs/windmill:main
|
|
||||||
DATABASE_URL=postgresql://postgres:YOUR_STRONG_PASSWORD@db:5432/windmill
|
|
||||||
LOG_MAX_SIZE=20m
|
|
||||||
LOG_MAX_FILE=10
|
|
||||||
```
|
|
||||||
|
|
||||||
⚠️ **重要**: `YOUR_STRONG_PASSWORD` を強力なパスワードに変更してください
|
|
||||||
|
|
||||||
## ステップ3: Traefikネットワークの確認
|
|
||||||
```bash
|
|
||||||
# traefik-netネットワークが存在するか確認
|
|
||||||
docker network ls | grep traefik-net
|
|
||||||
|
|
||||||
# もし存在しない場合は作成
|
|
||||||
docker network create traefik-net
|
|
||||||
```
|
|
||||||
|
|
||||||
## ステップ4: Caddyファイルの削除(不要)
|
|
||||||
```bash
|
|
||||||
cd /home/windmill/windmill
|
|
||||||
|
|
||||||
# Caddyfileは不要(Traefikを使用)
|
|
||||||
rm Caddyfile # または mv Caddyfile Caddyfile.bak
|
|
||||||
```
|
|
||||||
|
|
||||||
## ステップ5: sync_to_git.sh の更新
|
|
||||||
```bash
|
|
||||||
cd /home/windmill/windmill
|
|
||||||
|
|
||||||
# 既存のsync_to_git.shを新しいバージョンに置き換え
|
|
||||||
nano sync_to_git.sh
|
|
||||||
# (ダウンロードした内容をコピー)
|
|
||||||
|
|
||||||
chmod +x sync_to_git.sh
|
|
||||||
```
|
|
||||||
|
|
||||||
## ステップ6: Git認証情報の設定
|
|
||||||
|
|
||||||
プッシュ時に認証が必要な場合、以下のいずれかを設定:
|
|
||||||
|
|
||||||
### 方法A: Git Credential Helper(推奨)
|
|
||||||
```bash
|
|
||||||
cd /home/windmill/windmill
|
|
||||||
|
|
||||||
# 認証情報を保存
|
|
||||||
git config credential.helper store
|
|
||||||
|
|
||||||
# 一度手動でpush(パスワード/トークンを入力)
|
|
||||||
git push origin main
|
|
||||||
# Username: akira
|
|
||||||
# Password: <Gitea access token>
|
|
||||||
|
|
||||||
# 以降は認証情報が保存されている
|
|
||||||
```
|
|
||||||
|
|
||||||
### 方法B: SSH鍵(より安全)
|
|
||||||
```bash
|
|
||||||
# SSH鍵を生成
|
|
||||||
ssh-keygen -t ed25519 -C "windmill@keinafarm.net"
|
|
||||||
|
|
||||||
# 公開鍵をGiteaに登録
|
|
||||||
cat ~/.ssh/id_ed25519.pub
|
|
||||||
# Gitea → Settings → SSH/GPG Keys → Add Key
|
|
||||||
|
|
||||||
# リモートURLをSSHに変更
|
|
||||||
cd /home/windmill/windmill
|
|
||||||
git remote set-url origin git@gitea.keinafarm.net:akira/windmil.git
|
|
||||||
```
|
|
||||||
|
|
||||||
## ステップ7: Windmillの起動
|
|
||||||
```bash
|
|
||||||
cd /home/windmill/windmill
|
|
||||||
|
|
||||||
# コンテナを起動
|
|
||||||
docker-compose up -d
|
docker-compose up -d
|
||||||
|
|
||||||
# ログを確認
|
|
||||||
docker-compose logs -f windmill_server
|
|
||||||
|
|
||||||
# 状態確認
|
|
||||||
docker-compose ps
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## ステップ8: 動作確認
|
## ステップ4: Git同期用ワークフローのセットアップ
|
||||||
|
|
||||||
|
Windmill上で「登録されたワークフローをGitに保存する」機能を有効にする手順です。
|
||||||
|
`git_sync` フローが定期実行されると、Windmill DB上のスクリプト/フローの変更がGiteaリポジトリに自動コミット&プッシュされます。
|
||||||
|
|
||||||
|
### 4-1. Windmill APIトークンの取得
|
||||||
|
|
||||||
|
1. ブラウザで `https://windmill.keinafarm.net` にログイン
|
||||||
|
2. 左下の **Settings** → **Account** をクリック
|
||||||
|
3. **Tokens** セクションで **Create token** をクリック
|
||||||
|
4. Label(例: `git-sync`)を入力し、作成
|
||||||
|
5. 表示されたトークンをコピーしておく(後のステップで使用)
|
||||||
|
|
||||||
|
### 4-2. ワークフロー定義の取り込み(初回のみ)
|
||||||
|
|
||||||
|
リポジトリの `workflows/` にある定義ファイルをWindmill DBに取り込みます。
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# APIバージョンチェック
|
# Windmillサーバーコンテナに入る
|
||||||
curl -k https://windmill.keinafarm.net/api/version
|
docker exec -it windmill_server /bin/bash
|
||||||
|
|
||||||
# ブラウザでアクセス
|
# コンテナ内で実行:windmill-cli をインストール
|
||||||
# https://windmill.keinafarm.net
|
npm install -g windmill-cli
|
||||||
|
|
||||||
|
# wmill.yamlがあるディレクトリに移動して sync push
|
||||||
|
cd /workspace/workflows
|
||||||
|
wmill sync push \
|
||||||
|
--token "<4-1で取得したトークン>" \
|
||||||
|
--base-url "http://localhost:8000" \
|
||||||
|
--workspace admins \
|
||||||
|
--yes
|
||||||
|
|
||||||
|
exit
|
||||||
```
|
```
|
||||||
|
|
||||||
## ステップ9: Windmill初期設定
|
> **注意**: `wmill sync push` はディスク→DBへの反映です。
|
||||||
|
> 逆に `wmill sync pull` はDB→ディスクへの反映です。
|
||||||
|
> スケジュールされた `git_sync` フローが `sync pull` を実行するため、
|
||||||
|
> **UIで直接スクリプトを修正した場合、次回の sync pull で正しくディスクにも反映されます。**
|
||||||
|
|
||||||
1. ブラウザで `https://windmill.keinafarm.net` にアクセス
|
### 4-3. Gitea認証情報の設定(git push用)
|
||||||
2. 初回セットアップウィザードに従う
|
|
||||||
3. 管理者アカウントを作成
|
|
||||||
4. Workspaceを作成(例: `admins`)
|
|
||||||
|
|
||||||
## ステップ10: Git同期スクリプトの設定(Windmill内)
|
`git_sync` フローが Gitea へ `git push` できるよう、サーバー上のリモートURLにGiteaのアクセストークンを含めます。
|
||||||
|
|
||||||
1. Windmill UI → Scripts → New Script
|
|
||||||
2. 以下の内容でBashスクリプトを作成:
|
|
||||||
```bash
|
```bash
|
||||||
#!/bin/bash
|
# サーバーのホスト側で実行
|
||||||
set -x
|
cd ~/windmill
|
||||||
export WM_BASE_URL="http://windmill_server:8000"
|
|
||||||
export WM_WORKSPACE="admins" # あなたのworkspace名に変更
|
|
||||||
export PATH=$HOME/.npm-global/bin:$PATH
|
|
||||||
|
|
||||||
echo "=== START SYNC ==="
|
# 現在のリモートURLを確認
|
||||||
|
git remote -v
|
||||||
|
|
||||||
if ! command -v wmill &> /dev/null; then
|
# Giteaのアクセストークンを含んだURLに変更
|
||||||
npm install -g windmill-cli
|
git remote set-url origin https://<username>:<giteaトークン>@gitea.keinafarm.net/akira/windmill.git
|
||||||
fi
|
|
||||||
|
|
||||||
cd /workspace
|
|
||||||
|
|
||||||
wmill sync pull --token "$WM_TOKEN" --base-url "$WM_BASE_URL" --workspace "$WM_WORKSPACE" --skip-variables --skip-secrets --skip-resources --yes --verbose || exit 1
|
|
||||||
|
|
||||||
git config --global --add safe.directory /workspace
|
|
||||||
git config --global user.email "bot@example.com"
|
|
||||||
git config --global user.name "Bot"
|
|
||||||
|
|
||||||
git add .
|
|
||||||
git commit -m "Auto-sync $(date)" || echo "No changes"
|
|
||||||
git push origin main
|
|
||||||
|
|
||||||
echo "=== END SYNC ==="
|
|
||||||
```
|
```
|
||||||
|
|
||||||
3. Schedule → New Schedule
|
> **Giteaトークンの作成方法**: Gitea(`https://gitea.keinafarm.net`)にログイン →
|
||||||
- Cron: `*/15 * * * *` (15分ごと)
|
> 右上アバター → Settings → Applications → Generate New Token
|
||||||
- Script: 上記で作成したスクリプト
|
|
||||||
|
|
||||||
4. Variables → New Variable
|
### 4-4. WM_TOKEN Variable の設定
|
||||||
- `WM_TOKEN`: Windmill APIトークン(Settings → Tokensで作成)
|
|
||||||
|
WindmillのWeb画面で、`git_sync` フローが使用する変数を登録します。
|
||||||
|
|
||||||
|
1. 左メニューの **Variables** をクリック
|
||||||
|
2. **+ Variable** をクリック
|
||||||
|
3. 以下を入力:
|
||||||
|
- **Path**: `u/antigravity/wm_token`
|
||||||
|
- **Value**: 4-1で取得したWindmill APIトークン
|
||||||
|
- **Is Secret**: ✅ オン
|
||||||
|
4. **Save** をクリック
|
||||||
|
|
||||||
|
> **注意**: `git_sync` フローのスクリプト(`a.sh`)内で `$WM_TOKEN` として参照されます。
|
||||||
|
> フローのInput設定で、この変数が正しく紐づけられていることを確認してください。
|
||||||
|
|
||||||
|
### 4-5. git_sync フローの手動実行テスト
|
||||||
|
|
||||||
|
1. Windmill UI で **`u/antigravity/git_sync`** フローを開く
|
||||||
|
2. **Run** ボタンで手動実行
|
||||||
|
3. **Runs** ページで実行ログを確認
|
||||||
|
4. 成功すれば、Giteaリポジトリに自動コミットが作成されているはず
|
||||||
|
|
||||||
|
### 4-6. スケジュール実行の確認
|
||||||
|
|
||||||
|
`git_sync.schedule.yaml` により、2分ごとに自動実行されるスケジュールが登録されています。
|
||||||
|
左メニューの **Schedules** から、スケジュールが有効になっていることを確認してください。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## トラブルシューティング
|
## トラブルシューティング
|
||||||
|
|
||||||
### Traefikでアクセスできない
|
### ディスク上のファイルが古い内容に戻る
|
||||||
|
`git_sync` フローが `wmill sync pull`(DB→ディスク)を実行するため、UIで修正した内容がディスクに上書きされます。
|
||||||
|
スクリプトの修正は **Windmill UI上で直接編集** するのが確実です。
|
||||||
|
|
||||||
|
### git push が失敗する
|
||||||
```bash
|
```bash
|
||||||
# Traefikのログ確認
|
# サーバー上でリモートURLにトークンが含まれているか確認
|
||||||
docker logs traefik
|
cd ~/windmill
|
||||||
|
|
||||||
# windmill_serverがtraefik-netに接続されているか
|
|
||||||
docker network inspect traefik-net | grep windmill
|
|
||||||
```
|
|
||||||
|
|
||||||
### データベース接続エラー
|
|
||||||
```bash
|
|
||||||
# DBの状態確認
|
|
||||||
docker-compose ps db
|
|
||||||
docker-compose logs db
|
|
||||||
|
|
||||||
# 再起動
|
|
||||||
docker-compose restart db
|
|
||||||
```
|
|
||||||
|
|
||||||
### Gitプッシュが失敗する
|
|
||||||
```bash
|
|
||||||
# 認証情報の確認
|
|
||||||
cd /home/windmill/windmill
|
|
||||||
git remote -v
|
git remote -v
|
||||||
git config --list | grep credential
|
# https://<user>:<token>@gitea.keinafarm.net/... の形式であること
|
||||||
|
|
||||||
# 手動でテスト
|
|
||||||
git push origin main
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## 重要なコマンド
|
### 開発環境(ローカル)での起動
|
||||||
|
ローカルで起動する場合は `docker-compose-dev.yml` を使用します:
|
||||||
```bash
|
```bash
|
||||||
# 再起動
|
docker-compose -f docker-compose-dev.yml up -d
|
||||||
docker-compose restart
|
|
||||||
|
|
||||||
# ログ確認
|
|
||||||
docker-compose logs -f
|
|
||||||
|
|
||||||
# 停止
|
|
||||||
docker-compose down
|
|
||||||
|
|
||||||
# 完全削除(データも削除)
|
|
||||||
docker-compose down -v
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## セキュリティチェック
|
### ログ確認
|
||||||
|
```bash
|
||||||
- [ ] `.env` のパスワードを変更
|
docker-compose logs -f
|
||||||
- [ ] Windmill管理者に強力なパスワード設定
|
```
|
||||||
- [ ] Git認証をSSH鍵またはcredential helperで設定
|
|
||||||
- [ ] Traefik Basic認証の追加検討(必要に応じて)
|
|
||||||
|
|||||||
152
docker-compose-dev.yml
Normal file
152
docker-compose-dev.yml
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
version: "3.9"
|
||||||
|
|
||||||
|
x-logging: &default-logging
|
||||||
|
driver: "json-file"
|
||||||
|
options:
|
||||||
|
max-size: "${LOG_MAX_SIZE:-20m}"
|
||||||
|
max-file: "${LOG_MAX_FILE:-10}"
|
||||||
|
compress: "true"
|
||||||
|
|
||||||
|
networks:
|
||||||
|
windmill-internal:
|
||||||
|
driver: bridge
|
||||||
|
|
||||||
|
services:
|
||||||
|
db:
|
||||||
|
deploy:
|
||||||
|
replicas: 1
|
||||||
|
image: postgres:16
|
||||||
|
shm_size: 1g
|
||||||
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
- db_data:/var/lib/postgresql/data
|
||||||
|
expose:
|
||||||
|
- 5432
|
||||||
|
environment:
|
||||||
|
POSTGRES_PASSWORD: ${DATABASE_PASSWORD}
|
||||||
|
POSTGRES_DB: windmill
|
||||||
|
healthcheck:
|
||||||
|
test: [ "CMD-SHELL", "pg_isready -U postgres" ]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 5
|
||||||
|
logging: *default-logging
|
||||||
|
networks:
|
||||||
|
- windmill-internal
|
||||||
|
|
||||||
|
windmill_server:
|
||||||
|
image: ${WM_IMAGE}
|
||||||
|
container_name: windmill_server
|
||||||
|
pull_policy: if_not_present
|
||||||
|
deploy:
|
||||||
|
replicas: 1
|
||||||
|
restart: unless-stopped
|
||||||
|
expose:
|
||||||
|
- 8000
|
||||||
|
environment:
|
||||||
|
- DATABASE_URL=${DATABASE_URL}
|
||||||
|
- MODE=server
|
||||||
|
- BASE_URL=http://localhost
|
||||||
|
depends_on:
|
||||||
|
db:
|
||||||
|
condition: service_healthy
|
||||||
|
volumes:
|
||||||
|
- worker_logs:/tmp/windmill/logs
|
||||||
|
networks:
|
||||||
|
- windmill-internal
|
||||||
|
logging: *default-logging
|
||||||
|
|
||||||
|
windmill_worker:
|
||||||
|
image: ${WM_IMAGE}
|
||||||
|
pull_policy: if_not_present
|
||||||
|
deploy:
|
||||||
|
replicas: 1
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
cpus: "1"
|
||||||
|
memory: 2048M
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
- DATABASE_URL=${DATABASE_URL}
|
||||||
|
- MODE=worker
|
||||||
|
- WORKER_GROUP=default
|
||||||
|
depends_on:
|
||||||
|
db:
|
||||||
|
condition: service_healthy
|
||||||
|
volumes:
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
|
- worker_dependency_cache:/tmp/windmill/cache
|
||||||
|
- worker_logs:/tmp/windmill/logs
|
||||||
|
networks:
|
||||||
|
- windmill-internal
|
||||||
|
logging: *default-logging
|
||||||
|
|
||||||
|
windmill_worker_native:
|
||||||
|
image: ${WM_IMAGE}
|
||||||
|
pull_policy: if_not_present
|
||||||
|
deploy:
|
||||||
|
replicas: 1
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
cpus: "1"
|
||||||
|
memory: 2048M
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
- DATABASE_URL=${DATABASE_URL}
|
||||||
|
- MODE=worker
|
||||||
|
- WORKER_GROUP=native
|
||||||
|
- NUM_WORKERS=8
|
||||||
|
- SLEEP_QUEUE=200
|
||||||
|
depends_on:
|
||||||
|
db:
|
||||||
|
condition: service_healthy
|
||||||
|
volumes:
|
||||||
|
- worker_logs:/tmp/windmill/logs
|
||||||
|
networks:
|
||||||
|
- windmill-internal
|
||||||
|
logging: *default-logging
|
||||||
|
|
||||||
|
windmill_extra:
|
||||||
|
image: ghcr.io/windmill-labs/windmill-extra:${WM_VERSION}
|
||||||
|
pull_policy: if_not_present
|
||||||
|
restart: unless-stopped
|
||||||
|
expose:
|
||||||
|
- 3001
|
||||||
|
- 3003
|
||||||
|
environment:
|
||||||
|
- ENABLE_LSP=true
|
||||||
|
- ENABLE_MULTIPLAYER=false
|
||||||
|
- ENABLE_DEBUGGER=true
|
||||||
|
- DEBUGGER_PORT=3003
|
||||||
|
- ENABLE_NSJAIL=false
|
||||||
|
- REQUIRE_SIGNED_DEBUG_REQUESTS=false
|
||||||
|
- WINDMILL_BASE_URL=http://windmill_server:8000
|
||||||
|
volumes:
|
||||||
|
- lsp_cache:/pyls/.cache
|
||||||
|
networks:
|
||||||
|
- windmill-internal
|
||||||
|
logging: *default-logging
|
||||||
|
|
||||||
|
caddy:
|
||||||
|
image: caddy:2.9-alpine
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "80:80"
|
||||||
|
volumes:
|
||||||
|
- ./Caddyfile.local:/etc/caddy/Caddyfile
|
||||||
|
- caddy_data:/data
|
||||||
|
- caddy_config:/config
|
||||||
|
networks:
|
||||||
|
- windmill-internal
|
||||||
|
logging: *default-logging
|
||||||
|
depends_on:
|
||||||
|
- windmill_server
|
||||||
|
- windmill_extra
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
db_data: null
|
||||||
|
worker_dependency_cache: null
|
||||||
|
worker_logs: null
|
||||||
|
lsp_cache: null
|
||||||
|
caddy_data: null
|
||||||
|
caddy_config: null
|
||||||
@@ -1,5 +1,3 @@
|
|||||||
version: "3.9"
|
|
||||||
|
|
||||||
x-logging: &default-logging
|
x-logging: &default-logging
|
||||||
driver: "json-file"
|
driver: "json-file"
|
||||||
options:
|
options:
|
||||||
@@ -9,9 +7,9 @@ x-logging: &default-logging
|
|||||||
|
|
||||||
networks:
|
networks:
|
||||||
traefik-net:
|
traefik-net:
|
||||||
external: true # Traefik管理下のネットワーク
|
external: true # サーバー上の既存Traefikネットワーク
|
||||||
windmill-internal:
|
windmill-internal:
|
||||||
driver: bridge # Windmill内部通信用
|
driver: bridge
|
||||||
|
|
||||||
services:
|
services:
|
||||||
db:
|
db:
|
||||||
@@ -25,7 +23,7 @@ services:
|
|||||||
expose:
|
expose:
|
||||||
- 5432
|
- 5432
|
||||||
environment:
|
environment:
|
||||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
POSTGRES_PASSWORD: ${DATABASE_PASSWORD}
|
||||||
POSTGRES_DB: windmill
|
POSTGRES_DB: windmill
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: [ "CMD-SHELL", "pg_isready -U postgres" ]
|
test: [ "CMD-SHELL", "pg_isready -U postgres" ]
|
||||||
@@ -39,24 +37,29 @@ services:
|
|||||||
windmill_server:
|
windmill_server:
|
||||||
image: ${WM_IMAGE}
|
image: ${WM_IMAGE}
|
||||||
container_name: windmill_server
|
container_name: windmill_server
|
||||||
pull_policy: always
|
pull_policy: if_not_present
|
||||||
deploy:
|
deploy:
|
||||||
replicas: 1
|
replicas: 1
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
expose:
|
expose:
|
||||||
- 8000
|
- 8000
|
||||||
- 2525
|
|
||||||
environment:
|
environment:
|
||||||
- DATABASE_URL=${DATABASE_URL}
|
- DATABASE_URL=${DATABASE_URL}
|
||||||
- MODE=server
|
- MODE=server
|
||||||
|
- BASE_URL=https://windmill.keinafarm.net
|
||||||
|
- OAUTH_REDIRECT_BASE_URL=https://windmill.keinafarm.net
|
||||||
|
- GOOGLE_OAUTH_ENABLED=true
|
||||||
|
- GOOGLE_OAUTH_CLIENT_ID=${GOOGLE_OAUTH_CLIENT_ID}
|
||||||
|
- GOOGLE_OAUTH_CLIENT_SECRET=${GOOGLE_OAUTH_CLIENT_SECRET}
|
||||||
depends_on:
|
depends_on:
|
||||||
db:
|
db:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
volumes:
|
volumes:
|
||||||
- worker_logs:/tmp/windmill/logs
|
- worker_logs:/tmp/windmill/logs
|
||||||
- /home/windmill/windmill:/workspace
|
# Git同期のために、カレントディレクトリ(リポジトリルート)を/workspaceにマウント
|
||||||
|
# これにより、コンテナ内から .git ディレクトリにアクセス可能となり、git pushが可能になる
|
||||||
|
- .:/workspace
|
||||||
labels:
|
labels:
|
||||||
# Traefik設定
|
|
||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
# HTTPSルーター
|
# HTTPSルーター
|
||||||
- "traefik.http.routers.windmill.rule=Host(`windmill.keinafarm.net`)"
|
- "traefik.http.routers.windmill.rule=Host(`windmill.keinafarm.net`)"
|
||||||
@@ -76,7 +79,7 @@ services:
|
|||||||
|
|
||||||
windmill_worker:
|
windmill_worker:
|
||||||
image: ${WM_IMAGE}
|
image: ${WM_IMAGE}
|
||||||
pull_policy: always
|
pull_policy: if_not_present
|
||||||
deploy:
|
deploy:
|
||||||
replicas: 3
|
replicas: 3
|
||||||
resources:
|
resources:
|
||||||
@@ -95,14 +98,15 @@ services:
|
|||||||
- /var/run/docker.sock:/var/run/docker.sock
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
- worker_dependency_cache:/tmp/windmill/cache
|
- worker_dependency_cache:/tmp/windmill/cache
|
||||||
- worker_logs:/tmp/windmill/logs
|
- worker_logs:/tmp/windmill/logs
|
||||||
- /home/windmill/windmill:/workspace
|
# WorkerからもGit同期が必要な場合に備えてマウント
|
||||||
|
- .:/workspace
|
||||||
networks:
|
networks:
|
||||||
- windmill-internal
|
- windmill-internal
|
||||||
logging: *default-logging
|
logging: *default-logging
|
||||||
|
|
||||||
windmill_worker_native:
|
windmill_worker_native:
|
||||||
image: ${WM_IMAGE}
|
image: ${WM_IMAGE}
|
||||||
pull_policy: always
|
pull_policy: if_not_present
|
||||||
deploy:
|
deploy:
|
||||||
replicas: 1
|
replicas: 1
|
||||||
resources:
|
resources:
|
||||||
@@ -125,31 +129,9 @@ services:
|
|||||||
- windmill-internal
|
- windmill-internal
|
||||||
logging: *default-logging
|
logging: *default-logging
|
||||||
|
|
||||||
windmill_indexer:
|
|
||||||
image: ${WM_IMAGE}
|
|
||||||
pull_policy: always
|
|
||||||
deploy:
|
|
||||||
replicas: 0 # 必要に応じて1に変更
|
|
||||||
restart: unless-stopped
|
|
||||||
expose:
|
|
||||||
- 8002
|
|
||||||
environment:
|
|
||||||
- PORT=8002
|
|
||||||
- DATABASE_URL=${DATABASE_URL}
|
|
||||||
- MODE=indexer
|
|
||||||
depends_on:
|
|
||||||
db:
|
|
||||||
condition: service_healthy
|
|
||||||
volumes:
|
|
||||||
- windmill_index:/tmp/windmill/search
|
|
||||||
- worker_logs:/tmp/windmill/logs
|
|
||||||
networks:
|
|
||||||
- windmill-internal
|
|
||||||
logging: *default-logging
|
|
||||||
|
|
||||||
windmill_extra:
|
windmill_extra:
|
||||||
image: ghcr.io/windmill-labs/windmill-extra:latest
|
image: ghcr.io/windmill-labs/windmill-extra:${WM_VERSION}
|
||||||
pull_policy: always
|
pull_policy: if_not_present
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
expose:
|
expose:
|
||||||
- 3001
|
- 3001
|
||||||
@@ -168,15 +150,53 @@ services:
|
|||||||
networks:
|
networks:
|
||||||
- windmill-internal
|
- windmill-internal
|
||||||
logging: *default-logging
|
logging: *default-logging
|
||||||
# Caddyは使わない(Traefikを使用)
|
labels:
|
||||||
# caddy:
|
# LSPなどのWebSocket用設定(Caddyfileの代替)
|
||||||
# deploy:
|
- "traefik.enable=true"
|
||||||
# replicas: 0
|
# LSPへのルーティング (/ws/* -> 3001)
|
||||||
|
- "traefik.http.routers.windmill-lsp.rule=Host(`windmill.keinafarm.net`) && PathPrefix(`/ws/`)"
|
||||||
|
- "traefik.http.routers.windmill-lsp.entrypoints=websecure"
|
||||||
|
- "traefik.http.routers.windmill-lsp.tls=true"
|
||||||
|
- "traefik.http.services.windmill-lsp.loadbalancer.server.port=3001"
|
||||||
|
# Debuggerへのルーティング (/ws_debug/* -> 3003)
|
||||||
|
- "traefik.http.routers.windmill-debug.rule=Host(`windmill.keinafarm.net`) && PathPrefix(`/ws_debug/`)"
|
||||||
|
- "traefik.http.routers.windmill-debug.entrypoints=websecure"
|
||||||
|
- "traefik.http.routers.windmill-debug.tls=true"
|
||||||
|
- "traefik.http.services.windmill-debug.loadbalancer.server.port=3003"
|
||||||
|
|
||||||
|
windmill_mcp:
|
||||||
|
build:
|
||||||
|
context: ./mcp
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
container_name: windmill_mcp
|
||||||
|
restart: unless-stopped
|
||||||
|
expose:
|
||||||
|
- 8001
|
||||||
|
environment:
|
||||||
|
- WINDMILL_TOKEN=${WINDMILL_TOKEN}
|
||||||
|
- WINDMILL_URL=https://windmill.keinafarm.net
|
||||||
|
- WINDMILL_WORKSPACE=admins
|
||||||
|
- MCP_TRANSPORT=sse
|
||||||
|
- MCP_HOST=0.0.0.0
|
||||||
|
- MCP_PORT=8001
|
||||||
|
labels:
|
||||||
|
- "traefik.enable=true"
|
||||||
|
# HTTPS ルーター
|
||||||
|
- "traefik.http.routers.windmill-mcp.rule=Host(`windmill-mcp.keinafarm.net`)"
|
||||||
|
- "traefik.http.routers.windmill-mcp.entrypoints=websecure"
|
||||||
|
- "traefik.http.routers.windmill-mcp.tls=true"
|
||||||
|
- "traefik.http.routers.windmill-mcp.tls.certresolver=letsencrypt"
|
||||||
|
- "traefik.http.services.windmill-mcp.loadbalancer.server.port=8001"
|
||||||
|
# HTTP → HTTPS リダイレクト
|
||||||
|
- "traefik.http.routers.windmill-mcp-http.rule=Host(`windmill-mcp.keinafarm.net`)"
|
||||||
|
- "traefik.http.routers.windmill-mcp-http.entrypoints=web"
|
||||||
|
- "traefik.http.routers.windmill-mcp-http.middlewares=windmill-https-redirect"
|
||||||
|
networks:
|
||||||
|
- traefik-net
|
||||||
|
logging: *default-logging
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
db_data: null
|
db_data: null
|
||||||
worker_dependency_cache: null
|
worker_dependency_cache: null
|
||||||
worker_logs: null
|
worker_logs: null
|
||||||
worker_memory: null
|
|
||||||
windmill_index: null
|
|
||||||
lsp_cache: null
|
lsp_cache: null
|
||||||
|
|||||||
12
mcp/.env.example
Normal file
12
mcp/.env.example
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
# Windmill MCP Server の設定
|
||||||
|
# このファイルを .env にコピーして値を設定してください
|
||||||
|
|
||||||
|
# Windmill のベース URL(デフォルト: https://windmill.keinafarm.net)
|
||||||
|
WINDMILL_URL=https://windmill.keinafarm.net
|
||||||
|
|
||||||
|
# Windmill API トークン(必須)
|
||||||
|
# Windmill の「設定 > トークン」から作成してください
|
||||||
|
WINDMILL_TOKEN=your_token_here
|
||||||
|
|
||||||
|
# 対象ワークスペース(デフォルト: admins)
|
||||||
|
WINDMILL_WORKSPACE=admins
|
||||||
14
mcp/Dockerfile
Normal file
14
mcp/Dockerfile
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
FROM python:3.12-slim
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY requirements.txt .
|
||||||
|
RUN pip install --no-cache-dir -r requirements.txt
|
||||||
|
|
||||||
|
COPY windmill_mcp.py .
|
||||||
|
|
||||||
|
ENV MCP_TRANSPORT=sse
|
||||||
|
ENV MCP_HOST=0.0.0.0
|
||||||
|
ENV MCP_PORT=8001
|
||||||
|
|
||||||
|
CMD ["python", "windmill_mcp.py"]
|
||||||
14
mcp/claude_mcp_config.json
Normal file
14
mcp/claude_mcp_config.json
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"mcpServers": {
|
||||||
|
"windmill": {
|
||||||
|
"command": "python",
|
||||||
|
"args": ["windmill_mcp.py"],
|
||||||
|
"cwd": "/path/to/mcp",
|
||||||
|
"env": {
|
||||||
|
"WINDMILL_TOKEN": "your_api_token_here",
|
||||||
|
"WINDMILL_URL": "https://windmill.keinafarm.net",
|
||||||
|
"WINDMILL_WORKSPACE": "admins"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
2
mcp/requirements.txt
Normal file
2
mcp/requirements.txt
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
mcp>=1.0.0
|
||||||
|
httpx>=0.27.0
|
||||||
343
mcp/windmill_mcp.py
Normal file
343
mcp/windmill_mcp.py
Normal file
@@ -0,0 +1,343 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Windmill MCP Server - Claude が Windmill を直接操作できるようにする"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import json
|
||||||
|
import sys
|
||||||
|
import httpx
|
||||||
|
from mcp.server.fastmcp import FastMCP
|
||||||
|
|
||||||
|
WINDMILL_URL = os.environ.get("WINDMILL_URL", "https://windmill.keinafarm.net")
|
||||||
|
WINDMILL_TOKEN = os.environ.get("WINDMILL_TOKEN", "")
|
||||||
|
WINDMILL_WORKSPACE = os.environ.get("WINDMILL_WORKSPACE", "admins")
|
||||||
|
MCP_HOST = os.environ.get("MCP_HOST", "127.0.0.1")
|
||||||
|
MCP_PORT = int(os.environ.get("MCP_PORT", "8001"))
|
||||||
|
|
||||||
|
if not WINDMILL_TOKEN:
|
||||||
|
print("Error: WINDMILL_TOKEN 環境変数が設定されていません", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
mcp = FastMCP("windmill", host=MCP_HOST, port=MCP_PORT)
|
||||||
|
|
||||||
|
|
||||||
|
def _headers() -> dict:
|
||||||
|
return {"Authorization": f"Bearer {WINDMILL_TOKEN}"}
|
||||||
|
|
||||||
|
|
||||||
|
def _api(path: str) -> str:
|
||||||
|
return f"{WINDMILL_URL}/api/w/{WINDMILL_WORKSPACE}/{path}"
|
||||||
|
|
||||||
|
|
||||||
|
@mcp.tool()
|
||||||
|
def windmill_list_flows(per_page: int = 20) -> str:
|
||||||
|
"""Windmill のフロー一覧を取得する
|
||||||
|
|
||||||
|
Args:
|
||||||
|
per_page: 取得件数(最大100)
|
||||||
|
"""
|
||||||
|
resp = httpx.get(
|
||||||
|
_api("flows/list"),
|
||||||
|
headers=_headers(),
|
||||||
|
params={"per_page": min(per_page, 100)},
|
||||||
|
timeout=30,
|
||||||
|
)
|
||||||
|
resp.raise_for_status()
|
||||||
|
flows = resp.json()
|
||||||
|
if not flows:
|
||||||
|
return "フローが見つかりませんでした"
|
||||||
|
lines = [
|
||||||
|
f"- {f['path']}: {f.get('summary', '(概要なし)')}" for f in flows
|
||||||
|
]
|
||||||
|
return "\n".join(lines)
|
||||||
|
|
||||||
|
|
||||||
|
@mcp.tool()
|
||||||
|
def windmill_get_flow(path: str) -> str:
|
||||||
|
"""指定したパスのフロー定義(スクリプト含む)を取得する
|
||||||
|
|
||||||
|
Args:
|
||||||
|
path: フローのパス (例: u/antigravity/git_sync)
|
||||||
|
"""
|
||||||
|
resp = httpx.get(_api(f"flows/get/{path}"), headers=_headers(), timeout=30)
|
||||||
|
resp.raise_for_status()
|
||||||
|
return json.dumps(resp.json(), indent=2, ensure_ascii=False)
|
||||||
|
|
||||||
|
|
||||||
|
@mcp.tool()
|
||||||
|
def windmill_run_flow(path: str, args: str = "{}") -> str:
|
||||||
|
"""フローをトリガーして実行する
|
||||||
|
|
||||||
|
Args:
|
||||||
|
path: フローのパス (例: u/antigravity/git_sync)
|
||||||
|
args: JSON形式の入力引数 (例: {"key": "value"})
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
args_dict = json.loads(args)
|
||||||
|
except json.JSONDecodeError as e:
|
||||||
|
return f"Error: argsのJSON形式が不正です: {e}"
|
||||||
|
|
||||||
|
resp = httpx.post(
|
||||||
|
_api(f"jobs/run/f/{path}"),
|
||||||
|
headers=_headers(),
|
||||||
|
json=args_dict,
|
||||||
|
timeout=30,
|
||||||
|
)
|
||||||
|
resp.raise_for_status()
|
||||||
|
job_id = resp.text.strip().strip('"')
|
||||||
|
return (
|
||||||
|
f"フローを開始しました。\n"
|
||||||
|
f"ジョブID: {job_id}\n"
|
||||||
|
f"詳細URL: {WINDMILL_URL}/run/{job_id}?workspace={WINDMILL_WORKSPACE}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@mcp.tool()
|
||||||
|
def windmill_list_recent_jobs(
|
||||||
|
limit: int = 20,
|
||||||
|
success_only: bool = False,
|
||||||
|
failure_only: bool = False,
|
||||||
|
script_path_filter: str = "",
|
||||||
|
) -> str:
|
||||||
|
"""最近のジョブ一覧を取得する
|
||||||
|
|
||||||
|
Args:
|
||||||
|
limit: 取得件数(最大100)
|
||||||
|
success_only: Trueにすると成功ジョブのみ表示
|
||||||
|
failure_only: Trueにすると失敗ジョブのみ表示
|
||||||
|
script_path_filter: パスで絞り込む (例: u/antigravity/git_sync)
|
||||||
|
"""
|
||||||
|
params: dict = {"per_page": min(limit, 100)}
|
||||||
|
if success_only:
|
||||||
|
params["success"] = "true"
|
||||||
|
if failure_only:
|
||||||
|
params["success"] = "false"
|
||||||
|
if script_path_filter:
|
||||||
|
params["script_path_filter"] = script_path_filter
|
||||||
|
|
||||||
|
resp = httpx.get(_api("jobs/list"), headers=_headers(), params=params, timeout=30)
|
||||||
|
resp.raise_for_status()
|
||||||
|
jobs = resp.json()
|
||||||
|
if not jobs:
|
||||||
|
return "ジョブが見つかりませんでした"
|
||||||
|
|
||||||
|
lines = []
|
||||||
|
for j in jobs:
|
||||||
|
success = j.get("success")
|
||||||
|
if success is True:
|
||||||
|
status = "[OK]"
|
||||||
|
elif success is False:
|
||||||
|
status = "[FAIL]"
|
||||||
|
else:
|
||||||
|
status = "[RUNNING]"
|
||||||
|
path = j.get("script_path", "unknown")
|
||||||
|
started = (j.get("started_at") or "")[:19] or "pending"
|
||||||
|
job_id = j.get("id", "")
|
||||||
|
lines.append(f"{status} [{started}] {path} (ID: {job_id})")
|
||||||
|
|
||||||
|
return "\n".join(lines)
|
||||||
|
|
||||||
|
|
||||||
|
@mcp.tool()
|
||||||
|
def windmill_get_job_logs(job_id: str) -> str:
|
||||||
|
"""ジョブの詳細情報とログを取得する
|
||||||
|
|
||||||
|
Args:
|
||||||
|
job_id: ジョブのID(windmill_list_recent_jobs で確認できる)
|
||||||
|
"""
|
||||||
|
resp = httpx.get(_api(f"jobs_u/get/{job_id}"), headers=_headers(), timeout=30)
|
||||||
|
resp.raise_for_status()
|
||||||
|
job = resp.json()
|
||||||
|
|
||||||
|
success = job.get("success")
|
||||||
|
if success is True:
|
||||||
|
state = "成功 [OK]"
|
||||||
|
elif success is False:
|
||||||
|
state = "失敗 [FAIL]"
|
||||||
|
else:
|
||||||
|
state = "実行中 [RUNNING]"
|
||||||
|
|
||||||
|
result_parts = [
|
||||||
|
f"ジョブID: {job_id}",
|
||||||
|
f"パス: {job.get('script_path', 'N/A')}",
|
||||||
|
f"状態: {state}",
|
||||||
|
f"開始: {job.get('started_at', 'N/A')}",
|
||||||
|
f"終了: {job.get('created_at', 'N/A')}",
|
||||||
|
]
|
||||||
|
|
||||||
|
log_resp = httpx.get(
|
||||||
|
_api(f"jobs_u/getlogs/{job_id}"), headers=_headers(), timeout=30
|
||||||
|
)
|
||||||
|
if log_resp.status_code == 200:
|
||||||
|
result_parts.append("\n--- ログ ---")
|
||||||
|
result_parts.append(log_resp.text)
|
||||||
|
|
||||||
|
result_val = job.get("result")
|
||||||
|
if result_val is not None:
|
||||||
|
result_parts.append("\n--- 実行結果 ---")
|
||||||
|
result_parts.append(
|
||||||
|
json.dumps(result_val, indent=2, ensure_ascii=False)
|
||||||
|
if isinstance(result_val, (dict, list))
|
||||||
|
else str(result_val)
|
||||||
|
)
|
||||||
|
|
||||||
|
return "\n".join(result_parts)
|
||||||
|
|
||||||
|
|
||||||
|
@mcp.tool()
|
||||||
|
def windmill_create_flow(path: str, summary: str, flow_definition: str, description: str = "") -> str:
|
||||||
|
"""新しいフローを作成する
|
||||||
|
|
||||||
|
Args:
|
||||||
|
path: フローのパス (例: u/admin/my_flow)
|
||||||
|
summary: フローの概要
|
||||||
|
flow_definition: フローの定義 (JSON形式の文字列)
|
||||||
|
description: フローの詳細説明 (省略可)
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
flow_value = json.loads(flow_definition)
|
||||||
|
except json.JSONDecodeError as e:
|
||||||
|
return f"Error: flow_definitionのJSON形式が不正です: {e}"
|
||||||
|
|
||||||
|
payload = {
|
||||||
|
"path": path,
|
||||||
|
"summary": summary,
|
||||||
|
"description": description,
|
||||||
|
"value": flow_value,
|
||||||
|
}
|
||||||
|
|
||||||
|
resp = httpx.post(
|
||||||
|
_api("flows/create"),
|
||||||
|
headers=_headers(),
|
||||||
|
json=payload,
|
||||||
|
timeout=30,
|
||||||
|
)
|
||||||
|
resp.raise_for_status()
|
||||||
|
return (
|
||||||
|
f"フローを作成しました。\n"
|
||||||
|
f"パス: {path}\n"
|
||||||
|
f"URL: {WINDMILL_URL}/flows/edit/{path}?workspace={WINDMILL_WORKSPACE}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@mcp.tool()
|
||||||
|
def windmill_update_flow(path: str, summary: str, flow_definition: str, description: str = "") -> str:
|
||||||
|
"""既存のフローを更新する
|
||||||
|
|
||||||
|
Args:
|
||||||
|
path: フローのパス (例: u/admin/my_flow)
|
||||||
|
summary: フローの概要
|
||||||
|
flow_definition: フローの定義 (JSON形式の文字列)
|
||||||
|
description: フローの詳細説明 (省略可)
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
flow_value = json.loads(flow_definition)
|
||||||
|
except json.JSONDecodeError as e:
|
||||||
|
return f"Error: flow_definitionのJSON形式が不正です: {e}"
|
||||||
|
|
||||||
|
payload = {
|
||||||
|
"path": path,
|
||||||
|
"summary": summary,
|
||||||
|
"description": description,
|
||||||
|
"value": flow_value,
|
||||||
|
}
|
||||||
|
|
||||||
|
resp = httpx.post(
|
||||||
|
_api(f"flows/update/{path}"),
|
||||||
|
headers=_headers(),
|
||||||
|
json=payload,
|
||||||
|
timeout=30,
|
||||||
|
)
|
||||||
|
resp.raise_for_status()
|
||||||
|
return (
|
||||||
|
f"フローを更新しました。\n"
|
||||||
|
f"パス: {path}\n"
|
||||||
|
f"URL: {WINDMILL_URL}/flows/edit/{path}?workspace={WINDMILL_WORKSPACE}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@mcp.tool()
|
||||||
|
def windmill_create_script(
|
||||||
|
path: str, language: str, content: str, summary: str = "", description: str = ""
|
||||||
|
) -> str:
|
||||||
|
"""新しいスクリプトを作成する(既存パスの場合は新バージョンを登録する)
|
||||||
|
|
||||||
|
Args:
|
||||||
|
path: スクリプトのパス (例: u/admin/my_script)
|
||||||
|
language: 言語 (python3, deno, bun, bash など)
|
||||||
|
content: スクリプトのソースコード
|
||||||
|
summary: スクリプトの概要 (省略可)
|
||||||
|
description: スクリプトの詳細説明 (省略可)
|
||||||
|
"""
|
||||||
|
payload = {
|
||||||
|
"path": path,
|
||||||
|
"language": language,
|
||||||
|
"content": content,
|
||||||
|
"summary": summary,
|
||||||
|
"description": description,
|
||||||
|
}
|
||||||
|
|
||||||
|
resp = httpx.post(
|
||||||
|
_api("scripts/create"),
|
||||||
|
headers=_headers(),
|
||||||
|
json=payload,
|
||||||
|
timeout=30,
|
||||||
|
)
|
||||||
|
resp.raise_for_status()
|
||||||
|
hash_val = resp.text.strip().strip('"')
|
||||||
|
return (
|
||||||
|
f"スクリプトを作成しました。\n"
|
||||||
|
f"パス: {path}\n"
|
||||||
|
f"ハッシュ: {hash_val}\n"
|
||||||
|
f"URL: {WINDMILL_URL}/scripts/edit/{path}?workspace={WINDMILL_WORKSPACE}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@mcp.tool()
|
||||||
|
def windmill_list_scripts(per_page: int = 20) -> str:
|
||||||
|
"""Windmill のスクリプト一覧を取得する
|
||||||
|
|
||||||
|
Args:
|
||||||
|
per_page: 取得件数(最大100)
|
||||||
|
"""
|
||||||
|
resp = httpx.get(
|
||||||
|
_api("scripts/list"),
|
||||||
|
headers=_headers(),
|
||||||
|
params={"per_page": min(per_page, 100)},
|
||||||
|
timeout=30,
|
||||||
|
)
|
||||||
|
resp.raise_for_status()
|
||||||
|
scripts = resp.json()
|
||||||
|
if not scripts:
|
||||||
|
return "スクリプトが見つかりませんでした"
|
||||||
|
lines = [
|
||||||
|
f"- {s['path']} [{s.get('language', '?')}]: {s.get('summary', '(概要なし)')}"
|
||||||
|
for s in scripts
|
||||||
|
]
|
||||||
|
return "\n".join(lines)
|
||||||
|
|
||||||
|
|
||||||
|
@mcp.tool()
|
||||||
|
def windmill_get_script(path: str) -> str:
|
||||||
|
"""指定したパスのスクリプトのソースコードを取得する
|
||||||
|
|
||||||
|
Args:
|
||||||
|
path: スクリプトのパス (例: u/antigravity/test_git_sync)
|
||||||
|
"""
|
||||||
|
resp = httpx.get(_api(f"scripts/get/{path}"), headers=_headers(), timeout=30)
|
||||||
|
resp.raise_for_status()
|
||||||
|
script = resp.json()
|
||||||
|
|
||||||
|
result_parts = [
|
||||||
|
f"パス: {script.get('path', 'N/A')}",
|
||||||
|
f"言語: {script.get('language', 'N/A')}",
|
||||||
|
f"概要: {script.get('summary', 'N/A')}",
|
||||||
|
"",
|
||||||
|
"--- コード ---",
|
||||||
|
script.get("content", "(コードなし)"),
|
||||||
|
]
|
||||||
|
return "\n".join(result_parts)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
transport = os.environ.get("MCP_TRANSPORT", "stdio")
|
||||||
|
mcp.run(transport=transport)
|
||||||
0
state/tasks/.gitkeep
Normal file
0
state/tasks/.gitkeep
Normal file
25
state/tasks/TEMPLATE.md
Normal file
25
state/tasks/TEMPLATE.md
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
# Task Record Template
|
||||||
|
|
||||||
|
- task_id: TASK-YYYYMMDD-001
|
||||||
|
- date: YYYY-MM-DD
|
||||||
|
|
||||||
|
## Request (3 lines)
|
||||||
|
- Purpose:
|
||||||
|
- Target:
|
||||||
|
- Done:
|
||||||
|
|
||||||
|
## Changed Files
|
||||||
|
- path/to/file
|
||||||
|
|
||||||
|
## Commands
|
||||||
|
- cmd:
|
||||||
|
- result: success/fail
|
||||||
|
- notes:
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
- L1:
|
||||||
|
- L2:
|
||||||
|
- L3:
|
||||||
|
|
||||||
|
## Risks / Next Actions
|
||||||
|
-
|
||||||
@@ -50,10 +50,13 @@ if [[ -n $(git status --porcelain) ]]; then
|
|||||||
|
|
||||||
Synced workflows from Windmill workspace"
|
Synced workflows from Windmill workspace"
|
||||||
|
|
||||||
|
|
||||||
# Giteaにプッシュ
|
# Giteaにプッシュ
|
||||||
echo -e "${YELLOW}Pushing to Gitea...${NC}"
|
echo -e "${YELLOW}Pushing to Gitea...${NC}"
|
||||||
git push origin main || {
|
git push origin main || {
|
||||||
echo -e "${RED}Failed to push to Gitea. Check credentials.${NC}"
|
echo -e "${RED}Failed to push to Gitea. Check credentials.${NC}"
|
||||||
|
# トークンや認証情報が設定されていない場合のヒント
|
||||||
|
echo -e "${YELLOW}Hint: Ensure you have set up git credentials or use a token in the remote URL.${NC}"
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,36 +66,3 @@ else
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
echo -e "${GREEN}=== Sync Complete ===${NC}"
|
echo -e "${GREEN}=== Sync Complete ===${NC}"
|
||||||
NC='\033[0m' # No Color
|
|
||||||
|
|
||||||
echo -e "${GREEN}=== Windmill Workflow Git Sync ===${NC}"
|
|
||||||
|
|
||||||
# ディレクトリに移動
|
|
||||||
cd /home/akira/develop/windmill/workflows
|
|
||||||
|
|
||||||
# PATHを設定
|
|
||||||
export PATH=~/.npm-global/bin:$PATH
|
|
||||||
|
|
||||||
# Windmillから最新を取得
|
|
||||||
echo -e "${YELLOW}Pulling from Windmill...${NC}"
|
|
||||||
wmill sync pull --skip-variables --skip-secrets --skip-resources --yes
|
|
||||||
|
|
||||||
# 変更があるか確認
|
|
||||||
if [[ -n $(git status --porcelain) ]]; then
|
|
||||||
echo -e "${YELLOW}Changes detected, committing to Git...${NC}"
|
|
||||||
|
|
||||||
# 変更をステージング
|
|
||||||
git add -A
|
|
||||||
|
|
||||||
# コミット
|
|
||||||
TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')
|
|
||||||
git commit -m "Auto-sync: ${TIMESTAMP}
|
|
||||||
|
|
||||||
Synced workflows from Windmill workspace"
|
|
||||||
|
|
||||||
echo -e "${GREEN}✓ Changes committed to Git${NC}"
|
|
||||||
else
|
|
||||||
echo -e "${GREEN}✓ No changes detected${NC}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo -e "${GREEN}=== Sync Complete ===${NC}"
|
|
||||||
|
|||||||
0
tests/golden/.gitkeep
Normal file
0
tests/golden/.gitkeep
Normal file
@@ -1,22 +1,68 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
set -x
|
set -e
|
||||||
export WM_BASE_URL="http://windmill_server:8000"
|
|
||||||
export WM_WORKSPACE="admins"
|
|
||||||
export PATH=$HOME/.npm-global/bin:$PATH
|
|
||||||
|
|
||||||
echo "=== START SYNC ==="
|
# 色付き出力
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
RED='\033[0;31m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
echo -e "${GREEN}=== Windmill Workflow Git Sync ===${NC}"
|
||||||
|
|
||||||
|
# リポジトリルート(コンテナ内: docker-compose.ymlの .:/workspace マウント)
|
||||||
|
REPO_ROOT="/workspace"
|
||||||
|
# wmill.yamlがあるディレクトリ(Windmill CLIはここで実行する)
|
||||||
|
WMILL_DIR="${REPO_ROOT}/workflows"
|
||||||
|
|
||||||
|
# Windmill CLIのセットアップ
|
||||||
if ! command -v wmill &> /dev/null; then
|
if ! command -v wmill &> /dev/null; then
|
||||||
|
echo -e "${YELLOW}Installing windmill-cli...${NC}"
|
||||||
npm install -g windmill-cli
|
npm install -g windmill-cli
|
||||||
fi
|
fi
|
||||||
|
|
||||||
wmill sync pull --token "$WM_TOKEN" --base-url "$WM_BASE_URL" --workspace "$WM_WORKSPACE" --skip-variables --skip-secrets --skip-resources --yes --verbose || exit 1
|
# 環境変数チェック
|
||||||
|
if [ -z "$WM_TOKEN" ]; then
|
||||||
|
echo -e "${RED}Error: WM_TOKEN is not set.${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
# WM_BASE_URLはWindmill内で自動設定される場合があるが、念のため
|
||||||
|
: "${WM_BASE_URL:=http://windmill_server:8000}"
|
||||||
|
# Workspaceは環境変数または引数で
|
||||||
|
: "${WM_WORKSPACE:=admins}"
|
||||||
|
|
||||||
git config --global --add safe.directory /workspace
|
# Git設定(コンテナ内での一時設定)
|
||||||
git config --global user.email "bot@example.com"
|
git config --global --add safe.directory "$REPO_ROOT"
|
||||||
git config --global user.name "Bot"
|
git config --global user.email "bot@keinafarm.net"
|
||||||
|
git config --global user.name "Windmill Bot"
|
||||||
|
|
||||||
git add .
|
# 1. Windmill(DB) -> Local Disk(wmill.yamlがあるディレクトリで実行)
|
||||||
git commit -m "Auto-sync $(date)" || echo "No changes"
|
echo -e "${YELLOW}Pulling from Windmill...${NC}"
|
||||||
|
cd "$WMILL_DIR"
|
||||||
|
wmill sync pull --token "$WM_TOKEN" --base-url "$WM_BASE_URL" --workspace "$WM_WORKSPACE" --skip-variables --skip-secrets --skip-resources --yes || exit 1
|
||||||
|
|
||||||
echo "=== END SYNC ==="
|
# 2. Local Disk -> Git Remote(Gitリポジトリルートに戻ってgit操作)
|
||||||
|
cd "$REPO_ROOT"
|
||||||
|
if [[ -n $(git status --porcelain) ]]; then
|
||||||
|
echo -e "${YELLOW}Changes detected, committing to Git...${NC}"
|
||||||
|
|
||||||
|
git add -A
|
||||||
|
TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')
|
||||||
|
git commit -m "Auto-sync: ${TIMESTAMP}"
|
||||||
|
|
||||||
|
echo -e "${YELLOW}Pushing to Gitea...${NC}"
|
||||||
|
# リモートの変更を先に取り込む(ローカルPCからのpushがある場合に備えて)
|
||||||
|
git pull --rebase origin main || {
|
||||||
|
echo -e "${RED}Failed to pull from remote. Trying push anyway...${NC}"
|
||||||
|
}
|
||||||
|
git push origin main || {
|
||||||
|
echo -e "${RED}Failed to push. Need credentials in git remote url or credential helper.${NC}"
|
||||||
|
echo -e "${YELLOW}Hint: git remote set-url origin https://<token>@gitea.keinafarm.net/...${NC}"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
echo -e "${GREEN}✓ Changes pushed to Gitea${NC}"
|
||||||
|
else
|
||||||
|
echo -e "${GREEN}✓ No changes detected${NC}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -e "${GREEN}=== Sync Complete ===${NC}"
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
def main():
|
def main():
|
||||||
print("Hello from Git Sync Test")
|
print("Hello from Git Sync Test")
|
||||||
return {"status": "success"}
|
return {"status": "success"}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user