diff --git a/.forgejo/workflows/build-container.yaml b/.forgejo/workflows/build-container.yaml new file mode 100644 index 0000000..60771fe --- /dev/null +++ b/.forgejo/workflows/build-container.yaml @@ -0,0 +1,76 @@ +# Generic container build workflow +# Triggers on tags matching: -v +# Builds from containers//Dockerfile if it exists +# +# Examples: +# nettest-v1.0.0 -> builds containers/nettest/ +# devpi-v2.1.0 -> builds containers/devpi/ +# foo-v1.0.0 -> skips if containers/foo/ doesn't exist +name: Build Container + +on: + push: + tags: + - '*-v[0-9]*' + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Parse tag + id: parse + run: | + TAG="${GITHUB_REF_NAME}" + echo "Tag: $TAG" + + # Extract container name (everything before -v) + # e.g., "nettest-v1.0.0" -> "nettest", "my-app-v2.0.0" -> "my-app" + CONTAINER="${TAG%-v[0-9]*}" + VERSION="${TAG#"${CONTAINER}"-}" + + echo "container=$CONTAINER" >> "$GITHUB_OUTPUT" + echo "version=$VERSION" >> "$GITHUB_OUTPUT" + echo "Container: $CONTAINER" + echo "Version: $VERSION" + + - name: Checkout + uses: actions/checkout@v4 + + - name: Check if container exists + id: check + run: | + CONTAINER="${{ steps.parse.outputs.container }}" + CONTEXT="containers/$CONTAINER" + + if [ -f "$CONTEXT/Dockerfile" ]; then + echo "Found $CONTEXT/Dockerfile" + echo "exists=true" >> "$GITHUB_OUTPUT" + echo "context=$CONTEXT" >> "$GITHUB_OUTPUT" + else + echo "No Dockerfile found at $CONTEXT/Dockerfile" + echo "exists=false" >> "$GITHUB_OUTPUT" + fi + + - name: Skip if container not found + if: steps.check.outputs.exists != 'true' + run: | + echo "========================================" + echo "Container not found: ${{ steps.parse.outputs.container }}" + echo "========================================" + echo "" + echo "Tag '${{ github.ref_name }}' does not match any container in containers/" + echo "" + echo "Available containers:" + find containers -maxdepth 1 -mindepth 1 -type d -exec basename {} \; 2>/dev/null | sort | while read -r name; do + echo " - $name" + done || echo " (none)" + echo "" + echo "Skipping build." + + - name: Build and push image + if: steps.check.outputs.exists == 'true' + uses: ./.forgejo/actions/build-push-image + with: + context: ${{ steps.check.outputs.context }} + image_name: blumeops/${{ steps.parse.outputs.container }} + version: ${{ steps.parse.outputs.version }} diff --git a/.forgejo/workflows/test.yaml b/.forgejo/workflows/test.yaml deleted file mode 100644 index 6aa3236..0000000 --- a/.forgejo/workflows/test.yaml +++ /dev/null @@ -1,45 +0,0 @@ -# Workflow to verify CI environment and available tools -name: Test CI - -on: - push: - branches: [main] - pull_request: - workflow_dispatch: - -jobs: - test: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Verify tools - run: | - echo "=== Node.js ===" - node --version || echo "Node.js not available" - npm --version || echo "npm not available" - echo "" - echo "=== Git ===" - git --version - echo "" - echo "=== Build tools ===" - make --version 2>&1 | head -1 || echo "make not available" - gcc --version 2>&1 | head -1 || echo "gcc not available" - echo "" - echo "=== Container tools (Docker) ===" - docker --version || echo "Docker CLI not available" - echo "" - echo "=== Other tools ===" - curl --version 2>&1 | head -1 || echo "curl not available" - jq --version || echo "jq not available" - - - name: Show repo info - run: | - echo "Repository: ${{ github.repository }}" - echo "Event: ${{ github.event_name }}" - echo "Ref: ${{ github.ref }}" - echo "Branch: ${{ github.ref_name }}" - echo "" - echo "=== Files ===" - ls -la diff --git a/containers/nettest/Dockerfile b/containers/nettest/Dockerfile new file mode 100644 index 0000000..97b03d6 --- /dev/null +++ b/containers/nettest/Dockerfile @@ -0,0 +1,24 @@ +# Network connectivity test container for blumeops CI/CD debugging +# +# This container tests connectivity to tailnet services from various environments: +# - Docker on indri (during CI build) +# - Minikube pods (manual testing) +# +# Build: +# docker build -t registry.tail8d86e.ts.net/blumeops/nettest:latest . +# +# Run: +# docker run --rm registry.tail8d86e.ts.net/blumeops/nettest:latest + +FROM alpine:3.21 + +RUN apk add --no-cache \ + curl \ + ca-certificates \ + jq \ + bind-tools + +COPY test-connectivity.sh /test-connectivity.sh +RUN chmod +x /test-connectivity.sh + +ENTRYPOINT ["/test-connectivity.sh"] diff --git a/containers/nettest/test-connectivity.sh b/containers/nettest/test-connectivity.sh new file mode 100644 index 0000000..d1fb6e4 --- /dev/null +++ b/containers/nettest/test-connectivity.sh @@ -0,0 +1,115 @@ +#!/bin/ash +# shellcheck shell=dash +# Network connectivity test script for blumeops +# Tests access to tailnet services from within the container + +set -e + +echo "========================================" +echo "BlumeOps Network Connectivity Test" +echo "========================================" +echo "" +echo "Timestamp: $(date -Iseconds)" +echo "Hostname: $(hostname)" +echo "" + +# Test targets +FORGE_HOST="forge.tail8d86e.ts.net" +REGISTRY_HOST="registry.tail8d86e.ts.net" + +test_dns() { + local host="$1" + echo "--- DNS: $host ---" + if nslookup "$host" 2>/dev/null; then + echo "DNS: OK" + return 0 + else + echo "DNS: FAILED" + return 1 + fi +} + +test_https() { + local url="$1" + local name="$2" + echo "" + echo "--- HTTPS: $name ---" + echo "URL: $url" + + # Try to fetch with verbose output + http_code=$(curl -s -o /dev/null -w "%{http_code}" --max-time 10 "$url" 2>&1) || true + + if [ "$http_code" = "200" ] || [ "$http_code" = "401" ] || [ "$http_code" = "302" ]; then + echo "HTTP Status: $http_code" + echo "Result: OK (service reachable)" + return 0 + elif [ -n "$http_code" ] && [ "$http_code" != "000" ]; then + echo "HTTP Status: $http_code" + echo "Result: OK (service reachable, status $http_code)" + return 0 + else + echo "HTTP Status: $http_code" + echo "Result: FAILED (could not connect)" + return 1 + fi +} + +test_registry_api() { + local host="$1" + echo "" + echo "--- Registry API: $host ---" + + # Try to query the registry API + response=$(curl -sf --max-time 10 "https://$host/v2/_catalog" 2>/dev/null) || true + + if [ -n "$response" ]; then + echo "Response: $response" + repo_count=$(echo "$response" | jq -r '.repositories | length' 2>/dev/null) || repo_count="unknown" + echo "Repository count: $repo_count" + echo "Result: OK" + return 0 + else + echo "Result: FAILED (no response from /v2/_catalog)" + return 1 + fi +} + +echo "========================================" +echo "Testing DNS Resolution" +echo "========================================" +dns_ok=0 +test_dns "$FORGE_HOST" && dns_ok=$((dns_ok + 1)) || true +echo "" +test_dns "$REGISTRY_HOST" && dns_ok=$((dns_ok + 1)) || true + +echo "" +echo "========================================" +echo "Testing HTTPS Connectivity" +echo "========================================" +https_ok=0 +test_https "https://$FORGE_HOST" "Forgejo" && https_ok=$((https_ok + 1)) || true +test_https "https://$REGISTRY_HOST/v2/" "Zot Registry" && https_ok=$((https_ok + 1)) || true + +echo "" +echo "========================================" +echo "Testing Registry API" +echo "========================================" +api_ok=0 +test_registry_api "$REGISTRY_HOST" && api_ok=1 || true + +echo "" +echo "========================================" +echo "Summary" +echo "========================================" +echo "DNS tests passed: $dns_ok/2" +echo "HTTPS tests passed: $https_ok/2" +echo "Registry API: $([ $api_ok -eq 1 ] && echo 'OK' || echo 'FAILED')" +echo "" + +if [ "$dns_ok" -eq 2 ] && [ "$https_ok" -eq 2 ] && [ "$api_ok" -eq 1 ]; then + echo "OVERALL: ALL TESTS PASSED" + exit 0 +else + echo "OVERALL: SOME TESTS FAILED" + exit 1 +fi diff --git a/mise-tasks/container-list b/mise-tasks/container-list index 21a2ad9..3f572ad 100755 --- a/mise-tasks/container-list +++ b/mise-tasks/container-list @@ -4,32 +4,24 @@ set -euo pipefail REGISTRY="registry.tail8d86e.ts.net" -WORKFLOW_DIR=".forgejo/workflows" +CONTAINER_DIR="containers" echo "Container Images" echo "================" echo "" -# Find all build-*.yaml workflows -for workflow in "$WORKFLOW_DIR"/build-*.yaml; do - [[ -f "$workflow" ]] || continue +# Find all container directories with Dockerfiles +for dir in "$CONTAINER_DIR"/*/; do + [[ -d "$dir" ]] || continue + [[ -f "$dir/Dockerfile" ]] || continue - # Extract container name from filename: build-runner.yaml -> runner - filename=$(basename "$workflow") - container="${filename#build-}" - container="${container%.yaml}" - - # Skip if not a container build workflow (check for image_name) - if ! grep -q "image_name:" "$workflow" 2>/dev/null; then - continue - fi - - # Extract image name from workflow - image=$(grep -E "^\s+image_name:" "$workflow" | head -1 | awk '{print $2}') + # Extract container name from directory + container=$(basename "$dir") + image="blumeops/$container" echo "📦 $container" echo " Image: $REGISTRY/$image" - echo " Workflow: $workflow" + echo " Path: $dir" # Query zot for recent tags tags=$(curl -sf "https://$REGISTRY/v2/$image/tags/list" 2>/dev/null | jq -r '.tags // [] | .[]' | grep -E '^v[0-9]' | sort -V | tail -4 || true) @@ -47,7 +39,7 @@ done echo "---" echo "To release a new version:" -echo " mise run container-release " +echo " mise run container-tag-and-release " echo "" echo "Example:" -echo " mise run container-release runner v1.0.0" +echo " mise run container-tag-and-release nettest v1.0.0" diff --git a/mise-tasks/container-release b/mise-tasks/container-tag-and-release similarity index 66% rename from mise-tasks/container-release rename to mise-tasks/container-tag-and-release index 9e8802b..6f37d4e 100755 --- a/mise-tasks/container-release +++ b/mise-tasks/container-tag-and-release @@ -7,7 +7,7 @@ CONTAINER="${1:-}" VERSION="${2:-}" if [[ -z "$CONTAINER" || -z "$VERSION" ]]; then - echo "Usage: mise run container-release " + echo "Usage: mise run container-tag-and-release " echo "" echo "Run 'mise run container-list' to see available containers and recent tags." exit 1 @@ -32,24 +32,23 @@ if git rev-parse "$TAG" >/dev/null 2>&1; then exit 1 fi -# Find the workflow file to determine image name -WORKFLOW_FILE=".forgejo/workflows/build-${CONTAINER}.yaml" -if [[ ! -f "$WORKFLOW_FILE" ]]; then - echo "Error: No workflow found for container '$CONTAINER'" +# Check if container directory exists +CONTAINER_DIR="containers/${CONTAINER}" +if [[ ! -f "$CONTAINER_DIR/Dockerfile" ]]; then + echo "Error: No Dockerfile found at '$CONTAINER_DIR/Dockerfile'" echo "" - echo "Run 'mise run container-list' to see available containers." + echo "Available containers:" + for dir in containers/*/; do + [[ -d "$dir" ]] && echo " - $(basename "$dir")" + done exit 1 fi -# Extract image name from workflow -IMAGE=$(grep -E "^\s+image_name:" "$WORKFLOW_FILE" | head -1 | awk '{print $2}') -if [[ -z "$IMAGE" ]]; then - echo "Error: Could not determine image name from $WORKFLOW_FILE" - exit 1 -fi +# Image name follows convention: blumeops/ +IMAGE="blumeops/${CONTAINER}" echo "Container: $CONTAINER" -echo "Workflow: $WORKFLOW_FILE" +echo "Directory: $CONTAINER_DIR" echo "Image: registry.tail8d86e.ts.net/$IMAGE:$VERSION" echo "" @@ -66,7 +65,7 @@ git tag "$TAG" git push origin "$TAG" echo "" -echo "✅ Tag '$TAG' created and pushed" +echo "Tag '$TAG' created and pushed" echo "" echo "The workflow will now build and push:" echo " registry.tail8d86e.ts.net/$IMAGE:$VERSION"