Skip to content

ironclaw_image_published #380

ironclaw_image_published

ironclaw_image_published #380

Workflow file for this run

name: Build & Push
on:
workflow_dispatch:
inputs:
version:
description: 'Version tag to publish (for example 0.24.0). Leave empty to use default tag (staging).'
required: false
type: string
default: ''
force:
description: 'Force rebuild even if the version tag already exists'
required: false
type: boolean
default: false
push:
branches: [main]
tags: ["v*"]
repository_dispatch:
types: [ironclaw_image_published]
env:
IMAGE_NAME: nearaidev/ironclaw-dind
DEFAULT_TAG: staging
jobs:
build:
name: Build & Push
runs-on: [self-hosted, sysbox]
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Resolve build version and image
id: resolve
run: |
RAW_TAG=""
if [[ -n "${{ inputs.version }}" && "${{ github.event_name }}" == "workflow_dispatch" ]]; then
RAW_TAG="${{ inputs.version }}"
elif [[ "${{ github.event_name }}" == "repository_dispatch" && -n "${{ github.event.client_payload.version }}" ]]; then
RAW_TAG="${{ github.event.client_payload.version }}"
else
RAW_TAG="${{ env.DEFAULT_TAG }}"
fi
if [[ ! "$RAW_TAG" =~ ^[A-Za-z0-9_.-]+$ ]]; then
echo "Invalid docker tag '$RAW_TAG'. Allowed characters: [A-Za-z0-9_.-]"
exit 1
fi
echo "tag=$RAW_TAG" >> "$GITHUB_OUTPUT"
echo "image=nearaidev/ironclaw:$RAW_TAG" >> "$GITHUB_OUTPUT"
echo "worker_image=nearaidev/ironclaw-worker:$RAW_TAG" >> "$GITHUB_OUTPUT"
- name: Log in to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ vars.DOCKER_REGISTRY_USER }}
password: ${{ secrets.DOCKER_REGISTRY_TOKEN }}
- name: Pull ironclaw image and detect version
id: version
run: |
docker pull "${{ steps.resolve.outputs.image }}"
VERSION=$(docker run --rm "${{ steps.resolve.outputs.image }}" --version | awk '{print $2}')
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
echo "Detected IronClaw ${VERSION}"
- name: Get upstream digest
id: digest
run: |
UPSTREAM_DIGEST=$(docker inspect --format='{{index .RepoDigests 0}}' "${{ steps.resolve.outputs.image }}" | cut -d@ -f2)
echo "upstream=$UPSTREAM_DIGEST" >> "$GITHUB_OUTPUT"
echo "Upstream digest: $UPSTREAM_DIGEST"
- name: Check if rebuild needed (schedule only)
id: check
if: github.event_name == 'schedule'
run: |
CURRENT_DIGEST=""
if docker pull "${{ env.IMAGE_NAME }}:${{ env.DEFAULT_TAG }}" > /dev/null 2>&1; then
CURRENT_DIGEST=$(docker inspect --format='{{index .Config.Labels "ironclaw.source.digest"}}' "${{ env.IMAGE_NAME }}:${{ env.DEFAULT_TAG }}" 2>/dev/null || echo "")
fi
echo "Current source digest: ${CURRENT_DIGEST:-<none>}"
echo "Upstream digest: ${{ steps.digest.outputs.upstream }}"
if [[ "$CURRENT_DIGEST" == "${{ steps.digest.outputs.upstream }}" ]]; then
echo "skip=true" >> "$GITHUB_OUTPUT"
echo "No changes detected — skipping rebuild"
else
echo "skip=false" >> "$GITHUB_OUTPUT"
echo "Upstream changed — rebuilding"
fi
- name: Check existing version tag (skip unless forced)
id: tag_guard
if: steps.check.outputs.skip != 'true'
run: |
FORCE=false
if [[ "${{ github.event_name }}" == "workflow_dispatch" && "${{ inputs.force }}" == "true" ]]; then
FORCE=true
fi
echo "force=$FORCE" >> "$GITHUB_OUTPUT"
TAG="${{ steps.resolve.outputs.tag }}"
if [[ "$TAG" == "${{ env.DEFAULT_TAG }}" ]]; then
echo "skip=false" >> "$GITHUB_OUTPUT"
echo "Default tag '${{ env.DEFAULT_TAG }}' is mutable; skipping immutable tag check."
exit 0
fi
if docker manifest inspect "${{ env.IMAGE_NAME }}:${TAG}" > /dev/null 2>&1; then
if [[ "$FORCE" == "true" ]]; then
echo "skip=false" >> "$GITHUB_OUTPUT"
echo "Version tag already exists, but force=true so build will continue."
else
echo "skip=true" >> "$GITHUB_OUTPUT"
echo "Version tag already exists, skipping build: ${{ env.IMAGE_NAME }}:${TAG}"
fi
else
echo "skip=false" >> "$GITHUB_OUTPUT"
fi
- name: Build DinD image
if: steps.check.outputs.skip != 'true' && steps.tag_guard.outputs.skip != 'true'
env:
SANDBOX_IMAGE: ${{ steps.resolve.outputs.worker_image }}
run: |
bash ./scripts/build-dind-image.sh \
"${{ env.IMAGE_NAME }}:${{ steps.resolve.outputs.tag }}" \
"${{ steps.resolve.outputs.image }}" \
"${{ steps.digest.outputs.upstream }}"
- name: Push
if: steps.check.outputs.skip != 'true' && steps.tag_guard.outputs.skip != 'true'
run: |
docker push "${{ env.IMAGE_NAME }}:${{ steps.resolve.outputs.tag }}"
# Staging orchestrator allowlist: register immutable version tags only (not :staging)
- name: Register image on staging orchestrator allowlist
if: >-
steps.check.outputs.skip != 'true' &&
steps.tag_guard.outputs.skip != 'true' &&
steps.resolve.outputs.tag != env.DEFAULT_TAG
env:
ORCH_URL: https://api.agents-staging.near.ai
ORCH_ADMIN_SECRET: ${{ secrets.AGENTS_STG_ORCHESTRATOR_ADMIN_SECRET }}
IMAGE_REF: docker.io/${{ env.IMAGE_NAME }}:${{ steps.resolve.outputs.tag }}
run: |
set -euo pipefail
if [[ -z "${ORCH_ADMIN_SECRET:-}" ]]; then
echo "Skipping orchestrator allowlist (AGENTS_STG_ORCHESTRATOR_ADMIN_SECRET not set)."
exit 0
fi
python3 scripts/register_orchestrator_allowlist.py \
--no-resolve-digests \
--label "Ironclaw DinD (${{ steps.resolve.outputs.tag }})" \
--note-suffix "CI ${GITHUB_RUN_ID}"
- name: Resolve pushed ironclaw-dind digest
id: dind_digest
if: steps.check.outputs.skip != 'true' && steps.tag_guard.outputs.skip != 'true'
run: |
DIGEST=$(docker inspect --format='{{index .RepoDigests 0}}' "${{ env.IMAGE_NAME }}:${{ steps.resolve.outputs.tag }}" 2>/dev/null | cut -d@ -f2)
if [[ -z "$DIGEST" ]]; then
DIGEST="N/A"
fi
echo "digest=$DIGEST" >> "$GITHUB_OUTPUT"
- name: Summary
if: steps.check.outputs.skip != 'true' && steps.tag_guard.outputs.skip != 'true'
run: |
{
echo "## ironclaw-dind"
echo ""
echo "- tag: \`${{ env.IMAGE_NAME }}:${{ steps.resolve.outputs.tag }}\`"
echo "- ironclaw: \`${{ steps.resolve.outputs.image }}\` (v${{ steps.version.outputs.version }})"
echo "- ironclaw-worker: \`${{ steps.resolve.outputs.worker_image }}\`"
echo "- source digest: \`${{ steps.digest.outputs.upstream }}\`"
echo "- ironclaw-dind digest: \`${{ steps.dind_digest.outputs.digest }}\`"
} >> "$GITHUB_STEP_SUMMARY"
- name: Summary (skipped)
if: steps.check.outputs.skip == 'true' || steps.tag_guard.outputs.skip == 'true'
run: |
{
echo "## ironclaw-dind — skipped"
echo ""
if [[ "${{ steps.check.outputs.skip }}" == "true" ]]; then
echo "Upstream \`${{ steps.resolve.outputs.image }}\` digest unchanged."
else
echo "Version tag already exists and force was not set: \`${{ env.IMAGE_NAME }}:${{ steps.resolve.outputs.tag }}\`"
fi
} >> "$GITHUB_STEP_SUMMARY"