Fix payroll summary email (SES config-set permission) + isolate failures (#114)
Some checks failed
Deploy / deploy (push) Has been cancelled
Deploy / release (push) Has been cancelled

* Fix payroll email: grant SES config-set permission + isolate failures

The weekly pay-summary email to payroll has been failing with SES
AccessDenied since 2026-06-08. The sending identity (seahaven.com) gained
a default configuration set (seahaven-email-events), and SES authorizes
SendEmail against the config-set ARN as well as the identity — but the
WeeklyPostFunction role only granted ses:SendEmail on identity/*.

- template.yaml: add the configuration-set ARN (scoped to the known set
  name) to the SES policy so sends are authorized again.
- weekly-post/app.py: wrap _send_pay_email in try/except so a delivery
  failure can never abort the handler before the Slack schedule post.
  Previously the SES error also blocked the two-week schedule post.
- Add a regression test covering the isolation.

Cross-family GPT-4.1 IAM review: APPROVE.

* Bump to v1.10.1 in CHANGELOG and sync App Home copy
This commit is contained in:
Adam Moussa 2026-06-15 13:24:32 -04:00 • committed by GitHub
parent 8e90d41b6c
commit 1e1e5176a3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 54 additions and 2 deletions

View file

@ -10,6 +10,15 @@ fine and still supported.
---
## v1.10.1 — June 15, 2026
**Payroll summary emails are sending again.** The automated weekly pay summary
to payroll had stopped going out (an email-permissions change on our side blocked
it from June 8 onward). Fixed the permission so the emails send, and made the
Monday job sturdier: if the payroll email ever fails again, it no longer stops
the on-call schedule from being posted in Slack. The two missed summaries were
re-sent by hand.
## v1.10.0 — June 11, 2026
**The bot now tells you what's new.** Two things:

View file

@ -10,6 +10,15 @@ fine and still supported.
---
## v1.10.1 — June 15, 2026
**Payroll summary emails are sending again.** The automated weekly pay summary
to payroll had stopped going out (an email-permissions change on our side blocked
it from June 8 onward). Fixed the permission so the emails send, and made the
Monday job sturdier: if the payroll email ever fails again, it no longer stops
the on-call schedule from being posted in Slack. The two missed summaries were
re-sent by hand.
## v1.10.0 — June 11, 2026
**The bot now tells you what's new.** Two things:

View file

@ -183,8 +183,16 @@ def handler(event, context):
else:
logger.warning("PAY_REPORT_USER not set — skipping Slack pay summary")
# Email pay summary to payroll
_send_pay_email(week_label, pay_record)
# Email pay summary to payroll. Isolated so a delivery failure (e.g.
# an SES permission/identity issue) can never abort the rest of the
# handler — the Slack schedule post below must still go out.
try:
_send_pay_email(week_label, pay_record)
except Exception:
logger.exception(
"Failed to send pay summary email to payroll for week of %s",
week_key,
)
# --- Delete previous week's schedule post ---
old_post = schedule.get_schedule_post(channel_id)

View file

@ -138,6 +138,11 @@ Resources:
- ses:SendEmail
Resource:
- !Sub "arn:aws:ses:${AWS::Region}:${AWS::AccountId}:identity/*"
# The sending identity has a default configuration set
# (seahaven-email-events); SES authorizes SendEmail against the
# config-set resource too, so it must be granted alongside the
# identity or the send is denied. Scoped to the known set name.
- !Sub "arn:aws:ses:${AWS::Region}:${AWS::AccountId}:configuration-set/seahaven-email-events"
Events:
# EST: 7am ET = 12:00 UTC (Nov-Mar)
WeeklyPostEST:

View file

@ -60,6 +60,27 @@ def test_calculates_and_dms_pay(weeklypost_app, schedule, seed, slack, env):
assert dm_calls
@freeze_time(MON_0800)
def test_pay_email_failure_does_not_block_schedule_post(
weeklypost_app, schedule, seed, slack, env, monkeypatch
):
# Previous week has an assigned shift, so the pay/email path runs.
seed.config(shift_rate="50")
seed.weekly("Monday", "114", "Alice")
# SES delivery blows up (e.g. a permission/identity issue).
monkeypatch.setattr(
weeklypost_app,
"_send_pay_email",
MagicMock(side_effect=Exception("SES AccessDenied")),
)
result = weeklypost_app.handler({"force": True}, None)
# The email failure is swallowed; the schedule post still goes out.
assert result["posted"] is True
assert schedule.get_schedule_post("C_TEST")["message_ts"] == "999.000"
@freeze_time(MON_0800)
def test_deletes_previous_schedule_post(weeklypost_app, schedule, seed, slack, env):
seed.schedule_post("C_TEST", "111.111")