new-site/infra/cron/pw-hc-refresh
justin 4dc5690666 infra: codify the email-campaign pipeline in Ansible (new mail-pipeline role)
The entire outbound campaign pipeline lived ONLY on the host and was never in
IaC -- a fresh rebuild would have silently shipped NO campaigns, NO IP warmup/
ramp, and NO bounce processing. New mail-pipeline role + deploy-mail-pipeline.yml
playbook deploy it from the canonical repo copies:

  cron.d (infra/cron/):
    - pw-trucking-campaign-builder, pw-ifta-campaign, pw-ucr-campaign
    - pw-hc-campaign, pw-hc-nppes, pw-hc-refresh
    - pw-mta-warmup, pw-listmonk-rampcap, pw-hc-rampcap
    - pw-ip-rehab, pw-warmup-tg-alert
  helper scripts (-> /usr/local/bin):
    - pw-mta-warmup, pw-listmonk-rampcap, pw-hc-rampcap, pw-warmup-tg-alert
    - postfix-bounce-notify.sh, postfix-hc-bounce-notify.sh, listmonk-bounce-sync.py
  systemd services:
    - pw-bounce-watcher.service (was missing from repo), pw-hc-bounce-watcher.service

Also creates the deploy-owned {{project_dir}}/logs dir (deploy can't write
/var/log, so a missing dir made cron redirects fail). Added the 6 cron.d files
that existed only on the host, the trucking bounce-watcher unit, and synced
infra/cron/pw-hc-refresh to the live version (revalidation download + enrich
steps). Role wired into site.yml after the mail (OpenDKIM) role.

Part of the email-deliverability incident hardening.
2026-06-17 20:26:01 -05:00

18 lines
1.9 KiB
Text

# NOTE: logs go to /opt/performancewest/logs/ (deploy-owned). The deploy user
# cannot write /var/log, so a /var/log redirect makes cron silently fail before
# the command runs. Ensure /opt/performancewest/logs exists + is deploy-owned.
# Healthcare data refresh: re-check every emailable NPI against the live
# government sources (CMS Revalidation list, OIG LEIE) + MX re-classification
# (Google-host detection) so warmup sends never go stale. Runs Mon/Wed/Fri 06:00
# Central, ~1h before the 07:00 weekday send. Mon/Wed/Fri (vs weekly) shrinks the
# CMS data-lag window to ~2-3 days, so a provider who just completed their
# revalidation stops being targeted faster (fewer "already done" replies).
# Takes ~8 min. SAM is opt-in (--sam-pages); SAM exclusions rarely carry an NPI,
# so OIG LEIE is the NPI-bearing exclusion source. Pipeline:
# 1. hc_data_refresh.py -- re-verify NPIs vs CMS/OIG + MX reclassify
# 2. download CMS revalidation_base.csv (institutional revalidation dates)
# 3. enrich_institutional_revalidation.py -- merge reval dates into the
# institutional CSV consumed by the pw-hc-nppes builder
# 4. build_healthcare_campaigns_cron.py --prune-only -- evict newly-Google-
# hosted + suppressed subscribers from the warmup lists
0 6 * * 1,3,5 deploy cd /opt/performancewest && python3 -u scripts/hc_data_refresh.py >> /opt/performancewest/logs/pw-hc-refresh.log 2>&1 && curl -s "https://data.cms.gov/sites/default/files/2026-05/96484587-20ec-4070-a4de-cd7de3ec0093/revalidation_base.csv" -o data/npi_build/revalidation_base.csv 2>>/opt/performancewest/logs/pw-hc-refresh.log && python3 -u scripts/enrich_institutional_revalidation.py data/hc_nppes_institutional_verified.csv data/npi_build/revalidation_base.csv data/hc_nppes_institutional_enriched.csv >> /opt/performancewest/logs/pw-hc-refresh.log 2>&1 && python3 -u scripts/build_healthcare_campaigns_cron.py --prune-only >> /opt/performancewest/logs/pw-hc-refresh.log 2>&1