mirror of
https://github.com/Sea-Haven-Industries/afterhours-shift-manager.git
synced 2026-09-30 04:33:10 +00:00
fix(api): retarget holidays per account and ship App Home changelog (PLAT-216)
This commit is contained in:
parent
5b272f9b4a
commit
2a3f30a9cb
5 changed files with 55 additions and 12 deletions
|
|
@ -6,6 +6,7 @@ terraform
|
|||
tests
|
||||
docs
|
||||
*.md
|
||||
!src/slack-bot/CHANGELOG.md
|
||||
__pycache__
|
||||
.pytest_cache
|
||||
.mypy_cache
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ from botocore.exceptions import ClientError
|
|||
|
||||
PROD_ACCOUNT = "011934824531"
|
||||
DEV_ACCOUNT = "710827005802"
|
||||
HOLIDAY_SCHEDULER_ROLE = "afterhours-shift-manager-holiday-scheduler"
|
||||
PREFIXES = ("holiday-activate-", "holiday-deactivate-")
|
||||
_AT = re.compile(r"^at\((\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2})\)$")
|
||||
_DATE = re.compile(r"(\d{4}-\d{2}-\d{2})")
|
||||
|
|
@ -78,6 +79,10 @@ def action_and_date(name: str, existing_input: str) -> tuple[str, str]:
|
|||
return action, date
|
||||
|
||||
|
||||
def holiday_scheduler_role_arn(account: str) -> str:
|
||||
return f"arn:aws:iam::{account}:role/tf-managed/{HOLIDAY_SCHEDULER_ROLE}"
|
||||
|
||||
|
||||
def holiday_sqs_input(action: str, date: str) -> str:
|
||||
return json.dumps({"event": "holiday", "action": action, "date": date})
|
||||
|
||||
|
|
@ -106,7 +111,8 @@ def main() -> int:
|
|||
parser.add_argument("--queue-arn", required=True)
|
||||
parser.add_argument(
|
||||
"--role-arn",
|
||||
default="arn:aws:iam::011934824531:role/tf-managed/afterhours-shift-manager-holiday-scheduler",
|
||||
default="",
|
||||
help="Scheduler execution role. Empty uses the tf-managed holiday role in the caller account.",
|
||||
)
|
||||
parser.add_argument("--execute", action="store_true")
|
||||
args = parser.parse_args()
|
||||
|
|
@ -115,6 +121,7 @@ def main() -> int:
|
|||
if account not in {PROD_ACCOUNT, DEV_ACCOUNT}:
|
||||
print("profile is not seahaven-prod or seahaven-dev", file=sys.stderr)
|
||||
return 2
|
||||
role_arn = args.role_arn.strip() or holiday_scheduler_role_arn(account)
|
||||
|
||||
client = _client(args.profile, args.region)
|
||||
now = datetime.now(timezone.utc)
|
||||
|
|
@ -154,7 +161,7 @@ def main() -> int:
|
|||
ActionAfterCompletion=detail.get("ActionAfterCompletion", "DELETE"),
|
||||
Target={
|
||||
"Arn": args.queue_arn,
|
||||
"RoleArn": args.role_arn,
|
||||
"RoleArn": role_arn,
|
||||
"Input": payload,
|
||||
},
|
||||
)
|
||||
|
|
|
|||
|
|
@ -2168,19 +2168,31 @@ def _admin_holiday_list(respond, schedule):
|
|||
|
||||
@functools.lru_cache(maxsize=1)
|
||||
def _changelog_text() -> str:
|
||||
"""Read the CHANGELOG shipped in this function's package.
|
||||
"""Read the CHANGELOG shipped next to this module.
|
||||
|
||||
Lazy (never at import) and tolerant of a missing file, so the App Home tab
|
||||
degrades to "no What's New section" rather than erroring. The copy lives at
|
||||
``$LAMBDA_TASK_ROOT/CHANGELOG.md`` (synced from the repo root).
|
||||
degrades to "no What's New section" rather than erroring. Lambda zips and
|
||||
the Fargate image both keep ``CHANGELOG.md`` beside ``app.py``.
|
||||
``LAMBDA_TASK_ROOT`` remains a fallback for the zip layout.
|
||||
"""
|
||||
path = os.path.join(os.environ.get("LAMBDA_TASK_ROOT", "."), "CHANGELOG.md")
|
||||
try:
|
||||
with open(path, encoding="utf-8") as fh:
|
||||
return fh.read()
|
||||
except OSError:
|
||||
logger.warning("CHANGELOG.md not found at %s — App Home omits What's New", path)
|
||||
return ""
|
||||
tried = []
|
||||
for path in _changelog_paths():
|
||||
tried.append(path)
|
||||
try:
|
||||
with open(path, encoding="utf-8") as fh:
|
||||
return fh.read()
|
||||
except OSError:
|
||||
continue
|
||||
logger.warning("CHANGELOG.md not found at %s — App Home omits What's New", tried)
|
||||
return ""
|
||||
|
||||
|
||||
def _changelog_paths() -> list[str]:
|
||||
paths = [os.path.join(os.path.dirname(os.path.abspath(__file__)), "CHANGELOG.md")]
|
||||
task_root = os.environ.get("LAMBDA_TASK_ROOT", "").strip()
|
||||
if task_root:
|
||||
paths.append(os.path.join(task_root, "CHANGELOG.md"))
|
||||
return paths
|
||||
|
||||
|
||||
_HOME_OVERVIEW_DAYS = 60
|
||||
|
|
|
|||
|
|
@ -39,3 +39,14 @@ def test_action_and_date_from_schedule_name_when_input_empty():
|
|||
def test_holiday_sqs_input_shape():
|
||||
payload = mod.holiday_sqs_input("activate", "2026-07-04")
|
||||
assert payload == '{"event": "holiday", "action": "activate", "date": "2026-07-04"}'
|
||||
|
||||
|
||||
def test_holiday_scheduler_role_arn_follows_caller_account():
|
||||
assert mod.holiday_scheduler_role_arn(mod.PROD_ACCOUNT) == (
|
||||
"arn:aws:iam::011934824531:role/tf-managed/"
|
||||
"afterhours-shift-manager-holiday-scheduler"
|
||||
)
|
||||
assert mod.holiday_scheduler_role_arn(mod.DEV_ACCOUNT) == (
|
||||
"arn:aws:iam::710827005802:role/tf-managed/"
|
||||
"afterhours-shift-manager-holiday-scheduler"
|
||||
)
|
||||
|
|
|
|||
|
|
@ -42,6 +42,18 @@ def test_publish_home_publishes_latest_entry(slackbot_app, client):
|
|||
assert "v1.9.2" not in str(kwargs["view"]) # only the newest entry
|
||||
|
||||
|
||||
def test_changelog_paths_include_package_copy(slackbot_app):
|
||||
paths = slackbot_app._changelog_paths()
|
||||
assert any(p.endswith("src/slack-bot/CHANGELOG.md") for p in paths)
|
||||
|
||||
|
||||
def test_changelog_text_reads_package_copy(slackbot_app):
|
||||
slackbot_app._changelog_text.cache_clear()
|
||||
text = slackbot_app._changelog_text()
|
||||
assert text
|
||||
assert "## " in text
|
||||
|
||||
|
||||
def test_publish_home_degrades_without_changelog(slackbot_app, client):
|
||||
slackbot_app.publish_home(client, "U_BOB", "")
|
||||
view = client.views_publish.call_args.kwargs["view"]
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue