Skip to content

Commit ed20bcf

Browse files
committed
Refactor copilot-setup-steps.yml to streamline Docker setup and improve Unity initialization process
1 parent 93f186b commit ed20bcf

File tree

1 file changed

+89
-240
lines changed

1 file changed

+89
-240
lines changed
Lines changed: 89 additions & 240 deletions
Original file line numberDiff line numberDiff line change
@@ -1,117 +1,53 @@
1-
# Unity Editor + MCP Server for GitHub Copilot Coding Agent
2-
#
3-
# IMPORTANT: Both Unity and MCP Server run as GitHub Services so they persist
4-
# after the setup job completes, allowing Copilot to connect to them.
5-
#
6-
# Architecture:
7-
# ┌──────────────────────────────────────────────────────────────┐
8-
# │ GitHub Actions Runner │
9-
# │ │
10-
# │ ┌─────────────────┐ SignalR ┌───────────────────┐ │
11-
# │ │ Unity Editor │◄────────────────►│ Unity-MCP-Server │ │
12-
# │ │ (SERVICE) │ localhost │ (SERVICE) │ │
13-
# │ │ localhost:55123 │ │ localhost:55123 │ │
14-
# │ └─────────────────┘ └────────▲──────────┘ │
15-
# │ │ │
16-
# │ MCP Protocol │
17-
# │ │ │
18-
# │ ┌────────────────┴──────────┐ │
19-
# │ │ GitHub Copilot Agent │ │
20-
# │ └───────────────────────────┘ │
21-
# └──────────────────────────────────────────────────────────────┘
22-
#
23-
# How it works:
24-
# 1. Services start immediately (before steps run)
25-
# 2. Unity service waits (doesn't run Unity yet - no license)
26-
# 3. Steps run: checkout, activate license
27-
# 4. Copy license + project INTO Unity service via docker cp
28-
# 5. Start Unity inside the service via docker exec
29-
# 6. Both services persist after job completes for Copilot to use
30-
#
31-
# Prerequisites:
32-
# - UNITY_LICENSE: Contents of your .ulf license file
33-
# - UNITY_EMAIL: Your Unity account email
34-
# - UNITY_PASSWORD: Your Unity account password
35-
36-
name: Copilot Setup Steps
1+
name: "Copilot Setup Steps"
372

3+
# Automatically run the setup steps when they are changed to allow for easy validation, and
4+
# allow manual testing through the repository's "Actions" tab
385
on:
396
workflow_dispatch:
407
push:
418
paths:
42-
- ".github/workflows/copilot-setup-steps.yml"
9+
- .github/workflows/copilot-setup-steps.yml
4310
pull_request:
4411
paths:
45-
- ".github/workflows/copilot-setup-steps.yml"
12+
- .github/workflows/copilot-setup-steps.yml
4613

4714
jobs:
48-
# IMPORTANT: Job name MUST be exactly 'copilot-setup-steps'
15+
# The job MUST be called `copilot-setup-steps` or it will not be picked up by Copilot.
4916
copilot-setup-steps:
5017
runs-on: ubuntu-latest
51-
timeout-minutes: 59
18+
19+
# Set the permissions to the lowest permissions possible needed for your steps.
20+
# Copilot will be given its own token for its operations.
5221
permissions:
22+
# If you want to clone the repository as part of your setup steps, for example to install dependencies, you'll need the `contents: read` permission. If you don't clone the repository in your setup steps, Copilot will do this for you automatically after the steps complete.
5323
contents: read
5424

55-
# =========================================================================
56-
# SERVICE CONTAINERS - These persist after job completes!
57-
# =========================================================================
58-
services:
59-
# Unity Editor Service
60-
# Starts with 'tail' entrypoint to stay alive without running Unity
61-
# We'll start Unity later via docker exec after license is ready
62-
unity-editor:
63-
image: unityci/editor:ubuntu-2022.3.61f1-linux-il2cpp-3
64-
ports:
65-
- 55123:55123
66-
# Use 'bash' as entrypoint - keeps container waiting for input
67-
# The default ENTRYPOINT is ["bash", "-c"] which exits, but just "bash" waits
68-
options: >-
69-
--entrypoint /bin/bash
70-
-t
71-
72-
# Unity-MCP-Server Service
73-
# Bridges Copilot (MCP client) to Unity Editor
74-
mcp-server:
75-
image: ivanmurzakdev/unity-mcp-server:latest
76-
ports:
77-
- 55123:55123
78-
env:
79-
TRANSPORT: http
80-
# Connect to Unity service via localhost (both services on host network)
81-
SIGNALR_URL: http://localhost:55123/unityhub
82-
25+
# You can define any steps you want, and they will run before the agent starts.
26+
# If you do not check out your code, Copilot will do this for you.
8327
steps:
84-
# =========================================================================
85-
# SETUP
86-
# =========================================================================
87-
88-
- name: Checkout repository
89-
uses: actions/checkout@v4
28+
- name: Checkout code
29+
uses: actions/checkout@v5
9030

