commit
5acef27cd7
|
|
@ -19,6 +19,14 @@ Save your change type
|
||||||
<!-- If applicable, add screenshots or recordings to demonstrate the changes -->
|
<!-- If applicable, add screenshots or recordings to demonstrate the changes -->
|
||||||
|
|
||||||
|
|
||||||
|
## Documentation Updates
|
||||||
|
<!-- Check if you updated documentation for changes to tools/resources -->
|
||||||
|
- [ ] I have added/removed/modified tools or resources
|
||||||
|
- [ ] If yes, I have updated all documentation files using:
|
||||||
|
- [ ] The LLM prompt at `tools/UPDATE_DOCS_PROMPT.md` (recommended)
|
||||||
|
- [ ] Manual updates following the guide at `tools/UPDATE_DOCS.md`
|
||||||
|
|
||||||
|
|
||||||
## Related Issues
|
## Related Issues
|
||||||
<!-- Link any related issues using "Fixes #123" or "Relates to #123" -->
|
<!-- Link any related issues using "Fixes #123" or "Relates to #123" -->
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,9 @@
|
||||||
name: Release
|
name: Release
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: release-main
|
||||||
|
cancel-in-progress: false
|
||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
inputs:
|
inputs:
|
||||||
|
|
@ -19,13 +23,25 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
|
pull-requests: write
|
||||||
outputs:
|
outputs:
|
||||||
new_version: ${{ steps.compute.outputs.new_version }}
|
new_version: ${{ steps.compute.outputs.new_version }}
|
||||||
tag: ${{ steps.tag.outputs.tag }}
|
tag: ${{ steps.tag.outputs.tag }}
|
||||||
|
bump_branch: ${{ steps.bump_branch.outputs.name }}
|
||||||
steps:
|
steps:
|
||||||
|
- name: Ensure workflow is running on main
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
if [[ "${GITHUB_REF_NAME}" != "main" ]]; then
|
||||||
|
echo "This workflow must be run on the main branch. Current ref: ${GITHUB_REF_NAME}" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
|
ref: main
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Compute new version
|
- name: Compute new version
|
||||||
|
|
@ -75,57 +91,102 @@ jobs:
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
echo "Updating MCPForUnity/package.json to $NEW_VERSION"
|
echo "Updating all version references to $NEW_VERSION"
|
||||||
jq ".version = \"${NEW_VERSION}\"" MCPForUnity/package.json > MCPForUnity/package.json.tmp
|
python3 tools/update_versions.py --version "$NEW_VERSION"
|
||||||
mv MCPForUnity/package.json.tmp MCPForUnity/package.json
|
|
||||||
|
|
||||||
echo "Updating Server/pyproject.toml to $NEW_VERSION"
|
- name: Commit version bump to a temporary branch
|
||||||
sed -i '0,/^version = ".*"/s//version = "'"$NEW_VERSION"'"/' "Server/pyproject.toml"
|
id: bump_branch
|
||||||
|
|
||||||
echo "Updating Server/README.md version references to v$NEW_VERSION"
|
|
||||||
sed -i 's|git+https://github.com/CoplayDev/unity-mcp@v[0-9]\+\.[0-9]\+\.[0-9]\+#subdirectory=Server|git+https://github.com/CoplayDev/unity-mcp@v'"$NEW_VERSION"'#subdirectory=Server|g' Server/README.md
|
|
||||||
|
|
||||||
echo "Updating root README.md fixed version examples to v$NEW_VERSION"
|
|
||||||
sed -i 's|https://github.com/CoplayDev/unity-mcp.git?path=/MCPForUnity#v[0-9]\+\.[0-9]\+\.[0-9]\+|https://github.com/CoplayDev/unity-mcp.git?path=/MCPForUnity#v'"$NEW_VERSION"'|g' README.md
|
|
||||||
|
|
||||||
echo "Updating docs/i18n/README-zh.md fixed version examples to v$NEW_VERSION"
|
|
||||||
sed -i 's|https://github.com/CoplayDev/unity-mcp.git?path=/MCPForUnity#v[0-9]\+\.[0-9]\+\.[0-9]\+|https://github.com/CoplayDev/unity-mcp.git?path=/MCPForUnity#v'"$NEW_VERSION"'|g' docs/i18n/README-zh.md
|
|
||||||
|
|
||||||
- name: Commit and push changes
|
|
||||||
env:
|
env:
|
||||||
NEW_VERSION: ${{ steps.compute.outputs.new_version }}
|
NEW_VERSION: ${{ steps.compute.outputs.new_version }}
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
BRANCH="release/v${NEW_VERSION}"
|
||||||
|
echo "name=$BRANCH" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
git config user.name "GitHub Actions"
|
git config user.name "GitHub Actions"
|
||||||
git config user.email "actions@github.com"
|
git config user.email "actions@github.com"
|
||||||
git add MCPForUnity/package.json "Server/pyproject.toml" Server/README.md README.md docs/i18n/README-zh.md
|
git checkout -b "$BRANCH"
|
||||||
|
git add MCPForUnity/package.json manifest.json "Server/pyproject.toml" Server/README.md
|
||||||
if git diff --cached --quiet; then
|
if git diff --cached --quiet; then
|
||||||
echo "No version changes to commit."
|
echo "No version changes to commit."
|
||||||
else
|
else
|
||||||
git commit -m "chore: bump version to ${NEW_VERSION}"
|
git commit -m "chore: bump version to ${NEW_VERSION}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
BRANCH="${GITHUB_HEAD_REF:-${GITHUB_REF_NAME}}"
|
echo "Pushing bump branch $BRANCH"
|
||||||
echo "Pushing to branch: $BRANCH"
|
|
||||||
git push origin "$BRANCH"
|
git push origin "$BRANCH"
|
||||||
|
|
||||||
- name: Create and push tag
|
- name: Create PR for version bump into main
|
||||||
|
id: bump_pr
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ github.token }}
|
||||||
|
NEW_VERSION: ${{ steps.compute.outputs.new_version }}
|
||||||
|
BRANCH: ${{ steps.bump_branch.outputs.name }}
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
PR_URL=$(gh pr create \
|
||||||
|
--base main \
|
||||||
|
--head "$BRANCH" \
|
||||||
|
--title "chore: bump version to ${NEW_VERSION}" \
|
||||||
|
--body "Automated version bump to ${NEW_VERSION}.")
|
||||||
|
echo "pr_url=$PR_URL" >> "$GITHUB_OUTPUT"
|
||||||
|
PR_NUMBER=$(echo "$PR_URL" | grep -oE '[0-9]+$')
|
||||||
|
echo "pr_number=$PR_NUMBER" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
- name: Enable auto-merge and merge PR
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ github.token }}
|
||||||
|
PR_NUMBER: ${{ steps.bump_pr.outputs.pr_number }}
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
# Enable auto-merge (requires repo setting "Allow auto-merge")
|
||||||
|
gh pr merge "$PR_NUMBER" --merge --auto || true
|
||||||
|
# Wait for PR to be merged (poll up to 2 minutes)
|
||||||
|
for i in {1..24}; do
|
||||||
|
STATE=$(gh pr view "$PR_NUMBER" --json state -q '.state')
|
||||||
|
if [[ "$STATE" == "MERGED" ]]; then
|
||||||
|
echo "PR merged successfully."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
echo "Waiting for PR to merge... (state: $STATE)"
|
||||||
|
sleep 5
|
||||||
|
done
|
||||||
|
echo "PR did not merge in time. Attempting direct merge..."
|
||||||
|
gh pr merge "$PR_NUMBER" --merge
|
||||||
|
|
||||||
|
- name: Fetch merged main and create tag
|
||||||
env:
|
env:
|
||||||
TAG: ${{ steps.tag.outputs.tag }}
|
TAG: ${{ steps.tag.outputs.tag }}
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
git fetch origin main
|
||||||
|
git checkout main
|
||||||
|
git pull origin main
|
||||||
|
|
||||||
echo "Preparing to create tag $TAG"
|
echo "Preparing to create tag $TAG"
|
||||||
|
|
||||||
if git ls-remote --tags origin | grep -q "refs/tags/$TAG$"; then
|
if git ls-remote --tags origin | grep -q "refs/tags/$TAG$"; then
|
||||||
echo "Tag $TAG already exists on remote. Skipping tag creation."
|
echo "Tag $TAG already exists on remote. Refusing to release." >&2
|
||||||
exit 0
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
git tag -a "$TAG" -m "Version ${TAG#v}"
|
git tag -a "$TAG" -m "Version ${TAG#v}"
|
||||||
git push origin "$TAG"
|
git push origin "$TAG"
|
||||||
|
|
||||||
|
- name: Clean up release branch
|
||||||
|
if: always()
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ github.token }}
|
||||||
|
BRANCH: ${{ steps.bump_branch.outputs.name }}
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
git push origin --delete "$BRANCH" || true
|
||||||
|
|
||||||
- name: Create GitHub release
|
- name: Create GitHub release
|
||||||
uses: softprops/action-gh-release@v2
|
uses: softprops/action-gh-release@v2
|
||||||
with:
|
with:
|
||||||
|
|
@ -133,6 +194,70 @@ jobs:
|
||||||
name: ${{ steps.tag.outputs.tag }}
|
name: ${{ steps.tag.outputs.tag }}
|
||||||
generate_release_notes: true
|
generate_release_notes: true
|
||||||
|
|
||||||
|
sync_beta:
|
||||||
|
name: Merge main back into beta via PR
|
||||||
|
needs:
|
||||||
|
- bump
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
pull-requests: write
|
||||||
|
steps:
|
||||||
|
- name: Checkout main
|
||||||
|
uses: actions/checkout@v6
|
||||||
|
with:
|
||||||
|
ref: main
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Create PR to merge main into beta
|
||||||
|
id: sync_pr
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ github.token }}
|
||||||
|
NEW_VERSION: ${{ needs.bump.outputs.new_version }}
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
# Check if beta is behind main
|
||||||
|
git fetch origin beta
|
||||||
|
if git merge-base --is-ancestor origin/main origin/beta; then
|
||||||
|
echo "beta is already up to date with main. Skipping PR."
|
||||||
|
echo "skipped=true" >> "$GITHUB_OUTPUT"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
PR_URL=$(gh pr create \
|
||||||
|
--base beta \
|
||||||
|
--head main \
|
||||||
|
--title "chore: sync main (v${NEW_VERSION}) into beta" \
|
||||||
|
--body "Automated sync of version bump from main into beta.")
|
||||||
|
echo "pr_url=$PR_URL" >> "$GITHUB_OUTPUT"
|
||||||
|
PR_NUMBER=$(echo "$PR_URL" | grep -oE '[0-9]+$')
|
||||||
|
echo "pr_number=$PR_NUMBER" >> "$GITHUB_OUTPUT"
|
||||||
|
echo "skipped=false" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
- name: Enable auto-merge and merge sync PR
|
||||||
|
if: steps.sync_pr.outputs.skipped != 'true'
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ github.token }}
|
||||||
|
PR_NUMBER: ${{ steps.sync_pr.outputs.pr_number }}
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
# Enable auto-merge (requires repo setting "Allow auto-merge")
|
||||||
|
gh pr merge "$PR_NUMBER" --merge --auto || true
|
||||||
|
# Wait for PR to be merged (poll up to 2 minutes)
|
||||||
|
for i in {1..24}; do
|
||||||
|
STATE=$(gh pr view "$PR_NUMBER" --json state -q '.state')
|
||||||
|
if [[ "$STATE" == "MERGED" ]]; then
|
||||||
|
echo "Sync PR merged successfully."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
echo "Waiting for sync PR to merge... (state: $STATE)"
|
||||||
|
sleep 5
|
||||||
|
done
|
||||||
|
echo "Sync PR did not merge in time. Attempting direct merge..."
|
||||||
|
gh pr merge "$PR_NUMBER" --merge
|
||||||
|
|
||||||
publish_docker:
|
publish_docker:
|
||||||
name: Publish Docker image
|
name: Publish Docker image
|
||||||
needs:
|
needs:
|
||||||
|
|
@ -194,3 +319,43 @@ jobs:
|
||||||
uses: pypa/gh-action-pypi-publish@release/v1
|
uses: pypa/gh-action-pypi-publish@release/v1
|
||||||
with:
|
with:
|
||||||
packages-dir: Server/dist/
|
packages-dir: Server/dist/
|
||||||
|
|
||||||
|
publish_mcpb:
|
||||||
|
name: Generate and publish MCPB bundle
|
||||||
|
needs:
|
||||||
|
- bump
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
steps:
|
||||||
|
- name: Check out the repo
|
||||||
|
uses: actions/checkout@v6
|
||||||
|
with:
|
||||||
|
ref: ${{ needs.bump.outputs.tag }}
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Set up Node.js
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: "20"
|
||||||
|
|
||||||
|
- name: Set up Python
|
||||||
|
uses: actions/setup-python@v5
|
||||||
|
with:
|
||||||
|
python-version: "3.11"
|
||||||
|
|
||||||
|
- name: Generate MCPB bundle
|
||||||
|
env:
|
||||||
|
NEW_VERSION: ${{ needs.bump.outputs.new_version }}
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
python3 tools/generate_mcpb.py "$NEW_VERSION" \
|
||||||
|
--output "unity-mcp-${NEW_VERSION}.mcpb" \
|
||||||
|
--icon docs/images/coplay-logo.png
|
||||||
|
|
||||||
|
- name: Upload MCPB to release
|
||||||
|
uses: softprops/action-gh-release@v2
|
||||||
|
with:
|
||||||
|
tag_name: ${{ needs.bump.outputs.tag }}
|
||||||
|
files: unity-mcp-${{ needs.bump.outputs.new_version }}.mcpb
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,82 @@
|
||||||
|
# MCPB Ignore File
|
||||||
|
# This bundle uses uvx pattern - package downloaded from PyPI at runtime
|
||||||
|
# Only manifest.json, icon.png, README.md, and LICENSE are needed
|
||||||
|
|
||||||
|
# Server source code (downloaded via uvx from PyPI)
|
||||||
|
Server/
|
||||||
|
|
||||||
|
# Unity Client plugin (separate installation)
|
||||||
|
MCPForUnity/
|
||||||
|
|
||||||
|
# Test projects
|
||||||
|
TestProjects/
|
||||||
|
|
||||||
|
# Documentation folder
|
||||||
|
docs/
|
||||||
|
|
||||||
|
# Custom Tools (shipped separately)
|
||||||
|
CustomTools/
|
||||||
|
|
||||||
|
# Development scripts at root
|
||||||
|
scripts/
|
||||||
|
tools/
|
||||||
|
|
||||||
|
# Claude skill zip (separate distribution)
|
||||||
|
claude_skill_unity.zip
|
||||||
|
|
||||||
|
# Development batch files
|
||||||
|
deploy-dev.bat
|
||||||
|
restore-dev.bat
|
||||||
|
|
||||||
|
# Test files at root
|
||||||
|
test_unity_socket_framing.py
|
||||||
|
mcp_source.py
|
||||||
|
prune_tool_results.py
|
||||||
|
|
||||||
|
# Docker
|
||||||
|
docker-compose.yml
|
||||||
|
.dockerignore
|
||||||
|
Dockerfile
|
||||||
|
|
||||||
|
# Chinese README (keep English only)
|
||||||
|
README-zh.md
|
||||||
|
|
||||||
|
# GitHub and CI
|
||||||
|
.github/
|
||||||
|
.claude/
|
||||||
|
|
||||||
|
# IDE
|
||||||
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
|
||||||
|
# Python artifacts
|
||||||
|
*.pyc
|
||||||
|
__pycache__/
|
||||||
|
.pytest_cache/
|
||||||
|
.mypy_cache/
|
||||||
|
*.egg-info/
|
||||||
|
dist/
|
||||||
|
build/
|
||||||
|
|
||||||
|
# Environment
|
||||||
|
.env*
|
||||||
|
*.local
|
||||||
|
.venv/
|
||||||
|
venv/
|
||||||
|
|
||||||
|
# Git
|
||||||
|
.git/
|
||||||
|
.gitignore
|
||||||
|
.gitattributes
|
||||||
|
|
||||||
|
# Package management
|
||||||
|
uv.lock
|
||||||
|
poetry.lock
|
||||||
|
requirements*.txt
|
||||||
|
pyproject.toml
|
||||||
|
|
||||||
|
# Logs and temp
|
||||||
|
*.log
|
||||||
|
*.tmp
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
@ -36,9 +36,9 @@ In Unity: `Window > Package Manager > + > Add package from git URL...`
|
||||||
> https://github.com/CoplayDev/unity-mcp.git?path=/MCPForUnity
|
> https://github.com/CoplayDev/unity-mcp.git?path=/MCPForUnity
|
||||||
> ```
|
> ```
|
||||||
|
|
||||||
**Need a stable/fixed version?** Use a tagged URL (requires uninstall to update):
|
**Want the latest beta?** Use the beta branch:
|
||||||
```text
|
```text
|
||||||
https://github.com/CoplayDev/unity-mcp.git?path=/MCPForUnity#v9.1.0
|
https://github.com/CoplayDev/unity-mcp.git?path=/MCPForUnity#beta
|
||||||
```
|
```
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
|
|
|
||||||
|
|
@ -338,6 +338,36 @@ python3 tools/stress_mcp.py \
|
||||||
4. **迭代** - 按需重复 1-3
|
4. **迭代** - 按需重复 1-3
|
||||||
5. **Restore** 完成后用 `restore-dev.bat` 恢复原始文件
|
5. **Restore** 完成后用 `restore-dev.bat` 恢复原始文件
|
||||||
|
|
||||||
|
## 重要说明
|
||||||
|
|
||||||
|
### 更新工具和 Manifest
|
||||||
|
|
||||||
|
在 Unity 包中添加或修改 MCP 工具时:
|
||||||
|
- 工具定义位于仓库根目录的 manifest.json 文件中
|
||||||
|
- 在发布过程中,manifest.json 版本会自动与 MCPForUnity/package.json 保持同步
|
||||||
|
- 如果在发布过程之外手动更新工具,请确保相应更新 manifest.json 版本
|
||||||
|
- 使用综合版本更新脚本:`python3 tools/update_versions.py` 来同步项目中所有版本引用
|
||||||
|
|
||||||
|
`update_versions.py` 脚本会更新:
|
||||||
|
- MCPForUnity/package.json(Unity 包版本)
|
||||||
|
- manifest.json(MCP bundle manifest)
|
||||||
|
- Server/pyproject.toml(Python 包版本)
|
||||||
|
- Server/README.md(版本引用)
|
||||||
|
- README.md(固定版本示例)
|
||||||
|
- docs/i18n/README-zh.md(固定版本示例)
|
||||||
|
|
||||||
|
使用示例:
|
||||||
|
```bash
|
||||||
|
# 更新所有文件以匹配 package.json 版本
|
||||||
|
python3 tools/update_versions.py
|
||||||
|
|
||||||
|
# 更新所有文件到指定版本
|
||||||
|
python3 tools/update_versions.py --version 9.2.0
|
||||||
|
|
||||||
|
# 干运行以查看将要更新的内容
|
||||||
|
python3 tools/update_versions.py --dry-run
|
||||||
|
```
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|
||||||
### 运行 .bat 时出现 "Path not found"
|
### 运行 .bat 时出现 "Path not found"
|
||||||
|
|
|
||||||
|
|
@ -328,6 +328,36 @@ We provide a CI job to run a Natural Language Editing suite against the Unity te
|
||||||
4. **Iterate** - repeat steps 1-3 as needed
|
4. **Iterate** - repeat steps 1-3 as needed
|
||||||
5. **Restore** original files when done using `restore-dev.bat`
|
5. **Restore** original files when done using `restore-dev.bat`
|
||||||
|
|
||||||
|
## Important Notes
|
||||||
|
|
||||||
|
### Updating Tools and Manifest
|
||||||
|
|
||||||
|
When adding or modifying MCP tools in the Unity package:
|
||||||
|
- Tool definitions are located in the manifest.json file at the repository root
|
||||||
|
- The manifest.json version is automatically kept in sync with MCPForUnity/package.json during releases
|
||||||
|
- If you manually update tools outside of the release process, ensure to update the manifest.json version accordingly
|
||||||
|
- Use the comprehensive version update script: `python3 tools/update_versions.py` to sync all version references across the project
|
||||||
|
|
||||||
|
The `update_versions.py` script updates:
|
||||||
|
- MCPForUnity/package.json (Unity package version)
|
||||||
|
- manifest.json (MCP bundle manifest)
|
||||||
|
- Server/pyproject.toml (Python package version)
|
||||||
|
- Server/README.md (version references)
|
||||||
|
- README.md (fixed version examples)
|
||||||
|
- docs/i18n/README-zh.md (fixed version examples)
|
||||||
|
|
||||||
|
Usage examples:
|
||||||
|
```bash
|
||||||
|
# Update all files to match package.json version
|
||||||
|
python3 tools/update_versions.py
|
||||||
|
|
||||||
|
# Update all files to a specific version
|
||||||
|
python3 tools/update_versions.py --version 9.2.0
|
||||||
|
|
||||||
|
# Dry run to see what would be updated
|
||||||
|
python3 tools/update_versions.py --dry-run
|
||||||
|
```
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|
||||||
### "Path not found" errors running the .bat file
|
### "Path not found" errors running the .bat file
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,111 @@
|
||||||
|
# Releasing (Maintainers)
|
||||||
|
|
||||||
|
This repo uses a two-branch flow to keep `main` stable for users:
|
||||||
|
|
||||||
|
- `beta`: integration branch where feature PRs land
|
||||||
|
- `main`: stable branch that should match the latest release tag
|
||||||
|
|
||||||
|
## Release checklist
|
||||||
|
|
||||||
|
### 1) Promote `beta` to `main` via PR
|
||||||
|
|
||||||
|
- Create a PR with:
|
||||||
|
- base: `main`
|
||||||
|
- compare: `beta`
|
||||||
|
- Ensure required CI checks are green.
|
||||||
|
- Merge the PR.
|
||||||
|
|
||||||
|
Release note quality depends on how you merge:
|
||||||
|
|
||||||
|
- Squash-merging feature PRs into `beta` is OK.
|
||||||
|
- Avoid squash-merging the `beta -> main` promotion PR. Prefer a merge commit (or rebase merge) so GitHub can produce better auto-generated release notes.
|
||||||
|
|
||||||
|
### 2) Run the Release workflow (manual)
|
||||||
|
|
||||||
|
- Go to **GitHub → Actions → Release**
|
||||||
|
- Click **Run workflow**
|
||||||
|
- Select:
|
||||||
|
- `patch`, `minor`, or `major`
|
||||||
|
- Run it on branch: `main`
|
||||||
|
|
||||||
|
What the workflow does:
|
||||||
|
|
||||||
|
1. Creates a temporary `release/vX.Y.Z` branch with the version bump commit
|
||||||
|
2. Opens a PR from that branch into `main`
|
||||||
|
3. Auto-merges the PR (or waits for required checks, then merges)
|
||||||
|
4. Creates an annotated tag `vX.Y.Z` on the merged commit
|
||||||
|
5. Creates a GitHub Release for the tag
|
||||||
|
6. Publishes artifacts (Docker / PyPI / MCPB)
|
||||||
|
7. Opens a PR to merge `main` back into `beta` (so `beta` gets the bump)
|
||||||
|
8. Auto-merges the sync PR
|
||||||
|
9. Cleans up the temporary release branch
|
||||||
|
|
||||||
|
### 3) Verify release outputs
|
||||||
|
|
||||||
|
- Confirm a new tag exists: `vX.Y.Z`
|
||||||
|
- Confirm a GitHub Release exists for the tag
|
||||||
|
- Confirm artifacts:
|
||||||
|
- Docker image published with version `X.Y.Z`
|
||||||
|
- PyPI package published (if configured)
|
||||||
|
- `unity-mcp-X.Y.Z.mcpb` attached to the GitHub Release
|
||||||
|
|
||||||
|
## Required repo settings
|
||||||
|
|
||||||
|
### Branch protection (Rulesets)
|
||||||
|
|
||||||
|
The release workflow uses PRs instead of direct pushes, so it works with strict branch protection. No bypass actors are required.
|
||||||
|
|
||||||
|
Recommended ruleset for `main`:
|
||||||
|
|
||||||
|
- Require PR before merging
|
||||||
|
- Allowed merge methods: `merge`, `rebase` (no squash for promotion PRs)
|
||||||
|
- Required approvals: `0` (so automated PRs can merge without human review)
|
||||||
|
- Optionally require status checks
|
||||||
|
|
||||||
|
Recommended ruleset for `beta`:
|
||||||
|
|
||||||
|
- Require PR before merging
|
||||||
|
- Allowed merge methods: `squash` (for feature PRs)
|
||||||
|
- Required approvals: `0` (so the sync PR can auto-merge)
|
||||||
|
|
||||||
|
### Enable auto-merge (required)
|
||||||
|
|
||||||
|
The workflow uses `gh pr merge --auto` to automatically merge PRs once checks pass.
|
||||||
|
|
||||||
|
To enable:
|
||||||
|
|
||||||
|
1. Go to **Settings → General**
|
||||||
|
2. Scroll to **Pull Requests**
|
||||||
|
3. Check **Allow auto-merge**
|
||||||
|
|
||||||
|
Without this setting, the workflow will fall back to direct merge attempts, which may fail if branch protection requires checks.
|
||||||
|
|
||||||
|
## Failure modes and recovery
|
||||||
|
|
||||||
|
### Tag already exists
|
||||||
|
|
||||||
|
The workflow fails if the computed tag already exists. Pick a different bump type or investigate why a tag already exists for that version.
|
||||||
|
|
||||||
|
### Bump PR fails to merge
|
||||||
|
|
||||||
|
If the version bump PR cannot be merged (e.g., required checks fail):
|
||||||
|
|
||||||
|
- The workflow will fail before creating a tag.
|
||||||
|
- Fix the issue, then either:
|
||||||
|
- Manually merge the PR and create the tag/release, or
|
||||||
|
- Close the PR, delete the `release/vX.Y.Z` branch, and re-run the workflow.
|
||||||
|
|
||||||
|
### Sync PR (`main -> beta`) fails
|
||||||
|
|
||||||
|
If the sync PR has merge conflicts:
|
||||||
|
|
||||||
|
- The workflow will fail after the release is published (artifacts are already out).
|
||||||
|
- Manually resolve conflicts in the sync PR and merge it.
|
||||||
|
|
||||||
|
### Leftover release branch
|
||||||
|
|
||||||
|
If the workflow fails mid-run, a `release/vX.Y.Z` branch may remain. Delete it manually before re-running:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git push origin --delete release/vX.Y.Z
|
||||||
|
```
|
||||||
|
|
@ -36,9 +36,9 @@
|
||||||
> https://github.com/CoplayDev/unity-mcp.git?path=/MCPForUnity
|
> https://github.com/CoplayDev/unity-mcp.git?path=/MCPForUnity
|
||||||
> ```
|
> ```
|
||||||
|
|
||||||
**需要一个稳定/固定版本?** 使用带 tag 的 URL(更新时需要卸载才能更新):
|
**想要最新的 beta 版本?** 使用 beta 分支:
|
||||||
```text
|
```text
|
||||||
https://github.com/CoplayDev/unity-mcp.git?path=/MCPForUnity#v9.1.0
|
https://github.com/CoplayDev/unity-mcp.git?path=/MCPForUnity#beta
|
||||||
```
|
```
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,58 @@
|
||||||
|
{
|
||||||
|
"manifest_version": "0.3",
|
||||||
|
"name": "Unity MCP",
|
||||||
|
"version": "9.1.0",
|
||||||
|
"description": "AI-powered Unity Editor automation via MCP - manage GameObjects, scripts, materials, scenes, prefabs, VFX, and run tests",
|
||||||
|
"author": {
|
||||||
|
"name": "Coplay",
|
||||||
|
"url": "https://www.coplay.dev"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/CoplayDev/unity-mcp"
|
||||||
|
},
|
||||||
|
"homepage": "https://www.coplay.dev",
|
||||||
|
"documentation": "https://github.com/CoplayDev/unity-mcp#readme",
|
||||||
|
"support": "https://github.com/CoplayDev/unity-mcp/issues",
|
||||||
|
"icon": "coplay-logo.png",
|
||||||
|
"server": {
|
||||||
|
"type": "python",
|
||||||
|
"entry_point": "Server/src/main.py",
|
||||||
|
"mcp_config": {
|
||||||
|
"command": "uvx",
|
||||||
|
"args": ["--from", "mcpforunityserver", "mcp-for-unity"],
|
||||||
|
"env": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"tools": [
|
||||||
|
{"name": "batch_execute", "description": "Execute multiple Unity operations in a single batch"},
|
||||||
|
{"name": "debug_request_context", "description": "Debug and inspect MCP request context"},
|
||||||
|
{"name": "execute_custom_tool", "description": "Execute custom Unity Editor tools registered by the project"},
|
||||||
|
{"name": "execute_menu_item", "description": "Execute Unity Editor menu items"},
|
||||||
|
{"name": "find_gameobjects", "description": "Find GameObjects in the scene by various criteria"},
|
||||||
|
{"name": "find_in_file", "description": "Search for content within Unity project files"},
|
||||||
|
{"name": "manage_asset", "description": "Create, modify, search, and organize Unity assets"},
|
||||||
|
{"name": "manage_components", "description": "Add, remove, and configure GameObject components"},
|
||||||
|
{"name": "manage_editor", "description": "Control Unity Editor state, play mode, and preferences"},
|
||||||
|
{"name": "manage_gameobject", "description": "Create, modify, transform, and delete GameObjects"},
|
||||||
|
{"name": "manage_material", "description": "Create and modify Unity materials and shaders"},
|
||||||
|
{"name": "manage_prefabs", "description": "Create, instantiate, unpack, and modify prefabs"},
|
||||||
|
{"name": "manage_scene", "description": "Load, save, query hierarchy, and manage Unity scenes"},
|
||||||
|
{"name": "manage_script", "description": "Create, read, and modify C# scripts"},
|
||||||
|
{"name": "manage_scriptable_object", "description": "Create and modify ScriptableObjects"},
|
||||||
|
{"name": "manage_shader", "description": "Work with Unity shaders"},
|
||||||
|
{"name": "manage_vfx", "description": "Manage Visual Effects, particle systems, and trails"},
|
||||||
|
{"name": "read_console", "description": "Read Unity Editor console output (logs, warnings, errors)"},
|
||||||
|
{"name": "refresh_unity", "description": "Refresh Unity Editor asset database"},
|
||||||
|
{"name": "run_tests", "description": "Run Unity Test Framework tests"},
|
||||||
|
{"name": "get_test_job", "description": "Get status of async test job"},
|
||||||
|
{"name": "script_apply_edits", "description": "Apply code edits to C# scripts with validation"},
|
||||||
|
{"name": "set_active_instance", "description": "Set the active Unity Editor instance for multi-instance workflows"},
|
||||||
|
{"name": "apply_text_edits", "description": "Apply text edits to script content"},
|
||||||
|
{"name": "create_script", "description": "Create new C# scripts"},
|
||||||
|
{"name": "delete_script", "description": "Delete C# scripts"},
|
||||||
|
{"name": "validate_script", "description": "Validate C# script syntax and compilation"},
|
||||||
|
{"name": "manage_script_capabilities", "description": "Query script management capabilities"},
|
||||||
|
{"name": "get_sha", "description": "Get SHA hash of script content"}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,66 @@
|
||||||
|
# LLM Prompt for Updating Documentation
|
||||||
|
|
||||||
|
Copy and paste this prompt into your LLM when you need to update documentation after adding/removing/modifying MCP tools or resources.
|
||||||
|
|
||||||
|
## Example Usage
|
||||||
|
|
||||||
|
After adding a new tool called "manage_new_feature" and a new resource called "feature_resource", you would:
|
||||||
|
1. Copy the prompt in the section below
|
||||||
|
2. Paste it into your LLM
|
||||||
|
3. The LLM will analyze the codebase and update all documentation files
|
||||||
|
4. Review the changes and run the check script to verify
|
||||||
|
|
||||||
|
This ensures all documentation stays in sync across the repository.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Prompt
|
||||||
|
|
||||||
|
I've just made changes to MCP tools or resources in this Unity MCP repository. Please update all documentation files to keep them in sync.
|
||||||
|
|
||||||
|
Here's what you need to do:
|
||||||
|
|
||||||
|
1. **Check the current tools and resources** by examining:
|
||||||
|
- `Server/src/services/tools/` - Python tool implementations (look for @mcp_for_unity_tool decorators)
|
||||||
|
- `Server/src/services/resources/` - Python resource implementations (look for @mcp_for_unity_resource decorators)
|
||||||
|
|
||||||
|
2. **Update these files**:
|
||||||
|
|
||||||
|
a) **manifest.json** (root directory)
|
||||||
|
- Update the "tools" array (lines 27-57)
|
||||||
|
- Each tool needs: {"name": "tool_name", "description": "Brief description"}
|
||||||
|
- Keep tools in alphabetical order
|
||||||
|
- Note: Resources are not listed in manifest.json, only tools
|
||||||
|
|
||||||
|
b) **README.md** (root directory)
|
||||||
|
- Update "Available Tools" section (around line 78-79)
|
||||||
|
- Format: `tool1` • `tool2` • `tool3`
|
||||||
|
- Keep the same order as manifest.json
|
||||||
|
|
||||||
|
c) **README.md** - Resources section
|
||||||
|
- Update "Available Resources" section (around line 81-82)
|
||||||
|
- Format: `resource1` • `resource2` • `resource3`
|
||||||
|
- Resources come from Server/src/services/resources/ files
|
||||||
|
- Keep resources in alphabetical order
|
||||||
|
|
||||||
|
d) **docs/i18n/README-zh.md**
|
||||||
|
- Find and update the "可用工具" (Available Tools) section
|
||||||
|
- Find and update the "可用资源" (Available Resources) section
|
||||||
|
- Keep tool/resource names in English, but you can translate descriptions if helpful
|
||||||
|
|
||||||
|
3. **Important formatting rules**:
|
||||||
|
- Use backticks around tool/resource names
|
||||||
|
- Separate items with • (bullet point)
|
||||||
|
- Keep lists on single lines when possible
|
||||||
|
- Maintain alphabetical ordering
|
||||||
|
- Tools and resources are listed separately in documentation
|
||||||
|
|
||||||
|
4. **After updating**, run this check to verify:
|
||||||
|
```bash
|
||||||
|
python3 tools/check_docs_sync.py
|
||||||
|
```
|
||||||
|
It should show "All documentation is synchronized!"
|
||||||
|
|
||||||
|
Please show me the exact changes you're making to each file, and explain any discrepancies you find.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
@ -0,0 +1,147 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Generate MCPB bundle for Unity MCP.
|
||||||
|
|
||||||
|
This script creates a Model Context Protocol Bundle (.mcpb) file
|
||||||
|
for distribution as a GitHub release artifact.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
python3 tools/generate_mcpb.py VERSION [--output FILE] [--icon PATH]
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
python3 tools/generate_mcpb.py 9.0.8
|
||||||
|
python3 tools/generate_mcpb.py 9.0.8 --output unity-mcp-9.0.8.mcpb
|
||||||
|
python3 tools/generate_mcpb.py 9.0.8 --icon docs/images/coplay-logo.png
|
||||||
|
"""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import json
|
||||||
|
import shutil
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import tempfile
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
REPO_ROOT = Path(__file__).resolve().parents[1]
|
||||||
|
DEFAULT_ICON = REPO_ROOT / "docs" / "images" / "coplay-logo.png"
|
||||||
|
MANIFEST_TEMPLATE = REPO_ROOT / "manifest.json"
|
||||||
|
|
||||||
|
|
||||||
|
def create_manifest(version: str, icon_filename: str) -> dict:
|
||||||
|
"""Create manifest.json content with the specified version."""
|
||||||
|
if not MANIFEST_TEMPLATE.exists():
|
||||||
|
raise FileNotFoundError(f"Manifest template not found: {MANIFEST_TEMPLATE}")
|
||||||
|
|
||||||
|
manifest = json.loads(MANIFEST_TEMPLATE.read_text(encoding="utf-8"))
|
||||||
|
manifest["version"] = version
|
||||||
|
manifest["icon"] = icon_filename
|
||||||
|
return manifest
|
||||||
|
|
||||||
|
|
||||||
|
def generate_mcpb(
|
||||||
|
version: str,
|
||||||
|
output_path: Path,
|
||||||
|
icon_path: Path,
|
||||||
|
) -> Path:
|
||||||
|
"""Generate MCPB bundle file.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
version: Semantic version string (e.g., "9.0.8")
|
||||||
|
output_path: Output path for the .mcpb file
|
||||||
|
icon_path: Path to the icon file
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Path to the generated .mcpb file
|
||||||
|
"""
|
||||||
|
if not icon_path.exists():
|
||||||
|
raise FileNotFoundError(f"Icon not found: {icon_path}")
|
||||||
|
|
||||||
|
with tempfile.TemporaryDirectory() as tmpdir:
|
||||||
|
build_dir = Path(tmpdir) / "mcpb-build"
|
||||||
|
build_dir.mkdir()
|
||||||
|
|
||||||
|
# Copy icon
|
||||||
|
icon_filename = icon_path.name
|
||||||
|
shutil.copy2(icon_path, build_dir / icon_filename)
|
||||||
|
|
||||||
|
# Create manifest with version
|
||||||
|
manifest = create_manifest(version, icon_filename)
|
||||||
|
manifest_path = build_dir / "manifest.json"
|
||||||
|
manifest_path.write_text(
|
||||||
|
json.dumps(manifest, indent=2, ensure_ascii=False) + "\n",
|
||||||
|
encoding="utf-8",
|
||||||
|
)
|
||||||
|
|
||||||
|
# Copy LICENSE and README if they exist
|
||||||
|
for filename in ["LICENSE", "README.md"]:
|
||||||
|
src = REPO_ROOT / filename
|
||||||
|
if src.exists():
|
||||||
|
shutil.copy2(src, build_dir / filename)
|
||||||
|
|
||||||
|
# Pack using mcpb CLI
|
||||||
|
# Syntax: mcpb pack [directory] [output]
|
||||||
|
try:
|
||||||
|
result = subprocess.run(
|
||||||
|
["npx", "@anthropic-ai/mcpb", "pack", ".", str(output_path.absolute())],
|
||||||
|
cwd=build_dir,
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
check=True,
|
||||||
|
)
|
||||||
|
print(result.stdout)
|
||||||
|
except subprocess.CalledProcessError as e:
|
||||||
|
print(f"MCPB pack failed:\n{e.stderr}", file=sys.stderr)
|
||||||
|
raise
|
||||||
|
except FileNotFoundError:
|
||||||
|
print(
|
||||||
|
"Error: npx not found. Please install Node.js and npm.",
|
||||||
|
file=sys.stderr,
|
||||||
|
)
|
||||||
|
raise
|
||||||
|
|
||||||
|
if not output_path.exists():
|
||||||
|
raise RuntimeError(f"MCPB file was not created: {output_path}")
|
||||||
|
|
||||||
|
print(f"Generated: {output_path} ({output_path.stat().st_size:,} bytes)")
|
||||||
|
return output_path
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description="Generate MCPB bundle for Unity MCP",
|
||||||
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||||
|
epilog=__doc__,
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"version",
|
||||||
|
help="Version string for the bundle (e.g., 9.0.8)",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--output",
|
||||||
|
"-o",
|
||||||
|
type=Path,
|
||||||
|
help="Output path for the .mcpb file (default: unity-mcp-VERSION.mcpb)",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--icon",
|
||||||
|
type=Path,
|
||||||
|
default=DEFAULT_ICON,
|
||||||
|
help=f"Path to icon file (default: {DEFAULT_ICON.relative_to(REPO_ROOT)})",
|
||||||
|
)
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
# Default output name
|
||||||
|
if args.output is None:
|
||||||
|
args.output = Path(f"unity-mcp-{args.version}.mcpb")
|
||||||
|
|
||||||
|
try:
|
||||||
|
generate_mcpb(args.version, args.output, args.icon)
|
||||||
|
return 0
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error: {e}", file=sys.stderr)
|
||||||
|
return 1
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(main())
|
||||||
|
|
@ -0,0 +1,287 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Update version across all project files.
|
||||||
|
|
||||||
|
This script updates the version in all files that need it:
|
||||||
|
- MCPForUnity/package.json (Unity package version)
|
||||||
|
- manifest.json (MCP bundle manifest)
|
||||||
|
- Server/pyproject.toml (Python package version)
|
||||||
|
- Server/README.md (version references)
|
||||||
|
- README.md (fixed version examples)
|
||||||
|
- docs/i18n/README-zh.md (fixed version examples)
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
python3 tools/update_versions.py [--dry-run] [--version VERSION]
|
||||||
|
|
||||||
|
Options:
|
||||||
|
--dry-run: Show what would be updated without making changes
|
||||||
|
--version: Specify version to use (auto-detected from package.json if not provided)
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
# Update all files to match package.json version
|
||||||
|
python3 tools/update_versions.py
|
||||||
|
|
||||||
|
# Update all files to a specific version
|
||||||
|
python3 tools/update_versions.py --version 9.2.0
|
||||||
|
|
||||||
|
# Dry run to see what would be updated
|
||||||
|
python3 tools/update_versions.py --dry-run
|
||||||
|
"""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import json
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
REPO_ROOT = Path(__file__).resolve().parents[1]
|
||||||
|
PACKAGE_JSON = REPO_ROOT / "MCPForUnity" / "package.json"
|
||||||
|
MANIFEST_JSON = REPO_ROOT / "manifest.json"
|
||||||
|
PYPROJECT_TOML = REPO_ROOT / "Server" / "pyproject.toml"
|
||||||
|
SERVER_README = REPO_ROOT / "Server" / "README.md"
|
||||||
|
ROOT_README = REPO_ROOT / "README.md"
|
||||||
|
ZH_README = REPO_ROOT / "docs" / "i18n" / "README-zh.md"
|
||||||
|
|
||||||
|
|
||||||
|
def load_package_version() -> str:
|
||||||
|
"""Load version from package.json."""
|
||||||
|
if not PACKAGE_JSON.exists():
|
||||||
|
raise FileNotFoundError(f"Package file not found: {PACKAGE_JSON}")
|
||||||
|
|
||||||
|
package_data = json.loads(PACKAGE_JSON.read_text(encoding="utf-8"))
|
||||||
|
version = package_data.get("version")
|
||||||
|
|
||||||
|
if not version:
|
||||||
|
raise ValueError("No version found in package.json")
|
||||||
|
|
||||||
|
return version
|
||||||
|
|
||||||
|
|
||||||
|
def update_package_json(new_version: str, dry_run: bool = False) -> bool:
|
||||||
|
"""Update version in MCPForUnity/package.json."""
|
||||||
|
if not PACKAGE_JSON.exists():
|
||||||
|
print(f"Warning: {PACKAGE_JSON.relative_to(REPO_ROOT)} not found")
|
||||||
|
return False
|
||||||
|
|
||||||
|
package_data = json.loads(PACKAGE_JSON.read_text(encoding="utf-8"))
|
||||||
|
current_version = package_data.get("version", "unknown")
|
||||||
|
|
||||||
|
if current_version == new_version:
|
||||||
|
print(f"✓ {PACKAGE_JSON.relative_to(REPO_ROOT)} already at v{new_version}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
print(
|
||||||
|
f"Updating {PACKAGE_JSON.relative_to(REPO_ROOT)}: {current_version} → {new_version}")
|
||||||
|
|
||||||
|
if not dry_run:
|
||||||
|
package_data["version"] = new_version
|
||||||
|
PACKAGE_JSON.write_text(
|
||||||
|
json.dumps(package_data, indent=2, ensure_ascii=False) + "\n",
|
||||||
|
encoding="utf-8",
|
||||||
|
)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def update_manifest_json(new_version: str, dry_run: bool = False) -> bool:
|
||||||
|
"""Update version in manifest.json."""
|
||||||
|
if not MANIFEST_JSON.exists():
|
||||||
|
print(f"Warning: {MANIFEST_JSON.relative_to(REPO_ROOT)} not found")
|
||||||
|
return False
|
||||||
|
|
||||||
|
manifest = json.loads(MANIFEST_JSON.read_text(encoding="utf-8"))
|
||||||
|
current_version = manifest.get("version", "unknown")
|
||||||
|
|
||||||
|
if current_version == new_version:
|
||||||
|
print(f"✓ {MANIFEST_JSON.relative_to(REPO_ROOT)} already at v{new_version}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
print(
|
||||||
|
f"Updating {MANIFEST_JSON.relative_to(REPO_ROOT)}: {current_version} → {new_version}")
|
||||||
|
|
||||||
|
if not dry_run:
|
||||||
|
manifest["version"] = new_version
|
||||||
|
MANIFEST_JSON.write_text(
|
||||||
|
json.dumps(manifest, indent=2, ensure_ascii=False) + "\n",
|
||||||
|
encoding="utf-8",
|
||||||
|
)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def update_pyproject_toml(new_version: str, dry_run: bool = False) -> bool:
|
||||||
|
"""Update version in Server/pyproject.toml."""
|
||||||
|
if not PYPROJECT_TOML.exists():
|
||||||
|
print(f"Warning: {PYPROJECT_TOML.relative_to(REPO_ROOT)} not found")
|
||||||
|
return False
|
||||||
|
|
||||||
|
content = PYPROJECT_TOML.read_text(encoding="utf-8")
|
||||||
|
|
||||||
|
# Find current version
|
||||||
|
version_match = re.search(r'^version = "([^"]+)"', content, re.MULTILINE)
|
||||||
|
if not version_match:
|
||||||
|
print(
|
||||||
|
f"Warning: Could not find version in {PYPROJECT_TOML.relative_to(REPO_ROOT)}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
current_version = version_match.group(1)
|
||||||
|
|
||||||
|
if current_version == new_version:
|
||||||
|
print(f"✓ {PYPROJECT_TOML.relative_to(REPO_ROOT)} already at v{new_version}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
print(
|
||||||
|
f"Updating {PYPROJECT_TOML.relative_to(REPO_ROOT)}: {current_version} → {new_version}")
|
||||||
|
|
||||||
|
if not dry_run:
|
||||||
|
# Replace only the first occurrence (the version field)
|
||||||
|
content = re.sub(
|
||||||
|
r'^version = ".*"', f'version = "{new_version}"', content, count=1, flags=re.MULTILINE)
|
||||||
|
PYPROJECT_TOML.write_text(content, encoding="utf-8")
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def update_server_readme(new_version: str, dry_run: bool = False) -> bool:
|
||||||
|
"""Update version references in Server/README.md."""
|
||||||
|
if not SERVER_README.exists():
|
||||||
|
print(f"Warning: {SERVER_README.relative_to(REPO_ROOT)} not found")
|
||||||
|
return False
|
||||||
|
|
||||||
|
content = SERVER_README.read_text(encoding="utf-8")
|
||||||
|
|
||||||
|
# Pattern to match git+https URLs with version tags
|
||||||
|
pattern = r'git\+https://github\.com/CoplayDev/unity-mcp@v[0-9]+\.[0-9]+\.[0-9]+#subdirectory=Server'
|
||||||
|
replacement = f'git+https://github.com/CoplayDev/unity-mcp@v{new_version}#subdirectory=Server'
|
||||||
|
|
||||||
|
if not re.search(pattern, content):
|
||||||
|
print(
|
||||||
|
f"✓ {SERVER_README.relative_to(REPO_ROOT)} has no version references to update")
|
||||||
|
return False
|
||||||
|
|
||||||
|
print(
|
||||||
|
f"Updating version references in {SERVER_README.relative_to(REPO_ROOT)}")
|
||||||
|
|
||||||
|
if not dry_run:
|
||||||
|
content = re.sub(pattern, replacement, content)
|
||||||
|
SERVER_README.write_text(content, encoding="utf-8")
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def update_root_readme(new_version: str, dry_run: bool = False) -> bool:
|
||||||
|
"""Update fixed version examples in README.md."""
|
||||||
|
if not ROOT_README.exists():
|
||||||
|
print(f"Warning: {ROOT_README.relative_to(REPO_ROOT)} not found")
|
||||||
|
return False
|
||||||
|
|
||||||
|
content = ROOT_README.read_text(encoding="utf-8")
|
||||||
|
|
||||||
|
# Pattern to match git URLs with fixed version tags
|
||||||
|
pattern = r'https://github\.com/CoplayDev/unity-mcp\.git\?path=/MCPForUnity#v[0-9]+\.[0-9]+\.[0-9]+'
|
||||||
|
replacement = f'https://github.com/CoplayDev/unity-mcp.git?path=/MCPForUnity#v{new_version}'
|
||||||
|
|
||||||
|
if not re.search(pattern, content):
|
||||||
|
print(
|
||||||
|
f"✓ {ROOT_README.relative_to(REPO_ROOT)} has no version references to update")
|
||||||
|
return False
|
||||||
|
|
||||||
|
print(
|
||||||
|
f"Updating version references in {ROOT_README.relative_to(REPO_ROOT)}")
|
||||||
|
|
||||||
|
if not dry_run:
|
||||||
|
content = re.sub(pattern, replacement, content)
|
||||||
|
ROOT_README.write_text(content, encoding="utf-8")
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def update_zh_readme(new_version: str, dry_run: bool = False) -> bool:
|
||||||
|
"""Update fixed version examples in docs/i18n/README-zh.md."""
|
||||||
|
if not ZH_README.exists():
|
||||||
|
print(f"Warning: {ZH_README.relative_to(REPO_ROOT)} not found")
|
||||||
|
return False
|
||||||
|
|
||||||
|
content = ZH_README.read_text(encoding="utf-8")
|
||||||
|
|
||||||
|
# Pattern to match git URLs with fixed version tags
|
||||||
|
pattern = r'https://github\.com/CoplayDev/unity-mcp\.git\?path=/MCPForUnity#v[0-9]+\.[0-9]+\.[0-9]+'
|
||||||
|
replacement = f'https://github.com/CoplayDev/unity-mcp.git?path=/MCPForUnity#v{new_version}'
|
||||||
|
|
||||||
|
if not re.search(pattern, content):
|
||||||
|
print(
|
||||||
|
f"✓ {ZH_README.relative_to(REPO_ROOT)} has no version references to update")
|
||||||
|
return False
|
||||||
|
|
||||||
|
print(f"Updating version references in {ZH_README.relative_to(REPO_ROOT)}")
|
||||||
|
|
||||||
|
if not dry_run:
|
||||||
|
content = re.sub(pattern, replacement, content)
|
||||||
|
ZH_README.write_text(content, encoding="utf-8")
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description="Update version across all project files",
|
||||||
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||||
|
epilog=__doc__,
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--dry-run",
|
||||||
|
action="store_true",
|
||||||
|
help="Show what would be updated without making changes",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--version",
|
||||||
|
help="Version to set (auto-detected from package.json if not provided)",
|
||||||
|
)
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Determine version
|
||||||
|
if args.version:
|
||||||
|
version = args.version
|
||||||
|
print(f"Using specified version: {version}")
|
||||||
|
else:
|
||||||
|
version = load_package_version()
|
||||||
|
print(f"Auto-detected version from package.json: {version}")
|
||||||
|
|
||||||
|
# Update all files
|
||||||
|
updates_made = []
|
||||||
|
|
||||||
|
# Always update package.json if a version is specified
|
||||||
|
if args.version:
|
||||||
|
if update_package_json(version, args.dry_run):
|
||||||
|
updates_made.append("MCPForUnity/package.json")
|
||||||
|
|
||||||
|
if update_manifest_json(version, args.dry_run):
|
||||||
|
updates_made.append("manifest.json")
|
||||||
|
|
||||||
|
if update_pyproject_toml(version, args.dry_run):
|
||||||
|
updates_made.append("Server/pyproject.toml")
|
||||||
|
|
||||||
|
if update_server_readme(version, args.dry_run):
|
||||||
|
updates_made.append("Server/README.md")
|
||||||
|
|
||||||
|
|
||||||
|
# Summary
|
||||||
|
if args.dry_run:
|
||||||
|
print("\nDry run complete. No files were modified.")
|
||||||
|
else:
|
||||||
|
if updates_made:
|
||||||
|
print(
|
||||||
|
f"\nUpdated {len(updates_made)} files: {', '.join(updates_made)}")
|
||||||
|
else:
|
||||||
|
print("\nAll files already at the correct version.")
|
||||||
|
|
||||||
|
return 0
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error: {e}", file=sys.stderr)
|
||||||
|
return 1
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(main())
|
||||||
Loading…
Reference in New Issue