5656 needs : [check-permissions]
5757 outputs :
5858 agents : ${{ steps.get-agents.outputs.agents }}
59+ all_agents : ${{ steps.get-agents.outputs.all_agents }}
5960 has_agents : ${{ steps.get-agents.outputs.has_agents }}
6061 steps :
6162 - name : Checkout repository
7273 all_agents=$(find examples/tutorials -name "manifest.yaml" -exec dirname {} \; | sort)
7374 agents_to_build=()
7475
76+ # Output all agents for deprecation check
77+ all_agents_json=$(printf '%s\n' $all_agents | jq -R -s -c 'split("\n") | map(select(length > 0))')
78+ echo "all_agents=$all_agents_json" >> $GITHUB_OUTPUT
79+
7580 if [ "$REBUILD_ALL" = "true" ]; then
7681 echo "Rebuild all agents requested"
7782 agents_to_build=($(echo "$all_agents"))
@@ -163,15 +168,13 @@ jobs:
163168 echo "Agent name set to $AGENT_NAME"
164169
165170 - name : Login to GitHub Container Registry
166- # Only login if we're going to push (main branch or rebuild_all)
167- if : ${{ github.event_name == 'push' || inputs.rebuild_all }}
168171 uses : docker/login-action@v3
169172 with :
170173 registry : ghcr.io
171174 username : ${{ github.actor }}
172175 password : ${{ secrets.GITHUB_TOKEN }}
173176
174- - name : Build and Conditionally Push Agent Image
177+ - name : Build Agent Image
175178 env :
176179 REGISTRY : ghcr.io
177180 run : |
@@ -182,20 +185,174 @@ jobs:
182185 if [ "${{ github.event_name }}" = "push" ] || [ "${{ inputs.rebuild_all }}" = "true" ]; then
183186 SHOULD_PUSH=true
184187 VERSION_TAG="latest"
185- echo "🚀 Building and pushing agent : ${{ matrix.agent_path }}"
188+ echo "🚀 Building agent (will push after validation) : ${{ matrix.agent_path }}"
186189 else
187190 SHOULD_PUSH=false
188191 VERSION_TAG="${{ github.sha }}"
189- echo "🔍 Validating build for agent: ${{ matrix.agent_path }}"
192+ echo "🔍 Building agent for validation: ${{ matrix.agent_path }}"
193+ # Set full image name for validation step (local build)
194+ echo "FULL_IMAGE=${REGISTRY}/${REPOSITORY_NAME}:${VERSION_TAG}" >> $GITHUB_ENV
195+ # Skip image validation for PRs since Buildx doesn't load multi-platform images locally
196+ echo "SKIP_VALIDATION=true" >> $GITHUB_ENV
190197 fi
191198
192- # Build command - add --push only if we should push
199+ # Always build locally first (without push)
193200 BUILD_ARGS="--manifest ${{ matrix.agent_path }}/manifest.yaml --registry ${REGISTRY} --tag ${VERSION_TAG} --platforms linux/amd64,linux/arm64 --repository-name ${REPOSITORY_NAME}"
194201
195- if [ "$SHOULD_PUSH" = "true" ]; then
196- agentex agents build $BUILD_ARGS --push
197- echo "✅ Successfully built and pushed: ${REGISTRY}/${REPOSITORY_NAME}:${VERSION_TAG}"
202+ agentex agents build $BUILD_ARGS
203+ echo "✅ Successfully built: ${REGISTRY}/${REPOSITORY_NAME}:${VERSION_TAG}"
204+
205+ # Set environment variables for subsequent steps
206+ echo "FULL_IMAGE=${REGISTRY}/${REPOSITORY_NAME}:${VERSION_TAG}" >> $GITHUB_ENV
207+ echo "SHOULD_PUSH=${SHOULD_PUSH}" >> $GITHUB_ENV
208+
209+ - name : Validate agent image
210+ if : env.SKIP_VALIDATION != 'true'
211+ run : |
212+ set -e
213+
214+ FULL_IMAGE="${{ env.FULL_IMAGE }}"
215+ AGENT_PATH="${{ matrix.agent_path }}"
216+ AGENT_NAME="${{ env.AGENT_NAME }}"
217+
218+ echo "🔍 Validating agent image: $FULL_IMAGE"
219+
220+ # Determine ACP type from path
221+ if [[ "$AGENT_PATH" == *"10_async"* ]]; then
222+ ACP_TYPE="async"
198223 else
199- agentex agents build $BUILD_ARGS
200- echo "✅ Build validation successful for: ${{ matrix.agent_path }}"
224+ ACP_TYPE="sync"
201225 fi
226+
227+ # Common environment variables for validation
228+ ENV_VARS="-e ENVIRONMENT=development \
229+ -e AGENT_NAME=${AGENT_NAME} \
230+ -e ACP_URL=http://localhost:8000 \
231+ -e ACP_PORT=8000 \
232+ -e ACP_TYPE=${ACP_TYPE}"
233+
234+ # 1. Validate ACP entry point exists and is importable
235+ echo "📦 Checking ACP entry point..."
236+ docker run --rm $ENV_VARS "$FULL_IMAGE" python -c "from project.acp import acp; print('✅ ACP entry point validated')"
237+
238+ # 2. Check if tests/test_agent.py exists (required for integration tests)
239+ # Tests are located at /app/<agent_dir>/tests/test_agent.py
240+ echo "🧪 Checking for tests/test_agent.py..."
241+ TEST_FILE=$(docker run --rm "$FULL_IMAGE" find /app -name "test_agent.py" -path "*/tests/*" 2>/dev/null | head -1)
242+
243+ if [ -n "$TEST_FILE" ]; then
244+ echo "✅ Found test file at: $TEST_FILE"
245+ else
246+ echo "❌ No tests/test_agent.py found in image - this is required for all tutorial agents"
247+ echo " Please add a tests/test_agent.py file to your agent"
248+ exit 1
249+ fi
250+
251+ # 3. Validate container can start (may fail due to missing services, but should initialize)
252+ echo "🏥 Validating container starts..."
253+ CONTAINER_NAME="validate-agent-$$"
254+
255+ # Start container in background with required env vars
256+ docker run -d --name "$CONTAINER_NAME" \
257+ $ENV_VARS \
258+ -p 8000:8000 \
259+ "$FULL_IMAGE"
260+
261+ # Give it a few seconds to attempt startup
262+ sleep 5
263+
264+ # Check if container is still running (it may exit due to missing services, that's ok)
265+ # We just want to see that it attempted to start properly
266+ echo "📋 Container logs:"
267+ docker logs "$CONTAINER_NAME" 2>&1 || true
268+
269+ # Check for successful ACP initialization in logs
270+ if docker logs "$CONTAINER_NAME" 2>&1 | grep -q "instance created "; then
271+ echo "✅ Container initialized ACP successfully"
272+ else
273+ echo "⚠️ Could not verify ACP initialization from logs"
274+ fi
275+
276+ # Cleanup container
277+ docker stop "$CONTAINER_NAME" > /dev/null 2>&1 || true
278+ docker rm "$CONTAINER_NAME" > /dev/null 2>&1 || true
279+
280+ echo "✅ All validations passed for: $FULL_IMAGE"
281+
282+ - name : Push Agent Image
283+ if : env.SHOULD_PUSH == 'true'
284+ run : |
285+ FULL_IMAGE="${{ env.FULL_IMAGE }}"
286+ echo "🚀 Pushing validated image: $FULL_IMAGE"
287+ docker push "$FULL_IMAGE"
288+ echo "✅ Successfully pushed: $FULL_IMAGE"
289+
290+ deprecate-agents :
291+ name : " Deprecate Removed Agents"
292+ runs-on : ubuntu-latest
293+ needs : [find-agents]
294+ steps :
295+ - name : Find and delete deprecated agent packages
296+ env :
297+ GITHUB_TOKEN : ${{ secrets.PACKAGE_TOKEN }}
298+ run : |
299+ set -e
300+
301+ echo "🔍 Agents in repo (from find-agents):"
302+ # Convert JSON array of paths to package names
303+ # e.g., "examples/tutorials/00_sync/000_hello_acp" -> "00_sync-000_hello_acp"
304+ REPO_AGENTS=$(echo '${{ needs.find-agents.outputs.all_agents }}' | jq -r '.[]' | \
305+ sed 's|examples/tutorials/||' | \
306+ sed 's|/|-|g')
307+ echo "$REPO_AGENTS"
308+
309+ echo ""
310+ echo "🔍 Fetching packages from GitHub Container Registry..."
311+ PACKAGES=$(curl -s -H "Authorization: Bearer $GITHUB_TOKEN" \
312+ -H "Accept: application/vnd.github+json" \
313+ "https://api.github.com/orgs/scaleapi/packages?package_type=container&per_page=100")
314+
315+ # Check for API errors
316+ if echo "$PACKAGES" | jq -e '.message' > /dev/null 2>&1; then
317+ echo "❌ GitHub API error:"
318+ echo "$PACKAGES" | jq '.'
319+ exit 1
320+ fi
321+
322+ # Filter for tutorial-agents from this repo
323+ TUTORIAL_PACKAGES=$(echo "$PACKAGES" | \
324+ jq -r '.[] | select(.repository != null and .repository.name == "scale-agentex-python" and (.name | contains("tutorial-agents"))) | .name')
325+
326+ echo "Tutorial packages in registry:"
327+ echo "$TUTORIAL_PACKAGES"
328+
329+ echo ""
330+ echo "🔍 Checking for deprecated packages..."
331+ while IFS= read -r package_name; do
332+ [ -z "$package_name" ] && continue
333+
334+ # Extract agent name: scale-agentex-python/tutorial-agents/00_sync-000_hello_acp -> 00_sync-000_hello_acp
335+ agent_name=$(echo "$package_name" | sed 's|.*/tutorial-agents/||')
336+
337+ if ! echo "$REPO_AGENTS" | grep -q "^${agent_name}$"; then
338+ echo "🗑️ $agent_name - NOT in repo, deleting..."
339+ # URL encode the package name (replace / with %2F)
340+ encoded_package=$(echo "$package_name" | sed 's|/|%2F|g')
341+ response=$(curl -s -w "\n%{http_code}" -X DELETE \
342+ -H "Authorization: Bearer $GITHUB_TOKEN" \
343+ -H "Accept: application/vnd.github+json" \
344+ "https://api.github.com/orgs/scaleapi/packages/container/${encoded_package}")
345+
346+ http_code=$(echo "$response" | tail -n1)
347+ body=$(echo "$response" | sed '$d')
348+
349+ if [ "$http_code" = "204" ] || [ "$http_code" = "200" ]; then
350+ echo " ✅ Deleted: $package_name"
351+ else
352+ echo " ⚠️ Failed to delete $package_name (HTTP $http_code): $body"
353+ fi
354+ fi
355+ done <<< "$TUTORIAL_PACKAGES"
356+
357+ echo ""
358+ echo "✅ Deprecation check complete"
0 commit comments