diff --git a/.forgejo/actions/build-push-image/action.yaml b/.forgejo/actions/build-push-image/action.yaml index 8c88c32..93b2c11 100644 --- a/.forgejo/actions/build-push-image/action.yaml +++ b/.forgejo/actions/build-push-image/action.yaml @@ -1,15 +1,8 @@ name: 'Build and Push Image' -description: 'Build a container image with Docker and push to zot registry via Tailscale sidecar' +description: 'Build a container image with Docker and push to registry.ops.eblu.me using skopeo' -# 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 +# Note: Uses skopeo for push because Docker 27's manifest format has compatibility +# issues with zot registry. Skopeo handles manifest conversion correctly. inputs: context: @@ -29,122 +22,39 @@ inputs: description: 'Registry URL' required: false default: 'registry.ops.eblu.me' - 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 + - name: Build image 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 + - name: Push to registry 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 + docker save ${{ inputs.image_name }}:${{ inputs.version }} -o /tmp/image.tar - - name: Push to registry via Tailscale - shell: bash - run: | - echo "Pushing ${{ inputs.image_name }}:${{ inputs.version }} via Tailscale sidecar..." + echo "Installing skopeo..." + apk add --no-cache skopeo >/dev/null 2>&1 || apt-get update && apt-get install -y skopeo >/dev/null 2>&1 || true - # Copy tarball into the Tailscale container - docker cp /tmp/ci-images/image.tar ts-ci-gateway:/tmp/image.tar + echo "Pushing ${{ inputs.registry }}/${{ inputs.image_name }}:${{ inputs.version }}" + skopeo copy \ + docker-archive:/tmp/image.tar \ + docker://${{ inputs.registry }}/${{ inputs.image_name }}:${{ inputs.version }} - # 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 + echo "Pushing ${{ inputs.registry }}/${{ inputs.image_name }}:${{ github.sha }}" + skopeo copy \ + docker-archive:/tmp/image.tar \ + docker://${{ inputs.registry }}/${{ inputs.image_name }}:${{ github.sha }} - 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 + rm -f /tmp/image.tar - name: Summary shell: bash @@ -154,4 +64,4 @@ runs: 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 + curl -sf "https://${{ inputs.registry }}/v2/${{ inputs.image_name }}/tags/list" | jq -r '.tags[]' 2>/dev/null | sort -V | tail -10 || true diff --git a/.forgejo/workflows/build-container.yaml b/.forgejo/workflows/build-container.yaml index 59291f0..3afad1b 100644 --- a/.forgejo/workflows/build-container.yaml +++ b/.forgejo/workflows/build-container.yaml @@ -74,4 +74,3 @@ jobs: context: ${{ steps.check.outputs.context }} image_name: blumeops/${{ steps.parse.outputs.container }} version: ${{ steps.parse.outputs.version }} - tailscale_authkey: ${{ secrets.TS_CI_GATEWAY_AUTHKEY }}