Skip to content

Commit e2c090f

Browse files
committed
fix: email digest silent failures — exit non-zero on error + fallback to latest entries
Problems: 1. notify.py and announce.py returned False on failure but never called sys.exit(1), so GitHub Actions marked failed runs as successful. 2. notify.py silently skipped sending when no opportunities were added in the last 7 days, even if the sheet had older entries. Now falls back to the latest entries in the sheet. 3. run.py did not propagate the return value from notify, so a failed email send never surfaced as a non-zero exit code. Changes: - notify.py: add fetch_latest_from_tab() fallback; sys.exit(1) on failure - run.py: return notify result; sys.exit(1) if notify fails - announce.py: sys.exit(1) on send failure
1 parent ab9fe42 commit e2c090f

3 files changed

Lines changed: 52 additions & 5 deletions

File tree

announce.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
import argparse
1616
import os
17+
import sys
1718
import smtplib
1819
import logging
1920
import time
@@ -342,6 +343,9 @@ def main():
342343
if success and not args.force:
343344
_mark_as_sent()
344345

346+
if not success:
347+
sys.exit(1)
348+
345349

346350
if __name__ == "__main__":
347351
main()

notify.py

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
import os
1414
import re
15+
import sys
1516
import imaplib
1617
import smtplib
1718
import argparse
@@ -250,6 +251,36 @@ def fetch_recent_from_tab(tab_name, limit=25):
250251
return []
251252

252253

254+
def fetch_latest_from_tab(tab_name, limit=25):
255+
"""Fetch up to `limit` most recent entries regardless of date added."""
256+
try:
257+
client = _get_sheet_client()
258+
ss = client.open_by_key(SPREADSHEET_ID)
259+
try:
260+
ws = ss.worksheet(tab_name)
261+
except Exception:
262+
logger.warning(f"notify: Tab '{tab_name}' not found.")
263+
return []
264+
265+
all_values = ws.get_all_values()
266+
if not all_values:
267+
return []
268+
raw_headers = all_values[0]
269+
headers = [h.strip() for h in raw_headers]
270+
rows_raw = all_values[1:]
271+
rows = []
272+
for r in rows_raw:
273+
padded = list(r) + [""] * max(0, len(headers) - len(r))
274+
rows.append(dict(zip(headers, padded[:len(headers)])))
275+
276+
result = rows[-limit:] if len(rows) > limit else rows
277+
logger.info(f"notify: {len(result)} total entries from '{tab_name}' (fallback).")
278+
return result
279+
except Exception as exc:
280+
logger.error(f"notify: Could not fetch '{tab_name}' — {exc}")
281+
return []
282+
283+
253284
# ── Email HTML (SWElist-inspired clean format) ───────────────────────────────
254285

255286
def _opp_list_items(opps):
@@ -515,11 +546,19 @@ def run_notify(dry_run=False):
515546
nigeria_opps = fetch_recent_from_tab("Nigeria", limit=25)
516547
intl_opps = fetch_recent_from_tab("International", limit=25)
517548

549+
if not nigeria_opps and not intl_opps:
550+
logger.warning(
551+
"notify: No opportunities added in the last "
552+
f"{RECENT_DAYS} days. Falling back to latest entries."
553+
)
554+
nigeria_opps = fetch_latest_from_tab("Nigeria", limit=25)
555+
intl_opps = fetch_latest_from_tab("International", limit=25)
556+
518557
if not nigeria_opps and not intl_opps and not dry_run:
519-
logger.warning("notify: No recent opportunities. No email sent.")
558+
logger.error("notify: No opportunities found at all. No email sent.")
520559
return False
521560
if not nigeria_opps and not intl_opps:
522-
logger.warning("notify: No recent opportunities; writing empty dry-run preview.")
561+
logger.warning("notify: No opportunities found; writing empty dry-run preview.")
523562

524563
recipients = build_recipient_list()
525564

@@ -542,7 +581,9 @@ def main():
542581
help="Build email_preview.html without sending any email"
543582
)
544583
args = parser.parse_args()
545-
run_notify(dry_run=args.dry_run)
584+
ok = run_notify(dry_run=args.dry_run)
585+
if not ok:
586+
sys.exit(1)
546587

547588

548589
if __name__ == "__main__":

run.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ def run_notify(dry_run=False):
6262
"""Read the sheet and email the digest to all subscribers."""
6363
sys.path.insert(0, SCRIPT_DIR)
6464
from notify import run_notify as _run_notify
65-
_run_notify(dry_run=dry_run)
65+
return _run_notify(dry_run=dry_run)
6666

6767

6868
def run_broadcast():
@@ -137,7 +137,9 @@ def main():
137137
elif args.cleanup:
138138
run_cleanup()
139139
elif args.notify or args.dry_run:
140-
run_notify(dry_run=args.dry_run)
140+
ok = run_notify(dry_run=args.dry_run)
141+
if not ok:
142+
sys.exit(1)
141143
elif args.schedule:
142144
run_schedule()
143145
else:

0 commit comments

Comments
 (0)