-
Notifications
You must be signed in to change notification settings - Fork 0
170 lines (158 loc) · 6.86 KB
/
Copy pathaggregate.yml
File metadata and controls
170 lines (158 loc) · 6.86 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
name: nightly analytics regen
# Closes M6-B Phase 3: nightly aggregator run against the live DDB
# table. Replaces the manual `python3 tools/aggregate-analytics.py
# + git commit + push` loop that ran during M6-B verification.
#
# What it does:
# 1. Assumes the MagicMonitorGithubDeploy role via OIDC.
# 2. Runs `python3 tools/aggregate-analytics.py` (default source
# is ddb post-M6-B Phase 4).
# 3. If the regenerated snapshot/baselines differ from main,
# commits the diff back to main as github-actions[bot].
# Amplify auto-deploys the new snapshot ~3-5 min later.
#
# Cadence: 08:00 UTC = 04:00 EDT / 03:00 EST. Pre-dawn ET keeps
# the freshly-regenerated snapshot in front of every morning's
# users without competing with the poller's :00/:02 cadence.
#
# No-op runs (no data drift since last regen) commit nothing.
# A no-op shows up as a successful run with the
# "snapshot unchanged" log line — useful as a passive heartbeat
# that the OIDC role + DDB perms + aggregator script are all
# still working.
on:
schedule:
- cron: "0 8 * * *"
workflow_dispatch:
inputs:
dry_run:
description: "Run aggregator but skip the commit + push (smoke test)"
type: boolean
default: false
# Repo-level write needed so the bot commit can push back to main.
# id-token write is required for the OIDC -> IAM role exchange.
permissions:
contents: write
id-token: write
# Prevent overlapping runs if a manual dispatch fires while the
# scheduled one is still going. Subsequent run waits for the
# in-flight one to finish.
concurrency:
group: aggregate-snapshot
cancel-in-progress: false
jobs:
regen-snapshot:
runs-on: ubuntu-latest
# Local bare run takes ~85s. 5min budget covers DDB latency
# variance and the (rare) cold-DDB-scan case without burning
# the runner on a hung connection.
timeout-minutes: 5
steps:
- name: Checkout
uses: actions/checkout@v5
# Default GITHUB_TOKEN is sufficient for push-back to main
# because permissions.contents is set to write above.
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.12"
cache: pip
- name: Install boto3
# Aggregator pulls only stdlib + boto3 when --source=ddb.
# No requirements file lives at repo root; the inline
# install is simpler than spinning up a venv.
run: python -m pip install --upgrade pip boto3
- name: Configure AWS credentials via OIDC
uses: aws-actions/configure-aws-credentials@v4
with:
# Role ARN is deterministic (account + role name) and the
# account id is already public in infra/lib/disney-stack.ts.
# Hardcoding here avoids the friction of a per-repo secret
# for a value that isn't sensitive.
role-to-assume: arn:aws:iam::601669029997:role/MagicMonitorGithubDeploy
aws-region: us-east-2
- name: Run aggregator (DDB source)
# Tee stdout so the summary step can extract the stats line
# the aggregator prints (span, totals, rides) without
# re-running the script.
run: python3 tools/aggregate-analytics.py 2>&1 | tee /tmp/aggregator.log
- name: Upload snapshot + baselines to S3 (MCP analytics)
# The HTTPS MCP Lambda (DisneyMcpStack) reads these from S3 on
# cold start so the read-side analytics tools see the nightly
# regen without a redeploy. Always upload after a successful
# aggregator run (not gated on the git diff) so S3 stays
# authoritative even if a prior run's upload failed. The deploy
# role has AdministratorAccess, so PutObject needs no extra IAM.
# Bucket name is deterministic (account-suffixed) — see
# MCP_DATA_BUCKET_NAME in infra/lib/disney-mcp-stack.ts.
env:
DRY_RUN: ${{ github.event.inputs.dry_run }}
BUCKET: magic-monitor-mcp-data-601669029997
run: |
set -euo pipefail
if [ "${DRY_RUN:-false}" = "true" ]; then
echo "dry_run=true — skipping S3 upload"
exit 0
fi
aws s3 cp web/src/data/analytics-snapshot.json \
"s3://${BUCKET}/analytics-snapshot.json"
aws s3 cp infra/lambda/poller/baselines.json \
"s3://${BUCKET}/baselines.json"
echo "uploaded snapshot + baselines to s3://${BUCKET}/"
- name: Commit snapshot if changed
id: commit
env:
DRY_RUN: ${{ github.event.inputs.dry_run }}
run: |
set -euo pipefail
# Limit the diff check to the files the aggregator writes —
# avoids false positives if something else in the tree
# changes between checkout and now (shouldn't, but cheap
# to scope correctly).
targets=(
web/src/data/analytics-snapshot.json
web/src/data/analytics-snapshot.ts
infra/lambda/poller/baselines.json
)
if git diff --quiet -- "${targets[@]}"; then
echo "snapshot unchanged — nothing to commit"
echo "outcome=unchanged" >> "$GITHUB_OUTPUT"
exit 0
fi
if [ "${DRY_RUN:-false}" = "true" ]; then
echo "dry_run=true — aggregator produced a diff but skipping commit + push"
git diff --stat -- "${targets[@]}"
echo "outcome=dry-run-diff" >> "$GITHUB_OUTPUT"
exit 0
fi
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add "${targets[@]}"
# Single-line commit message keeps the YAML block scalar
# simple. Workflow file itself documents the "why".
git commit -m "chore(analytics): nightly snapshot regen from DDB"
# Rebase before push to handle the small race window where
# a human pushed to main between checkout and now. If the
# rebase fails (real conflict on snapshot/baselines), the
# run fails loudly — the next nightly catches up.
git pull --rebase origin main
git push origin main
echo "outcome=committed" >> "$GITHUB_OUTPUT"
- name: Write run summary
# Always runs (even on failure) so a failed nightly still
# surfaces the aggregator log on the run page.
if: always()
run: |
{
echo "## Aggregator run"
echo ""
outcome="${{ steps.commit.outputs.outcome || 'failed' }}"
echo "- **Outcome:** \`$outcome\`"
echo ""
echo "### Aggregator output"
echo '```'
# Last 25 lines covers the "wrote …" block + totals
# without dumping the per-cluster spam.
tail -n 25 /tmp/aggregator.log 2>/dev/null || echo "(no log captured)"
echo '```'
} >> "$GITHUB_STEP_SUMMARY"