Skip to content

Update README.md

Update README.md #87

name: Build and Push Kyon Docker Images
on:
push:
tags:
- 'v[0-9]+.[0-9]+.[0-9]+'
branches:
- master
- main
pull_request:
branches:
- master
- main
env:
DOCKER_REGISTRY: hhcmhub
TAG_NAME: ${{ github.ref_name }}
jobs:
detect-changes:
runs-on: ubuntu-latest
outputs:
ros1: ${{ steps.changes.outputs.ros1 == 'true' || steps.manual-check.outputs.ros1-manual == 'true' || steps.commit-message.outputs.rebuild-ros1 == 'true' }}
ros2: ${{ steps.changes.outputs.ros2 == 'true' || steps.manual-check.outputs.ros2-manual == 'true' || steps.commit-message.outputs.rebuild-ros2 == 'true' }}
steps:
- name: Checkout Repository
uses: actions/checkout@v4
with:
fetch-depth: 0
submodules: recursive
- name: Check commit message for rebuild triggers
id: commit-message
run: |
echo "=== Commit Message Check ==="
# Initialize outputs
REBUILD_ROS1=false
REBUILD_ROS2=false
# Get the commit message
# For push events, use the head commit message
# For pull requests, use the PR title or last commit message
if [[ "${{ github.event_name }}" == "pull_request" ]]; then
# Try to get the PR title first, then fall back to last commit
COMMIT_MSG="${{ github.event.pull_request.title }}"
if [[ -z "$COMMIT_MSG" ]]; then
COMMIT_MSG=$(git log --format=%B -n 1 HEAD)
fi
else
# For push events, get the commit message
COMMIT_MSG=$(git log --format=%B -n 1 ${{ github.sha }})
fi
echo "Commit message: $COMMIT_MSG"
# Convert to lowercase for case-insensitive matching
COMMIT_MSG_LOWER=$(echo "$COMMIT_MSG" | tr '[:upper:]' '[:lower:]')
# Check for rebuild triggers in commit message
if echo "$COMMIT_MSG_LOWER" | grep -q "rebuild ros1 ros2\|rebuild ros2 ros1\|rebuild both\|rebuild all"; then
echo "✅ Found trigger to rebuild both ROS1 and ROS2"
REBUILD_ROS1=true
REBUILD_ROS2=true
elif echo "$COMMIT_MSG_LOWER" | grep -q "rebuild ros1"; then
echo "✅ Found trigger to rebuild ROS1"
REBUILD_ROS1=true
elif echo "$COMMIT_MSG_LOWER" | grep -q "rebuild ros2"; then
echo "✅ Found trigger to rebuild ROS2"
REBUILD_ROS2=true
else
echo "ℹ️ No rebuild triggers found in commit message"
fi
# Output results
echo "rebuild-ros1=$REBUILD_ROS1" >> $GITHUB_OUTPUT
echo "rebuild-ros2=$REBUILD_ROS2" >> $GITHUB_OUTPUT
echo "🎯 Commit message triggers - ROS1: $REBUILD_ROS1, ROS2: $REBUILD_ROS2"
- name: Detect changed files
uses: dorny/paths-filter@v3
id: changes
with:
# For submodules, compare against a more reliable base
base: ${{ github.event_name == 'pull_request' && 'HEAD' || github.event.before }}
# Enable submodule tracking explicitly
list-files: shell
filters: |
ros1:
- 'docker-base/robot-template/_build/robot-focal-ros1/**'
ros2:
- 'docker-base/robot-template/_build/robot-noble-ros2/**'
- name: Manual submodule change detection
id: manual-check
run: |
echo "=== Manual Submodule Check ==="
# Initialize outputs
ROS1_CHANGED=false
ROS2_CHANGED=false
# Check if docker-base submodule was updated
if git diff --name-only ${{ github.event.before }}..${{ github.sha }} | grep -q "^docker-base$"; then
echo "✅ docker-base submodule was updated - checking internal changes"
# Get the submodule commit hashes
cd docker-base
# Get the previous submodule commit hash
cd ..
SUBMODULE_BEFORE=$(git rev-parse ${{ github.event.before }}:docker-base 2>/dev/null || echo "")
SUBMODULE_CURRENT=$(git rev-parse HEAD:docker-base)
echo "Submodule before: $SUBMODULE_BEFORE"
echo "Submodule current: $SUBMODULE_CURRENT"
if [[ -n "$SUBMODULE_BEFORE" ]] && [[ "$SUBMODULE_BEFORE" != "$SUBMODULE_CURRENT" ]]; then
cd docker-base
# Check what files changed in the submodule between commits
echo "Checking what changed in submodule..."
CHANGED_FILES=$(git diff --name-only $SUBMODULE_BEFORE..$SUBMODULE_CURRENT 2>/dev/null || echo "")
echo "Changed files in submodule:"
echo "$CHANGED_FILES"
# Check for ROS1 changes
if echo "$CHANGED_FILES" | grep -q "robot-template/_build/robot-focal-ros1/"; then
echo "✅ ROS1 files changed in submodule"
ROS1_CHANGED=true
fi
# Check for ROS2 changes
if echo "$CHANGED_FILES" | grep -q "robot-template/_build/robot-noble-ros2/"; then
echo "✅ ROS2 files changed in submodule"
ROS2_CHANGED=true
fi
cd ..
fi
else
echo "ℹ️ No docker-base submodule update detected"
fi
# Output results for use in job conditions
echo "ros1-manual=$ROS1_CHANGED" >> $GITHUB_OUTPUT
echo "ros2-manual=$ROS2_CHANGED" >> $GITHUB_OUTPUT
echo "🎯 Final manual detection - ROS1: $ROS1_CHANGED, ROS2: $ROS2_CHANGED"
- name: Debug change detection summary
run: |
echo "=== Change Detection Summary ==="
echo ""
echo "📋 Detection Results:"
echo " Path filter ROS1: ${{ steps.changes.outputs.ros1 }}"
echo " Path filter ROS2: ${{ steps.changes.outputs.ros2 }}"
echo " Manual check ROS1: ${{ steps.manual-check.outputs.ros1-manual }}"
echo " Manual check ROS2: ${{ steps.manual-check.outputs.ros2-manual }}"
echo " Commit message ROS1: ${{ steps.commit-message.outputs.rebuild-ros1 }}"
echo " Commit message ROS2: ${{ steps.commit-message.outputs.rebuild-ros2 }}"
echo ""
echo "📦 Final Build Decision:"
echo " Build ROS1: ${{ steps.changes.outputs.ros1 == 'true' || steps.manual-check.outputs.ros1-manual == 'true' || steps.commit-message.outputs.rebuild-ros1 == 'true' }}"
echo " Build ROS2: ${{ steps.changes.outputs.ros2 == 'true' || steps.manual-check.outputs.ros2-manual == 'true' || steps.commit-message.outputs.rebuild-ros2 == 'true' }}"
echo ""
# Show the commit message that was analyzed
echo "💬 Analyzed commit message:"
if [[ "${{ github.event_name }}" == "pull_request" ]]; then
echo " PR Title: ${{ github.event.pull_request.title }}"
fi
git log --format=" %B" -n 1 ${{ github.sha }}
build-ros1:
needs: detect-changes
# Build on tags or when ROS1 changes detected (including commit message triggers)
if: |
startsWith(github.ref, 'refs/tags/v') ||
needs.detect-changes.outputs.ros1 == 'true'
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v4
with:
submodules: recursive
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to Docker Hub
if: |
startsWith(github.ref, 'refs/tags/v') ||
github.ref == 'refs/heads/main' ||
github.ref == 'refs/heads/master'
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Setup .netrc for Docker build
run: |
cat > ~/.netrc << EOF
machine github.com
login ${{ secrets.GH_USERNAME }}
password ${{ secrets.GH_TOKEN }}
EOF
chmod 600 ~/.netrc
echo "✅ .netrc file created for Docker build authentication"
- name: Build & push ROS1 images
env:
TAGNAME: ${{ env.TAG_NAME }}
SHOULD_PUSH: ${{ startsWith(github.ref, 'refs/tags/') || github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master' }}
run: |
cd docker
export TAGNAME="${TAGNAME}"
if [[ "${SHOULD_PUSH}" == "true" ]]; then
./build-kyon.bash --ros1 --push
else
./build-kyon.bash --ros1
fi
build-ros2:
needs: detect-changes
# Build on tags or when ROS2 changes detected (including commit message triggers)
if: |
startsWith(github.ref, 'refs/tags/v') ||
needs.detect-changes.outputs.ros2 == 'true'
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v4
with:
submodules: recursive
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to Docker Hub
if: |
startsWith(github.ref, 'refs/tags/v') ||
github.ref == 'refs/heads/main' ||
github.ref == 'refs/heads/master'
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Setup .netrc for Docker build
run: |
cat > ~/.netrc << EOF
machine github.com
login ${{ secrets.GH_USERNAME }}
password ${{ secrets.GH_TOKEN }}
EOF
chmod 600 ~/.netrc
echo "✅ .netrc file created for Docker build authentication"
- name: Build & push ROS2 images
env:
TAGNAME: ${{ env.TAG_NAME }}
SHOULD_PUSH: ${{ startsWith(github.ref, 'refs/tags/') || github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master' }}
run: |
cd docker
export TAGNAME="${TAGNAME}"
if [[ "${SHOULD_PUSH}" == "true" ]]; then
./build-kyon.bash --ros2 --push
else
./build-kyon.bash --ros2
fi
summary:
needs: [detect-changes, build-ros1, build-ros2]
if: always()
runs-on: ubuntu-latest
steps:
- name: Build Summary
run: |
echo "## Build and Push Summary" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
# Display the trigger information
echo "### Trigger Information" >> $GITHUB_STEP_SUMMARY
echo "**Branch/Tag:** ${{ env.TAG_NAME }}" >> $GITHUB_STEP_SUMMARY
echo "**Event:** ${{ github.event_name }}" >> $GITHUB_STEP_SUMMARY
if [[ "${{ github.event_name }}" == "pull_request" ]]; then
echo "**PR:** #${{ github.event.pull_request.number }}" >> $GITHUB_STEP_SUMMARY
fi
echo "" >> $GITHUB_STEP_SUMMARY
# Track what actually happened
ROS1_BUILT=false
ROS2_BUILT=false
ANYTHING_BUILT=false
PUSH_ATTEMPTED=false
# Display detected changes
echo "### Changes Detected" >> $GITHUB_STEP_SUMMARY
if [[ "${{ needs.detect-changes.outputs.ros1 }}" == "true" ]]; then
echo "- ✅ ROS1 build triggered" >> $GITHUB_STEP_SUMMARY
else
echo "- ⚪ No ROS1 trigger" >> $GITHUB_STEP_SUMMARY
fi
if [[ "${{ needs.detect-changes.outputs.ros2 }}" == "true" ]]; then
echo "- ✅ ROS2 build triggered" >> $GITHUB_STEP_SUMMARY
else
echo "- ⚪ No ROS2 trigger" >> $GITHUB_STEP_SUMMARY
fi
echo "" >> $GITHUB_STEP_SUMMARY
# Build Results section
echo "### Build Results" >> $GITHUB_STEP_SUMMARY
# ROS1 Build Status
if [[ "${{ needs.build-ros1.result }}" == "success" ]]; then
echo "- ✅ **ROS1**: All images built successfully" >> $GITHUB_STEP_SUMMARY
ROS1_BUILT=true
ANYTHING_BUILT=true
elif [[ "${{ needs.build-ros1.result }}" == "failure" ]]; then
echo "- ❌ **ROS1**: Build failed (check logs)" >> $GITHUB_STEP_SUMMARY
elif [[ "${{ needs.build-ros1.result }}" == "skipped" ]]; then
# Check if it was skipped due to no changes or due to tag trigger
if [[ "${{ github.ref }}" == refs/tags/* ]]; then
echo "- ⚪ **ROS1**: Skipped (tag trigger but no ROS1 changes)" >> $GITHUB_STEP_SUMMARY
else
echo "- ⚪ **ROS1**: Skipped (no changes detected)" >> $GITHUB_STEP_SUMMARY
fi
fi
# ROS2 Build Status
if [[ "${{ needs.build-ros2.result }}" == "success" ]]; then
echo "- ✅ **ROS2**: All images built successfully" >> $GITHUB_STEP_SUMMARY
ROS2_BUILT=true
ANYTHING_BUILT=true
elif [[ "${{ needs.build-ros2.result }}" == "failure" ]]; then
echo "- ❌ **ROS2**: Build failed (check logs)" >> $GITHUB_STEP_SUMMARY
elif [[ "${{ needs.build-ros2.result }}" == "skipped" ]]; then
if [[ "${{ github.ref }}" == refs/tags/* ]]; then
echo "- ⚪ **ROS2**: Skipped (tag trigger but no ROS2 changes)" >> $GITHUB_STEP_SUMMARY
else
echo "- ⚪ **ROS2**: Skipped (no changes detected)" >> $GITHUB_STEP_SUMMARY
fi
fi
echo "" >> $GITHUB_STEP_SUMMARY
# Push Status section - only show if builds occurred
echo "### Registry Push Status" >> $GITHUB_STEP_SUMMARY
# Determine if push was attempted based on ref and build success
if [[ "$ANYTHING_BUILT" == "true" ]]; then
if [[ "${{ github.ref }}" == refs/tags/* ]] || \
[[ "${{ github.ref }}" == "refs/heads/main" ]] || \
[[ "${{ github.ref }}" == "refs/heads/master" ]]; then
PUSH_ATTEMPTED=true
fi
fi
# Display push status
if [[ "$PUSH_ATTEMPTED" == "true" ]]; then
echo "**Status:** Images pushed to registry" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "Images pushed:" >> $GITHUB_STEP_SUMMARY
if [[ "$ROS1_BUILT" == "true" ]]; then
echo "- ROS1 images → \`${{ env.DOCKER_REGISTRY }}/kyon-cetc-focal-ros1-*:${{ env.TAG_NAME }}\`" >> $GITHUB_STEP_SUMMARY
fi
if [[ "$ROS2_BUILT" == "true" ]]; then
echo "- ROS2 images → \`${{ env.DOCKER_REGISTRY }}/kyon-cetc-noble-ros2-*:${{ env.TAG_NAME }}\`" >> $GITHUB_STEP_SUMMARY
fi
elif [[ "$ANYTHING_BUILT" == "true" ]]; then
echo "**Status:** Images built locally only (not pushed)" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
if [[ "${{ github.event_name }}" == "pull_request" ]]; then
echo "_Note: Images are not pushed for pull requests_" >> $GITHUB_STEP_SUMMARY
else
echo "_Note: Images are only pushed from main/master branches or tags_" >> $GITHUB_STEP_SUMMARY
fi
else
echo "**Status:** No images built or pushed" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "_No changes detected that require rebuilding images_" >> $GITHUB_STEP_SUMMARY
fi
echo "" >> $GITHUB_STEP_SUMMARY
# Add helpful next steps based on the outcome
echo "### Next Steps" >> $GITHUB_STEP_SUMMARY
if [[ "$ANYTHING_BUILT" == "false" ]]; then
echo "No action needed - no relevant changes detected." >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "To trigger builds via commit message, use:" >> $GITHUB_STEP_SUMMARY
echo "- \`rebuild ros1\` - Build ROS1 images" >> $GITHUB_STEP_SUMMARY
echo "- \`rebuild ros2\` - Build ROS2 images" >> $GITHUB_STEP_SUMMARY
echo "- \`rebuild ros1 ros2\` or \`rebuild both\` - Build both" >> $GITHUB_STEP_SUMMARY
elif [[ "$PUSH_ATTEMPTED" == "true" ]]; then
echo "✅ Images have been pushed to the registry and are ready for use." >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "To use these images:" >> $GITHUB_STEP_SUMMARY
echo "\`\`\`bash" >> $GITHUB_STEP_SUMMARY
echo "docker pull ${{ env.DOCKER_REGISTRY }}/kyon-cetc-focal-ros1-base:${{ env.TAG_NAME }}" >> $GITHUB_STEP_SUMMARY
echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
else
echo "Images were built but not pushed. To use them:" >> $GITHUB_STEP_SUMMARY
echo "- Merge this PR to main/master to trigger automatic push" >> $GITHUB_STEP_SUMMARY
echo "- Or create a release tag to push images" >> $GITHUB_STEP_SUMMARY
fi