Build Openclaw Docker Images #747
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 Openclaw Docker Images | |
| on: | |
| schedule: | |
| # Check for new openclaw releases every hour | |
| - cron: '0 * * * *' | |
| workflow_dispatch: | |
| inputs: | |
| version: | |
| description: 'Openclaw version to build (e.g., 2026.1.29). Leave empty to build latest.' | |
| required: false | |
| type: string | |
| force_rebuild: | |
| description: 'Force rebuild even if image already exists' | |
| required: false | |
| type: boolean | |
| default: false | |
| skip_latest_tag: | |
| description: 'Skip tagging as latest (useful for building older versions)' | |
| required: false | |
| type: boolean | |
| default: false | |
| env: | |
| GITHUB_REGISTRY: ghcr.io | |
| DOCKER_REGISTRY: docker.io | |
| IMAGE_NAME: coollabsio/openclaw | |
| BASE_IMAGE_NAME: coollabsio/openclaw-base | |
| jobs: | |
| check-release: | |
| runs-on: ubuntu-24.04 | |
| outputs: | |
| version: ${{ steps.openclaw-release.outputs.version }} | |
| should_build: ${{ steps.check-image.outputs.exists == 'false' }} | |
| steps: | |
| - name: Get latest Openclaw release | |
| id: openclaw-release | |
| run: | | |
| if [ -n "${{ github.event.inputs.version }}" ]; then | |
| VERSION="${{ github.event.inputs.version }}" | |
| echo "Using manually specified version: $VERSION" | |
| else | |
| LATEST_RELEASE=$(curl -s https://api.github.com/repos/openclaw/openclaw/releases/latest | jq -r '.tag_name') | |
| # Strip leading 'v' if present (openclaw uses v2026.1.29 format) | |
| VERSION=$(echo "$LATEST_RELEASE" | sed 's/^v//') | |
| echo "Latest Openclaw release: $VERSION" | |
| fi | |
| echo "version=$VERSION" >> $GITHUB_OUTPUT | |
| - name: Check if image already exists | |
| id: check-image | |
| run: | | |
| VERSION="${{ steps.openclaw-release.outputs.version }}" | |
| GHCR_IMAGE="${{ env.GITHUB_REGISTRY }}/${{ env.IMAGE_NAME }}" | |
| FORCE_REBUILD="${{ github.event.inputs.force_rebuild }}" | |
| if [ "$FORCE_REBUILD" = "true" ]; then | |
| echo "Force rebuild enabled, skipping existence check" | |
| echo "exists=false" >> $GITHUB_OUTPUT | |
| elif docker manifest inspect "${GHCR_IMAGE}:${VERSION}" > /dev/null 2>&1; then | |
| echo "Image ${GHCR_IMAGE}:${VERSION} already exists" | |
| echo "exists=true" >> $GITHUB_OUTPUT | |
| else | |
| echo "Image ${GHCR_IMAGE}:${VERSION} does not exist, will build" | |
| echo "exists=false" >> $GITHUB_OUTPUT | |
| fi | |
| build-base: | |
| strategy: | |
| matrix: | |
| include: | |
| - arch: amd64 | |
| runner: ubuntu-24.04 | |
| - arch: arm64 | |
| runner: ubuntu-24.04-arm | |
| runs-on: ${{ matrix.runner }} | |
| needs: check-release | |
| if: needs.check-release.outputs.should_build == 'true' | |
| permissions: | |
| contents: read | |
| packages: write | |
| steps: | |
| - uses: actions/checkout@v5 | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| - name: Login to ${{ env.GITHUB_REGISTRY }} | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ${{ env.GITHUB_REGISTRY }} | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Login to ${{ env.DOCKER_REGISTRY }} | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ${{ env.DOCKER_REGISTRY }} | |
| username: ${{ secrets.DOCKERHUB_USERNAME }} | |
| password: ${{ secrets.DOCKERHUB_TOKEN }} | |
| - name: Build and Push Base Image (${{ matrix.arch }}) | |
| uses: docker/build-push-action@v6 | |
| with: | |
| context: . | |
| file: ./Dockerfile.base | |
| platforms: linux/${{ matrix.arch }} | |
| push: true | |
| build-args: | | |
| OPENCLAW_GIT_REF=v${{ needs.check-release.outputs.version }} | |
| tags: | | |
| ${{ env.DOCKER_REGISTRY }}/${{ env.BASE_IMAGE_NAME }}:${{ needs.check-release.outputs.version }}-${{ matrix.arch }} | |
| ${{ env.GITHUB_REGISTRY }}/${{ env.BASE_IMAGE_NAME }}:${{ needs.check-release.outputs.version }}-${{ matrix.arch }} | |
| cache-from: type=gha,scope=base-${{ matrix.arch }} | |
| cache-to: type=gha,mode=max,scope=base-${{ matrix.arch }} | |
| merge-base-manifest: | |
| runs-on: ubuntu-latest | |
| needs: [check-release, build-base] | |
| if: needs.check-release.outputs.should_build == 'true' | |
| permissions: | |
| contents: read | |
| packages: write | |
| steps: | |
| - uses: docker/setup-buildx-action@v3 | |
| - name: Login to ${{ env.GITHUB_REGISTRY }} | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ${{ env.GITHUB_REGISTRY }} | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Login to ${{ env.DOCKER_REGISTRY }} | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ${{ env.DOCKER_REGISTRY }} | |
| username: ${{ secrets.DOCKERHUB_USERNAME }} | |
| password: ${{ secrets.DOCKERHUB_TOKEN }} | |
| - name: Create & publish base manifest on ${{ env.GITHUB_REGISTRY }} | |
| run: | | |
| VERSION="${{ needs.check-release.outputs.version }}" | |
| BASE="${{ env.GITHUB_REGISTRY }}/${{ env.BASE_IMAGE_NAME }}" | |
| TAGS="-t ${BASE}:${VERSION}" | |
| if [ "${{ github.event.inputs.skip_latest_tag }}" != "true" ]; then | |
| TAGS="$TAGS -t ${BASE}:latest" | |
| fi | |
| docker buildx imagetools create \ | |
| $TAGS \ | |
| "${BASE}:${VERSION}-amd64" \ | |
| "${BASE}:${VERSION}-arm64" | |
| - name: Create & publish base manifest on ${{ env.DOCKER_REGISTRY }} | |
| run: | | |
| VERSION="${{ needs.check-release.outputs.version }}" | |
| BASE="${{ env.DOCKER_REGISTRY }}/${{ env.BASE_IMAGE_NAME }}" | |
| TAGS="-t ${BASE}:${VERSION}" | |
| if [ "${{ github.event.inputs.skip_latest_tag }}" != "true" ]; then | |
| TAGS="$TAGS -t ${BASE}:latest" | |
| fi | |
| docker buildx imagetools create \ | |
| $TAGS \ | |
| "${BASE}:${VERSION}-amd64" \ | |
| "${BASE}:${VERSION}-arm64" | |
| build-final: | |
| strategy: | |
| matrix: | |
| include: | |
| - arch: amd64 | |
| runner: ubuntu-24.04 | |
| - arch: arm64 | |
| runner: ubuntu-24.04-arm | |
| runs-on: ${{ matrix.runner }} | |
| needs: [check-release, merge-base-manifest] | |
| if: needs.check-release.outputs.should_build == 'true' | |
| permissions: | |
| contents: read | |
| packages: write | |
| steps: | |
| - uses: actions/checkout@v5 | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| - name: Login to ${{ env.GITHUB_REGISTRY }} | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ${{ env.GITHUB_REGISTRY }} | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Login to ${{ env.DOCKER_REGISTRY }} | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ${{ env.DOCKER_REGISTRY }} | |
| username: ${{ secrets.DOCKERHUB_USERNAME }} | |
| password: ${{ secrets.DOCKERHUB_TOKEN }} | |
| - name: Build and Push Final Image (${{ matrix.arch }}) | |
| uses: docker/build-push-action@v6 | |
| with: | |
| context: . | |
| file: ./Dockerfile | |
| platforms: linux/${{ matrix.arch }} | |
| push: true | |
| build-args: | | |
| BASE_IMAGE=${{ env.GITHUB_REGISTRY }}/${{ env.BASE_IMAGE_NAME }}:${{ needs.check-release.outputs.version }}-${{ matrix.arch }} | |
| tags: | | |
| ${{ env.DOCKER_REGISTRY }}/${{ env.IMAGE_NAME }}:${{ needs.check-release.outputs.version }}-${{ matrix.arch }} | |
| ${{ env.GITHUB_REGISTRY }}/${{ env.IMAGE_NAME }}:${{ needs.check-release.outputs.version }}-${{ matrix.arch }} | |
| cache-from: type=gha,scope=final-${{ matrix.arch }} | |
| cache-to: type=gha,mode=max,scope=final-${{ matrix.arch }} | |
| merge-final-manifest: | |
| runs-on: ubuntu-latest | |
| needs: [check-release, build-final] | |
| if: needs.check-release.outputs.should_build == 'true' | |
| permissions: | |
| contents: read | |
| packages: write | |
| steps: | |
| - uses: docker/setup-buildx-action@v3 | |
| - name: Login to ${{ env.GITHUB_REGISTRY }} | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ${{ env.GITHUB_REGISTRY }} | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Login to ${{ env.DOCKER_REGISTRY }} | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ${{ env.DOCKER_REGISTRY }} | |
| username: ${{ secrets.DOCKERHUB_USERNAME }} | |
| password: ${{ secrets.DOCKERHUB_TOKEN }} | |
| - name: Create & publish final manifest on ${{ env.GITHUB_REGISTRY }} | |
| run: | | |
| VERSION="${{ needs.check-release.outputs.version }}" | |
| IMAGE="${{ env.GITHUB_REGISTRY }}/${{ env.IMAGE_NAME }}" | |
| TAGS="-t ${IMAGE}:${VERSION}" | |
| if [ "${{ github.event.inputs.skip_latest_tag }}" != "true" ]; then | |
| TAGS="$TAGS -t ${IMAGE}:latest" | |
| fi | |
| docker buildx imagetools create \ | |
| $TAGS \ | |
| "${IMAGE}:${VERSION}-amd64" \ | |
| "${IMAGE}:${VERSION}-arm64" | |
| - name: Create & publish final manifest on ${{ env.DOCKER_REGISTRY }} | |
| run: | | |
| VERSION="${{ needs.check-release.outputs.version }}" | |
| IMAGE="${{ env.DOCKER_REGISTRY }}/${{ env.IMAGE_NAME }}" | |
| TAGS="-t ${IMAGE}:${VERSION}" | |
| if [ "${{ github.event.inputs.skip_latest_tag }}" != "true" ]; then | |
| TAGS="$TAGS -t ${IMAGE}:latest" | |
| fi | |
| docker buildx imagetools create \ | |
| $TAGS \ | |
| "${IMAGE}:${VERSION}-amd64" \ | |
| "${IMAGE}:${VERSION}-arm64" | |
| - name: Summary | |
| run: | | |
| VERSION="${{ needs.check-release.outputs.version }}" | |
| echo "## Openclaw ${VERSION}" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "Successfully built and published:" >> $GITHUB_STEP_SUMMARY | |
| echo "- \`${{ env.GITHUB_REGISTRY }}/${{ env.BASE_IMAGE_NAME }}:${VERSION}\` (base)" >> $GITHUB_STEP_SUMMARY | |
| echo "- \`${{ env.GITHUB_REGISTRY }}/${{ env.IMAGE_NAME }}:${VERSION}\` (final)" >> $GITHUB_STEP_SUMMARY | |
| echo "- \`${{ env.DOCKER_REGISTRY }}/${{ env.BASE_IMAGE_NAME }}:${VERSION}\` (base)" >> $GITHUB_STEP_SUMMARY | |
| echo "- \`${{ env.DOCKER_REGISTRY }}/${{ env.IMAGE_NAME }}:${VERSION}\` (final)" >> $GITHUB_STEP_SUMMARY |