blumeops/.forgejo/actions/build-push-image/action.yaml
Erich Blume af3536bc17
Some checks failed
Build Container / build (push) Failing after 13s
Simplify indri IP extraction from tailscale status
Use simple grep and awk to parse plain text tailscale status output
instead of trying to parse JSON. Also show the status output for
debugging.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-24 20:04:28 -08:00

157 lines
5.3 KiB
YAML

name: 'Build and Push Image'
description: 'Build a container image with Docker and push to zot registry via Tailscale sidecar'
# Uses a Tailscale sidecar container to push images to the registry.
# This works around Docker Desktop's VM not having access to the tailnet.
#
# Flow:
# 1. Start Tailscale sidecar container (joins tailnet)
# 2. Build image with docker build
# 3. Save image to OCI tarball
# 4. Push tarball to registry using skopeo (via Tailscale network)
# 5. Cleanup sidecar
inputs:
context:
description: 'Build context path'
required: true
dockerfile:
description: 'Dockerfile path (relative to context)'
required: false
default: 'Dockerfile'
image_name:
description: 'Image name (without registry, e.g. blumeops/devpi)'
required: true
version:
description: 'Version tag (e.g. v1.0.0)'
required: true
registry:
description: 'Registry URL'
required: false
default: 'registry.tail8d86e.ts.net'
tailscale_authkey:
description: 'Tailscale OAuth client secret for ci-gateway'
required: true
runs:
using: 'composite'
steps:
- name: Start Tailscale sidecar
shell: bash
run: |
echo "Starting Tailscale sidecar..."
# Clean up any existing sidecar
docker rm -f ts-ci-gateway 2>/dev/null || true
# Start Tailscale container
docker run -d \
--name ts-ci-gateway \
--hostname ci-gateway \
-e TS_AUTHKEY="${{ inputs.tailscale_authkey }}" \
-e TS_EXTRA_ARGS="--advertise-tags=tag:ci-gateway" \
-e TS_ACCEPT_DNS=true \
-e TS_USERSPACE=true \
tailscale/tailscale:latest
# Wait for Tailscale to connect
echo "Waiting for Tailscale to connect..."
for i in {1..30}; do
# Check if container is still running
if ! docker ps -q -f name=ts-ci-gateway | grep -q .; then
echo "ERROR: Tailscale container exited unexpectedly!"
echo "Container logs:"
docker logs ts-ci-gateway 2>&1 || true
exit 1
fi
if docker exec ts-ci-gateway tailscale status >/dev/null 2>&1; then
echo "Tailscale connected!"
docker exec ts-ci-gateway tailscale status
break
fi
echo " Attempt $i/30..."
sleep 2
done
# Verify DNS resolution
echo "Testing DNS resolution..."
docker exec ts-ci-gateway nslookup ${{ inputs.registry }} || echo "DNS test failed, continuing anyway..."
- name: Build image with Docker
shell: bash
run: |
echo "Building ${{ inputs.image_name }}:${{ inputs.version }}"
docker build \
--tag ${{ inputs.image_name }}:${{ inputs.version }} \
--tag ${{ inputs.image_name }}:${{ github.sha }} \
--file ${{ inputs.context }}/${{ inputs.dockerfile }} \
${{ inputs.context }}
- name: Save image to tarball
shell: bash
run: |
echo "Saving image to tarball..."
mkdir -p /tmp/ci-images
docker save ${{ inputs.image_name }}:${{ inputs.version }} -o /tmp/ci-images/image.tar
ls -lh /tmp/ci-images/image.tar
- name: Push to registry via Tailscale
shell: bash
run: |
echo "Pushing ${{ inputs.image_name }}:${{ inputs.version }} via Tailscale sidecar..."
# Copy tarball into the Tailscale container
docker cp /tmp/ci-images/image.tar ts-ci-gateway:/tmp/image.tar
# Get indri's Tailscale IP from tailscale status (registry runs on indri)
echo "Getting indri's Tailscale IP..."
echo "Tailscale status:"
docker exec ts-ci-gateway tailscale status || true
INDRI_IP=$(docker exec ts-ci-gateway tailscale status 2>/dev/null | grep -E '\bindri\b' | awk '{print $1}')
if [ -z "$INDRI_IP" ]; then
echo "ERROR: Could not get indri's Tailscale IP from tailscale status"
exit 1
fi
echo "Indri Tailscale IP: $INDRI_IP"
# Install skopeo and push
docker exec ts-ci-gateway sh -c "
apk add --no-cache skopeo >/dev/null 2>&1
# Add registry hostname pointing to indri's Tailscale IP
echo '$INDRI_IP ${{ inputs.registry }}' >> /etc/hosts
echo 'Added /etc/hosts entry: $INDRI_IP ${{ inputs.registry }}'
echo 'Pushing version tag...'
skopeo copy \
--dest-tls-verify=false \
docker-archive:/tmp/image.tar \
docker://${{ inputs.registry }}/${{ inputs.image_name }}:${{ inputs.version }}
echo 'Pushing SHA tag...'
skopeo copy \
--dest-tls-verify=false \
docker-archive:/tmp/image.tar \
docker://${{ inputs.registry }}/${{ inputs.image_name }}:${{ github.sha }}
"
- name: Cleanup
shell: bash
if: always()
run: |
echo "Cleaning up..."
docker rm -f ts-ci-gateway 2>/dev/null || true
rm -rf /tmp/ci-images
- name: Summary
shell: bash
run: |
echo "Built and pushed:"
echo " ${{ inputs.registry }}/${{ inputs.image_name }}:${{ inputs.version }}"
echo " ${{ inputs.registry }}/${{ inputs.image_name }}:${{ github.sha }}"
echo ""
echo "Registry tags:"
curl -sf "https://${{ inputs.registry }}/v2/${{ inputs.image_name }}/tags/list" | jq -r '.tags[]' | sort -V | tail -10 || true