> [build focal][build noble][build jammy] #90
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 |