@@ -128,6 +128,18 @@ jobs:
128128 fi
129129 echo "version=${VERSION}" >> $GITHUB_OUTPUT
130130
131+ # Version format validation (skip for 'latest')
132+ if [ "${VERSION}" != "latest" ]; then
133+ # Version must start with a digit and contain only letters, numbers, dots, hyphens, or underscores
134+ if ! [[ "${VERSION}" =~ ^[0-9][0-9A-Za-z._-]*$ ]]; then
135+ echo "::error::Invalid version format '${VERSION}'"
136+ echo "Version must start with a digit and only contain letters, numbers, dots, hyphens, or underscores."
137+ echo "Example: 1.2.3, 1.2.3-beta.1"
138+ exit 1
139+ fi
140+ echo "✓ Version format validated: ${VERSION}"
141+ fi
142+
131143 # Determine platform - default to 'all' if not specified
132144 if [ -z "${{ inputs.platform }}" ]; then
133145 # When platform is not specified or empty, default to 'all'
@@ -216,7 +228,7 @@ jobs:
216228 if : steps.config.outputs.version != 'latest'
217229 run : |
218230 VERSION="${{ steps.config.outputs.version }}"
219- # Remove v prefix for Docker image tags
231+ # Remove v prefix for Docker image tags (for backward compatibility with tag push trigger)
220232 CLEAN_VERSION="${VERSION#v}"
221233 PLATFORM="${{ steps.config.outputs.platform }}"
222234 IMAGE_NAME="hagicode"
@@ -271,7 +283,7 @@ jobs:
271283 if : steps.config.outputs.version != 'latest' && (inputs.dry_run != 'true')
272284 run : |
273285 VERSION="${{ steps.config.outputs.version }}"
274- # Remove v prefix for tag operations
286+ # Remove v prefix for tag operations (for backward compatibility with tag push trigger)
275287 CLEAN_VERSION="${VERSION#v}"
276288 IMAGE_NAME="hagicode"
277289 REGISTRY="${{ secrets.AZURE_ACR_REGISTRY }}"
@@ -310,8 +322,66 @@ jobs:
310322
311323 echo "Verifying images in registry..."
312324
313- docker manifest inspect ${REGISTRY}/${IMAGE_NAME}:${VERSION} || echo "Warning: Failed to verify version tag"
314- docker manifest inspect ${REGISTRY}/${IMAGE_NAME}:latest || echo "Warning: Failed to verify latest tag"
325+ # Parse version parts for tag verification
326+ MAJOR=$(echo ${VERSION} | cut -d. -f1)
327+ MINOR=$(echo ${VERSION} | cut -d. -f2)
328+
329+ # Multi-architecture verification requirements:
330+ # - All published images MUST contain both linux/amd64 and linux/arm64 architectures
331+ # - Verification is done for each tag: version, major.minor, major, and latest (if stable)
332+ # - If either architecture is missing, the workflow fails with a clear error message
333+ # - This ensures users on both AMD64 and ARM64 platforms can pull compatible images
334+
335+ # Verify image architectures for each tag
336+ for TAG in "${VERSION}" "${MAJOR}.${MINOR}" "${MAJOR}"; do
337+ echo "Verifying tag: ${TAG}"
338+
339+ # Extract architecture list from manifest using jq
340+ # jq is required for parsing the JSON output of docker manifest inspect
341+ ARCHITECTURES=$(docker manifest inspect ${REGISTRY}/${IMAGE_NAME}:${TAG} 2>/dev/null | jq -r '.[].Platform[].architecture' 2>/dev/null || echo "")
342+
343+ if [ -z "${ARCHITECTURES}" ]; then
344+ echo "::error::Failed to inspect manifest for tag ${TAG}"
345+ exit 1
346+ fi
347+
348+ echo "Found architectures for tag ${TAG}: ${ARCHITECTURES}"
349+
350+ # Check if amd64 is present (required for AMD64/x86_64 platforms)
351+ if ! echo "${ARCHITECTURES}" | grep -q "amd64"; then
352+ echo "::error::Tag ${TAG} does not contain amd64 architecture"
353+ exit 1
354+ fi
355+
356+ # Check if arm64 is present (required for ARM64/AArch64 platforms)
357+ if ! echo "${ARCHITECTURES}" | grep -q "arm64"; then
358+ echo "::error::Tag ${TAG} does not contain arm64 architecture"
359+ exit 1
360+ fi
361+
362+ echo "✓ Tag ${TAG} contains both amd64 and arm64 architectures"
363+ done
364+
365+ # Verify latest tag only for stable releases
366+ if [[ ! ${VERSION} =~ ^(rc|beta|alpha|preview|dev)[0-9]*$ ]] && [[ ! ${VERSION} =~ -(rc|beta|alpha|preview|dev) ]]; then
367+ echo "Verifying tag: latest"
368+ ARCHITECTURES=$(docker manifest inspect ${REGISTRY}/${IMAGE_NAME}:latest 2>/dev/null | jq -r '.[].Platform[].architecture' 2>/dev/null || echo "")
369+
370+ if [ -z "${ARCHITECTURES}" ]; then
371+ echo "::warning::Failed to inspect manifest for tag latest (may not exist)"
372+ else
373+ echo "Found architectures for tag latest: ${ARCHITECTURES}"
374+
375+ if ! echo "${ARCHITECTURES}" | grep -q "amd64" || ! echo "${ARCHITECTURES}" | grep -q "arm64"; then
376+ echo "::error::Tag latest does not contain both amd64 and arm64 architectures"
377+ exit 1
378+ fi
379+
380+ echo "✓ Tag latest contains both amd64 and arm64 architectures"
381+ fi
382+ fi
383+
384+ echo "All image tags verified successfully!"
315385
316386 - name : Post Login to Edge ACR
317387 if : steps.config.outputs.version != 'latest' && (inputs.dry_run != 'true')
@@ -334,7 +404,7 @@ jobs:
334404 if : steps.config.outputs.version != 'latest' && (inputs.dry_run != 'true')
335405 run : |
336406 VERSION="${{ steps.config.outputs.version }}"
337- # Remove v prefix for Docker image tags
407+ # Remove v prefix for Docker image tags (for backward compatibility with tag push trigger)
338408 CLEAN_VERSION="${VERSION#v}"
339409 IMAGE_NAME="hagicode"
340410 AZURE_REGISTRY="${{ secrets.AZURE_ACR_REGISTRY }}"
@@ -408,27 +478,45 @@ jobs:
408478
409479 echo "Verifying images in Aliyun ACR..."
410480
411- # Verify base image
412- echo "Checking base image..."
413- docker manifest inspect ${ALIYUN_IMAGE_NAME}:base || echo "Warning: Failed to verify base tag"
481+ # Multi-architecture verification for Aliyun ACR:
482+ # - Same requirements as Azure ACR: both amd64 and arm64 must be present
483+ # - Verified for all pushed tags: version, major.minor, major, and latest (if stable)
484+ # - Ensures Aliyun ACR images are also multi-arch compatible
414485
415- # Verify version tag
416- echo "Checking version tag..."
417- docker manifest inspect ${ALIYUN_IMAGE_NAME}:${CLEAN_VERSION} || echo "Warning: Failed to verify version tag"
486+ # Define tags to verify
487+ TAGS=("${CLEAN_VERSION}" "${MAJOR}.${MINOR}" "${MAJOR}")
488+ if [ "$IS_STABLE" = "true" ]; then
489+ TAGS+=("latest")
490+ fi
418491
419- # Verify major.minor tag
420- echo "Checking major.minor tag..."
421- docker manifest inspect ${ALIYUN_IMAGE_NAME}:${MAJOR}.${MINOR} || echo "Warning: Failed to verify major.minor tag"
492+ # Verify each tag's architectures
493+ for TAG in "${TAGS[@]}"; do
494+ echo "Verifying tag: ${TAG} "
422495
423- # Verify major tag
424- echo "Checking major tag..."
425- docker manifest inspect ${ALIYUN_IMAGE_NAME}:${MAJOR} || echo "Warning: Failed to verify major tag"
496+ # Extract architecture list from manifest using jq
497+ ARCHITECTURES=$(docker manifest inspect ${ALIYUN_IMAGE_NAME}:${TAG} 2>/dev/null | jq -r '.[].Platform[].architecture' 2>/dev/null || echo "")
426498
427- # Verify latest tag (only for stable releases)
428- if [ "$IS_STABLE" = "true" ]; then
429- echo "Checking latest tag..."
430- docker manifest inspect ${ALIYUN_IMAGE_NAME}:latest || echo "Warning: Failed to verify latest tag"
431- fi
499+ if [ -z "${ARCHITECTURES}" ]; then
500+ echo "::error::Failed to inspect manifest for tag ${TAG} in Aliyun ACR"
501+ exit 1
502+ fi
503+
504+ echo "Found architectures for tag ${TAG}: ${ARCHITECTURES}"
505+
506+ # Check if amd64 is present (required for AMD64/x86_64 platforms)
507+ if ! echo "${ARCHITECTURES}" | grep -q "amd64"; then
508+ echo "::error::Tag ${TAG} in Aliyun ACR does not contain amd64 architecture"
509+ exit 1
510+ fi
511+
512+ # Check if arm64 is present (required for ARM64/AArch64 platforms)
513+ if ! echo "${ARCHITECTURES}" | grep -q "arm64"; then
514+ echo "::error::Tag ${TAG} in Aliyun ACR does not contain arm64 architecture"
515+ exit 1
516+ fi
517+
518+ echo "✓ Tag ${TAG} contains both amd64 and arm64 architectures"
519+ done
432520
433521 echo "Aliyun ACR image verification completed!"
434522
0 commit comments