From c6986f80425f3bfaccd348656739ad22d7326bb0 Mon Sep 17 00:00:00 2001 From: dsarno Date: Tue, 10 Feb 2026 15:06:27 -0800 Subject: [PATCH 1/3] fix(ci): backport deterministic sync_beta logic to main release workflow --- .github/workflows/release.yml | 256 +++++++++++++++++++++++++++++----- 1 file changed, 219 insertions(+), 37 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 198652e77..af5309aed 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -8,12 +8,13 @@ on: workflow_dispatch: inputs: version_bump: - description: "Version bump type" + description: "Version bump type (none = release beta version as-is)" type: choice options: - patch - minor - major + - none default: patch required: true @@ -44,8 +45,102 @@ jobs: ref: main fetch-depth: 0 + - name: Show current versions + id: preview + shell: bash + run: | + set -euo pipefail + echo "============================================" + echo "CURRENT VERSION STATUS" + echo "============================================" + + # Get main version + MAIN_VERSION=$(jq -r '.version' "MCPForUnity/package.json") + MAIN_PYPI=$(grep -oP '(?<=version = ")[^"]+' Server/pyproject.toml) + echo "Main branch:" + echo " Unity package: $MAIN_VERSION" + echo " PyPI server: $MAIN_PYPI" + echo "" + + # Get beta version + git fetch origin beta + BETA_VERSION=$(git show origin/beta:MCPForUnity/package.json | jq -r '.version') + BETA_PYPI=$(git show origin/beta:Server/pyproject.toml | grep -oP '(?<=version = ")[^"]+') + echo "Beta branch:" + echo " Unity package: $BETA_VERSION" + echo " PyPI server: $BETA_PYPI" + echo "" + + # Compute stripped version (used for "none" bump option) + STRIPPED=$(echo "$BETA_VERSION" | sed -E 's/-[a-zA-Z]+\.[0-9]+$//') + echo "stripped_version=$STRIPPED" >> "$GITHUB_OUTPUT" + + # Show what will happen + BUMP="${{ inputs.version_bump }}" + echo "Selected bump type: $BUMP" + echo "After stripping beta suffix: $STRIPPED" + + if [[ "$BUMP" == "none" ]]; then + echo "Release version will be: $STRIPPED" + else + IFS='.' read -r MA MI PA <<< "$STRIPPED" + case "$BUMP" in + major) ((MA+=1)); MI=0; PA=0 ;; + minor) ((MI+=1)); PA=0 ;; + patch) ((PA+=1)) ;; + esac + echo "Release version will be: $MA.$MI.$PA" + fi + echo "============================================" + + - name: Merge beta into main + shell: bash + run: | + set -euo pipefail + git config user.name "GitHub Actions" + git config user.email "actions@github.com" + + # Fetch beta branch + git fetch origin beta + + # Check if beta has changes not in main + if git merge-base --is-ancestor origin/beta HEAD; then + echo "beta is already merged into main. Nothing to merge." + else + echo "Merging beta into main..." + git merge origin/beta --no-edit -m "chore: merge beta into main for release" + echo "Beta merged successfully." + fi + + - name: Strip beta suffix from version if present + shell: bash + run: | + set -euo pipefail + CURRENT_VERSION=$(jq -r '.version' "MCPForUnity/package.json") + echo "Current version: $CURRENT_VERSION" + + # Strip beta/alpha/rc suffix if present (e.g., "9.4.0-beta.1" -> "9.4.0") + if [[ "$CURRENT_VERSION" == *"-"* ]]; then + STABLE_VERSION=$(echo "$CURRENT_VERSION" | sed -E 's/-[a-zA-Z]+\.[0-9]+$//') + # Validate we have a proper X.Y.Z format after stripping + if ! [[ "$STABLE_VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "Error: Could not parse version '$CURRENT_VERSION' -> '$STABLE_VERSION'" >&2 + exit 1 + fi + echo "Stripping prerelease suffix: $CURRENT_VERSION -> $STABLE_VERSION" + jq --arg v "$STABLE_VERSION" '.version = $v' MCPForUnity/package.json > tmp.json + mv tmp.json MCPForUnity/package.json + + # Also update pyproject.toml + sed -i "s/^version = .*/version = \"${STABLE_VERSION}\"/" Server/pyproject.toml + else + echo "Version is already stable: $CURRENT_VERSION" + fi + - name: Compute new version id: compute + env: + PREVIEWED_STRIPPED: ${{ steps.preview.outputs.stripped_version }} shell: bash run: | set -euo pipefail @@ -53,24 +148,35 @@ jobs: CURRENT_VERSION=$(jq -r '.version' "MCPForUnity/package.json") echo "Current version: $CURRENT_VERSION" - IFS='.' read -r MA MI PA <<< "$CURRENT_VERSION" - case "$BUMP" in - major) - ((MA+=1)); MI=0; PA=0 - ;; - minor) - ((MI+=1)); PA=0 - ;; - patch) - ((PA+=1)) - ;; - *) - echo "Unknown version_bump: $BUMP" >&2 - exit 1 - ;; - esac + # Sanity check: ensure current version matches what was previewed + if [[ "$CURRENT_VERSION" != "$PREVIEWED_STRIPPED" ]]; then + echo "Warning: Current version ($CURRENT_VERSION) differs from previewed ($PREVIEWED_STRIPPED)" + echo "This may indicate an unexpected merge result. Proceeding with current version." + fi + + if [[ "$BUMP" == "none" ]]; then + # Use the previewed stripped version to ensure consistency with what user saw + NEW_VERSION="$PREVIEWED_STRIPPED" + else + IFS='.' read -r MA MI PA <<< "$CURRENT_VERSION" + case "$BUMP" in + major) + ((MA+=1)); MI=0; PA=0 + ;; + minor) + ((MI+=1)); PA=0 + ;; + patch) + ((PA+=1)) + ;; + *) + echo "Unknown version_bump: $BUMP" >&2 + exit 1 + ;; + esac + NEW_VERSION="$MA.$MI.$PA" + fi - NEW_VERSION="$MA.$MI.$PA" echo "New version: $NEW_VERSION" echo "new_version=$NEW_VERSION" >> "$GITHUB_OUTPUT" echo "current_version=$CURRENT_VERSION" >> "$GITHUB_OUTPUT" @@ -203,60 +309,136 @@ jobs: contents: write pull-requests: write steps: - - name: Checkout main + - name: Checkout beta uses: actions/checkout@v6 with: - ref: main + ref: beta fetch-depth: 0 - - name: Create PR to merge main into beta - id: sync_pr + - name: Prepare sync branch from beta with merged main + id: sync_branch 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 + git config user.name "GitHub Actions" + git config user.email "actions@github.com" + + # Fetch both branches so we can build a merge commit in CI. + git fetch origin main beta if git merge-base --is-ancestor origin/main origin/beta; then - echo "beta is already up to date with main. Skipping PR." + echo "beta is already up to date with main. Skipping sync." echo "skipped=true" >> "$GITHUB_OUTPUT" exit 0 fi + SYNC_BRANCH="sync/main-v${NEW_VERSION}-into-beta-${GITHUB_RUN_ID}" + echo "name=$SYNC_BRANCH" >> "$GITHUB_OUTPUT" + echo "skipped=false" >> "$GITHUB_OUTPUT" + + git checkout -b "$SYNC_BRANCH" origin/beta + + if git merge origin/main --no-ff --no-commit; then + echo "main merged cleanly into sync branch." + else + echo "Merge conflicts detected. Attempting expected conflict resolution for beta version files." + CONFLICTS=$(git diff --name-only --diff-filter=U || true) + if [[ -n "$CONFLICTS" ]]; then + echo "$CONFLICTS" + fi + + # Keep beta-side prerelease versions if these files conflict. + for file in MCPForUnity/package.json Server/pyproject.toml; do + if git ls-files -u -- "$file" | grep -q .; then + echo "Keeping beta version for $file" + git checkout --ours -- "$file" + git add "$file" + fi + done + + REMAINING=$(git diff --name-only --diff-filter=U || true) + if [[ -n "$REMAINING" ]]; then + echo "Unexpected unresolved conflicts remain:" + echo "$REMAINING" + exit 1 + fi + fi + + git commit -m "chore: sync main (v${NEW_VERSION}) into beta" + + # After releasing X.Y.Z on main, beta should move to X.Y.(Z+1)-beta.1. + IFS='.' read -r MAJOR MINOR PATCH <<< "$NEW_VERSION" + NEXT_PATCH=$((PATCH + 1)) + NEXT_BETA_VERSION="${MAJOR}.${MINOR}.${NEXT_PATCH}-beta.1" + echo "beta_version=$NEXT_BETA_VERSION" >> "$GITHUB_OUTPUT" + echo "Setting beta version to $NEXT_BETA_VERSION" + + CURRENT_BETA_VERSION=$(jq -r '.version' MCPForUnity/package.json) + if [[ "$CURRENT_BETA_VERSION" != "$NEXT_BETA_VERSION" ]]; then + jq --arg v "$NEXT_BETA_VERSION" '.version = $v' MCPForUnity/package.json > tmp.json + mv tmp.json MCPForUnity/package.json + git add MCPForUnity/package.json + git commit -m "chore: set beta version to ${NEXT_BETA_VERSION} after release v${NEW_VERSION}" + else + echo "Beta version already at target: $NEXT_BETA_VERSION" + fi + + echo "Pushing sync branch $SYNC_BRANCH" + git push origin "$SYNC_BRANCH" + + - name: Create PR to merge sync branch into beta + if: steps.sync_branch.outputs.skipped != 'true' + id: sync_pr + env: + GH_TOKEN: ${{ github.token }} + NEW_VERSION: ${{ needs.bump.outputs.new_version }} + NEXT_BETA_VERSION: ${{ steps.sync_branch.outputs.beta_version }} + SYNC_BRANCH: ${{ steps.sync_branch.outputs.name }} + shell: bash + run: | + set -euo pipefail PR_URL=$(gh pr create \ --base beta \ - --head main \ + --head "$SYNC_BRANCH" \ --title "chore: sync main (v${NEW_VERSION}) into beta" \ - --body "Automated sync of version bump from main into beta.") + --body "Automated sync of main back into beta after release v${NEW_VERSION}, including beta version set to ${NEXT_BETA_VERSION}.") 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' + - name: Merge sync PR + if: steps.sync_branch.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) + + # Best effort: auto-merge if repository settings allow it. + gh pr merge "$PR_NUMBER" --merge --auto --delete-branch || true + + # Retry direct merge for up to 2 minutes while checks settle. 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)" + + if gh pr merge "$PR_NUMBER" --merge --delete-branch >/dev/null 2>&1; then + echo "Sync PR merged successfully." + exit 0 + fi + + echo "Waiting for sync PR to become mergeable... (state: $STATE)" sleep 5 done - echo "Sync PR did not merge in time. Attempting direct merge..." - gh pr merge "$PR_NUMBER" --merge + + echo "Sync PR did not merge in time." + gh pr view "$PR_NUMBER" --json state,mergeStateStatus,isDraft -q '{state: .state, mergeStateStatus: .mergeStateStatus, isDraft: .isDraft}' + exit 1 publish_docker: name: Publish Docker image From 4db9bb1ce3ad910946e93f502fb28850a88af68a Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Wed, 11 Feb 2026 01:49:22 +0000 Subject: [PATCH 2/3] chore: bump version to 9.4.4 --- MCPForUnity/package.json | 2 +- Server/README.md | 2 +- Server/pyproject.toml | 2 +- manifest.json | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/MCPForUnity/package.json b/MCPForUnity/package.json index b149e29d1..13aee608c 100644 --- a/MCPForUnity/package.json +++ b/MCPForUnity/package.json @@ -1,6 +1,6 @@ { "name": "com.coplaydev.unity-mcp", - "version": "9.4.3-beta.2", + "version": "9.4.4", "displayName": "MCP for Unity", "description": "A bridge that connects AI assistants to Unity via the MCP (Model Context Protocol). Allows AI clients like Claude Code, Cursor, and VSCode to directly control your Unity Editor for enhanced development workflows.\n\nFeatures automated setup wizard, cross-platform support, and seamless integration with popular AI development tools.\n\nJoin Our Discord: https://discord.gg/y4p8KfzrN4", "unity": "2021.3", diff --git a/Server/README.md b/Server/README.md index 9d6bf9316..d66c92204 100644 --- a/Server/README.md +++ b/Server/README.md @@ -69,7 +69,7 @@ Use this to run the latest released version from the repository. Change the vers "command": "uvx", "args": [ "--from", - "git+https://github.com/CoplayDev/unity-mcp@v9.4.2#subdirectory=Server", + "git+https://github.com/CoplayDev/unity-mcp@v9.4.4#subdirectory=Server", "mcp-for-unity", "--transport", "stdio" diff --git a/Server/pyproject.toml b/Server/pyproject.toml index c4ecab1d2..4ed4fb57e 100644 --- a/Server/pyproject.toml +++ b/Server/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "mcpforunityserver" -version = "9.4.2" +version = "9.4.4" description = "MCP for Unity Server: A Unity package for Unity Editor integration via the Model Context Protocol (MCP)." readme = "README.md" license = "MIT" diff --git a/manifest.json b/manifest.json index 59d1d34ba..8f86bed69 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": "0.3", "name": "Unity MCP", - "version": "9.4.2", + "version": "9.4.4", "description": "AI-powered Unity Editor automation via MCP - manage GameObjects, scripts, materials, scenes, prefabs, VFX, and run tests", "author": { "name": "Coplay", From be1fc59efa16ac037accc180587ff722fd4d09b5 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Wed, 11 Feb 2026 01:51:51 +0000 Subject: [PATCH 3/3] chore: set beta version to 9.4.5-beta.1 after release v9.4.4 --- MCPForUnity/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MCPForUnity/package.json b/MCPForUnity/package.json index 13aee608c..75275ccd1 100644 --- a/MCPForUnity/package.json +++ b/MCPForUnity/package.json @@ -1,6 +1,6 @@ { "name": "com.coplaydev.unity-mcp", - "version": "9.4.4", + "version": "9.4.5-beta.1", "displayName": "MCP for Unity", "description": "A bridge that connects AI assistants to Unity via the MCP (Model Context Protocol). Allows AI clients like Claude Code, Cursor, and VSCode to directly control your Unity Editor for enhanced development workflows.\n\nFeatures automated setup wizard, cross-platform support, and seamless integration with popular AI development tools.\n\nJoin Our Discord: https://discord.gg/y4p8KfzrN4", "unity": "2021.3",