diff --git a/cmd/webhook/main.go b/cmd/webhook/main.go index c241f33..7c06422 100644 --- a/cmd/webhook/main.go +++ b/cmd/webhook/main.go @@ -30,7 +30,9 @@ func main() { repoDir = os.Getenv("HOME") + "/Projects/oikos" } - secret := resolveWebhookHMAC(ctx) + // Create secrets manager once, share between HMAC resolution and deploy + sec := newSecrets() + secret := resolveWebhookHMAC(ctx, sec) if secret == "" { fmt.Fprintln(os.Stderr, "WEBHOOK_HMAC_SECRET must be set (env var or Infisical webhook_hmac-secret)") os.Exit(1) @@ -70,11 +72,16 @@ func main() { w.Write([]byte(`{"status":"deploy started"}`)) safego.Go("webhook:deploy", func() { + apiToken := "" + if sec != nil { + apiToken = secrets.ResolveSecret(ctx, sec, "api_token", "") + } cmd := exec.Command(repoDir + "/scripts/deploy.sh") cmd.Dir = repoDir cmd.Env = append(os.Environ(), "REPO_DIR="+repoDir, "PROFILE=full", + "OIKOS_API_TOKEN="+apiToken, ) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr @@ -99,10 +106,9 @@ func main() { } } -// resolveWebhookHMAC fetches the webhook HMAC secret from Infisical, -// falling back to the WEBHOOK_HMAC_SECRET env var. -func resolveWebhookHMAC(ctx context.Context) string { - sec := secrets.NewManagerFromConfig( +// newSecrets creates the Infisical secrets manager from env vars. +func newSecrets() *secrets.Manager { + return secrets.NewManagerFromConfig( os.Getenv("OIKOS_INFISICAL_SITE_URL"), os.Getenv("OIKOS_INFISICAL_CLIENT_ID"), os.Getenv("OIKOS_INFISICAL_CLIENT_SECRET"), @@ -110,6 +116,14 @@ func resolveWebhookHMAC(ctx context.Context) string { os.Getenv("OIKOS_INFISICAL_ENV"), os.Getenv("OIKOS_SECRETS_DIR"), ) - envFallback := os.Getenv("WEBHOOK_HMAC_SECRET") - return secrets.ResolveSecret(ctx, sec, "webhook_hmac-secret", envFallback) } + +// resolveWebhookHMAC fetches the webhook HMAC secret from Infisical, +// falling back to the WEBHOOK_HMAC_SECRET env var. +func resolveWebhookHMAC(ctx context.Context, sec *secrets.Manager) string { + envFallback := os.Getenv("WEBHOOK_HMAC_SECRET") + if sec == nil { + return envFallback + } + return secrets.ResolveSecret(ctx, sec, "webhook_hmac-secret", envFallback) +} \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 1ef68e9..1281d3b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -307,7 +307,7 @@ services: # Infisical self-hosted (Phase 5 secrets management) infisical: - image: infisical/infisical:v0.99.1 + image: infisical/infisical:v0.162.19 restart: unless-stopped profiles: ["infisical", "full"] depends_on: diff --git a/scripts/deploy.sh b/scripts/deploy.sh index 25ee249..ba5200a 100755 --- a/scripts/deploy.sh +++ b/scripts/deploy.sh @@ -8,6 +8,29 @@ # D2 — versioned images: tags every built image v$VERSION (from VERSION file), # keeps the last 3 tags per service for rollback. +# Notify on deploy failure via Matrix. Uses Oikos API to raise an event +# so the scheduler picks it up and alerts via the notifier. +notify_deploy_failure() { + local reason="$1" + local sha="${SHA:-unknown}" + echo "NOTIFY: deploy failed — $reason" + # Try to raise an event through the Oikos API (best-effort, silent failure) + if [ -n "${OIKOS_API_TOKEN:-}" ]; then + curl -sf -X POST "http://localhost:8090/api/v1/events" \ + -H "Authorization: Bearer $OIKOS_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d "{\"type\":\"deploy.failed\",\"severity\":\"critical\",\"source\":\"webhook\",\"data\":{\"sha\":\"$sha\",\"reason\":\"$reason\"}}" \ + >/dev/null 2>&1 || true + fi + # Also try Matrix directly via the notifier's webhook endpoint if configured + if [ -n "${MATRIX_WEBHOOK_URL:-}" ]; then + curl -sf -X POST "$MATRIX_WEBHOOK_URL" \ + -H "Content-Type: application/json" \ + -d "{\"msgtype\":\"m.text\",\"body\":\"🚨 Deploy failed: $reason (sha: $sha)\"}" \ + >/dev/null 2>&1 || true + fi +} + set -e REPO_DIR="${REPO_DIR:-$PWD}" @@ -45,7 +68,8 @@ if ! mkdir "$LOCKDIR" 2>/dev/null; then mkdir "$LOCKDIR" fi echo $$ > "$LOCKDIR/pid" -trap 'rm -rf "$LOCKDIR" 2>/dev/null || true' EXIT INT TERM +trap 'rc=$?; rm -rf "$LOCKDIR" 2>/dev/null || true; if [ "$_ok" != "1" ]; then notify_deploy_failure "deploy aborted (exit $rc)"; fi' EXIT +_ok=0 cd "$REPO_DIR" @@ -248,4 +272,6 @@ else echo "SKIP: seed-secrets.sh not found" fi +# All steps completed successfully — clear failure trap +_ok=1 exit 0