diff --git a/src/common/dates.py b/src/common/dates.py index db0a42f6..9d320534 100644 --- a/src/common/dates.py +++ b/src/common/dates.py @@ -20,6 +20,7 @@ def parse_date(text: str, formats: list[str] = frozenset([ def parse_month_year_date(text: str, formats: list[str] = frozenset([ "%B %Y", # January 2020 "%b %Y", # Jan 2020 + "%Y-%m", # 2020-01 ])) -> datetime: """Parse a given text representing a partial date using a list of formats, adjusting it to the last day of the month. diff --git a/src/common/releasedata.py b/src/common/releasedata.py index 91f42553..eec0d667 100644 --- a/src/common/releasedata.py +++ b/src/common/releasedata.py @@ -50,6 +50,9 @@ class ProductRelease: else: logging.info(f"set '{field}' in {self} to {new_value}") + def is_empty(self) -> bool: + return len(self.data) == 1 # only the name is set + def __repr__(self) -> str: return f"{self.product}#{self.name()}" @@ -132,6 +135,13 @@ class ProductData: self.updated = True return self.releases[release] + def remove_release(self, release: str) -> None: + if release not in self.releases: + logging.warning(f"release {release} cannot be removed as it does not exist for {self}") + return + + logging.info(f"removing release {release} ({self.releases.pop(release)}) from {self}") + def get_version(self, version: str) -> ProductVersion: return self.versions[version] if version in self.versions else None @@ -149,7 +159,7 @@ class ProductData: self.declare_version(version, date) def remove_version(self, version: str) -> None: - if not self.get_version(version): + if version not in self.versions: logging.warning(f"version {version} cannot be removed as it does not exist for {self}") return diff --git a/src/release_table.py b/src/release_table.py index a3460942..8382f12f 100644 --- a/src/release_table.py +++ b/src/release_table.py @@ -90,7 +90,7 @@ class Field: if self.name == "releaseCycle": return None # skipping entire rows is allowed - msg = f"field {self}'s value {raw_value} does not matching any regex in {self.include_version_patterns}" + msg = f"field {self}'s value '{raw_value}' does not match any regex in {self.include_version_patterns}" raise ValueError(msg) def __repr__(self) -> str: @@ -121,20 +121,25 @@ for config in endoflife.list_configs(p_filter, METHOD, m_filter): logging.info(f"skipping row {cells}: not enough columns") continue - raw_release_cycle = cells[fields_index[release_cycle_field.name]] - release_cycle = release_cycle_field.extract_from(raw_release_cycle) - if not release_cycle: - logging.info(f"skipping row {cells}: invalid release cycle '{raw_release_cycle}', " - f"should match any of {release_cycle_field.include_version_patterns} " - f"and not match any of {release_cycle_field.exclude_version_patterns}") + raw_release_name = cells[fields_index[release_cycle_field.name]] + release_name = release_cycle_field.extract_from(raw_release_name) + if not release_name: + logging.info(f"skipping row {cells}: invalid release cycle '{raw_release_name}', " + f"should match one of {release_cycle_field.include_version_patterns} " + f"and not match all of {release_cycle_field.exclude_version_patterns}") continue - release = product_data.get_release(release_cycle) + release = product_data.get_release(release_name) for field in fields: raw_field = cells[fields_index[field.name]] try: release.set_field(field.name, field.extract_from(raw_field)) except ValueError as e: logging.info(f"skipping cell {raw_field} for {release}: {e}") + + if config.data.get("ignore_empty_releases", False) and release.is_empty(): + logging.info(f"removing empty release '{release}'") + product_data.remove_release(release_name) + except ValueError as e: logging.info(f"skipping table with headers {headers}: {e}")