91-
- name: Find service containers
92-
id: containers
31+
- name: Remove existing Docker network and containers
9332
run: |
94-
echo "All running containers:"
95-
docker ps --format "table {{.ID}}\t{{.Image}}\t{{.Status}}"
96-
echo ""
33+
docker rm -f unity-editor || true
34+
docker rm -f unity-mcp-server || true
35+
docker network rm unity-mcp-network || true
9736
98-
# Find Unity service container (use grep for partial match on image name)
99-
UNITY_CONTAINER=$(docker ps --format "{{.ID}} {{.Image}}" | grep -i "unityci/editor" | awk '{print $1}' | head -1)
100-
if [ -z "$UNITY_CONTAINER" ]; then
101-
echo "WARNING: Unity container not found by image name, trying by port..."
102-
UNITY_CONTAINER=$(docker ps --format "{{.ID}} {{.Ports}}" | grep "55123" | awk '{print $1}' | head -1)
103-
fi
104-
echo "unity_container=$UNITY_CONTAINER" >> $GITHUB_OUTPUT
105-
echo "Unity container: $UNITY_CONTAINER"
37+
- name: Create Docker network
38+
run: docker network create unity-mcp-network
10639

107-
# Find MCP Server container
108-
MCP_CONTAINER=$(docker ps --format "{{.ID}} {{.Image}}" | grep -i "unity-mcp-server" | awk '{print $1}' | head -1)
109-
if [ -z "$MCP_CONTAINER" ]; then
110-
echo "WARNING: MCP container not found by image name, trying by port..."
111-
MCP_CONTAINER=$(docker ps --format "{{.ID}} {{.Ports}}" | grep "55123" | awk '{print $1}' | head -1)
112-
fi
113-
echo "mcp_container=$MCP_CONTAINER" >> $GITHUB_OUTPUT
114-
echo "MCP Server container: $MCP_CONTAINER"
40+
- name: Create Unity Editor container
41+
run: |
42+
docker create \
43+
-v ${{ github.workspace }}/Unity-MCP-Plugin:/project \
44+
--network unity-mcp-network \
45+
--name unity-editor \
46+
unityci/editor:ubuntu-2022.3.61f1-linux-il2cpp-3 \
47+
tail -f /dev/null
48+
49+
- name: Start Unity Editor container
50+
run: docker start unity-editor
11551

11652
- name: Activate Unity License
11753
uses: game-ci/unity-activate@v2
@@ -120,163 +56,76 @@ jobs:
12056
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
12157
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
12258

