Some checks failed
Build Container / build (push) Failing after 13s
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>
157 lines
5.3 KiB
YAML
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
|