Our release automation had a hard-coded freeze window: no deploys between 18 December and 4 January, and on any day flagged as a French public holiday. It lived in a single Python module, deploy_window.py, and had been copied from a previous project. The data was a list of MM-DD strings, no year, no region.
Last May it blocked a Friday afternoon hotfix. The list said 05-08. The service was a small internal API used only by the Lyon office, and the team there was working. The automation had no idea that 05-08 in 2024 was a Wednesday, that Victoire 1945 is a national holiday, or that the Lyon team’s support contract explicitly covered it. It only knew the string matched.
What I did was replace the string list with a generated table keyed by year and by the holiday’s scope, and make the freeze decision explicit rather than implicit:
from datetime import date
# generated from a small CSV we keep in the repo; one row per year
# columns: date, name, scope (national | alsace-moselle | local)
HOLIDAYS = {
date(2024, 5, 8): ("Victoire 1945", "national"),
date(2024, 5, 9): ("Ascension", "national"),
date(2024, 5, 20): ("Pentecote", "national"),
}
def freeze_reason(day: date, service_scope: str = "national") -> str | None:
if day.month == 12 and day.day >= 18:
return "year-end freeze"
if day.month == 1 and day.day <= 4:
return "year-end freeze"
hit = HOLIDAYS.get(day)
if hit and (hit[1] == "national" or hit[1] == service_scope):
return hit[0]
return None
The point is less the code and more the shape: the freeze is now a function that returns a reason string, so the CI log says blocked: Ascension instead of deploy not allowed today. The previous version had a boolean, and nobody could tell why a deploy was rejected without reading the source.
Why keep a year table instead of computing Easter? Because the table is auditable. Anyone can open the CSV and check it against the official calendar. Computing Easter is correct but harder to review, and the failure mode of a wrong computus is a silent wrong freeze. The cost is that someone must regenerate the table each autumn. I put that on the same runbook as the TLS renewal, which is the only reason it happens.
The place this breaks: Alsace-Moselle. Good Friday and 26 December are holidays there, and our first table marked them national, which blocked deploys for the whole company on a day when only one office was closed. I caught it because a Paris-based engineer asked why Friday was frozen. The fix was a scope column and a per-service setting. If you have teams in multiple regions, the freeze must be per service, not global, or people learn to bypass the automation.
Second failure: the year-end window is calendar-based, not business-day-based. In 2023 the freeze started on a Monday and the last working Friday was the 15th. The team effectively lost a week. I have not fixed this because nobody has agreed on what the window should be, and I would rather leave the ugly rule visible than invent a policy.
If your team runs a holiday-aware freeze, how do you scope it when only part of the company is actually off?
0 条评论
还没有评论,写下第一条。