Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ Unity-MCP/
│ ├── Assets/root/ # Plugin source code
│ ├── ProjectSettings/ # Unity project settings
│ └── Packages/ # Unity packages
├── AssetStore-Installer/ # Unity package installer
├── Installer/ # Unity package installer
└── .github/workflows/ # CI/CD pipelines
```

Expand Down
105 changes: 105 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
name: deploy

on:
release:
types: [released]

workflow_dispatch:
inputs:
version:
description: 'Deploy to NuGet and Docker Hub version (e.g., 1.0.0).'
required: true
type: string

workflow_call:
inputs:
version: { required: true, type: string }
secrets:
NUGET_API_KEY: { required: true }
DOCKER_USERNAME: { required: true }
DOCKER_PASSWORD: { required: true }

jobs:
deploy-mcp-server-to-nuget:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Extract version from tag
id: version
run: |
version=${{ inputs.version || github.event.release.tag_name }}
echo "version=$version" >> $GITHUB_OUTPUT
echo "Extracted version: $version"

- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: '9.0.x'

- name: Restore dependencies
run: |
cd Unity-MCP-Server
dotnet restore

- name: Build
run: |
cd Unity-MCP-Server
dotnet build --no-restore --configuration Release

- name: Test
run: |
cd Unity-MCP-Server
dotnet test --no-build --verbosity normal --configuration Release

- name: Pack NuGet package
run: |
cd Unity-MCP-Server
dotnet pack com.IvanMurzak.Unity.MCP.Server.csproj --no-build --configuration Release --output ./nupkg

- name: Deploy MCP Server NuGet package
run: |
cd Unity-MCP-Server
# Construct the expected .nupkg filename using the version
nupkg_file="./nupkg/com.IvanMurzak.Unity.MCP.Server.${{ steps.version.outputs.version }}.nupkg"
if [ ! -f "$nupkg_file" ]; then
echo "Error: Expected .nupkg file not found: $nupkg_file"
exit 1
fi
echo "Deploying NuGet package: $nupkg_file (version: ${{ steps.version.outputs.version }})"
dotnet nuget push "$nupkg_file" --api-key ${{ secrets.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json

deploy-docker-image:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Extract version from tag
id: version
run: |
version=${{ inputs.version || github.event.release.tag_name }}
echo "version=$version" >> $GITHUB_OUTPUT
echo "Extracted version: $version"

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Log in to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}

- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: ./Unity-MCP-Server
push: true
tags: |
ivanmurzakdev/unity-mcp-server:${{ steps.version.outputs.version }}
ivanmurzakdev/unity-mcp-server:latest
platforms: linux/amd64,linux/arm64
cache-from: type=gha
cache-to: type=gha,mode=max
4 changes: 4 additions & 0 deletions .github/workflows/deploy_server_executables.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# This workflow builds the MCP server executables for all platforms and uploads them as release assets.
# It is triggered when a new release is published manually.
# It is NOT triggered by CI/CD pipelines or other automated processes.

name: deploy-server-executables

on:
Expand Down
157 changes: 116 additions & 41 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ jobs:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
fetch-tags: true

- name: Get version from package.json
Expand All @@ -34,9 +35,33 @@ jobs:
with:
tag: ${{ steps.get_version.outputs.current-version }}

# test-unity-mcp-common:
# runs-on: ubuntu-latest
# needs: check-version-tag
# steps:
# - name: Checkout repository
# uses: actions/checkout@v4

# - name: Setup .NET
# uses: actions/setup-dotnet@v4
# with:
# dotnet-version: '9.0.x'

# - name: Restore dependencies
# working-directory: Unity-MCP-Plugin/Assets/root/Unity-MCP-Common
# run: dotnet restore

# - name: Build
# working-directory: Unity-MCP-Plugin/Assets/root/Unity-MCP-Common
# run: dotnet build --no-restore --configuration Release

# - name: Test
# working-directory: Unity-MCP-Plugin/Assets/root/Unity-MCP-Common
# run: dotnet test --no-build --verbosity normal --configuration Release

build-unity-installer:
runs-on: ubuntu-latest
needs: check-version-tag
needs: [check-version-tag]
if: needs.check-version-tag.outputs.tag_exists == 'false'
steps:
- name: Checkout repository
Expand All @@ -45,7 +70,7 @@ jobs:
- name: Cache Unity Library
uses: actions/cache@v4
with:
path: ./AssetStore-Installer/Library
path: ./Installer/Library
key: Library-Unity-Installer

- name: Test Unity Installer (EditMode)
Expand All @@ -55,7 +80,7 @@ jobs:
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
with:
projectPath: ./AssetStore-Installer
projectPath: ./Installer
unityVersion: 2022.3.61f1
customImage: 'unityci/editor:ubuntu-2022.3.61f1-base-3'
testMode: editmode
Expand All @@ -67,8 +92,8 @@ jobs:
- name: Clean Unity artifacts and reset git state
run: |
# Force remove Unity generated files with restricted permissions
sudo rm -rf ./AssetStore-Installer/Logs/ || true
sudo rm -rf ./AssetStore-Installer/Temp/ || true
sudo rm -rf ./Installer/Logs/ || true
sudo rm -rf ./Installer/Temp/ || true
sudo rm -rf ./artifacts-installer-editmode/ || true

# Reset only tracked files to their committed state
Expand All @@ -82,19 +107,19 @@ jobs:
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
with:
projectPath: ./AssetStore-Installer
projectPath: ./Installer
unityVersion: 2022.3.61f1
customImage: 'unityci/editor:ubuntu-2022.3.61f1-base-3'
buildName: AI-Game-Dev-Installer
buildsPath: build
buildMethod: PackageExporter.ExportPackage
buildMethod: com.IvanMurzak.Unity.MCP.Installer.PackageExporter.ExportPackage
customParameters: -CI true -GITHUB_ACTIONS true

- name: Upload Unity Package as artifact
uses: actions/upload-artifact@v4
with:
name: unity-installer-package
path: ./AssetStore-Installer/build/AI-Game-Dev-Installer.unitypackage
path: ./Installer/build/AI-Game-Dev-Installer.unitypackage

build-and-zip-mcp-server:
runs-on: macos-latest
Expand Down Expand Up @@ -234,6 +259,7 @@ jobs:
outputs:
version: ${{ needs.check-version-tag.outputs.version }}
success: ${{ steps.rel_desc.outputs.success }}
release_notes: ${{ steps.rel_desc.outputs.release_body }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
Expand All @@ -250,12 +276,12 @@ jobs:
version=${{ needs.check-version-tag.outputs.version }}
prev_tag=${{ needs.check-version-tag.outputs.prev_tag }}
repo_url="https://github.com/${GITHUB_REPOSITORY}"
today=$(date +'%B %e, %Y')
today=$(date +'%B %e, %Y')

echo "repo_url: $repo_url"
echo "today: $today"

echo "# Unity-MCP Version $version" > release.md
echo "# AI Game Developer (Unity MCP) $version" > release.md
echo "**Released:** *$today*" >> release.md

echo "" >> release.md
Expand All @@ -269,24 +295,25 @@ jobs:
echo "" >> release.md
echo "---" >> release.md
echo "" >> release.md

echo "## Commit Summary (Newest → Oldest)" >> release.md
for sha in $(git log --pretty=format:'%H' $prev_tag..HEAD); do
username=$(gh api repos/${GITHUB_REPOSITORY}/commits/$sha --jq '.author.login // .commit.author.name')
message=$(git log -1 --pretty=format:'%s' $sha)
short_sha=$(git log -1 --pretty=format:'%h' $sha)
echo "- [\`$short_sha\`]($repo_url/commit/$sha) — $message by @$username" >> release.md
done
fi

echo "## Commit Summary (Newest → Oldest)" >> release.md
for sha in $(git log --pretty=format:'%H' $prev_tag..HEAD); do
username=$(gh api repos/${GITHUB_REPOSITORY}/commits/$sha --jq '.author.login // .commit.author.name')
message=$(git log -1 --pretty=format:'%s' $sha)
short_sha=$(git log -1 --pretty=format:'%h' $sha)
echo "- [\`$short_sha\`]($repo_url/commit/$sha) — $message by @$username" >> release.md
done
printf "release_body<<ENDOFRELEASEBODY\n%s\nENDOFRELEASEBODY\n" "$(cat release.md)" >> $GITHUB_ENV
printf "release_body<<ENDOFRELEASEBODY\n%s\nENDOFRELEASEBODY\n" "$(cat release.md)" >> $GITHUB_OUTPUT
echo "success=true" >> $GITHUB_OUTPUT

- name: Create Tag and Release
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ needs.check-version-tag.outputs.version }}
name: ${{ needs.check-version-tag.outputs.version }}
body: ${{ env.release_body }}
body: ${{ steps.rel_desc.outputs.release_body }}
draft: false
prerelease: false
env:
Expand All @@ -295,6 +322,7 @@ jobs:
publish-unity-installer:
runs-on: ubuntu-latest
needs: release-unity-plugin
if: needs.release-unity-plugin.outputs.success == 'true'
steps:
- name: Download Unity Package artifact
uses: actions/download-artifact@v4
Expand All @@ -313,6 +341,7 @@ jobs:
publish-mcp-server:
runs-on: macos-latest
needs: release-unity-plugin
if: needs.release-unity-plugin.outputs.success == 'true'
steps:
- name: Checkout repository
uses: actions/checkout@v4
Expand All @@ -331,38 +360,84 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

publish-docker-image:
publish_discord:
runs-on: ubuntu-latest
needs: release-unity-plugin
if: needs.release-unity-plugin.outputs.success == 'true'
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Send Release Notes to Discord
env:
DISCORD_WEBHOOK: ${{ secrets.DISCORD_RELEASE_NOTES_WEBHOOK }}
run: |
# Get the release notes from the previous job
release_notes='${{ needs.release-unity-plugin.outputs.release_notes }}'
version='${{ needs.release-unity-plugin.outputs.version }}'

# Convert usernames to GitHub profile links
# Convert "by @username" to "by [@username](https://github.com/username)"
release_notes_with_links=$(echo "$release_notes" | sed -E 's/by @([A-Za-z0-9_-]+)/by [@\1](https:\/\/github.com\/\1)/g')

# Remove horizontal separators (---) and clean up extra blank lines
# 1. Remove lines containing only --- and whitespace
# 2. Remove lines that contain only whitespace
# 3. Compress multiple consecutive empty lines into one
release_notes_cleaned=$(echo "$release_notes_with_links" | \
sed '/^[[:space:]]*---[[:space:]]*$/d' | \
sed 's/^[[:space:]]*$//' | \
sed '/^$/N;/^\n$/d')

# Create the release URL
release_url="https://github.com/${{ github.repository }}/releases/tag/${version}"

# Append the release link to the notes
full_message="${release_notes_cleaned}"$'\n\n'"📦 **[View Full Release](${release_url})**"

# Discord has a 2000 character limit, so we'll truncate if needed
if [ ${#full_message} -gt 2000 ]; then
# Remove lines from the end until it fits, keeping the link
link_text=$'\n\n'"📦 **[View Full Release](${release_url})**"
max_length=$((2000 - ${#link_text}))

# Split into lines and rebuild until we exceed the limit
truncated_notes=""
while IFS= read -r line; do
test_notes="${truncated_notes}${line}"$'\n'
if [ ${#test_notes} -gt $max_length ]; then
break
fi
truncated_notes="$test_notes"
done <<< "$release_notes_cleaned"

# Add ellipsis on its own line if we truncated
full_message="${truncated_notes}"$'\n'"...${link_text}"
fi

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
# Create JSON payload for Discord webhook
# flags: 4 sets the SUPPRESS_EMBEDS flag to suppress embeds/link previews.
# See: https://discord.com/developers/docs/resources/channel#create-message
json_payload=$(jq -n \
--arg content "$full_message" \
'{content: $content, flags: 4}')

- name: Log in to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
# Send to Discord webhook
curl -X POST "$DISCORD_WEBHOOK" \
-H "Content-Type: application/json" \
-d "$json_payload"

- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: ./Unity-MCP-Server
push: true
tags: |
ivanmurzakdev/unity-mcp-server:${{ needs.release-unity-plugin.outputs.version }}
ivanmurzakdev/unity-mcp-server:latest
platforms: linux/amd64,linux/arm64
cache-from: type=gha
cache-to: type=gha,mode=max
echo "Release notes sent to Discord for version $version"

deploy:
needs: release-unity-plugin
if: needs.release-unity-plugin.outputs.success == 'true'
uses: ./.github/workflows/deploy.yml
with:
version: ${{ needs.release-unity-plugin.outputs.version }}
secrets: inherit

# Cleanup job to remove build artifacts after publishing
cleanup-artifacts:
runs-on: ubuntu-latest
needs: [publish-mcp-server, publish-unity-installer, publish-docker-image]
needs: [publish-mcp-server, publish-unity-installer, deploy]
if: always()
steps:
- name: Delete MCP Server artifacts
Expand Down
Loading