123-
# =========================================================================
124-
# INJECT FILES INTO UNITY SERVICE
125-
# =========================================================================
126-
127-
- name: Copy license to Unity service
59+
- name: Start Unity Editor and execute startup method
12860
run: |
129-
UNITY_CONTAINER="${{ steps.containers.outputs.unity_container }}"
130-
131-
if [ -z "$UNITY_CONTAINER" ]; then
132-
echo "ERROR: Unity container not found!"
133-
exit 1
134-
fi
135-
136-
echo "Copying license to Unity service container..."
137-
138-
# Create license directory in container
139-
docker exec "$UNITY_CONTAINER" mkdir -p /root/.local/share/unity3d/Unity
140-
141-
# Copy GameCI activated license
142-
if [ -d ~/.local/share/unity3d/Unity ]; then
143-
docker cp ~/.local/share/unity3d/Unity/. "$UNITY_CONTAINER":/root/.local/share/unity3d/Unity/
144-
echo "Copied activated license from GameCI"
145-
fi
146-
147-
# Also write the secret directly as backup
148-
echo '${{ secrets.UNITY_LICENSE }}' | docker exec -i "$UNITY_CONTAINER" tee /root/.local/share/unity3d/Unity/Unity_lic.ulf > /dev/null
149-
150-
echo "License files in container:"
151-
docker exec "$UNITY_CONTAINER" ls -la /root/.local/share/unity3d/Unity/
152-
153-
- name: Copy project to Unity service
154-
run: |
155-
UNITY_CONTAINER="${{ steps.containers.outputs.unity_container }}"
156-
157-
echo "Copying Unity project to service container..."
158-
159-
# Create project directory
160-
docker exec "$UNITY_CONTAINER" mkdir -p /project
161-
162-
# Copy the entire Unity project
163-
docker cp "${{ github.workspace }}/UnityProject/." "$UNITY_CONTAINER":/project/
164-
165-
echo "Project copied. Contents:"
166-
docker exec "$UNITY_CONTAINER" ls -la /project/
167-
168-
# =========================================================================
169-
# START UNITY IN THE SERVICE
170-
# =========================================================================
171-
172-
- name: Start Unity Editor in service
173-
run: |
174-
UNITY_CONTAINER="${{ steps.containers.outputs.unity_container }}"
175-
176-
echo "Starting Unity Editor inside service container..."
177-
178-
# Start Unity in background inside the service container
179-
docker exec -d "$UNITY_CONTAINER" /bin/bash -c "
180-
unity-editor \
181-
-batchmode \
182-
-nographics \
183-
-logFile /tmp/unity.log \
184-
-projectPath /project \
185-
-executeMethod Editor.Startup.Init \
186-
2>&1 &
187-
188-
# Keep this exec alive to monitor
189-
tail -f /tmp/unity.log 2>/dev/null || sleep infinity
190-
"
191-
192-
echo "Unity Editor starting in service container..."
193-
194-
- name: Wait for Unity initialization
195-
run: |
196-
UNITY_CONTAINER="${{ steps.containers.outputs.unity_container }}"
197-
198-
echo "Waiting for Unity to initialize..."
199-
echo "This may take 2-5 minutes for first-time project import..."
200-
201-
timeout=300
61+
echo "Starting Unity Editor with startup method execution..."
62+
63+
# Start Unity with executeMethod (without -quit to keep it running)
64+
docker exec unity-editor bash -c "unity-editor \
65+
-projectPath /project \
66+
-batchmode \
67+
-nographics \
68+
-username ${{ secrets.UNITY_EMAIL }} \
69+
-password ${{ secrets.UNITY_PASSWORD }} \
70+
-executeMethod com.IvanMurzak.Unity.MCP.Editor.Startup.Init \
71+
-logFile /dev/stdout 2>&1"
72+
73+
echo "Unity Editor started, streaming logs until method completes..."
74+
echo "================================================"
75+
76+
# Follow logs and look for completion indicators
77+
timeout=600 # 10 minutes timeout for method execution
20278
elapsed=0
79+
last_log_count=0
20380
20481
while [ $elapsed -lt $timeout ]; do
205-
# Check for ready marker
206-
if docker exec "$UNITY_CONTAINER" grep -q "Unity-MCP-Ready" /tmp/unity.log 2>/dev/null; then
207-
echo ""
208-
echo "Unity initialized successfully!"
209-
exit 0
82+
# Get current logs
83+
current_logs=$(docker logs unity-editor 2>&1)
84+
85+
# Print new logs only
86+
current_log_count=$(echo "$current_logs" | wc -l)
87+
if [ $current_log_count -gt $last_log_count ]; then
88+
echo "$current_logs" | tail -n +$((last_log_count + 1))
89+
last_log_count=$current_log_count
21090
fi
21191
212-
# Check for license errors
213-
if docker exec "$UNITY_CONTAINER" grep -q "No valid Unity Editor license" /tmp/unity.log 2>/dev/null; then
214-
echo "ERROR: License activation failed!"
215-
docker exec "$UNITY_CONTAINER" tail -50 /tmp/unity.log
216-
exit 1
217-
fi
92+
# Check for method completion indicators
93+
if echo "$current_logs" | grep -q "com.IvanMurzak.Unity.MCP.Editor.Startup.Init\|Refreshing native plugins\|Initialize mono"; then
94+
# Wait a bit more to capture any final logs
95+
sleep 5
21896
219-
# Check Unity process
220-
if docker exec "$UNITY_CONTAINER" pgrep -f "Unity" > /dev/null 2>&1; then
221-
: # Unity is running, continue waiting
222-
else
223-
# Check if Unity exited with error
224-
if docker exec "$UNITY_CONTAINER" test -f /tmp/unity.log 2>/dev/null; then
225-
if docker exec "$UNITY_CONTAINER" grep -q "Fatal Error" /tmp/unity.log 2>/dev/null; then
226-
echo "ERROR: Unity crashed!"
227-
docker exec "$UNITY_CONTAINER" tail -50 /tmp/unity.log
228-
exit 1
229-
fi
97+
# Print final logs
98+
final_logs=$(docker logs unity-editor 2>&1)
99+
final_count=$(echo "$final_logs" | wc -l)
100+
if [ $final_count -gt $last_log_count ]; then
101+
echo "$final_logs" | tail -n +$((last_log_count + 1))
230102
fi
103+
104+
echo "================================================"
105+
echo "Unity startup method execution detected, detaching from logs"
106+
break
231107
fi
232108
233-
sleep 10
234-
elapsed=$((elapsed + 10))
235-
printf "Waiting... %d/%ds " "$elapsed" "$timeout"
236-
docker exec "$UNITY_CONTAINER" tail -1 /tmp/unity.log 2>/dev/null | head -c 60 || true
237-
echo ""
109+
sleep 2
110+
elapsed=$((elapsed + 2))
238111
done
239112
240-
echo ""
241-
echo "Timeout reached. Final logs:"
242-
docker exec "$UNITY_CONTAINER" tail -30 /tmp/unity.log 2>/dev/null || true
113+
if [ $elapsed -ge $timeout ]; then
114+
echo "================================================"
115+
echo "Warning: Timeout waiting for method completion, but continuing..."
116+
echo "Last 20 lines of logs:"
117+
docker logs unity-editor 2>&1 | tail -n 20
118+
fi
243119
244-
# =========================================================================
245-
# VERIFICATION
246-
# =========================================================================
120+
echo "Unity Editor is now running in background"
247121
248-
- name: Verify setup
122+
- name: Install Unity-MCP-Server docker image
249123
run: |
250-
UNITY_CONTAINER="${{ steps.containers.outputs.unity_container }}"
251-
MCP_CONTAINER="${{ steps.containers.outputs.mcp_container }}"
252-
253-
echo "=========================================="
254-
echo " COPILOT ENVIRONMENT READY"
255-
echo "=========================================="
256-
echo ""
257-
258-
echo "SERVICE CONTAINERS (persist after job):"
259-
docker ps --format " {{.ID}} | {{.Image}} | {{.Status}}" | head -5
260-
261-
echo ""
262-
echo "UNITY SERVICE:"
263-
echo " Container: $UNITY_CONTAINER"
264-
docker exec "$UNITY_CONTAINER" pgrep -a Unity 2>/dev/null | head -1 | sed 's/^/ Process: /' || echo " Process: Starting..."
265-
nc -zv localhost 55123 2>&1 | grep -q succeeded && echo " Port 55123: OPEN" || echo " Port 55123: Closed"
266-
267-
echo ""
268-
echo "MCP SERVER SERVICE:"
269-
echo " Container: $MCP_CONTAINER"
270-
nc -zv localhost 55123 2>&1 | grep -q succeeded && echo " Port 55123: OPEN" || echo " Port 55123: Closed"
271-
272-
echo ""
273-
echo "UNITY LOGS (last 15 lines):"
274-
docker exec "$UNITY_CONTAINER" tail -15 /tmp/unity.log 2>/dev/null | sed 's/^/ /' || echo " No logs yet"
275-
276-
echo ""
277-
echo "=========================================="
278-
echo "Both services will persist for Copilot!"
279-
echo ""
280-
echo "Copilot MCP endpoint: http://localhost:55123"
281-
echo "Unity health check: http://localhost:55123"
282-
echo "=========================================="
124+
docker run -d \
125+
-p 8080:8080 \
126+
-e UNITY_MCP_PORT=8080 \
127+
-e UNITY_MCP_CLIENT_TRANSPORT=http \
128+
-e UNITY_MCP_PLUGIN_TIMEOUT=120000 \
129+
--network unity-mcp-network \
130+
--name unity-mcp-server \
131+
ivanmurzakdev/unity-mcp-server:latest

0 commit comments

Comments
 (0)