# formulas/run-publish-site.toml — Deploy disinto.ai landing page from site/ directory
#
# Trigger: action issue created by planner (gap analysis), dev-poll (post-merge
# hook detecting site/ changes), or gardener (periodic SHA drift check).
#
# The action-agent picks up the issue, executes these steps, posts results
# as a comment, and closes the issue.

name        = "run-publish-site"
description = "Deploy disinto.ai landing page from site/ directory"
version     = 1

[vars.commit_sha]
description = "Git commit SHA to deploy (default: HEAD of main)"
required    = false
default     = "HEAD"

[vars.site_source]
description = "Source directory within the repo containing site files"
required    = false
default     = "site/"

[vars.site_root]
description = "Symlink path that Caddy serves (points to latest deploy)"
required    = false
default     = "/home/debian/disinto-site"

[vars.deploy_dir]
description = "Parent directory for timestamped deploy snapshots"
required    = false
default     = "/home/debian/disinto-deploys"

[vars.max_deploys]
description = "Number of historical deploys to keep (older ones are pruned)"
required    = false
default     = "5"

[vars.repo_root]
description = "Local checkout path for the disinto repo"
required    = false
default     = "/home/debian/dark-factory"

[[steps]]
id          = "pull-latest"
title       = "Pull latest main and resolve target SHA"
description = """
Pull the latest changes and resolve the deploy target:

  cd {{repo_root}}
  git pull origin main

Resolve the commit SHA to deploy:
  - If {{commit_sha}} is "HEAD", use the current HEAD of main.
  - Otherwise, verify {{commit_sha}} exists in the repo.

Record the resolved SHA to a temp file so subsequent steps can read it
(each step runs in a separate shell — variables do not persist):
  DEPLOY_SHA=$(git rev-parse {{commit_sha}})
  echo "$DEPLOY_SHA" > /tmp/publish-site-deploy-sha

Check whether the currently deployed SHA (if any) already matches:
  if [ -L {{site_root}} ]; then
    CURRENT=$(cat "$(readlink -f {{site_root}})/.deploy-sha" 2>/dev/null || echo "none")
    if [ "$CURRENT" = "$DEPLOY_SHA" ]; then
      echo "Already deployed at $DEPLOY_SHA — nothing to do."
      echo "NOOP" > /tmp/publish-site-deploy-sha
      Post a comment on the issue noting the no-op, then close the issue
      and stop. Do NOT proceed to subsequent steps.
    fi
  fi
"""

[[steps]]
id          = "create-deploy"
title       = "Create timestamped deploy directory"
description = """
First, read the deploy SHA from the temp file written by pull-latest.
If it says NOOP, stop — the site is already at the target SHA:
  DEPLOY_SHA=$(cat /tmp/publish-site-deploy-sha)
  [ "$DEPLOY_SHA" = "NOOP" ] && { echo "No-op deploy — skipping."; exit 0; }

Create a timestamped deploy directory and extract site files into it:

  TIMESTAMP=$(date -u '+%Y-%m-%d-%H%M%S')
  TARGET="{{deploy_dir}}/${TIMESTAMP}"
  mkdir -p "$TARGET"

Extract site files from the resolved commit:
  cd {{repo_root}}
  git archive "${DEPLOY_SHA}:{{site_source}}" | tar -x -C "$TARGET"

Write deploy metadata:
  echo "$DEPLOY_SHA" > "$TARGET/.deploy-sha"

Persist TARGET path for subsequent steps:
  echo "$TARGET" > /tmp/publish-site-deploy-target

Verify the extraction produced files:
  ls -la "$TARGET/"
  [ -f "$TARGET/index.html" ] || { echo "ERROR: index.html missing"; exit 1; }
"""
needs       = ["pull-latest"]

[[steps]]
id          = "activate"
title       = "Atomically switch symlink to new deploy"
description = """
Read the deploy target from the temp file:
  TARGET=$(cat /tmp/publish-site-deploy-target)

Atomically switch the site root symlink to the new deploy.
Use ln + mv -T for a true atomic rename (single syscall):

  ln -s "$TARGET" "{{site_root}}.new"
  mv -T "{{site_root}}.new" {{site_root}}

Verify the symlink points to the correct directory:
  readlink -f {{site_root}}
"""
needs       = ["create-deploy"]

