From dcca6ee0564239a9490ac307aa8c6b39fec28bf8 Mon Sep 17 00:00:00 2001 From: Windmill Bot Date: Mon, 2 Mar 2026 02:24:04 +0900 Subject: [PATCH] =?UTF-8?q?sync=20branch:=20=E3=82=A4=E3=83=B3=E3=83=95?= =?UTF-8?q?=E3=83=A9=E3=83=95=E3=82=A1=E3=82=A4=E3=83=AB=E3=82=92=E9=99=A4?= =?UTF-8?q?=E5=A4=96=E3=80=81=E3=83=AF=E3=83=BC=E3=82=AF=E3=83=95=E3=83=AD?= =?UTF-8?q?=E3=83=BC=E5=AE=9A=E7=BE=A9=E3=81=AE=E3=81=BF=E8=BF=BD=E8=B7=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 9 ++ Caddyfile | 35 ----- SERVER_SETUP.md | 155 ------------------ docker-compose-dev.yml | 182 ---------------------- docker-compose.yml | 202 ------------------------ env.host | 5 - mcp/Dockerfile | 14 -- mcp/requirements.txt | 2 - mcp/windmill_mcp.py | 346 ----------------------------------------- sync_to_git.sh | 68 -------- 10 files changed, 9 insertions(+), 1009 deletions(-) delete mode 100644 Caddyfile delete mode 100644 SERVER_SETUP.md delete mode 100644 docker-compose-dev.yml delete mode 100644 docker-compose.yml delete mode 100644 env.host delete mode 100644 mcp/Dockerfile delete mode 100644 mcp/requirements.txt delete mode 100644 mcp/windmill_mcp.py delete mode 100755 sync_to_git.sh diff --git a/.gitignore b/.gitignore index 3443c3d..796e4af 100644 --- a/.gitignore +++ b/.gitignore @@ -46,3 +46,12 @@ workflows/.wmill/tmp/ !workflows/g/ !workflows/wmill.yaml !workflows/wmill-lock.yaml + +# sync ブランチではインフラファイルを追跡しない +docker-compose.yml +docker-compose-dev.yml +Caddyfile +SERVER_SETUP.md +env.host +sync_to_git.sh +mcp/ diff --git a/Caddyfile b/Caddyfile deleted file mode 100644 index 4b330de..0000000 --- a/Caddyfile +++ /dev/null @@ -1,35 +0,0 @@ -{ - layer4 { - :25 { - proxy { - to windmill_server:2525 - } - } - } -} - -{$BASE_URL} { - bind {$ADDRESS} - - # LSP - Language Server Protocol for code intelligence (windmill_extra:3001) - reverse_proxy /ws/* http://windmill_extra:3001 - - # Multiplayer - Real-time collaboration, Enterprise Edition (windmill_extra:3002) - # Uncomment and set ENABLE_MULTIPLAYER=true in docker-compose.yml - # reverse_proxy /ws_mp/* http://windmill_extra:3002 - - # Debugger - Interactive debugging via DAP WebSocket (windmill_extra:3003) - # Set ENABLE_DEBUGGER=true in docker-compose.yml to enable - handle_path /ws_debug/* { - reverse_proxy http://windmill_extra:3003 - } - - # Search indexer, Enterprise Edition (windmill_indexer:8002) - # reverse_proxy /api/srch/* http://windmill_indexer:8002 - - # Default: Windmill server - reverse_proxy /* http://windmill_server:8000 - - # TLS with custom certificates - # tls /certs/cert.pem /certs/key.pem -} diff --git a/SERVER_SETUP.md b/SERVER_SETUP.md deleted file mode 100644 index c9ea688..0000000 --- a/SERVER_SETUP.md +++ /dev/null @@ -1,155 +0,0 @@ -# Windmill サーバー設定手順 (VPS移行版) - -本番環境(VPS)へのデプロイ手順です。 -既にTraefikが稼働している環境(`traefik-net` ネットワークが存在する環境)を前提としています。 - -## 前提条件 - -- サーバー上でTraefikが稼働しており、`traefik-net` ネットワークが存在すること。 -- ドメイン `windmill.keinafarm.net` がサーバーのIPに向けられていること。 - -## ステップ1: リポジトリの準備 - -サーバー上の任意の場所(例: `/home/windmill/windmill`)にリポジトリをクローンします。 -**重要**: WindmillのGit同期機能を使用するため、このディレクトリパスは重要です。 - -```bash -mkdir -p /home/windmill -cd /home/windmill -git clone https://gitea.keinafarm.net/akira/windmill.git windmill -cd windmill -``` - -## ステップ2: 環境変数の設定 - -`.env` ファイルを作成し、本番用の設定を行います。 - -```bash -cp .env .env.production -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 -docker-compose up -d -``` - -## ステップ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 -# Windmillサーバーコンテナに入る -docker exec -it windmill_server /bin/bash - -# コンテナ内で実行:windmill-cli をインストール -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 -``` - -> **注意**: `wmill sync push` はディスク→DBへの反映です。 -> 逆に `wmill sync pull` はDB→ディスクへの反映です。 -> スケジュールされた `git_sync` フローが `sync pull` を実行するため、 -> **UIで直接スクリプトを修正した場合、次回の sync pull で正しくディスクにも反映されます。** - -### 4-3. Gitea認証情報の設定(git push用) - -`git_sync` フローが Gitea へ `git push` できるよう、サーバー上のリモートURLにGiteaのアクセストークンを含めます。 - -```bash -# サーバーのホスト側で実行 -cd ~/windmill - -# 現在のリモートURLを確認 -git remote -v - -# Giteaのアクセストークンを含んだURLに変更 -git remote set-url origin https://:@gitea.keinafarm.net/akira/windmill.git -``` - -> **Giteaトークンの作成方法**: Gitea(`https://gitea.keinafarm.net`)にログイン → -> 右上アバター → Settings → Applications → Generate New Token - -### 4-4. WM_TOKEN Variable の設定 - -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** から、スケジュールが有効になっていることを確認してください。 - ---- - -## トラブルシューティング - -### ディスク上のファイルが古い内容に戻る -`git_sync` フローが `wmill sync pull`(DB→ディスク)を実行するため、UIで修正した内容がディスクに上書きされます。 -スクリプトの修正は **Windmill UI上で直接編集** するのが確実です。 - -### git push が失敗する -```bash -# サーバー上でリモートURLにトークンが含まれているか確認 -cd ~/windmill -git remote -v -# https://:@gitea.keinafarm.net/... の形式であること -``` - -### 開発環境(ローカル)での起動 -ローカルで起動する場合は `docker-compose-dev.yml` を使用します: -```bash -docker-compose -f docker-compose-dev.yml up -d -``` - -### ログ確認 -```bash -docker-compose logs -f -``` diff --git a/docker-compose-dev.yml b/docker-compose-dev.yml deleted file mode 100644 index aa4b50a..0000000 --- a/docker-compose-dev.yml +++ /dev/null @@ -1,182 +0,0 @@ -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: - traefik-net: - external: true # Traefik管理下のネットワーク - windmill-internal: - driver: bridge # Windmill内部通信用 - -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: ${POSTGRES_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: always - deploy: - replicas: 1 - restart: unless-stopped - expose: - - 8000 - - 2525 - environment: - - DATABASE_URL=${DATABASE_URL} - - MODE=server - depends_on: - db: - condition: service_healthy - volumes: - - worker_logs:/tmp/windmill/logs - - /home/windmill/windmill:/workspace - labels: - # Traefik設定 - - "traefik.enable=true" - # HTTPSルーター - - "traefik.http.routers.windmill.rule=Host(`windmill.keinafarm.net`)" - - "traefik.http.routers.windmill.entrypoints=websecure" - - "traefik.http.routers.windmill.tls=true" - - "traefik.http.routers.windmill.tls.certresolver=letsencrypt" - - "traefik.http.services.windmill.loadbalancer.server.port=8000" - # HTTPからHTTPSへのリダイレクト - - "traefik.http.routers.windmill-http.rule=Host(`windmill.keinafarm.net`)" - - "traefik.http.routers.windmill-http.entrypoints=web" - - "traefik.http.routers.windmill-http.middlewares=windmill-https-redirect" - - "traefik.http.middlewares.windmill-https-redirect.redirectscheme.scheme=https" - networks: - - traefik-net - - windmill-internal - logging: *default-logging - - windmill_worker: - image: ${WM_IMAGE} - pull_policy: always - deploy: - replicas: 3 - 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 - - /home/windmill/windmill:/workspace - networks: - - windmill-internal - logging: *default-logging - - windmill_worker_native: - image: ${WM_IMAGE} - pull_policy: always - 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_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: - image: ghcr.io/windmill-labs/windmill-extra:latest - pull_policy: always - restart: unless-stopped - expose: - - 3001 - - 3002 - - 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は使わない(Traefikを使用) - # caddy: - # deploy: - # replicas: 0 - -volumes: - db_data: null - worker_dependency_cache: null - worker_logs: null - worker_memory: null - windmill_index: null - lsp_cache: null diff --git a/docker-compose.yml b/docker-compose.yml deleted file mode 100644 index b642571..0000000 --- a/docker-compose.yml +++ /dev/null @@ -1,202 +0,0 @@ -x-logging: &default-logging - driver: "json-file" - options: - max-size: "${LOG_MAX_SIZE:-20m}" - max-file: "${LOG_MAX_FILE:-10}" - compress: "true" - -networks: - traefik-net: - external: true # サーバー上の既存Traefikネットワーク - 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=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: - db: - condition: service_healthy - volumes: - - worker_logs:/tmp/windmill/logs - # Git同期のために、カレントディレクトリ(リポジトリルート)を/workspaceにマウント - # これにより、コンテナ内から .git ディレクトリにアクセス可能となり、git pushが可能になる - - .:/workspace - labels: - - "traefik.enable=true" - # HTTPSルーター - - "traefik.http.routers.windmill.rule=Host(`windmill.keinafarm.net`)" - - "traefik.http.routers.windmill.entrypoints=websecure" - - "traefik.http.routers.windmill.tls=true" - - "traefik.http.routers.windmill.tls.certresolver=letsencrypt" - - "traefik.http.services.windmill.loadbalancer.server.port=8000" - # HTTPからHTTPSへのリダイレクト - - "traefik.http.routers.windmill-http.rule=Host(`windmill.keinafarm.net`)" - - "traefik.http.routers.windmill-http.entrypoints=web" - - "traefik.http.routers.windmill-http.middlewares=windmill-https-redirect" - - "traefik.http.middlewares.windmill-https-redirect.redirectscheme.scheme=https" - networks: - - traefik-net - - windmill-internal - logging: *default-logging - - windmill_worker: - image: ${WM_IMAGE} - pull_policy: if_not_present - deploy: - replicas: 3 - 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 - # WorkerからもGit同期が必要な場合に備えてマウント - - .:/workspace - 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 - - 3002 - - 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 - labels: - # LSPなどのWebSocket用設定(Caddyfileの代替) - - "traefik.enable=true" - # 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: - db_data: null - worker_dependency_cache: null - worker_logs: null - lsp_cache: null diff --git a/env.host b/env.host deleted file mode 100644 index 287436b..0000000 --- a/env.host +++ /dev/null @@ -1,5 +0,0 @@ -WM_IMAGE=ghcr.io/windmill-labs/windmill:main -POSTGRES_PASSWORD=MyS3cur3P@ssw0rd!2024 -DATABASE_URL=postgresql://postgres:${POSTGRES_PASSWORD}@db:5432/windmill -LOG_MAX_SIZE=20m -LOG_MAX_FILE=10 diff --git a/mcp/Dockerfile b/mcp/Dockerfile deleted file mode 100644 index ffaee7e..0000000 --- a/mcp/Dockerfile +++ /dev/null @@ -1,14 +0,0 @@ -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"] diff --git a/mcp/requirements.txt b/mcp/requirements.txt deleted file mode 100644 index 69d9263..0000000 --- a/mcp/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -mcp>=1.0.0 -httpx>=0.27.0 diff --git a/mcp/windmill_mcp.py b/mcp/windmill_mcp.py deleted file mode 100644 index 3b276a2..0000000 --- a/mcp/windmill_mcp.py +++ /dev/null @@ -1,346 +0,0 @@ -#!/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") - -if not WINDMILL_TOKEN: - print("Error: WINDMILL_TOKEN 環境変数が設定されていません", file=sys.stderr) - sys.exit(1) - -mcp = FastMCP("windmill") - - -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/edit/{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") - if transport == "sse": - host = os.environ.get("MCP_HOST", "0.0.0.0") - port = int(os.environ.get("MCP_PORT", "8001")) - mcp.run(transport="sse", host=host, port=port) - else: - mcp.run(transport="stdio") diff --git a/sync_to_git.sh b/sync_to_git.sh deleted file mode 100755 index ed3c77d..0000000 --- a/sync_to_git.sh +++ /dev/null @@ -1,68 +0,0 @@ -#!/bin/bash - -# Windmill Workflow Git Auto-Sync Script -# このスクリプトは、Windmillワークフローを自動的にGitにコミットします - -set -e - -# 色付き出力 -GREEN='\033[0;32m' -YELLOW='\033[1;33m'#!/bin/bash - -# Windmill Workflow Git Auto-Sync Script for Gitea -# このスクリプトは、Windmillワークフローを自動的にGiteaにコミット&プッシュします - -set -e - -# 色付き出力 -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -RED='\033[0;31m' -NC='\033[0m' # No Color - -echo -e "${GREEN}=== Windmill Workflow Git Sync (Gitea) ===${NC}" - -# 作業ディレクトリに移動 -cd /workspace - -# PATHを設定 -export PATH=~/.npm-global/bin:$PATH - -# Git設定(safe.directoryエラー対策) -git config --global --add safe.directory /workspace -git config --global user.email "bot@example.com" -git config --global user.name "Windmill Bot" - -# 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" - - - # Giteaにプッシュ - echo -e "${YELLOW}Pushing to Gitea...${NC}" - git push origin main || { - 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 - } - - echo -e "${GREEN}✓ Changes pushed to Gitea${NC}" -else - echo -e "${GREEN}✓ No changes detected${NC}" -fi - -echo -e "${GREEN}=== Sync Complete ===${NC}"