diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f9114b..6418eb7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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: diff --git a/src/slack-bot/CHANGELOG.md b/src/slack-bot/CHANGELOG.md index 0f9114b..6418eb7 100644 --- a/src/slack-bot/CHANGELOG.md +++ b/src/slack-bot/CHANGELOG.md @@ -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: diff --git a/src/weekly-post/app.py b/src/weekly-post/app.py index 1fded02..e904409 100644 --- a/src/weekly-post/app.py +++ b/src/weekly-post/app.py @@ -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) diff --git a/template.yaml b/template.yaml index c2bb6f6..6a1dcf3 100644 --- a/template.yaml +++ b/template.yaml @@ -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: diff --git a/tests/weekly_post/test_handler.py b/tests/weekly_post/test_handler.py index cb80609..0100185 100644 --- a/tests/weekly_post/test_handler.py +++ b/tests/weekly_post/test_handler.py @@ -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")