[[steps]]
id          = "prune-old-deploys"
title       = "Log deploy and prune old snapshots"
description = """
Read state from temp files:
  DEPLOY_SHA=$(cat /tmp/publish-site-deploy-sha)
  TARGET=$(cat /tmp/publish-site-deploy-target)

Append to deploy history log BEFORE pruning, so the record is written
even if an old entry is about to be removed:
  printf '{"sha":"%s","ts":"%s","dir":"%s"}\n' \
    "$DEPLOY_SHA" "$(date -u +%Y-%m-%dT%H:%M:%SZ)" "$TARGET" \
    >> "{{deploy_dir}}/.deploy-history.jsonl"

Keep only the last {{max_deploys}} deploys. Remove older ones:

  cd {{deploy_dir}}
  ls -1d 20*/ 2>/dev/null | sort | head -n -{{max_deploys}} | while read -r old; do
    echo "Pruning old deploy: $old"
    rm -rf "$old"
  done
"""
needs       = ["activate"]

[[steps]]
id          = "verify"
title       = "Verify live site matches deployed content"
description = """
Read state from temp files:
  DEPLOY_SHA=$(cat /tmp/publish-site-deploy-sha)
  TARGET=$(cat /tmp/publish-site-deploy-target)

Verify the site is serving the new content:

  # Check site is reachable (follow redirects with -L)
  HTTP_CODE=$(curl -sL -o /dev/null -w '%{http_code}' https://disinto.ai/)
  [ "$HTTP_CODE" = "200" ] || { echo "ERROR: site returned HTTP $HTTP_CODE"; exit 1; }

  # Extract a unique string from the deployed file to verify content
  EXPECTED=$(grep -oP '(?<=<title>)[^<]+' "$TARGET/index.html" | head -1)
  if [ -z "$EXPECTED" ]; then
    echo "ERROR: could not extract <title> from deployed index.html"
    exit 1
  fi
  curl -sL https://disinto.ai/ | grep -qF "$EXPECTED" \
    && echo "VERIFIED: title matches" \
    || echo "WARNING: title mismatch — cache may need time to clear"

  # List deployed files
  echo "Deployed files:"
  find "$TARGET" -type f | sort

Report:
  echo "Deploy complete: SHA=$DEPLOY_SHA dir=$TARGET"

Clean up temp files:
  rm -f /tmp/publish-site-deploy-sha /tmp/publish-site-deploy-target
"""
needs       = ["prune-old-deploys"]

[[steps]]
id          = "verify-observable"
title       = "Verify engagement measurement is active"
description = """
Every deploy must confirm that the addressable has a return path (observable).
This is the bridge from Ship (Fold 2) to Learn (Fold 3).

Check 1 — Caddy access log exists and is being written:
  CADDY_LOG="${CADDY_ACCESS_LOG:-/var/log/caddy/access.log}"
  if [ ! -f "$CADDY_LOG" ]; then
    echo "WARNING: Caddy access log not found at $CADDY_LOG"
    echo "Engagement measurement is NOT active — set CADDY_ACCESS_LOG if the path differs."
  else
    AGE_MIN=$(( ($(date +%s) - $(stat -c %Y "$CADDY_LOG" 2>/dev/null || echo 0)) / 60 ))
    if [ "$AGE_MIN" -gt 60 ]; then
      echo "WARNING: Caddy access log is ${AGE_MIN} minutes old — may not be active"
    else
      echo "OK: Caddy access log is active (last written ${AGE_MIN}m ago)"
    fi
  fi

Check 2 — collect-engagement.sh is present in the repo:
  FACTORY_ROOT="${FACTORY_ROOT:-/home/debian/dark-factory}"
  if [ -x "$FACTORY_ROOT/site/collect-engagement.sh" ]; then
    echo "OK: collect-engagement.sh is present and executable"
  else
    echo "WARNING: collect-engagement.sh not found or not executable"
  fi

Check 3 — engagement evidence has been collected at least once:
  EVIDENCE_DIR="$OPS_REPO_ROOT/evidence/engagement"
  LATEST=$(ls -1t "$EVIDENCE_DIR"/*.json 2>/dev/null | head -1 || true)
  if [ -n "$LATEST" ]; then
    echo "OK: Latest engagement report: $LATEST"
    jq -r '"  visitors=\(.unique_visitors) pages=\(.page_views) referrals=\(.referred_visitors)"' "$LATEST" 2>/dev/null || true
  else
    echo "NOTE: No engagement reports yet — run: bash site/collect-engagement.sh"
    echo "The first report will appear after the cron job runs (daily at 23:55 UTC)."
  fi

Summary:
  echo ""
  echo "Observable status: addressable=disinto.ai measurement=caddy-access-logs"
  echo "Evidence path: \$OPS_REPO_ROOT/evidence/engagement/YYYY-MM-DD.json"
  echo "Consumer: planner reads ops repo evidence/engagement/ during gap analysis"
"""
needs       = ["verify"]
