# CI/CD Pipeline for foundary-test-1770773605 # Components will add their build steps below the marker clone: git: image: woodpeckerci/plugin-git settings: depth: 1 steps: deps: image: golang:1.25 commands: - go work sync - | for dir in services/*/; do if [ -f "$dir/go.mod" ]; then (cd "$dir" && go mod tidy) fi done - | for dir in workers/*/; do if [ -f "$dir/go.mod" ]; then (cd "$dir" && go mod tidy) fi done - | for dir in cli/*/; do if [ -f "$dir/go.mod" ]; then (cd "$dir" && go mod tidy) fi done when: branch: main event: push # Pre-flight registry health check before builds preflight: depends_on: [deps] image: alpine/curl commands: - | echo "==> Checking registry health before builds" HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" --insecure --connect-timeout 10 https://registry.threesix.ai/v2/) if [ "$HTTP_CODE" != "200" ] && [ "$HTTP_CODE" != "401" ]; then echo "ERROR: Registry unhealthy (HTTP $HTTP_CODE), aborting build" echo "Registry must return 200 or 401 (auth required) to proceed" exit 1 fi echo "==> Registry healthy (HTTP $HTTP_CODE)" when: branch: main event: push # COMPONENT_STEPS_BELOW # Woodpecker CI step for studio-api service # Add this step to your .woodpecker.yml # NOTE: verify step is replicated in all component templates (service, app-react, # app-astro, app-nextjs, worker). Update all 5 if changing the verify logic. build-studio-api: depends_on: [preflight] image: woodpeckerci/plugin-kaniko settings: registry: registry.threesix.ai repo: foundary-test-1770773605/studio-api tags: - latest - ${CI_COMMIT_SHA:0:8} context: . dockerfile: services/studio-api/Dockerfile cache: true skip-tls-verify: true when: branch: main event: push verify-studio-api: depends_on: [build-studio-api] image: alpine/curl failure: ignore commands: - | TAG="${CI_COMMIT_SHA:0:8}" REPO="foundary-test-1770773605/studio-api" REGISTRY="registry.threesix.ai" echo "==> Verifying image $REGISTRY/$REPO:$TAG exists in registry" HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" \ --insecure \ --connect-timeout 10 \ --max-time 15 \ "https://$REGISTRY/v2/$REPO/manifests/$TAG" \ -H "Accept: application/vnd.docker.distribution.manifest.v2+json") if [ "$HTTP_CODE" = "200" ]; then echo "==> Image verified: $REGISTRY/$REPO:$TAG" exit 0 elif [ "$HTTP_CODE" = "404" ]; then echo "==> WARNING: Image $REGISTRY/$REPO:$TAG not found in registry" echo " Build may have failed. Deploy will be skipped." exit 1 else echo "==> WARNING: Registry check returned HTTP $HTTP_CODE" exit 0 fi when: branch: main event: push deploy-studio-api: depends_on: [verify-studio-api] image: bitnami/kubectl:latest commands: - kubectl set image deployment/foundary-test-1770773605-studio-api studio-api=registry.threesix.ai/foundary-test-1770773605/studio-api:${CI_COMMIT_SHA:0:8} -n projects || echo "Deployment not found, skipping" - kubectl scale deployment/foundary-test-1770773605-studio-api --replicas=1 -n projects 2>/dev/null || true when: branch: main event: push # Woodpecker CI step for studio-ui React app # Add this step to your .woodpecker.yml build-studio-ui: depends_on: [preflight] image: woodpeckerci/plugin-kaniko settings: registry: registry.threesix.ai repo: foundary-test-1770773605/studio-ui tags: - latest - ${CI_COMMIT_SHA:0:8} context: . dockerfile: apps/studio-ui/Dockerfile cache: true skip-tls-verify: true when: branch: main event: push verify-studio-ui: depends_on: [build-studio-ui] image: alpine/curl failure: ignore commands: - | TAG="${CI_COMMIT_SHA:0:8}" REPO="foundary-test-1770773605/studio-ui" REGISTRY="registry.threesix.ai" echo "==> Verifying image $REGISTRY/$REPO:$TAG exists in registry" HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" \ --insecure \ --connect-timeout 10 \ --max-time 15 \ "https://$REGISTRY/v2/$REPO/manifests/$TAG" \ -H "Accept: application/vnd.docker.distribution.manifest.v2+json") if [ "$HTTP_CODE" = "200" ]; then echo "==> Image verified: $REGISTRY/$REPO:$TAG" exit 0 elif [ "$HTTP_CODE" = "404" ]; then echo "==> WARNING: Image $REGISTRY/$REPO:$TAG not found in registry" echo " Build may have failed. Deploy will be skipped." exit 1 else echo "==> WARNING: Registry check returned HTTP $HTTP_CODE" exit 0 fi when: branch: main event: push deploy-studio-ui: depends_on: [verify-studio-ui] image: bitnami/kubectl:latest commands: - kubectl set image deployment/foundary-test-1770773605-studio-ui studio-ui=registry.threesix.ai/foundary-test-1770773605/studio-ui:${CI_COMMIT_SHA:0:8} -n projects || echo "Deployment not found, skipping" - kubectl scale deployment/foundary-test-1770773605-studio-ui --replicas=1 -n projects 2>/dev/null || true when: branch: main event: push # Do not remove the marker above - component steps are inserted here # Sync point after all component builds/deploys complete # depends_on is updated dynamically when components are added build-complete: depends_on: [preflight, deploy-studio-ui, deploy-studio-api] # BUILD_COMPLETE_DEPS image: alpine:3.19 commands: - echo "All component builds complete" when: branch: main event: push # Services deployed sync point - fires after all deployments complete # Use this to detect when services are ready (before docs generation) # This allows wait_pipeline to succeed before docs steps run services-deployed: depends_on: [build-complete] image: alpine:3.19 commands: - echo "==> All services deployed successfully" - echo " Pipeline is now considered successful for service deployment" - echo " Documentation generation continues independently" when: branch: main event: push # Export OpenAPI specs from built services # Runs after services-deployed to ensure all services are ready # Uses failure:ignore so doc failures don't block pipeline success export-openapi: depends_on: [services-deployed] failure: ignore image: golang:1.25 commands: - | echo "==> Exporting OpenAPI specs from services" for svc_dir in services/*/; do [ -d "$svc_dir" ] || continue svc=$(basename "$svc_dir") [ "$svc" = ".gitkeep" ] && continue if [ -f "$svc_dir/cmd/server/main.go" ]; then echo " Exporting spec for $svc" (cd "$svc_dir" && go run ./cmd/server --export-openapi > openapi.json) || { echo " WARNING: Failed to export spec for $svc" continue } echo " Generated $svc_dir/openapi.json" fi done when: branch: main event: push # Generate API documentation from OpenAPI specs generate-docs: image: node:20-slim depends_on: [export-openapi] commands: - npm install -g widdershins - | echo "==> Generating Slate markdown from OpenAPI specs" mkdir -p docs/source/includes found_specs=0 for spec in services/*/openapi.json; do if [ -f "$spec" ]; then svc=$(dirname "$spec" | xargs basename) echo " Converting $svc" widdershins \ --language_tabs 'shell:curl' 'go:Go' \ --summary \ --omitHeader \ --resolve \ --shallowSchemas \ "$spec" \ -o "docs/source/includes/_${svc}.md" found_specs=$((found_specs + 1)) fi done echo "==> Converted $found_specs service specs" when: branch: main event: push # Build Slate static documentation (skipped if no docs infrastructure) build-docs: image: ruby:3.2-slim depends_on: [generate-docs] commands: - | if [ ! -d "docs" ] || [ ! -f "docs/Gemfile" ]; then echo "==> No docs/ directory or Gemfile found, skipping Slate build" exit 0 fi - apt-get update && apt-get install -y build-essential nodejs - cd docs && bundle install --jobs 4 - cd docs && bundle exec middleman build --clean - echo "==> Docs built to docs/build/" when: branch: main event: push # Build and push docs-nginx image (skipped if no docs build output) # failure: ignore allows pipeline to continue if docs weren't built build-docs-image: depends_on: [build-docs] image: woodpeckerci/plugin-kaniko failure: ignore settings: registry: registry.threesix.ai repo: foundary-test-1770773605-docs tags: - latest - ${CI_COMMIT_SHA:0:8} context: docs dockerfile: docs/Dockerfile.nginx cache: true skip-tls-verify: true when: branch: main event: push # Verify docs image exists in registry before deploying # Prevents ImagePullBackOff errors from missing/failed image builds verify-docs-image: image: alpine/curl depends_on: [build-docs-image] failure: ignore commands: - | TAG="${CI_COMMIT_SHA:0:8}" REPO="foundary-test-1770773605-docs" REGISTRY="registry.threesix.ai" # Check if docs were built (same check as deploy-docs) if [ ! -d "docs/build" ]; then echo "==> No docs build output, skipping verification" exit 0 fi echo "==> Verifying image $REGISTRY/$REPO:$TAG exists in registry" # Query registry v2 API to check if manifest exists # Returns 200 if image exists, 404 if not HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" \ --insecure \ "https://$REGISTRY/v2/$REPO/manifests/$TAG" \ -H "Accept: application/vnd.docker.distribution.manifest.v2+json") if [ "$HTTP_CODE" = "200" ]; then echo "==> Image verified: $REGISTRY/$REPO:$TAG" # Create marker file for deploy-docs to check touch /tmp/image-verified exit 0 elif [ "$HTTP_CODE" = "404" ]; then echo "==> WARNING: Image $REGISTRY/$REPO:$TAG not found in registry" echo " This may indicate the build step failed or is still pushing" echo " Deploy step will be skipped to prevent ImagePullBackOff" exit 1 else echo "==> WARNING: Registry check returned HTTP $HTTP_CODE" echo " Proceeding cautiously - deploy may fail if image missing" exit 0 fi when: branch: main event: push # Deploy docs to docs.dfq64dsk.threesix.ai (skipped if no docs image was built or verified) deploy-docs: image: bitnami/kubectl:latest depends_on: [verify-docs-image] failure: ignore commands: - | # Check if docs image exists by trying to describe the deployment # If this is the first build, the deployment won't exist yet if [ ! -d "docs/build" ]; then echo "==> No docs build output, skipping deployment" exit 0 fi - | cat <