Docker Publish #301
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: Docker Publish | |
| on: | |
| push: | |
| branches: [ "main" ] | |
| paths: | |
| - 'Dockerfile*' | |
| - 'docker-entrypoint.sh' | |
| - 'Makefile' | |
| - '.github/workflows/docker-publish.yml' | |
| tags: [ 'v*' ] | |
| repository_dispatch: | |
| types: [upstream_release] | |
| schedule: | |
| - cron: '0 22 * * *' # 北京时间每天凌晨 6:00 (UTC 前一天 22:00) | |
| pull_request: | |
| branches: [ "main" ] | |
| paths: | |
| - 'Dockerfile*' | |
| - 'docker-entrypoint.sh' | |
| - 'Makefile' | |
| - '.github/workflows/docker-publish.yml' | |
| workflow_dispatch: | |
| inputs: | |
| force_publish: | |
| description: 'Force publish even without version change' | |
| required: false | |
| default: 'false' | |
| env: | |
| REGISTRY: ghcr.io | |
| # Use lower case for image names to satisfy GHCR requirements | |
| IMAGE_BASE: openclaw-runtime | |
| FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true | |
| jobs: | |
| # Job 1: Check upstream version and determine if publish is needed | |
| check-upstream: | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write | |
| statuses: read | |
| outputs: | |
| has_new_version: ${{ steps.check.outputs.has_new_version }} | |
| latest_version: ${{ steps.check.outputs.latest_version }} | |
| should_publish: ${{ steps.check.outputs.should_publish }} | |
| should_build: ${{ steps.check.outputs.should_build }} | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v5 | |
| - name: Check upstream version | |
| id: check | |
| run: | | |
| # Get latest upstream version | |
| LATEST=$(curl -fsSL "https://api.github.com/repos/openclaw/openclaw/releases/latest" | jq -r '.tag_name') | |
| echo "latest_version=${LATEST}" >> $GITHUB_OUTPUT | |
| # Always publish on tag push (manual release) | |
| if [[ "${{ github.ref }}" == "refs/tags/"* ]]; then | |
| echo "Tag push detected - will build and publish" | |
| echo "has_new_version=true" >> $GITHUB_OUTPUT | |
| echo "should_publish=true" >> $GITHUB_OUTPUT | |
| echo "should_build=true" >> $GITHUB_OUTPUT | |
| exit 0 | |
| fi | |
| # Publish on repository_dispatch (upstream triggered) | |
| if [[ "${{ github.event_name }}" == "repository_dispatch" ]]; then | |
| echo "repository_dispatch triggered - will build and publish" | |
| echo "has_new_version=true" >> $GITHUB_OUTPUT | |
| echo "should_publish=true" >> $GITHUB_OUTPUT | |
| echo "should_build=true" >> $GITHUB_OUTPUT | |
| exit 0 | |
| fi | |
| # Publish on workflow_dispatch if force is enabled | |
| if [[ "${{ github.event_name }}" == "workflow_dispatch" ]] && [[ "${{ inputs.force_publish }}" == "true" ]]; then | |
| echo "workflow_dispatch with force_publish - will build and publish" | |
| echo "has_new_version=true" >> $GITHUB_OUTPUT | |
| echo "should_publish=true" >> $GITHUB_OUTPUT | |
| echo "should_build=true" >> $GITHUB_OUTPUT | |
| exit 0 | |
| fi | |
| # Schedule or push to main: publish if new version detected | |
| if [[ "${{ github.event_name }}" == "schedule" ]] || \ | |
| [[ "${{ github.event_name }}" == "push" && "${{ github.ref }}" == "refs/heads/main" ]]; then | |
| if [[ -z "$LATEST" || "$LATEST" == "null" ]]; then | |
| echo "Failed to fetch upstream version - skipping build" | |
| echo "has_new_version=false" >> $GITHUB_OUTPUT | |
| echo "should_publish=false" >> $GITHUB_OUTPUT | |
| echo "should_build=false" >> $GITHUB_OUTPUT | |
| exit 0 | |
| fi | |
| CURRENT="${{ secrets.OPENCLAW_VERSION }}" | |
| LATEST_NORM="${LATEST#v}" | |
| CURRENT_NORM="${CURRENT#v}" | |
| if [[ -n "$CURRENT_NORM" && "$LATEST_NORM" == "$CURRENT_NORM" ]]; then | |
| echo "::notice::No version change: ${LATEST} == ${CURRENT} - skipping" | |
| echo "has_new_version=false" >> $GITHUB_OUTPUT | |
| echo "should_publish=false" >> $GITHUB_OUTPUT | |
| echo "should_build=false" >> $GITHUB_OUTPUT | |
| else | |
| echo "New version detected: ${LATEST} (was: ${CURRENT:-none}) - will build and publish" | |
| echo "has_new_version=true" >> $GITHUB_OUTPUT | |
| echo "should_publish=true" >> $GITHUB_OUTPUT | |
| echo "should_build=true" >> $GITHUB_OUTPUT | |
| fi | |
| exit 0 | |
| fi | |
| # PR: build only (no publish) | |
| echo "PR build verification - will build but not publish" | |
| echo "has_new_version=false" >> $GITHUB_OUTPUT | |
| echo "should_publish=false" >> $GITHUB_OUTPUT | |
| echo "should_build=true" >> $GITHUB_OUTPUT | |
| # Job 2: Prepare - reuse version from check-upstream | |
| prepare: | |
| needs: check-upstream | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| outputs: | |
| version: ${{ steps.version.outputs.version }} | |
| repo_owner: ${{ steps.repo.outputs.owner }} | |
| steps: | |
| - name: Set version | |
| id: version | |
| run: | | |
| TAG="${{ needs.check-upstream.outputs.latest_version }}" | |
| NPM_VERSION="${TAG#v}" | |
| if [[ -z "$NPM_VERSION" || "$NPM_VERSION" == "null" ]]; then | |
| echo "ERROR: No version from check-upstream, using latest" | |
| NPM_VERSION="latest" | |
| fi | |
| echo "version=${NPM_VERSION}" >> $GITHUB_OUTPUT | |
| echo "::notice::Building with OpenClaw version ${NPM_VERSION}" | |
| - name: Get repo owner | |
| id: repo | |
| run: echo "owner=${GITHUB_REPOSITORY_OWNER,,}" >> $GITHUB_OUTPUT | |
| # Tier 1: Foundation | |
| build-base: | |
| needs: [check-upstream, prepare] | |
| if: needs.check-upstream.outputs.should_build == 'true' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| packages: write | |
| contents: read | |
| steps: | |
| - uses: actions/checkout@v5 | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v4 | |
| - name: Login to GHCR | |
| if: needs.check-upstream.outputs.should_publish == 'true' | |
| uses: docker/login-action@v4 | |
| with: | |
| registry: ${{ env.REGISTRY }} | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Build and push base | |
| uses: docker/build-push-action@v7 | |
| with: | |
| context: . | |
| file: Dockerfile.base | |
| push: ${{ needs.check-upstream.outputs.should_publish == 'true' }} | |
| platforms: linux/amd64,linux/arm64 | |
| cache-from: type=gha | |
| cache-to: type=gha,mode=max | |
| tags: ${{ env.REGISTRY }}/${{ needs.prepare.outputs.repo_owner }}/${{ env.IMAGE_BASE }}:base | |
| # Tier 2: Stacks | |
| build-stacks: | |
| needs: [check-upstream, prepare, build-base] | |
| if: needs.check-upstream.outputs.should_build == 'true' | |
| runs-on: ubuntu-latest | |
| strategy: | |
| matrix: | |
| stack: [go, java, office] | |
| permissions: | |
| packages: write | |
| contents: read | |
| steps: | |
| - uses: actions/checkout@v5 | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v4 | |
| - name: Login to GHCR | |
| if: needs.check-upstream.outputs.should_publish == 'true' | |
| uses: docker/login-action@v4 | |
| with: | |
| registry: ${{ env.REGISTRY }} | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Build and push stack | |
| uses: docker/build-push-action@v7 | |
| with: | |
| context: . | |
| file: Dockerfile.stacks | |
| target: stack-${{ matrix.stack }} | |
| push: ${{ needs.check-upstream.outputs.should_publish == 'true' }} | |
| platforms: linux/amd64,linux/arm64 | |
| cache-from: type=gha | |
| cache-to: type=gha,mode=max | |
| build-args: | | |
| BASE_IMAGE=${{ env.REGISTRY }}/${{ needs.prepare.outputs.repo_owner }}/${{ env.IMAGE_BASE }}:base | |
| tags: ${{ env.REGISTRY }}/${{ needs.prepare.outputs.repo_owner }}/${{ env.IMAGE_BASE }}:${{ matrix.stack }} | |
| # Tier 3: Product Images | |
| build-products: | |
| needs: [check-upstream, prepare, build-stacks] | |
| if: needs.check-upstream.outputs.should_build == 'true' | |
| runs-on: ubuntu-latest | |
| strategy: | |
| matrix: | |
| include: | |
| - stack: base | |
| tag: 'latest' | |
| install_ai_tools: 1 | |
| - stack: go | |
| tag: 'go' | |
| install_ai_tools: 1 | |
| - stack: java | |
| tag: 'java' | |
| install_ai_tools: 1 | |
| - stack: office | |
| tag: 'office' | |
| install_ai_tools: 0 | |
| permissions: | |
| packages: write | |
| contents: read | |
| steps: | |
| - uses: actions/checkout@v5 | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v4 | |
| - name: Login to GHCR | |
| if: needs.check-upstream.outputs.should_publish == 'true' | |
| uses: docker/login-action@v4 | |
| with: | |
| registry: ${{ env.REGISTRY }} | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Build and push product | |
| uses: docker/build-push-action@v7 | |
| with: | |
| context: . | |
| file: Dockerfile | |
| push: ${{ needs.check-upstream.outputs.should_publish == 'true' }} | |
| platforms: linux/amd64,linux/arm64 | |
| cache-from: type=gha | |
| cache-to: type=gha,mode=max | |
| build-args: | | |
| BASE_IMAGE=${{ env.REGISTRY }}/${{ needs.prepare.outputs.repo_owner }}/${{ env.IMAGE_BASE }}:${{ matrix.stack }} | |
| OPENCLAW_VERSION=${{ needs.prepare.outputs.version }} | |
| INSTALL_BROWSER=0 | |
| INSTALL_AI_TOOLS=${{ matrix.install_ai_tools }} | |
| tags: | | |
| ${{ env.REGISTRY }}/${{ needs.prepare.outputs.repo_owner }}/openclaw-devkit:${{ matrix.tag }} | |
| ${{ env.REGISTRY }}/${{ needs.prepare.outputs.repo_owner }}/openclaw-devkit:${{ needs.prepare.outputs.version }}${{ matrix.tag != 'latest' && format('-{0}', matrix.tag) || '' }} | |
| # Job: Update version secret after successful publish | |
| update-secret: | |
| needs: [check-upstream, build-products, prepare] | |
| if: | | |
| always() && | |
| needs.check-upstream.result == 'success' && | |
| needs.build-products.result == 'success' && | |
| needs.check-upstream.outputs.should_publish == 'true' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| steps: | |
| - name: Update OPENCLAW_VERSION secret | |
| env: | |
| GH_TOKEN: ${{ secrets.PAT_TOKEN }} | |
| run: | | |
| TAG="${{ needs.check-upstream.outputs.latest_version }}" | |
| echo "Updating OPENCLAW_VERSION secret to ${TAG}" | |
| gh secret set OPENCLAW_VERSION --body "${TAG}" --repo "${GITHUB_REPOSITORY}" | |
| echo "::notice::OPENCLAW_VERSION updated to ${TAG}" | |
| # Job: Notify on failure | |
| notify-failure: | |
| needs: [check-upstream, build-base, build-stacks, build-products] | |
| if: failure() | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| issues: write | |
| steps: | |
| - uses: actions/checkout@v5 | |
| - name: Create issue comment on failure | |
| run: | | |
| gh issue create \ | |
| --title "Docker Build Failed - Run ${{ github.run_id }}" \ | |
| --body "## Docker Build Failed | |
| **Workflow:** ${{ github.workflow }} | |
| **Run:** ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} | |
| **Trigger:** ${{ github.event_name }} | |
| **Commit:** ${{ github.server_url }}/${{ github.repository }}/commit/${{ github.sha }} | |
| Please check the workflow logs for details." \ | |
| --label "bug,ci" | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} |