Make releasedata.Product usable in 'with' expression (#294)
This way the writing of the JSON file is handled automatically if the update does not fail. It pave the way to further global improvements, such as a better error handling.
This commit is contained in:
@@ -8,7 +8,7 @@ from common import dates, http, releasedata
|
|||||||
RSS_URL = "https://docs.aws.amazon.com/neptune/latest/userguide/rssupdates.rss"
|
RSS_URL = "https://docs.aws.amazon.com/neptune/latest/userguide/rssupdates.rss"
|
||||||
VERSION_PATTERN = re.compile(r"^Engine version (?P<version>[0-9R.]+)$")
|
VERSION_PATTERN = re.compile(r"^Engine version (?P<version>[0-9R.]+)$")
|
||||||
|
|
||||||
product = releasedata.Product("amazon-neptune")
|
with releasedata.ProductData("amazon-neptune") as product_data:
|
||||||
rss_response = http.fetch_url(RSS_URL)
|
rss_response = http.fetch_url(RSS_URL)
|
||||||
rss = xml.dom.minidom.parseString(rss_response.text)
|
rss = xml.dom.minidom.parseString(rss_response.text)
|
||||||
|
|
||||||
@@ -18,6 +18,4 @@ for entry in rss.getElementsByTagName("item"):
|
|||||||
|
|
||||||
version_match = VERSION_PATTERN.match(version_str)
|
version_match = VERSION_PATTERN.match(version_str)
|
||||||
if version_match:
|
if version_match:
|
||||||
product.declare_version(version_match['version'], dates.parse_datetime(date_str))
|
product_data.declare_version(version_match['version'], dates.parse_datetime(date_str))
|
||||||
|
|
||||||
product.write()
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ VERSION_AND_DATE_PATTERNS = [
|
|||||||
re.compile(r"\s+(?P<version>\d+\.\d+\.\d+)\s*:.*Tagged and [rR]olled\s(?:on\s)?(?P<date>\w+\.?\s\d\d?,\s\d{4})"),
|
re.compile(r"\s+(?P<version>\d+\.\d+\.\d+)\s*:.*Tagged and [rR]olled\s(?:on\s)?(?P<date>\w+\.?\s\d\d?,\s\d{4})"),
|
||||||
]
|
]
|
||||||
|
|
||||||
product = releasedata.Product("apache-http-server")
|
with releasedata.ProductData("apache-http-server") as product_data:
|
||||||
git = Git("https://github.com/apache/httpd.git")
|
git = Git("https://github.com/apache/httpd.git")
|
||||||
git.setup()
|
git.setup()
|
||||||
|
|
||||||
@@ -31,6 +31,4 @@ for branch in git.list_branches("refs/heads/?.?.x"):
|
|||||||
|
|
||||||
for pattern in VERSION_AND_DATE_PATTERNS:
|
for pattern in VERSION_AND_DATE_PATTERNS:
|
||||||
for (version, date_str) in pattern.findall(release_notes):
|
for (version, date_str) in pattern.findall(release_notes):
|
||||||
product.declare_version(version, dates.parse_date(date_str))
|
product_data.declare_version(version, dates.parse_date(date_str))
|
||||||
|
|
||||||
product.write()
|
|
||||||
|
|||||||
12
src/apple.py
12
src/apple.py
@@ -56,7 +56,7 @@ soups = [BeautifulSoup(response.text, features="html5lib") for response in http.
|
|||||||
logging.info("::endgroup::")
|
logging.info("::endgroup::")
|
||||||
|
|
||||||
for product_name in VERSION_PATTERNS:
|
for product_name in VERSION_PATTERNS:
|
||||||
product = releasedata.Product(product_name)
|
with releasedata.ProductData(product_name) as product_data:
|
||||||
for soup in soups:
|
for soup in soups:
|
||||||
versions_table = soup.find(id="tableWraper")
|
versions_table = soup.find(id="tableWraper")
|
||||||
versions_table = versions_table if versions_table else soup.find('table', class_="gb-table")
|
versions_table = versions_table if versions_table else soup.find('table', class_="gb-table")
|
||||||
@@ -73,12 +73,10 @@ for product_name in VERSION_PATTERNS:
|
|||||||
|
|
||||||
date_str = date_match.group(0).replace("Sept ", "Sep ")
|
date_str = date_match.group(0).replace("Sept ", "Sep ")
|
||||||
date = dates.parse_date(date_str)
|
date = dates.parse_date(date_str)
|
||||||
for version_pattern in VERSION_PATTERNS[product.name]:
|
for version_pattern in VERSION_PATTERNS[product_data.name]:
|
||||||
for version_str in version_pattern.findall(version_text):
|
for version_str in version_pattern.findall(version_text):
|
||||||
version = product.get_version(version_str)
|
version = product_data.get_version(version_str)
|
||||||
if not version or version.date() > date:
|
if not version or version.date() > date:
|
||||||
product.declare_version(version_str, date)
|
product_data.declare_version(version_str, date)
|
||||||
else:
|
else:
|
||||||
logging.info(f"ignoring version {version_str} ({date}) for {product.name}")
|
logging.info(f"ignoring version {version_str} ({date}) for {product_data.name}")
|
||||||
|
|
||||||
product.write()
|
|
||||||
|
|||||||
@@ -12,9 +12,9 @@ If one day release dates are available in the AWS documentation, it would be bet
|
|||||||
them though. Note that this would also be unnecessary if it was possible to disable release/latest
|
them though. Note that this would also be unnecessary if it was possible to disable release/latest
|
||||||
release dates updates in the latest.py script."""
|
release dates updates in the latest.py script."""
|
||||||
|
|
||||||
product = releasedata.Product("aws-lambda")
|
with releasedata.ProductData("aws-lambda") as product_data:
|
||||||
old_product = releasedata.Product.from_file(product.name)
|
old_product_data = releasedata.ProductData.from_file(product_data.name)
|
||||||
product_frontmatter = endoflife.ProductFrontmatter(product.name)
|
product_frontmatter = endoflife.ProductFrontmatter(product_data.name)
|
||||||
response = http.fetch_url("https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html")
|
response = http.fetch_url("https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html")
|
||||||
soup = BeautifulSoup(response.text, features="html5lib")
|
soup = BeautifulSoup(response.text, features="html5lib")
|
||||||
|
|
||||||
@@ -30,10 +30,8 @@ for table in soup.find_all("table"):
|
|||||||
|
|
||||||
date = product_frontmatter.get_release_date(identifier) # use the product releaseDate if available
|
date = product_frontmatter.get_release_date(identifier) # use the product releaseDate if available
|
||||||
if date is None:
|
if date is None:
|
||||||
date = old_product.get_version(identifier).date() # else use the previously found date
|
date = old_product_data.get_version(identifier).date() # else use the previously found date
|
||||||
if date is None:
|
if date is None:
|
||||||
date = dates.today() # else use today's date
|
date = dates.today() # else use today's date
|
||||||
|
|
||||||
product.declare_version(identifier, date)
|
product_data.declare_version(identifier, date)
|
||||||
|
|
||||||
product.write()
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ METHOD = "cgit"
|
|||||||
|
|
||||||
p_filter = sys.argv[1] if len(sys.argv) > 1 else None
|
p_filter = sys.argv[1] if len(sys.argv) > 1 else None
|
||||||
for product in endoflife.list_products(METHOD, p_filter):
|
for product in endoflife.list_products(METHOD, p_filter):
|
||||||
product_data = releasedata.Product(product.name)
|
with releasedata.ProductData(product.name) as product_data:
|
||||||
for config in product.get_auto_configs(METHOD):
|
for config in product.get_auto_configs(METHOD):
|
||||||
response = http.fetch_url(config.url + '/refs/tags')
|
response = http.fetch_url(config.url + '/refs/tags')
|
||||||
soup = BeautifulSoup(response.text, features="html5lib")
|
soup = BeautifulSoup(response.text, features="html5lib")
|
||||||
@@ -34,5 +34,3 @@ for product in endoflife.list_products(METHOD, p_filter):
|
|||||||
version = config.render(version_match)
|
version = config.render(version_match)
|
||||||
date = dates.parse_datetime(datetime_str)
|
date = dates.parse_datetime(datetime_str)
|
||||||
product_data.declare_version(version, date)
|
product_data.declare_version(version, date)
|
||||||
|
|
||||||
product_data.write()
|
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ FIXED_VERSIONS = {
|
|||||||
"2023.0.0": dates.date(2022, 5, 16), # https://coldfusion.adobe.com/2023/05/coldfusion2023-release/
|
"2023.0.0": dates.date(2022, 5, 16), # https://coldfusion.adobe.com/2023/05/coldfusion2023-release/
|
||||||
}
|
}
|
||||||
|
|
||||||
product = releasedata.Product("coldfusion")
|
with releasedata.ProductData("coldfusion") as product_data:
|
||||||
for changelog in http.fetch_urls(URLS):
|
for changelog in http.fetch_urls(URLS):
|
||||||
changelog_soup = BeautifulSoup(changelog.text, features="html5lib")
|
changelog_soup = BeautifulSoup(changelog.text, features="html5lib")
|
||||||
|
|
||||||
@@ -40,7 +40,6 @@ for changelog in http.fetch_urls(URLS):
|
|||||||
for (date_str, version_str) in VERSION_AND_DATE_PATTERN.findall(version_and_date_str):
|
for (date_str, version_str) in VERSION_AND_DATE_PATTERN.findall(version_and_date_str):
|
||||||
date = dates.parse_date(date_str)
|
date = dates.parse_date(date_str)
|
||||||
version = version_str.strip().replace(",", ".") # 11,0,0,289974 -> 11.0.0.289974
|
version = version_str.strip().replace(",", ".") # 11,0,0,289974 -> 11.0.0.289974
|
||||||
product.declare_version(version, date)
|
product_data.declare_version(version, date)
|
||||||
|
|
||||||
product.declare_versions(FIXED_VERSIONS)
|
product_data.declare_versions(FIXED_VERSIONS)
|
||||||
product.write()
|
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ import logging
|
|||||||
import os
|
import os
|
||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from types import TracebackType
|
||||||
|
from typing import Optional, Type
|
||||||
|
|
||||||
# Do not update the format: it's also used to declare groups in the GitHub Actions logs.
|
# Do not update the format: it's also used to declare groups in the GitHub Actions logs.
|
||||||
logging.basicConfig(format="%(message)s", level=logging.INFO)
|
logging.basicConfig(format="%(message)s", level=logging.INFO)
|
||||||
@@ -10,13 +12,16 @@ logging.basicConfig(format="%(message)s", level=logging.INFO)
|
|||||||
VERSIONS_PATH = Path(os.environ.get("VERSIONS_PATH", "releases"))
|
VERSIONS_PATH = Path(os.environ.get("VERSIONS_PATH", "releases"))
|
||||||
|
|
||||||
|
|
||||||
|
class ProductUpdateError(Exception):
|
||||||
|
"""Custom exceptions raised when unexpected errors occur during product updates."""
|
||||||
|
|
||||||
class ProductVersion:
|
class ProductVersion:
|
||||||
def __init__(self, product: "Product", data: dict) -> None:
|
def __init__(self, product: "ProductData", data: dict) -> None:
|
||||||
self.product = str(product)
|
self.product = str(product)
|
||||||
self.data = data
|
self.data = data
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def of(product: "Product", name: str, date: datetime) -> "ProductVersion":
|
def of(product: "ProductData", name: str, date: datetime) -> "ProductVersion":
|
||||||
return ProductVersion(product, {
|
return ProductVersion(product, {
|
||||||
"name": name,
|
"name": name,
|
||||||
"date": date.strftime("%Y-%m-%d"),
|
"date": date.strftime("%Y-%m-%d"),
|
||||||
@@ -35,16 +40,37 @@ class ProductVersion:
|
|||||||
return f"{self.product}#{self.name()} ({self.date()})"
|
return f"{self.product}#{self.name()} ({self.date()})"
|
||||||
|
|
||||||
|
|
||||||
class Product:
|
class ProductData:
|
||||||
def __init__(self, name: str) -> None:
|
def __init__(self, name: str) -> None:
|
||||||
self.name: str = name
|
self.name: str = name
|
||||||
self.versions_path: Path = VERSIONS_PATH / f"{name}.json"
|
self.versions_path: Path = VERSIONS_PATH / f"{name}.json"
|
||||||
self.versions: dict[str, ProductVersion] = {}
|
self.versions: dict[str, ProductVersion] = {}
|
||||||
|
|
||||||
|
def __enter__(self) -> "ProductData":
|
||||||
logging.info(f"::group::{self}")
|
logging.info(f"::group::{self}")
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __exit__(self, exc_type: Optional[Type[BaseException]], exc_value: Optional[BaseException],
|
||||||
|
exc_traceback: Optional[TracebackType]) -> None:
|
||||||
|
try:
|
||||||
|
if exc_value:
|
||||||
|
message = f"an unexpected error occurred while updating {self} data"
|
||||||
|
logging.error(message, exc_info=exc_value)
|
||||||
|
raise ProductUpdateError(message) from exc_value
|
||||||
|
|
||||||
|
logging.info("updating %s data",self)
|
||||||
|
# sort by date then version (desc)
|
||||||
|
ordered_versions = sorted(self.versions.values(), key=lambda v: (v.date(), v.name()), reverse=True)
|
||||||
|
with self.versions_path.open("w") as f:
|
||||||
|
f.write(json.dumps({
|
||||||
|
"versions": {version.name(): version.data for version in ordered_versions},
|
||||||
|
}, indent=2))
|
||||||
|
finally:
|
||||||
|
logging.info("::endgroup::")
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def from_file(name: str) -> "Product":
|
def from_file(name: str) -> "ProductData":
|
||||||
product = Product(name)
|
product = ProductData(name)
|
||||||
|
|
||||||
if product.versions_path.is_file():
|
if product.versions_path.is_file():
|
||||||
with product.versions_path.open() as f:
|
with product.versions_path.open() as f:
|
||||||
@@ -79,14 +105,5 @@ class Product:
|
|||||||
|
|
||||||
logging.info(f"removing version {version} ({self.versions.pop(version)}) from {self}")
|
logging.info(f"removing version {version} ({self.versions.pop(version)}) from {self}")
|
||||||
|
|
||||||
def write(self) -> None:
|
|
||||||
# sort by date then version (desc)
|
|
||||||
ordered_versions = sorted(self.versions.values(), key=lambda v: (v.date(), v.name()), reverse=True)
|
|
||||||
with self.versions_path.open("w") as f:
|
|
||||||
f.write(json.dumps({
|
|
||||||
"versions": {version.name(): version.data for version in ordered_versions},
|
|
||||||
}, indent=2))
|
|
||||||
logging.info("::endgroup::")
|
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return self.name
|
return self.name
|
||||||
|
|||||||
@@ -5,13 +5,11 @@ from requests_html import HTMLSession
|
|||||||
|
|
||||||
Note that requests_html is used because JavaScript is needed to render the page."""
|
Note that requests_html is used because JavaScript is needed to render the page."""
|
||||||
|
|
||||||
product = releasedata.Product("confluence")
|
with releasedata.ProductData("confluence") as product_data:
|
||||||
r = HTMLSession().get("https://www.atlassian.com/software/confluence/download-archives")
|
r = HTMLSession().get("https://www.atlassian.com/software/confluence/download-archives")
|
||||||
r.html.render(sleep=1, scrolldown=3)
|
r.html.render(sleep=1, scrolldown=3)
|
||||||
|
|
||||||
for version_block in r.html.find('.versions-list'):
|
for version_block in r.html.find('.versions-list'):
|
||||||
version = version_block.find('a.product-versions', first=True).attrs['data-version']
|
version = version_block.find('a.product-versions', first=True).attrs['data-version']
|
||||||
date = dates.parse_date(version_block.find('.release-date', first=True).text)
|
date = dates.parse_date(version_block.find('.release-date', first=True).text)
|
||||||
product.declare_version(version, date)
|
product_data.declare_version(version, date)
|
||||||
|
|
||||||
product.write()
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ def parse_date(date_text: str) -> datetime:
|
|||||||
return dates.parse_date(date_text)
|
return dates.parse_date(date_text)
|
||||||
|
|
||||||
|
|
||||||
product = releasedata.Product("cos")
|
with releasedata.ProductData("cos") as product_data:
|
||||||
main = http.fetch_url("https://cloud.google.com/container-optimized-os/docs/release-notes/")
|
main = http.fetch_url("https://cloud.google.com/container-optimized-os/docs/release-notes/")
|
||||||
main_soup = BeautifulSoup(main.text, features="html5lib")
|
main_soup = BeautifulSoup(main.text, features="html5lib")
|
||||||
milestones = [cell.text.split(' ')[1] for cell in main_soup.find_all('td', string=MILESTONE_PATTERN)]
|
milestones = [cell.text.split(' ')[1] for cell in main_soup.find_all('td', string=MILESTONE_PATTERN)]
|
||||||
@@ -40,6 +40,4 @@ for milestone in http.fetch_urls(milestones_urls):
|
|||||||
date_str = heading.find_previous('h2').get('data-text')
|
date_str = heading.find_previous('h2').get('data-text')
|
||||||
date = parse_date(date_str)
|
date = parse_date(date_str)
|
||||||
|
|
||||||
product.declare_version(version_match.group(1), date)
|
product_data.declare_version(version_match.group(1), date)
|
||||||
|
|
||||||
product.write()
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ MANUAL_VERSIONS = {
|
|||||||
"7.2.0": dates.date(2023, 6, 1), # https://www.couchbase.com/blog/couchbase-capella-spring-release-72/
|
"7.2.0": dates.date(2023, 6, 1), # https://www.couchbase.com/blog/couchbase-capella-spring-release-72/
|
||||||
}
|
}
|
||||||
|
|
||||||
product = releasedata.Product("couchbase-server")
|
with releasedata.ProductData("couchbase-server") as product_data:
|
||||||
main = http.fetch_url(f"{URLS}/current/install/install-intro.html")
|
main = http.fetch_url(f"{URLS}/current/install/install-intro.html")
|
||||||
main_soup = BeautifulSoup(main.text, features="html5lib")
|
main_soup = BeautifulSoup(main.text, features="html5lib")
|
||||||
|
|
||||||
@@ -34,7 +34,6 @@ for minor_version in http.fetch_urls(minor_version_urls):
|
|||||||
version = match["version"]
|
version = match["version"]
|
||||||
version = f"{version}.0" if len(version.split(".")) == 2 else version
|
version = f"{version}.0" if len(version.split(".")) == 2 else version
|
||||||
date = dates.parse_month_year_date(match['date'])
|
date = dates.parse_month_year_date(match['date'])
|
||||||
product.declare_version(version, date)
|
product_data.declare_version(version, date)
|
||||||
|
|
||||||
product.declare_versions(MANUAL_VERSIONS)
|
product_data.declare_versions(MANUAL_VERSIONS)
|
||||||
product.write()
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ from common.git import Git
|
|||||||
"""Fetch Debian versions by parsing news in www.debian.org source repository."""
|
"""Fetch Debian versions by parsing news in www.debian.org source repository."""
|
||||||
|
|
||||||
|
|
||||||
def extract_major_versions(p: releasedata.Product, repo_dir: Path) -> None:
|
def extract_major_versions(p: releasedata.ProductData, repo_dir: Path) -> None:
|
||||||
child = run(
|
child = run(
|
||||||
f"grep -RhE -A 1 '<define-tag pagetitle>Debian [0-9]+.+</q> released' {repo_dir}/english/News "
|
f"grep -RhE -A 1 '<define-tag pagetitle>Debian [0-9]+.+</q> released' {repo_dir}/english/News "
|
||||||
f"| cut -d '<' -f 2 "
|
f"| cut -d '<' -f 2 "
|
||||||
@@ -26,7 +26,7 @@ def extract_major_versions(p: releasedata.Product, repo_dir: Path) -> None:
|
|||||||
is_release_line = True
|
is_release_line = True
|
||||||
|
|
||||||
|
|
||||||
def extract_point_versions(p: releasedata.Product, repo_dir: Path) -> None:
|
def extract_point_versions(p: releasedata.ProductData, repo_dir: Path) -> None:
|
||||||
child = run(
|
child = run(
|
||||||
f"grep -Rh -B 10 '<define-tag revision>' {repo_dir}/english/News "
|
f"grep -Rh -B 10 '<define-tag revision>' {repo_dir}/english/News "
|
||||||
"| grep -Eo '(release_date>(.*)<|revision>(.*)<)' "
|
"| grep -Eo '(release_date>(.*)<|revision>(.*)<)' "
|
||||||
@@ -41,12 +41,10 @@ def extract_point_versions(p: releasedata.Product, repo_dir: Path) -> None:
|
|||||||
p.declare_version(version, dates.parse_date(date))
|
p.declare_version(version, dates.parse_date(date))
|
||||||
|
|
||||||
|
|
||||||
product = releasedata.Product("debian")
|
with releasedata.ProductData("debian") as product_data:
|
||||||
git = Git("https://salsa.debian.org/webmaster-team/webwml.git")
|
git = Git("https://salsa.debian.org/webmaster-team/webwml.git")
|
||||||
git.setup()
|
git.setup()
|
||||||
git.checkout("master", file_list=["english/News"])
|
git.checkout("master", file_list=["english/News"])
|
||||||
|
|
||||||
extract_major_versions(product, git.repo_dir)
|
extract_major_versions(product_data, git.repo_dir)
|
||||||
extract_point_versions(product, git.repo_dir)
|
extract_point_versions(product_data, git.repo_dir)
|
||||||
|
|
||||||
product.write()
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ METHOD = 'distrowatch'
|
|||||||
|
|
||||||
p_filter = sys.argv[1] if len(sys.argv) > 1 else None
|
p_filter = sys.argv[1] if len(sys.argv) > 1 else None
|
||||||
for product in endoflife.list_products(METHOD, p_filter):
|
for product in endoflife.list_products(METHOD, p_filter):
|
||||||
product_data = releasedata.Product(product.name)
|
with releasedata.ProductData(product.name) as product_data:
|
||||||
for config in product.get_auto_configs(METHOD):
|
for config in product.get_auto_configs(METHOD):
|
||||||
response = http.fetch_url(f"https://distrowatch.com/index.php?distribution={config.url}")
|
response = http.fetch_url(f"https://distrowatch.com/index.php?distribution={config.url}")
|
||||||
soup = BeautifulSoup(response.text, features="html5lib")
|
soup = BeautifulSoup(response.text, features="html5lib")
|
||||||
@@ -24,5 +24,3 @@ for product in endoflife.list_products(METHOD, p_filter):
|
|||||||
|
|
||||||
for version in versions:
|
for version in versions:
|
||||||
product_data.declare_version(version, date)
|
product_data.declare_version(version, date)
|
||||||
|
|
||||||
product_data.write()
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ Unfortunately images creation date cannot be retrieved, so we had to use the tag
|
|||||||
METHOD = "docker_hub"
|
METHOD = "docker_hub"
|
||||||
|
|
||||||
|
|
||||||
def fetch_releases(p: releasedata.Product, c: endoflife.AutoConfig, url: str) -> None:
|
def fetch_releases(p: releasedata.ProductData, c: endoflife.AutoConfig, url: str) -> None:
|
||||||
data = http.fetch_url(url).json()
|
data = http.fetch_url(url).json()
|
||||||
|
|
||||||
for result in data["results"]:
|
for result in data["results"]:
|
||||||
@@ -24,7 +24,6 @@ def fetch_releases(p: releasedata.Product, c: endoflife.AutoConfig, url: str) ->
|
|||||||
|
|
||||||
p_filter = sys.argv[1] if len(sys.argv) > 1 else None
|
p_filter = sys.argv[1] if len(sys.argv) > 1 else None
|
||||||
for product in endoflife.list_products(METHOD, p_filter):
|
for product in endoflife.list_products(METHOD, p_filter):
|
||||||
product_data = releasedata.Product(product.name)
|
with releasedata.ProductData(product.name) as product_data:
|
||||||
for config in product.get_auto_configs(METHOD):
|
for config in product.get_auto_configs(METHOD):
|
||||||
fetch_releases(product_data, config, f"https://hub.docker.com/v2/repositories/{config.url}/tags?page_size=100&page=1")
|
fetch_releases(product_data, config, f"https://hub.docker.com/v2/repositories/{config.url}/tags?page_size=100&page=1")
|
||||||
product_data.write()
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ URLS = [
|
|||||||
"https://docs.aws.amazon.com/eks/latest/userguide/platform-versions.html",
|
"https://docs.aws.amazon.com/eks/latest/userguide/platform-versions.html",
|
||||||
]
|
]
|
||||||
|
|
||||||
product = releasedata.Product("eks")
|
with releasedata.ProductData("eks") as product_data:
|
||||||
for version_list in http.fetch_urls(URLS):
|
for version_list in http.fetch_urls(URLS):
|
||||||
version_list_soup = BeautifulSoup(version_list.text, features="html5lib")
|
version_list_soup = BeautifulSoup(version_list.text, features="html5lib")
|
||||||
for tr in version_list_soup.select("#main-col-body")[0].findAll("tr"):
|
for tr in version_list_soup.select("#main-col-body")[0].findAll("tr"):
|
||||||
@@ -30,6 +30,4 @@ for version_list in http.fetch_urls(URLS):
|
|||||||
date = dates.parse_date(date_str)
|
date = dates.parse_date(date_str)
|
||||||
# K8S patch version is not kept to match versions on https://github.com/aws/eks-distro/tags.
|
# K8S patch version is not kept to match versions on https://github.com/aws/eks-distro/tags.
|
||||||
version = f"{k8s_version_match.group('major')}.{k8s_version_match.group('minor')}-{eks_version.replace('.', '-')}"
|
version = f"{k8s_version_match.group('major')}.{k8s_version_match.group('minor')}-{eks_version.replace('.', '-')}"
|
||||||
product.declare_version(version, date)
|
product_data.declare_version(version, date)
|
||||||
|
|
||||||
product.write()
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ def batched(iterable: iter, n: int) -> iter:
|
|||||||
yield batch
|
yield batch
|
||||||
|
|
||||||
|
|
||||||
product = releasedata.Product("firefox")
|
with releasedata.ProductData("firefox") as product_data:
|
||||||
releases_page = http.fetch_url("https://www.mozilla.org/en-US/firefox/releases/")
|
releases_page = http.fetch_url("https://www.mozilla.org/en-US/firefox/releases/")
|
||||||
releases_soup = BeautifulSoup(releases_page.text, features="html5lib")
|
releases_soup = BeautifulSoup(releases_page.text, features="html5lib")
|
||||||
releases_list = releases_soup.find_all("ol", class_="c-release-list")
|
releases_list = releases_soup.find_all("ol", class_="c-release-list")
|
||||||
@@ -35,13 +35,11 @@ for batch_release_notes_urls in batched(release_notes_urls, 20):
|
|||||||
date_elt = release_notes_soup.find(class_="c-release-date")
|
date_elt = release_notes_soup.find(class_="c-release-date")
|
||||||
if date_elt:
|
if date_elt:
|
||||||
date = dates.parse_date(date_elt.get_text())
|
date = dates.parse_date(date_elt.get_text())
|
||||||
product.declare_version(version, date)
|
product_data.declare_version(version, date)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
date_elt = release_notes_soup.find("small", string=re.compile("^.?First offered"))
|
date_elt = release_notes_soup.find("small", string=re.compile("^.?First offered"))
|
||||||
if date_elt:
|
if date_elt:
|
||||||
date = dates.parse_date(' '.join(date_elt.get_text().split(" ")[-3:])) # get last 3 words
|
date = dates.parse_date(' '.join(date_elt.get_text().split(" ")[-3:])) # get last 3 words
|
||||||
product.declare_version(version, date)
|
product_data.declare_version(version, date)
|
||||||
# versions < 10.0 are ignored
|
# versions < 10.0 are ignored
|
||||||
|
|
||||||
product.write()
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ METHOD = 'git'
|
|||||||
|
|
||||||
p_filter = sys.argv[1] if len(sys.argv) > 1 else None
|
p_filter = sys.argv[1] if len(sys.argv) > 1 else None
|
||||||
for product in endoflife.list_products(METHOD, p_filter):
|
for product in endoflife.list_products(METHOD, p_filter):
|
||||||
product_data = releasedata.Product(product.name)
|
with releasedata.ProductData(product.name) as product_data:
|
||||||
for config in product.get_auto_configs(METHOD):
|
for config in product.get_auto_configs(METHOD):
|
||||||
git = Git(config.url)
|
git = Git(config.url)
|
||||||
git.setup(bare=True)
|
git.setup(bare=True)
|
||||||
@@ -21,5 +21,3 @@ for product in endoflife.list_products(METHOD, p_filter):
|
|||||||
version = config.render(version_match)
|
version = config.render(version_match)
|
||||||
date = dates.parse_date(date_str)
|
date = dates.parse_date(date_str)
|
||||||
product_data.declare_version(version, date)
|
product_data.declare_version(version, date)
|
||||||
|
|
||||||
product_data.write()
|
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ query($endCursor: String) {
|
|||||||
|
|
||||||
p_filter = sys.argv[1] if len(sys.argv) > 1 else None
|
p_filter = sys.argv[1] if len(sys.argv) > 1 else None
|
||||||
for product in endoflife.list_products(METHOD, p_filter):
|
for product in endoflife.list_products(METHOD, p_filter):
|
||||||
product_data = releasedata.Product(product.name)
|
with releasedata.ProductData(product.name) as product_data:
|
||||||
for config in product.get_auto_configs(METHOD):
|
for config in product.get_auto_configs(METHOD):
|
||||||
for page in fetch_releases(config.url):
|
for page in fetch_releases(config.url):
|
||||||
releases = [edge['node'] for edge in (page['data']['repository']['releases']['edges'])]
|
releases = [edge['node'] for edge in (page['data']['repository']['releases']['edges'])]
|
||||||
@@ -57,5 +57,3 @@ for product in endoflife.list_products(METHOD, p_filter):
|
|||||||
version = config.render(version_match)
|
version = config.render(version_match)
|
||||||
date = dates.parse_datetime(release['publishedAt'])
|
date = dates.parse_datetime(release['publishedAt'])
|
||||||
product_data.declare_version(version, date)
|
product_data.declare_version(version, date)
|
||||||
|
|
||||||
product_data.write()
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ URL_BY_PRODUCT = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for product_name, url in URL_BY_PRODUCT.items():
|
for product_name, url in URL_BY_PRODUCT.items():
|
||||||
product = releasedata.Product(product_name)
|
with releasedata.ProductData(product_name) as product_data:
|
||||||
relnotes = http.fetch_url(url)
|
relnotes = http.fetch_url(url)
|
||||||
relnotes_soup = BeautifulSoup(relnotes.text, features="html5lib")
|
relnotes_soup = BeautifulSoup(relnotes.text, features="html5lib")
|
||||||
|
|
||||||
@@ -25,6 +25,4 @@ for product_name, url in URL_BY_PRODUCT.items():
|
|||||||
for li in next_div.find_all('li'):
|
for li in next_div.find_all('li'):
|
||||||
if "versions are now available" in li.text:
|
if "versions are now available" in li.text:
|
||||||
for version in VERSION_PATTERN.findall(li.find('ul').text):
|
for version in VERSION_PATTERN.findall(li.find('ul').text):
|
||||||
product.declare_version(version, date)
|
product_data.declare_version(version, date)
|
||||||
|
|
||||||
product.write()
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
from bs4 import BeautifulSoup
|
from bs4 import BeautifulSoup
|
||||||
from common import dates, http, releasedata
|
from common import dates, http, releasedata
|
||||||
|
|
||||||
product = releasedata.Product("graalvm")
|
with releasedata.ProductData("graalvm") as product_data:
|
||||||
release_calendar = http.fetch_url("https://www.graalvm.org/release-calendar/")
|
release_calendar = http.fetch_url("https://www.graalvm.org/release-calendar/")
|
||||||
release_calendar_soup = BeautifulSoup(release_calendar.text, features="html5lib")
|
release_calendar_soup = BeautifulSoup(release_calendar.text, features="html5lib")
|
||||||
|
|
||||||
@@ -13,6 +13,4 @@ for tr in release_calendar_soup.find("h2", id="previous-releases").find_next("ta
|
|||||||
# GraalVM release cycles. Example: GraalVM for JDK 20 and 20.0.
|
# GraalVM release cycles. Example: GraalVM for JDK 20 and 20.0.
|
||||||
versions_str = cells[2].get_text().replace("GraalVM for JDK ", "jdk-")
|
versions_str = cells[2].get_text().replace("GraalVM for JDK ", "jdk-")
|
||||||
for version in versions_str.split(", "):
|
for version in versions_str.split(", "):
|
||||||
product.declare_version(version, date)
|
product_data.declare_version(version, date)
|
||||||
|
|
||||||
product.write()
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ from common import dates, http, releasedata
|
|||||||
CYCLE_PATTERN = re.compile(r"^(\d+\.\d+)/$")
|
CYCLE_PATTERN = re.compile(r"^(\d+\.\d+)/$")
|
||||||
DATE_AND_VERSION_PATTERN = re.compile(r"^(\d{4})/(\d{2})/(\d{2})\s+:\s+(\d+\.\d+\.\d.?)$") # https://regex101.com/r/1JCnFC/1
|
DATE_AND_VERSION_PATTERN = re.compile(r"^(\d{4})/(\d{2})/(\d{2})\s+:\s+(\d+\.\d+\.\d.?)$") # https://regex101.com/r/1JCnFC/1
|
||||||
|
|
||||||
product = releasedata.Product("haproxy")
|
with releasedata.ProductData("haproxy") as product_data:
|
||||||
# First, get all minor releases from the download page
|
# First, get all minor releases from the download page
|
||||||
download = http.fetch_url('https://www.haproxy.org/download/')
|
download = http.fetch_url('https://www.haproxy.org/download/')
|
||||||
download_soup = BeautifulSoup(download.text, features="html5lib")
|
download_soup = BeautifulSoup(download.text, features="html5lib")
|
||||||
@@ -27,6 +27,4 @@ for changelog in http.fetch_urls(changelog_urls):
|
|||||||
date_and_version_match = DATE_AND_VERSION_PATTERN.match(line)
|
date_and_version_match = DATE_AND_VERSION_PATTERN.match(line)
|
||||||
if date_and_version_match:
|
if date_and_version_match:
|
||||||
year, month, day, version = date_and_version_match.groups()
|
year, month, day, version = date_and_version_match.groups()
|
||||||
product.declare_version(version, dates.date(int(year), int(month), int(day)))
|
product_data.declare_version(version, dates.date(int(year), int(month), int(day)))
|
||||||
|
|
||||||
product.write()
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ URLS = [
|
|||||||
"https://www.ibm.com/support/pages/aix-support-lifecycle-information",
|
"https://www.ibm.com/support/pages/aix-support-lifecycle-information",
|
||||||
]
|
]
|
||||||
|
|
||||||
product = releasedata.Product("ibm-aix")
|
with releasedata.ProductData("ibm-aix") as product_data:
|
||||||
for page in http.fetch_urls(URLS):
|
for page in http.fetch_urls(URLS):
|
||||||
page_soup = BeautifulSoup(page.text, features="html5lib")
|
page_soup = BeautifulSoup(page.text, features="html5lib")
|
||||||
|
|
||||||
@@ -16,6 +16,4 @@ for page in http.fetch_urls(URLS):
|
|||||||
cells = row.find_all("td")
|
cells = row.find_all("td")
|
||||||
version = cells[0].text.strip("AIX ").replace(' TL', '.')
|
version = cells[0].text.strip("AIX ").replace(' TL', '.')
|
||||||
date = dates.parse_month_year_date(cells[1].text)
|
date = dates.parse_month_year_date(cells[1].text)
|
||||||
product.declare_version(version, date)
|
product_data.declare_version(version, date)
|
||||||
|
|
||||||
product.write()
|
|
||||||
|
|||||||
@@ -5,13 +5,11 @@ from requests_html import HTMLSession
|
|||||||
|
|
||||||
Note that requests_html is used because JavaScript is needed to render the page."""
|
Note that requests_html is used because JavaScript is needed to render the page."""
|
||||||
|
|
||||||
product = releasedata.Product("jira")
|
with releasedata.ProductData("jira") as product_data:
|
||||||
r = HTMLSession().get("https://www.atlassian.com/software/jira/update")
|
r = HTMLSession().get("https://www.atlassian.com/software/jira/update")
|
||||||
r.html.render(sleep=1, scrolldown=3)
|
r.html.render(sleep=1, scrolldown=3)
|
||||||
|
|
||||||
for version_block in r.html.find('.versions-list'):
|
for version_block in r.html.find('.versions-list'):
|
||||||
version = version_block.find('a.product-versions', first=True).attrs['data-version']
|
version = version_block.find('a.product-versions', first=True).attrs['data-version']
|
||||||
date = dates.parse_date(version_block.find('.release-date', first=True).text)
|
date = dates.parse_date(version_block.find('.release-date', first=True).text)
|
||||||
product.declare_version(version, date)
|
product_data.declare_version(version, date)
|
||||||
|
|
||||||
product.write()
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ from common import dates, http, releasedata
|
|||||||
ANNOUNCEMENT_PATTERN = re.compile(r"includes\s+the\s+following\s+changes", re.IGNORECASE)
|
ANNOUNCEMENT_PATTERN = re.compile(r"includes\s+the\s+following\s+changes", re.IGNORECASE)
|
||||||
VERSION_PATTERN = re.compile(r"Looker\s+(?P<version>\d+\.\d+)", re.IGNORECASE)
|
VERSION_PATTERN = re.compile(r"Looker\s+(?P<version>\d+\.\d+)", re.IGNORECASE)
|
||||||
|
|
||||||
product = releasedata.Product("looker")
|
with releasedata.ProductData("looker") as product_data:
|
||||||
response = http.fetch_url("https://cloud.google.com/feeds/looker-release-notes.xml")
|
response = http.fetch_url("https://cloud.google.com/feeds/looker-release-notes.xml")
|
||||||
rss = xml.dom.minidom.parseString(response.text)
|
rss = xml.dom.minidom.parseString(response.text)
|
||||||
|
|
||||||
@@ -29,6 +29,4 @@ for item in rss.getElementsByTagName("entry"):
|
|||||||
version = version_match.group("version")
|
version = version_match.group("version")
|
||||||
date_str = item.getElementsByTagName("updated")[0].firstChild.nodeValue
|
date_str = item.getElementsByTagName("updated")[0].firstChild.nodeValue
|
||||||
date = dates.parse_datetime(date_str)
|
date = dates.parse_datetime(date_str)
|
||||||
product.declare_version(version, date)
|
product_data.declare_version(version, date)
|
||||||
|
|
||||||
product.write()
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ METHOD = "maven"
|
|||||||
|
|
||||||
p_filter = sys.argv[1] if len(sys.argv) > 1 else None
|
p_filter = sys.argv[1] if len(sys.argv) > 1 else None
|
||||||
for product in endoflife.list_products(METHOD, p_filter):
|
for product in endoflife.list_products(METHOD, p_filter):
|
||||||
product_data = releasedata.Product(product.name)
|
with releasedata.ProductData(product.name) as product_data:
|
||||||
for config in product.get_auto_configs(METHOD):
|
for config in product.get_auto_configs(METHOD):
|
||||||
start = 0
|
start = 0
|
||||||
group_id, artifact_id = config.url.split("/")
|
group_id, artifact_id = config.url.split("/")
|
||||||
@@ -26,5 +26,3 @@ for product in endoflife.list_products(METHOD, p_filter):
|
|||||||
start += 100
|
start += 100
|
||||||
if data["response"]["numFound"] <= start:
|
if data["response"]["numFound"] <= start:
|
||||||
break
|
break
|
||||||
|
|
||||||
product_data.write()
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ METHOD = "npm"
|
|||||||
|
|
||||||
p_filter = sys.argv[1] if len(sys.argv) > 1 else None
|
p_filter = sys.argv[1] if len(sys.argv) > 1 else None
|
||||||
for product in endoflife.list_products(METHOD, p_filter):
|
for product in endoflife.list_products(METHOD, p_filter):
|
||||||
product_data = releasedata.Product(product.name)
|
with releasedata.ProductData(product.name) as product_data:
|
||||||
for config in product.get_auto_configs(METHOD):
|
for config in product.get_auto_configs(METHOD):
|
||||||
data = http.fetch_url(f"https://registry.npmjs.org/{config.url}").json()
|
data = http.fetch_url(f"https://registry.npmjs.org/{config.url}").json()
|
||||||
for version_str in data["versions"]:
|
for version_str in data["versions"]:
|
||||||
@@ -15,5 +15,3 @@ for product in endoflife.list_products(METHOD, p_filter):
|
|||||||
version = config.render(version_match)
|
version = config.render(version_match)
|
||||||
date = dates.parse_datetime(data["time"][version_str])
|
date = dates.parse_datetime(data["time"][version_str])
|
||||||
product_data.declare_version(version, date)
|
product_data.declare_version(version, date)
|
||||||
|
|
||||||
product_data.write()
|
|
||||||
|
|||||||
@@ -9,13 +9,10 @@ PRODUCTS = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for product_name, url in PRODUCTS.items():
|
for product_name, url in PRODUCTS.items():
|
||||||
product = releasedata.Product(product_name)
|
with releasedata.ProductData(product_name) as product_data:
|
||||||
|
|
||||||
data = http.fetch_url(url).json()
|
data = http.fetch_url(url).json()
|
||||||
for version_data in data["contents"]:
|
for version_data in data["contents"]:
|
||||||
if 'GENERAL_AVAILABILITY' in version_data:
|
if 'GENERAL_AVAILABILITY' in version_data:
|
||||||
version = version_data["version"]
|
version = version_data["version"]
|
||||||
date = dates.parse_datetime(version_data["GENERAL_AVAILABILITY"])
|
date = dates.parse_datetime(version_data["GENERAL_AVAILABILITY"])
|
||||||
product.declare_version(version, date)
|
product_data.declare_version(version, date)
|
||||||
|
|
||||||
product.write()
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ from requests_html import HTMLSession
|
|||||||
|
|
||||||
This script is using requests-html because the page needs JavaScript to render correctly."""
|
This script is using requests-html because the page needs JavaScript to render correctly."""
|
||||||
|
|
||||||
product = releasedata.Product("oracle-jdk")
|
with releasedata.ProductData("oracle-jdk") as product_data:
|
||||||
r = HTMLSession().get('https://www.java.com/releases/')
|
r = HTMLSession().get('https://www.java.com/releases/')
|
||||||
r.html.render(sleep=1, scrolldown=3)
|
r.html.render(sleep=1, scrolldown=3)
|
||||||
|
|
||||||
@@ -16,8 +16,7 @@ for row in r.html.find('#released tr'):
|
|||||||
version = version_cell.attrs['id']
|
version = version_cell.attrs['id']
|
||||||
date_str = row.find('td')[1].text
|
date_str = row.find('td')[1].text
|
||||||
date = dates.parse_date(date_str) if date_str else previous_date
|
date = dates.parse_date(date_str) if date_str else previous_date
|
||||||
product.declare_version(version, date)
|
product_data.declare_version(version, date)
|
||||||
previous_date = date
|
previous_date = date
|
||||||
|
|
||||||
product.remove_version('1.0_alpha') # the only version we don't want, a regex is not needed
|
product_data.remove_version('1.0_alpha') # the only version we don't want, a regex is not needed
|
||||||
product.write()
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ soup = BeautifulSoup(response.text, features="html5lib")
|
|||||||
logging.info("::endgroup::")
|
logging.info("::endgroup::")
|
||||||
|
|
||||||
for product_name, identifier in IDENTIFIERS_BY_PRODUCT.items():
|
for product_name, identifier in IDENTIFIERS_BY_PRODUCT.items():
|
||||||
product = releasedata.Product(product_name)
|
with releasedata.ProductData(product_name) as product_data:
|
||||||
table = soup.find(id=identifier)
|
table = soup.find(id=identifier)
|
||||||
for tr in table.findAll("tr")[3:]:
|
for tr in table.findAll("tr")[3:]:
|
||||||
td_list = tr.findAll("td")
|
td_list = tr.findAll("td")
|
||||||
@@ -33,6 +33,4 @@ for product_name, identifier in IDENTIFIERS_BY_PRODUCT.items():
|
|||||||
date_str = re.sub(r'(\w+) (\d{1,2})\w{2}, (\d{4})', r'\1 \2, \3', td_list[1].get_text())
|
date_str = re.sub(r'(\w+) (\d{1,2})\w{2}, (\d{4})', r'\1 \2, \3', td_list[1].get_text())
|
||||||
date = dates.parse_date(date_str)
|
date = dates.parse_date(date_str)
|
||||||
|
|
||||||
product.declare_version(version, date)
|
product_data.declare_version(version, date)
|
||||||
|
|
||||||
product.write()
|
|
||||||
|
|||||||
@@ -2,8 +2,7 @@ from common import dates, endoflife, http, releasedata
|
|||||||
|
|
||||||
MAIN_URL = "https://www.php.net/releases/index.php?json&max=-1"
|
MAIN_URL = "https://www.php.net/releases/index.php?json&max=-1"
|
||||||
|
|
||||||
product = releasedata.Product("php")
|
with releasedata.ProductData("php") as product_data:
|
||||||
|
|
||||||
# Fetch major versions
|
# Fetch major versions
|
||||||
latest_by_major = http.fetch_url(MAIN_URL).json()
|
latest_by_major = http.fetch_url(MAIN_URL).json()
|
||||||
major_version_urls = [f"{MAIN_URL}&version={major_version}" for major_version in latest_by_major]
|
major_version_urls = [f"{MAIN_URL}&version={major_version}" for major_version in latest_by_major]
|
||||||
@@ -14,6 +13,4 @@ for major_versions_response in http.fetch_urls(major_version_urls):
|
|||||||
for version in major_versions_data:
|
for version in major_versions_data:
|
||||||
if endoflife.DEFAULT_VERSION_PATTERN.match(version): # exclude versions such as "3.0.x (latest)"
|
if endoflife.DEFAULT_VERSION_PATTERN.match(version): # exclude versions such as "3.0.x (latest)"
|
||||||
date = dates.parse_date(major_versions_data[version]["date"])
|
date = dates.parse_date(major_versions_data[version]["date"])
|
||||||
product.declare_version(version, date)
|
product_data.declare_version(version, date)
|
||||||
|
|
||||||
product.write()
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ from common import dates, http, releasedata
|
|||||||
Only 18.0.20.3 and later will be picked up, as the format of the change log for 18.0.20 and 18.0.19 are different and
|
Only 18.0.20.3 and later will be picked up, as the format of the change log for 18.0.20 and 18.0.19 are different and
|
||||||
there is no entry for GA of version 18.0.18 and older."""
|
there is no entry for GA of version 18.0.18 and older."""
|
||||||
|
|
||||||
product = releasedata.Product("plesk")
|
with releasedata.ProductData("plesk") as product_data:
|
||||||
response = http.fetch_url("https://docs.plesk.com/release-notes/obsidian/change-log")
|
response = http.fetch_url("https://docs.plesk.com/release-notes/obsidian/change-log")
|
||||||
soup = BeautifulSoup(response.text, features="html5lib")
|
soup = BeautifulSoup(response.text, features="html5lib")
|
||||||
|
|
||||||
@@ -20,6 +20,4 @@ for release in soup.find_all("div", class_="changelog-entry--obsidian"):
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
date = dates.parse_date(release.p.text)
|
date = dates.parse_date(release.p.text)
|
||||||
product.declare_version(version, date)
|
product_data.declare_version(version, date)
|
||||||
|
|
||||||
product.write()
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ METHOD = "pypi"
|
|||||||
|
|
||||||
p_filter = sys.argv[1] if len(sys.argv) > 1 else None
|
p_filter = sys.argv[1] if len(sys.argv) > 1 else None
|
||||||
for product in endoflife.list_products(METHOD, p_filter):
|
for product in endoflife.list_products(METHOD, p_filter):
|
||||||
product_data = releasedata.Product(product.name)
|
with releasedata.ProductData(product.name) as product_data:
|
||||||
for config in product.get_auto_configs(METHOD):
|
for config in product.get_auto_configs(METHOD):
|
||||||
data = http.fetch_url(f"https://pypi.org/pypi/{config.url}/json").json()
|
data = http.fetch_url(f"https://pypi.org/pypi/{config.url}/json").json()
|
||||||
|
|
||||||
@@ -18,5 +18,3 @@ for product in endoflife.list_products(METHOD, p_filter):
|
|||||||
version = config.render(version_match)
|
version = config.render(version_match)
|
||||||
date = dates.parse_datetime(version_data[0]["upload_time_iso_8601"])
|
date = dates.parse_datetime(version_data[0]["upload_time_iso_8601"])
|
||||||
product_data.declare_version(version, date)
|
product_data.declare_version(version, date)
|
||||||
|
|
||||||
product_data.write()
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ PRODUCTS = {
|
|||||||
VERSION_REGEX = re.compile(r"(?P<version>\d+(?:\.\d+)*)", flags=re.IGNORECASE) # https://regex101.com/r/BY1vwV/1
|
VERSION_REGEX = re.compile(r"(?P<version>\d+(?:\.\d+)*)", flags=re.IGNORECASE) # https://regex101.com/r/BY1vwV/1
|
||||||
|
|
||||||
for product_name, url in PRODUCTS.items():
|
for product_name, url in PRODUCTS.items():
|
||||||
product = releasedata.Product(product_name)
|
with releasedata.ProductData(product_name) as product_data:
|
||||||
response = http.fetch_url(url)
|
response = http.fetch_url(url)
|
||||||
soup = BeautifulSoup(response.text, features="html5lib")
|
soup = BeautifulSoup(response.text, features="html5lib")
|
||||||
|
|
||||||
@@ -30,6 +30,4 @@ for product_name, url in PRODUCTS.items():
|
|||||||
if version_match:
|
if version_match:
|
||||||
version = version_match.group("version")
|
version = version_match.group("version")
|
||||||
date = dates.parse_date(columns[2].text)
|
date = dates.parse_date(columns[2].text)
|
||||||
product.declare_version(version, date)
|
product_data.declare_version(version, date)
|
||||||
|
|
||||||
product.write()
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ from common.git import Git
|
|||||||
|
|
||||||
VERSION_AND_DATE_PATTERN = re.compile(r"{product-title}\s(?P<version>\d+\.\d+\.\d+).*\n+Issued:\s(?P<date>\d{4}-\d\d-\d\d)$", re.MULTILINE)
|
VERSION_AND_DATE_PATTERN = re.compile(r"{product-title}\s(?P<version>\d+\.\d+\.\d+).*\n+Issued:\s(?P<date>\d{4}-\d\d-\d\d)$", re.MULTILINE)
|
||||||
|
|
||||||
product = releasedata.Product("red-hat-openshift")
|
with releasedata.ProductData("red-hat-openshift") as product_data:
|
||||||
git = Git("https://github.com/openshift/openshift-docs.git")
|
git = Git("https://github.com/openshift/openshift-docs.git")
|
||||||
git.setup()
|
git.setup()
|
||||||
|
|
||||||
@@ -24,6 +24,4 @@ for branch in git.list_branches("refs/heads/enterprise-[4-9]*"):
|
|||||||
with release_notes_file.open("rb") as f:
|
with release_notes_file.open("rb") as f:
|
||||||
content = f.read().decode("utf-8")
|
content = f.read().decode("utf-8")
|
||||||
for (version, date_str) in VERSION_AND_DATE_PATTERN.findall(content):
|
for (version, date_str) in VERSION_AND_DATE_PATTERN.findall(content):
|
||||||
product.declare_version(version, dates.parse_date(date_str))
|
product_data.declare_version(version, dates.parse_date(date_str))
|
||||||
|
|
||||||
product.write()
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ A few of the older versions, such as 'Satellite 6.1 GA Release (Build 6.1.1)', w
|
|||||||
# https://regex101.com/r/m8aWXG/1
|
# https://regex101.com/r/m8aWXG/1
|
||||||
VERSION_PATTERN = re.compile(r"^Satellite (?P<version>\d+\.\d+\.\d+([.-]\d+)?) ([Uu]pdate|[Rr]elease)$")
|
VERSION_PATTERN = re.compile(r"^Satellite (?P<version>\d+\.\d+\.\d+([.-]\d+)?) ([Uu]pdate|[Rr]elease)$")
|
||||||
|
|
||||||
product = releasedata.Product("redhat-satellite")
|
with releasedata.ProductData("redhat-satellite") as product_data:
|
||||||
response = http.fetch_url("https://access.redhat.com/articles/1365633")
|
response = http.fetch_url("https://access.redhat.com/articles/1365633")
|
||||||
soup = BeautifulSoup(response.text, features="html5lib")
|
soup = BeautifulSoup(response.text, features="html5lib")
|
||||||
|
|
||||||
@@ -23,6 +23,4 @@ for table in soup.findAll("tbody"):
|
|||||||
if version_match:
|
if version_match:
|
||||||
version = version_match["version"].replace('-', '.') # a.b.c-d => a.b.c.d
|
version = version_match["version"].replace('-', '.') # a.b.c-d => a.b.c.d
|
||||||
date = dates.parse_date(td_list[1].get_text().strip())
|
date = dates.parse_date(td_list[1].get_text().strip())
|
||||||
product.declare_version(version, date)
|
product_data.declare_version(version, date)
|
||||||
|
|
||||||
product.write()
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ from common import dates, http, releasedata
|
|||||||
# https://regex101.com/r/877ibq/1
|
# https://regex101.com/r/877ibq/1
|
||||||
VERSION_PATTERN = re.compile(r"RHEL (?P<major>\d)(\. ?(?P<minor>\d+))?(( Update (?P<minor2>\d))| GA)?")
|
VERSION_PATTERN = re.compile(r"RHEL (?P<major>\d)(\. ?(?P<minor>\d+))?(( Update (?P<minor2>\d))| GA)?")
|
||||||
|
|
||||||
product = releasedata.Product("redhat")
|
with releasedata.ProductData("redhat") as product_data:
|
||||||
response = http.fetch_url("https://access.redhat.com/articles/3078")
|
response = http.fetch_url("https://access.redhat.com/articles/3078")
|
||||||
soup = BeautifulSoup(response.text, features="html5lib")
|
soup = BeautifulSoup(response.text, features="html5lib")
|
||||||
|
|
||||||
@@ -21,7 +21,4 @@ for tr in soup.findAll("tr"):
|
|||||||
version += ("." + version_match["minor"]) if version_match["minor"] else ""
|
version += ("." + version_match["minor"]) if version_match["minor"] else ""
|
||||||
version += ("." + version_match["minor2"]) if version_match["minor2"] else ""
|
version += ("." + version_match["minor2"]) if version_match["minor2"] else ""
|
||||||
date = dates.parse_date(td_list[1].get_text())
|
date = dates.parse_date(td_list[1].get_text())
|
||||||
|
product_data.declare_version(version, date)
|
||||||
product.declare_version(version, date)
|
|
||||||
|
|
||||||
product.write()
|
|
||||||
|
|||||||
@@ -1,13 +1,10 @@
|
|||||||
from common import dates, endoflife, http, releasedata
|
from common import dates, endoflife, http, releasedata
|
||||||
|
|
||||||
product = releasedata.Product("rockylinux")
|
with releasedata.ProductData("rockylinux") as product_data:
|
||||||
response = http.fetch_url("https://raw.githubusercontent.com/rocky-linux/wiki.rockylinux.org/development/docs/include/releng/version_table.md")
|
response = http.fetch_url("https://raw.githubusercontent.com/rocky-linux/wiki.rockylinux.org/development/docs/include/releng/version_table.md")
|
||||||
|
|
||||||
for line in response.text.strip().split('\n'):
|
for line in response.text.strip().split('\n'):
|
||||||
items = line.split('|')
|
items = line.split('|')
|
||||||
if len(items) >= 5 and endoflife.DEFAULT_VERSION_PATTERN.match(items[1].strip()):
|
if len(items) >= 5 and endoflife.DEFAULT_VERSION_PATTERN.match(items[1].strip()):
|
||||||
version = items[1].strip()
|
version = items[1].strip()
|
||||||
date = dates.parse_date(items[3])
|
date = dates.parse_date(items[3])
|
||||||
product.declare_version(version, date)
|
product_data.declare_version(version, date)
|
||||||
|
|
||||||
product.write()
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ from common import dates, http, releasedata
|
|||||||
# https://regex101.com/r/c1ribd/1
|
# https://regex101.com/r/c1ribd/1
|
||||||
VERSION_PATTERN = re.compile(r"^ROS (?P<name>(\w| )+)")
|
VERSION_PATTERN = re.compile(r"^ROS (?P<name>(\w| )+)")
|
||||||
|
|
||||||
product = releasedata.Product("ros")
|
with releasedata.ProductData("ros") as product_data:
|
||||||
response = http.fetch_url("https://wiki.ros.org/Distributions")
|
response = http.fetch_url("https://wiki.ros.org/Distributions")
|
||||||
soup = BeautifulSoup(response.text, features="html5lib")
|
soup = BeautifulSoup(response.text, features="html5lib")
|
||||||
|
|
||||||
@@ -25,6 +25,4 @@ for tr in soup.findAll("tr"):
|
|||||||
x = td_list[1].get_text().split(",")
|
x = td_list[1].get_text().split(",")
|
||||||
date = dates.parse_date(x[0][:-2] + x[1])
|
date = dates.parse_date(x[0][:-2] + x[1])
|
||||||
|
|
||||||
product.declare_version(version, date)
|
product_data.declare_version(version, date)
|
||||||
|
|
||||||
product.write()
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import logging
|
|||||||
from bs4 import BeautifulSoup
|
from bs4 import BeautifulSoup
|
||||||
from common import dates, http, releasedata
|
from common import dates, http, releasedata
|
||||||
|
|
||||||
product = releasedata.Product("sles")
|
with releasedata.ProductData("sles") as product_data:
|
||||||
response = http.fetch_url("https://www.suse.com/lifecycle")
|
response = http.fetch_url("https://www.suse.com/lifecycle")
|
||||||
soup = BeautifulSoup(response.text, features="html5lib")
|
soup = BeautifulSoup(response.text, features="html5lib")
|
||||||
|
|
||||||
@@ -25,8 +25,6 @@ for detail_id in [f"detail{row['id']}" for row in sles_header_rows]:
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
date = dates.parse_date(date_str)
|
date = dates.parse_date(date_str)
|
||||||
product.declare_version(version, date)
|
product_data.declare_version(version, date)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
logging.info(f"Ignoring {version}: date '{date_str}' could not be parsed")
|
logging.info(f"Ignoring {version}: date '{date_str}' could not be parsed")
|
||||||
|
|
||||||
product.write()
|
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ def get_latest_minor_versions(versions: list[str]) -> list[str]:
|
|||||||
return latest_versions
|
return latest_versions
|
||||||
|
|
||||||
|
|
||||||
product = releasedata.Product("splunk")
|
with releasedata.ProductData("splunk") as product_data:
|
||||||
main = http.fetch_url("https://docs.splunk.com/Documentation/Splunk")
|
main = http.fetch_url("https://docs.splunk.com/Documentation/Splunk")
|
||||||
soup = BeautifulSoup(main.text, features="html5lib")
|
soup = BeautifulSoup(main.text, features="html5lib")
|
||||||
|
|
||||||
@@ -44,6 +44,4 @@ for response in http.fetch_urls(latest_minor_versions_urls):
|
|||||||
for (version_str, date_str) in VERSION_DATE_PATTERN.findall(response.text):
|
for (version_str, date_str) in VERSION_DATE_PATTERN.findall(response.text):
|
||||||
version_str = f"{version_str}.0" if len(version_str.split(".")) == 2 else version_str # convert x.y to x.y.0
|
version_str = f"{version_str}.0" if len(version_str.split(".")) == 2 else version_str # convert x.y to x.y.0
|
||||||
date = dates.parse_date(date_str)
|
date = dates.parse_date(date_str)
|
||||||
product.declare_version(version_str, date)
|
product_data.declare_version(version_str, date)
|
||||||
|
|
||||||
product.write()
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
from common import dates, http, releasedata
|
from common import dates, http, releasedata
|
||||||
|
|
||||||
product = releasedata.Product("typo3")
|
with releasedata.ProductData("typo3") as product_data:
|
||||||
data = http.fetch_url("https://get.typo3.org/api/v1/release/").json()
|
data = http.fetch_url("https://get.typo3.org/api/v1/release/").json()
|
||||||
for v in data:
|
for v in data:
|
||||||
if v['type'] == 'development':
|
if v['type'] == 'development':
|
||||||
@@ -8,6 +8,4 @@ for v in data:
|
|||||||
|
|
||||||
version = v["version"]
|
version = v["version"]
|
||||||
date = dates.parse_datetime(v["date"], to_utc=False) # utc kept for now for backwards compatibility
|
date = dates.parse_datetime(v["date"], to_utc=False) # utc kept for now for backwards compatibility
|
||||||
product.declare_version(version, date)
|
product_data.declare_version(version, date)
|
||||||
|
|
||||||
product.write()
|
|
||||||
|
|||||||
@@ -9,9 +9,8 @@ from common import dates, http, releasedata
|
|||||||
|
|
||||||
BASE_URL = "https://unity.com/releases/editor/qa/lts-releases"
|
BASE_URL = "https://unity.com/releases/editor/qa/lts-releases"
|
||||||
|
|
||||||
product = releasedata.Product("unity")
|
|
||||||
next_page_url = BASE_URL
|
next_page_url = BASE_URL
|
||||||
|
with releasedata.ProductData("unity") as product_data:
|
||||||
# Do not try to fetch multiple pages in parallel: it is raising a lot of errors and make the overall process slower.
|
# Do not try to fetch multiple pages in parallel: it is raising a lot of errors and make the overall process slower.
|
||||||
while next_page_url:
|
while next_page_url:
|
||||||
response = http.fetch_url(next_page_url)
|
response = http.fetch_url(next_page_url)
|
||||||
@@ -20,9 +19,7 @@ while next_page_url:
|
|||||||
for release in soup.find_all('div', class_='component-releases-item__show__inner-header'):
|
for release in soup.find_all('div', class_='component-releases-item__show__inner-header'):
|
||||||
version = release.find('h4').find('span').text
|
version = release.find('h4').find('span').text
|
||||||
date = dates.parse_datetime(release.find('time').attrs['datetime'])
|
date = dates.parse_datetime(release.find('time').attrs['datetime'])
|
||||||
product.declare_version(version, date)
|
product_data.declare_version(version, date)
|
||||||
|
|
||||||
next_link = soup.find('a', {"rel": "next"})
|
next_link = soup.find('a', {"rel": "next"})
|
||||||
next_page_url = BASE_URL + next_link.attrs['href'] if next_link else None
|
next_page_url = BASE_URL + next_link.attrs['href'] if next_link else None
|
||||||
|
|
||||||
product.write()
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ from common import dates, endoflife, http, releasedata
|
|||||||
|
|
||||||
DATE_PATTERN = re.compile(r"\d{4}-\d{2}-\d{2}")
|
DATE_PATTERN = re.compile(r"\d{4}-\d{2}-\d{2}")
|
||||||
|
|
||||||
product = releasedata.Product("unrealircd")
|
with releasedata.ProductData("unrealircd") as product_data:
|
||||||
response = http.fetch_url("https://www.unrealircd.org/docwiki/index.php?title=History_of_UnrealIRCd_releases&action=raw")
|
response = http.fetch_url("https://www.unrealircd.org/docwiki/index.php?title=History_of_UnrealIRCd_releases&action=raw")
|
||||||
wikicode = mwparserfromhell.parse(response.text)
|
wikicode = mwparserfromhell.parse(response.text)
|
||||||
|
|
||||||
@@ -18,6 +18,4 @@ for tr in wikicode.ifilter_tags(matches=lambda node: node.tag == "tr"):
|
|||||||
date_str = items[1].__strip__()
|
date_str = items[1].__strip__()
|
||||||
if endoflife.DEFAULT_VERSION_PATTERN.match(version) and DATE_PATTERN.match(date_str):
|
if endoflife.DEFAULT_VERSION_PATTERN.match(version) and DATE_PATTERN.match(date_str):
|
||||||
date = dates.parse_date(date_str)
|
date = dates.parse_date(date_str)
|
||||||
product.declare_version(version, date)
|
product_data.declare_version(version, date)
|
||||||
|
|
||||||
product.write()
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ from common import dates, http, releasedata
|
|||||||
|
|
||||||
"""Fetches Veeam versions from https://www.veeam.com."""
|
"""Fetches Veeam versions from https://www.veeam.com."""
|
||||||
|
|
||||||
product = releasedata.Product("veeam-backup-and-replication")
|
with releasedata.ProductData("veeam-backup-and-replication") as product_data:
|
||||||
response = http.fetch_url("https://www.veeam.com/kb2680")
|
response = http.fetch_url("https://www.veeam.com/kb2680")
|
||||||
soup = BeautifulSoup(response.text, features="html5lib")
|
soup = BeautifulSoup(response.text, features="html5lib")
|
||||||
|
|
||||||
@@ -26,6 +26,4 @@ for table in soup.find_all("table"):
|
|||||||
# whitespaces in version numbers are replaced with dashes
|
# whitespaces in version numbers are replaced with dashes
|
||||||
version = re.sub(r'\s+', "-", cells[version_index].get_text().strip())
|
version = re.sub(r'\s+', "-", cells[version_index].get_text().strip())
|
||||||
date = dates.parse_date(date_str)
|
date = dates.parse_date(date_str)
|
||||||
product.declare_version(version, date)
|
product_data.declare_version(version, date)
|
||||||
|
|
||||||
product.write()
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ URLS = [
|
|||||||
"https://learn.microsoft.com/en-us/visualstudio/releases/2022/release-history",
|
"https://learn.microsoft.com/en-us/visualstudio/releases/2022/release-history",
|
||||||
]
|
]
|
||||||
|
|
||||||
product = releasedata.Product("visualstudio")
|
with releasedata.ProductData("visualstudio") as product_data:
|
||||||
for response in http.fetch_urls(URLS):
|
for response in http.fetch_urls(URLS):
|
||||||
soup = BeautifulSoup(response.text, features="html5lib")
|
soup = BeautifulSoup(response.text, features="html5lib")
|
||||||
|
|
||||||
@@ -30,6 +30,4 @@ for response in http.fetch_urls(URLS):
|
|||||||
date = dates.parse_date(date)
|
date = dates.parse_date(date)
|
||||||
|
|
||||||
if date and version and endoflife.DEFAULT_VERSION_PATTERN.match(version):
|
if date and version and endoflife.DEFAULT_VERSION_PATTERN.match(version):
|
||||||
product.declare_version(version, date)
|
product_data.declare_version(version, date)
|
||||||
|
|
||||||
product.write()
|
|
||||||
|
|||||||
Reference in New Issue
Block a user