Skip to content

Commit a642814

Browse files
authored
Merge pull request #345 from KaiStarkk/feature/copilot-setup-steps
Add copilot-setup-steps.yml for GitHub Copilot coding agent
2 parents 06a7f09 + 2519c2e commit a642814

File tree

1 file changed

+282
-0
lines changed

1 file changed

+282
-0
lines changed
Lines changed: 282 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,282 @@
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:8090 │ │ localhost:8080 │ │
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
37+
38+
on:
39+
workflow_dispatch:
40+
push:
41+
paths:
42+
- '.github/workflows/copilot-setup-steps.yml'
43+
pull_request:
44+
paths:
45+
- '.github/workflows/copilot-setup-steps.yml'
46+
47+
jobs:
48+
# IMPORTANT: Job name MUST be exactly 'copilot-setup-steps'
49+
copilot-setup-steps:
50+
runs-on: ubuntu-latest
51+
timeout-minutes: 59
52+
permissions:
53+
contents: read
54+
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+
- 8090:8090
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+
- 8080:8080
78+
env:
79+
TRANSPORT: http
80+
# Connect to Unity service via localhost (both services on host network)
81+
SIGNALR_URL: http://localhost:8090/unityhub
82+
83+
steps:
84+
# =========================================================================
85+
# SETUP
86+
# =========================================================================
87+
88+
- name: Checkout repository
89+
uses: actions/checkout@v4
90+
91+
- name: Find service containers
92+
id: containers
93+
run: |
94+
echo "All running containers:"
95+
docker ps --format "table {{.ID}}\t{{.Image}}\t{{.Status}}"
96+
echo ""
97+
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 "8090" | awk '{print $1}' | head -1)
103+
fi
104+
echo "unity_container=$UNITY_CONTAINER" >> $GITHUB_OUTPUT
105+
echo "Unity container: $UNITY_CONTAINER"
106+
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 "8080" | awk '{print $1}' | head -1)
112+
fi
113+
echo "mcp_container=$MCP_CONTAINER" >> $GITHUB_OUTPUT
114+
echo "MCP Server container: $MCP_CONTAINER"
115+
116+
- name: Activate Unity License
117+
uses: game-ci/unity-activate@v2
118+
env:
119+
UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}
120+
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
121+
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
122+
123+
# =========================================================================
124+
# INJECT FILES INTO UNITY SERVICE
125+
# =========================================================================
126+
127+
- name: Copy license to Unity service
128+
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
202+
elapsed=0
203+
204+
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
210+
fi
211+
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
218+
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
230+
fi
231+
fi
232+
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 ""
238+
done
239+
240+
echo ""
241+
echo "Timeout reached. Final logs:"
242+
docker exec "$UNITY_CONTAINER" tail -30 /tmp/unity.log 2>/dev/null || true
243+
244+
# =========================================================================
245+
# VERIFICATION
246+
# =========================================================================
247+
248+
- name: Verify setup
249+
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 8090 2>&1 | grep -q succeeded && echo " Port 8090: OPEN" || echo " Port 8090: Closed"
266+
267+
echo ""
268+
echo "MCP SERVER SERVICE:"
269+
echo " Container: $MCP_CONTAINER"
270+
nc -zv localhost 8080 2>&1 | grep -q succeeded && echo " Port 8080: OPEN" || echo " Port 8080: 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:8080"
281+
echo "Unity health check: http://localhost:8090"
282+
echo "=========================================="

0 commit comments

Comments
 (0)