diff --git a/releases/firefox.json b/releases/firefox.json new file mode 100644 index 00000000..a24e1e5f --- /dev/null +++ b/releases/firefox.json @@ -0,0 +1,503 @@ +{ + "108.0.2": "2023-01-05", + "108.0.1": "2022-12-16", + "108.0": "2022-12-13", + "102.6.0": "2022-12-13", + "107.0.1": "2022-11-29", + "107.0": "2022-11-15", + "102.5.0": "2022-11-15", + "106.0.5": "2022-11-04", + "106.0.4": "2022-11-03", + "106.0.3": "2022-10-31", + "106.0.2": "2022-10-26", + "106.0.1": "2022-10-20", + "106.0": "2022-10-18", + "102.4.0": "2022-10-18", + "105.0.3": "2022-10-07", + "105.0.2": "2022-10-04", + "105.0.1": "2022-09-23", + "105.0": "2022-09-20", + "102.3.0": "2022-09-20", + "104.0.2": "2022-09-06", + "104.0.1": "2022-08-30", + "91.13.0": "2022-08-23", + "104.0": "2022-08-23", + "102.2.0": "2022-08-23", + "103.0.2": "2022-08-09", + "103.0.1": "2022-08-01", + "91.12.0": "2022-07-26", + "103.0": "2022-07-26", + "102.1.0": "2022-07-26", + "102.0.1": "2022-07-06", + "91.11.0": "2022-06-28", + "102.0": "2022-06-28", + "101.0.1": "2022-06-09", + "91.10.0": "2022-05-31", + "101.0": "2022-05-31", + "91.9.1": "2022-05-20", + "100.0.2": "2022-05-20", + "100.0.1": "2022-05-16", + "91.9.0": "2022-05-03", + "100.0": "2022-05-03", + "99.0.1": "2022-04-12", + "99.0": "2022-04-05", + "91.8.0": "2022-04-05", + "98.0.2": "2022-03-23", + "98.0.1": "2022-03-14", + "91.7.1": "2022-03-14", + "98.0": "2022-03-08", + "91.7.0": "2022-03-08", + "97.0.2": "2022-03-05", + "91.6.1": "2022-03-05", + "97.0.1": "2022-02-17", + "97.0": "2022-02-08", + "91.6.0": "2022-02-08", + "96.0.3": "2022-01-27", + "91.5.1": "2022-01-27", + "96.0.2": "2022-01-20", + "96.0.1": "2022-01-14", + "96.0": "2022-01-11", + "91.5.0": "2022-01-11", + "95.0.2": "2021-12-19", + "95.0.1": "2021-12-16", + "91.4.1": "2021-12-16", + "95.0": "2021-12-07", + "91.4.0": "2021-12-07", + "94.0.2": "2021-11-22", + "94.0.1": "2021-11-04", + "94.0": "2021-11-02", + "91.3.0": "2021-11-02", + "93.0": "2021-10-05", + "91.2.0": "2021-10-05", + "78.15.0": "2021-10-05", + "92.0.1": "2021-09-23", + "92.0": "2021-09-07", + "91.1.0": "2021-09-07", + "78.14.0": "2021-09-07", + "91.0.2": "2021-08-24", + "91.0.1": "2021-08-17", + "91.0": "2021-08-10", + "78.13.0": "2021-08-10", + "90.0.2": "2021-07-22", + "90.0.1": "2021-07-19", + "90.0": "2021-07-13", + "78.12.0": "2021-07-13", + "89.0.2": "2021-06-23", + "89.0.1": "2021-06-17", + "89.0": "2021-06-01", + "78.11.0": "2021-06-01", + "88.0.1": "2021-05-05", + "78.10.1": "2021-05-04", + "88.0": "2021-04-19", + "78.10.0": "2021-04-19", + "87.0": "2021-03-23", + "78.9.0": "2021-03-23", + "86.0.1": "2021-03-11", + "86.0": "2021-02-23", + "78.8.0": "2021-02-23", + "85.0.2": "2021-02-09", + "85.0.1": "2021-02-05", + "78.7.1": "2021-02-05", + "85.0": "2021-01-26", + "78.7.0": "2021-01-26", + "84.0.2": "2021-01-06", + "78.6.1": "2021-01-06", + "84.0.1": "2020-12-22", + "84.0": "2020-12-15", + "78.6.0": "2020-12-15", + "83.0": "2020-11-17", + "78.5.0": "2020-11-17", + "82.0.3": "2020-11-10", + "78.4.1": "2020-11-10", + "82.0.2": "2020-10-28", + "82.0.1": "2020-10-27", + "82.0": "2020-10-20", + "78.4.0": "2020-10-20", + "81.0.2": "2020-10-13", + "81.0.1": "2020-10-01", + "78.3.1": "2020-10-01", + "81.0": "2020-09-22", + "78.3.0": "2020-09-22", + "80.0.1": "2020-09-01", + "80.0": "2020-08-25", + "78.2.0": "2020-08-25", + "68.12.0": "2020-08-25", + "79.0": "2020-07-28", + "78.1.0": "2020-07-28", + "68.11.0": "2020-07-28", + "78.0.2": "2020-07-09", + "78.0.1": "2020-07-01", + "78.0": "2020-06-30", + "68.10.0": "2020-06-30", + "77.0.1": "2020-06-03", + "77.0": "2020-06-02", + "68.9.0": "2020-06-02", + "76.0.1": "2020-05-08", + "76.0": "2020-05-05", + "68.8.0": "2020-05-05", + "75.0": "2020-04-07", + "68.7.0": "2020-04-07", + "74.0.1": "2020-04-03", + "68.6.1": "2020-04-03", + "74.0": "2020-03-10", + "68.6.0": "2020-03-10", + "73.0.1": "2020-02-18", + "73.0": "2020-02-11", + "68.5.0": "2020-02-11", + "72.0.2": "2020-01-20", + "68.4.2": "2020-01-20", + "72.0.1": "2020-01-08", + "68.4.1": "2020-01-08", + "72.0": "2020-01-07", + "68.4.0": "2020-01-07", + "71.0": "2019-12-03", + "68.3.0": "2019-12-03", + "70.0.1": "2019-10-31", + "70.0": "2019-10-22", + "68.2.0": "2019-10-22", + "69.0.3": "2019-10-10", + "69.0.2": "2019-10-03", + "69.0.1": "2019-09-18", + "69.0": "2019-09-03", + "68.1.0": "2019-09-03", + "60.9.0": "2019-09-03", + "68.0.2": "2019-08-14", + "68.0.1": "2019-07-18", + "68.0": "2019-07-09", + "60.8.0": "2019-07-09", + "67.0.4": "2019-06-20", + "60.7.2": "2019-06-20", + "67.0.3": "2019-06-18", + "60.7.1": "2019-06-18", + "67.0.2": "2019-06-11", + "67.0.1": "2019-06-04", + "67.0": "2019-05-21", + "60.7.0": "2019-05-21", + "60.6.3": "2019-05-08", + "66.0.5": "2019-05-07", + "66.0.4": "2019-05-05", + "60.6.2": "2019-05-05", + "66.0.3": "2019-04-10", + "66.0.2": "2019-03-27", + "66.0.1": "2019-03-22", + "60.6.1": "2019-03-22", + "66.0": "2019-03-19", + "60.6.0": "2019-03-19", + "65.0.2": "2019-02-28", + "60.5.2": "2019-02-22", + "65.0.1": "2019-02-12", + "60.5.1": "2019-02-12", + "65.0": "2019-01-29", + "60.5.0": "2019-01-29", + "64.0.2": "2019-01-09", + "64.0": "2018-12-11", + "60.4.0": "2018-12-11", + "63.0.3": "2018-11-15", + "63.0.1": "2018-10-31", + "63.0": "2018-10-23", + "60.3.0": "2018-10-23", + "62.0.3": "2018-10-02", + "60.2.2": "2018-10-02", + "62.0.2": "2018-09-21", + "60.2.1": "2018-09-21", + "62.0": "2018-09-05", + "60.2.0": "2018-09-05", + "61.0.2": "2018-08-08", + "61.0.1": "2018-07-05", + "61.0": "2018-06-26", + "60.1.0": "2018-06-26", + "52.9.0": "2018-06-26", + "60.0.2": "2018-06-06", + "52.8.1": "2018-06-06", + "60.0.1": "2018-05-16", + "60.0": "2018-05-09", + "52.8.0": "2018-05-09", + "59.0.3": "2018-04-30", + "52.7.4": "2018-04-30", + "59.0.2": "2018-03-26", + "52.7.3": "2018-03-26", + "59.0.1": "2018-03-16", + "52.7.2": "2018-03-16", + "52.7.1": "2018-03-14", + "59.0": "2018-03-13", + "52.7.0": "2018-03-13", + "58.0.2": "2018-02-07", + "58.0.1": "2018-01-29", + "58.0": "2018-01-23", + "52.6.0": "2018-01-23", + "57.0.4": "2018-01-04", + "57.0.3": "2017-12-28", + "52.5.3": "2017-12-28", + "57.0.2": "2017-12-07", + "52.5.2": "2017-12-07", + "57.0.1": "2017-11-29", + "57.0": "2017-11-14", + "52.5.0": "2017-11-14", + "56.0.2": "2017-10-26", + "56.0.1": "2017-10-09", + "52.4.1": "2017-10-09", + "56.0": "2017-09-28", + "52.4.0": "2017-09-28", + "55.0.3": "2017-08-25", + "55.0.2": "2017-08-16", + "55.0.1": "2017-08-10", + "55.0": "2017-08-08", + "52.3.0": "2017-08-08", + "54.0.1": "2017-06-29", + "52.2.1": "2017-06-29", + "54.0": "2017-06-13", + "52.2.0": "2017-06-13", + "53.0.3": "2017-05-19", + "52.1.2": "2017-05-19", + "53.0.2": "2017-05-05", + "52.1.1": "2017-05-05", + "53.0": "2017-04-19", + "52.1.0": "2017-04-19", + "45.9.0": "2017-04-19", + "52.0.2": "2017-03-28", + "52.0.1": "2017-03-17", + "52.0": "2017-03-07", + "45.8.0": "2017-03-07", + "51.0.1": "2017-01-26", + "51.0": "2017-01-24", + "45.7.0": "2017-01-24", + "50.1.0": "2016-12-13", + "45.6.0": "2016-12-13", + "50.0.2": "2016-11-30", + "45.5.1": "2016-11-30", + "50.0.1": "2016-11-28", + "50.0": "2016-11-15", + "45.5.0": "2016-11-15", + "47.0.2": "2016-11-01", + "49.0.2": "2016-10-20", + "49.0.1": "2016-09-23", + "49.0": "2016-09-20", + "45.4.0": "2016-09-20", + "48.0.2": "2016-08-24", + "48.0.1": "2016-08-18", + "48.0": "2016-08-02", + "45.3.0": "2016-08-02", + "47.0.1": "2016-06-28", + "47.0": "2016-06-07", + "45.2.0": "2016-06-07", + "46.0.1": "2016-05-03", + "45.1.1": "2016-05-03", + "46.0": "2016-04-26", + "38.8.0": "2016-04-26", + "45.0.2": "2016-04-11", + "45.0.1": "2016-03-16", + "38.7.1": "2016-03-16", + "45.0": "2016-03-08", + "38.7.0": "2016-03-08", + "44.0.2": "2016-02-11", + "38.6.1": "2016-02-11", + "44.0.1": "2016-02-08", + "44.0": "2016-01-26", + "38.6.0": "2016-01-26", + "43.0.4": "2016-01-06", + "43.0.3": "2015-12-28", + "43.0.2": "2015-12-22", + "38.5.2": "2015-12-22", + "38.5.1": "2015-12-21", + "43.0.1": "2015-12-18", + "43.0": "2015-12-15", + "38.5.0": "2015-12-15", + "42.0": "2015-11-03", + "38.4.0": "2015-11-03", + "41.0.2": "2015-10-15", + "41.0.1": "2015-09-30", + "41.0": "2015-09-22", + "38.3.0": "2015-09-22", + "40.0.3": "2015-08-27", + "38.2.1": "2015-08-27", + "40.0.2": "2015-08-13", + "40.0": "2015-08-11", + "38.2.0": "2015-08-11", + "39.0.3": "2015-08-06", + "38.1.1": "2015-08-06", + "39.0": "2015-07-02", + "38.1.0": "2015-07-02", + "31.8.0": "2015-07-02", + "38.0.5": "2015-06-02", + "38.0.1": "2015-05-14", + "38.0": "2015-05-12", + "31.7.0": "2015-05-12", + "37.0.2": "2015-04-20", + "37.0.1": "2015-04-03", + "37.0": "2015-03-31", + "31.6.0": "2015-03-31", + "36.0.4": "2015-03-21", + "31.5.3": "2015-03-21", + "36.0.3": "2015-03-20", + "31.5.2": "2015-03-20", + "36.0.1": "2015-03-05", + "36.0": "2015-02-24", + "31.5.0": "2015-02-24", + "35.0.1": "2015-01-26", + "35.0": "2015-01-13", + "31.4.0": "2015-01-13", + "34.0.5": "2014-12-01", + "34.0": "2014-12-01", + "31.3.0": "2014-12-01", + "33.1.1": "2014-11-14", + "33.1": "2014-11-10", + "33.0.3": "2014-11-06", + "33.0.2": "2014-10-28", + "33.0.1": "2014-10-24", + "31.2.0": "2014-10-14", + "33.0": "2014-10-13", + "32.0.3": "2014-09-24", + "24.8.1": "2014-09-24", + "31.1.1": "2014-09-23", + "32.0.2": "2014-09-18", + "32.0.1": "2014-09-12", + "32.0": "2014-09-02", + "24.8.0": "2014-09-02", + "31.1.0": "2014-09-01", + "31.0": "2014-07-22", + "24.7.0": "2014-07-22", + "30.0": "2014-06-10", + "24.6.0": "2014-06-10", + "29.0.1": "2014-05-09", + "29.0": "2014-04-29", + "24.5.0": "2014-04-29", + "28.0": "2014-03-18", + "24.4.0": "2014-03-18", + "27.0.1": "2014-02-13", + "27.0": "2014-02-04", + "24.3.0": "2014-02-04", + "26.0": "2013-12-10", + "24.2.0": "2013-12-10", + "25.0.1": "2013-11-15", + "24.1.1": "2013-11-15", + "17.0.11": "2013-11-15", + "25.0": "2013-10-29", + "24.1.0": "2013-10-29", + "17.0.10": "2013-10-29", + "24.0": "2013-09-17", + "17.0.9": "2013-09-17", + "23.0.1": "2013-08-16", + "23.0": "2013-08-06", + "17.0.8": "2013-08-06", + "22.0": "2013-06-25", + "17.0.7": "2013-06-25", + "21.0": "2013-05-14", + "17.0.6": "2013-05-14", + "20.0.1": "2013-04-11", + "20.0": "2013-04-02", + "17.0.5": "2013-04-02", + "19.0.2": "2013-03-07", + "17.0.4": "2013-03-07", + "19.0.1": "2013-02-27", + "19.0": "2013-02-19", + "17.0.3": "2013-02-19", + "18.0.2": "2013-02-05", + "18.0.1": "2013-01-18", + "18.0": "2013-01-08", + "17.0.2": "2013-01-08", + "10.0.12": "2013-01-08", + "17.0.1": "2012-11-30", + "17.0": "2012-11-20", + "10.0.11": "2012-11-20", + "16.0.2": "2012-10-26", + "10.0.10": "2012-10-26", + "10.0.9": "2012-10-12", + "16.0.1": "2012-10-11", + "16.0": "2012-10-09", + "10.0.8": "2012-10-09", + "15.0.1": "2012-09-06", + "15.0": "2012-08-28", + "10.0.7": "2012-08-28", + "14.0.1": "2012-07-17", + "10.0.6": "2012-07-17", + "13.0.1": "2012-06-15", + "13.0": "2012-06-05", + "10.0.5": "2012-06-05", + "12.0": "2012-04-24", + "10.0.4": "2012-04-24", + "3.6.28": "2012-03-13", + "11.0": "2012-03-13", + "10.0.3": "2012-03-13", + "3.6.27": "2012-02-17", + "10.0.2": "2012-02-16", + "10.0.1": "2012-02-10", + "3.6.26": "2012-01-31", + "10.0": "2012-01-31", + "9.0.1": "2011-12-21", + "9.0": "2011-12-20", + "3.6.25": "2011-12-20", + "8.0.1": "2011-11-21", + "8.0": "2011-11-08", + "3.6.24": "2011-11-08", + "7.0.1": "2011-09-29", + "7.0": "2011-09-27", + "3.6.23": "2011-09-27", + "6.0.2": "2011-09-06", + "3.6.22": "2011-09-06", + "6.0.1": "2011-08-30", + "3.6.21": "2011-08-30", + "6.0": "2011-08-16", + "3.6.20": "2011-08-16", + "5.0.1": "2011-07-11", + "3.6.19": "2011-07-11", + "5.0": "2011-06-21", + "3.6.18": "2011-06-21", + "4.0.1": "2011-04-28", + "3.6.17": "2011-04-28", + "3.5.19": "2011-04-28", + "4.0": "2011-03-22", + "3.6.16": "2011-03-22", + "3.5.18": "2011-03-22", + "3.6.15": "2011-03-04", + "3.6.14": "2011-03-01", + "3.5.17": "2011-03-01", + "3.6.13": "2010-12-09", + "3.5.16": "2010-12-09", + "3.6.12": "2010-10-27", + "3.5.15": "2010-10-27", + "3.6.11": "2010-10-19", + "3.5.14": "2010-10-19", + "3.6.10": "2010-09-15", + "3.5.13": "2010-09-15", + "3.6.9": "2010-09-07", + "3.5.12": "2010-09-07", + "3.6.8": "2010-07-23", + "3.6.7": "2010-07-20", + "3.5.11": "2010-07-20", + "3.6.6": "2010-06-26", + "3.6.4": "2010-06-22", + "3.5.10": "2010-06-22", + "3.6.3": "2010-04-01", + "3.5.9": "2010-03-30", + "3.0.19": "2010-03-30", + "3.6.2": "2010-03-22", + "3.5.8": "2010-02-17", + "3.0.18": "2010-02-17", + "3.6": "2010-01-21", + "3.5.7": "2010-01-05", + "3.0.17": "2010-01-05", + "3.5.6": "2009-12-15", + "3.0.16": "2009-12-15", + "3.5.5": "2009-11-05", + "3.5.4": "2009-10-27", + "3.0.15": "2009-10-27", + "3.5.3": "2009-09-09", + "3.0.14": "2009-09-09", + "3.5.2": "2009-08-03", + "3.0.13": "2009-08-03", + "3.0.12": "2009-07-21", + "3.5.1": "2009-07-16", + "3.5": "2009-06-30", + "3.0.11": "2009-06-11", + "3.0.10": "2009-04-27", + "3.0.9": "2009-04-21", + "3.0.8": "2009-03-27", + "3.0.7": "2009-03-04", + "3.0.6": "2009-02-03", + "3.0.5": "2008-12-16", + "3.0.4": "2008-11-12", + "3.0.3": "2008-09-26", + "3.0.2": "2008-09-23", + "3.0.1": "2008-07-16", + "3.0": "2008-06-17" +} \ No newline at end of file diff --git a/src/firefox.py b/src/firefox.py new file mode 100644 index 00000000..a95717a5 --- /dev/null +++ b/src/firefox.py @@ -0,0 +1,153 @@ +import json +from typing import Tuple +from datetime import datetime +import re +import requests +import urllib.request +from bs4 import BeautifulSoup +import concurrent.futures + +"""Fetch Firefox versions with their dates from https://www.mozilla.org/en-US/firefox/releases/""" +URL = "https://www.mozilla.org/en-US/firefox/releases/" +PRODUCT = "firefox" + +DATE_REGEX = r"(January|Jan|February|Feb|March|Mar|April|Apr|May|June|Jun|July|Jul|August|Aug|September|Sept|October|Oct|November|Nov|December|Dec)\s+\d{1,2}(st|nd|rd|th)?,\s+\d{4}" +VERSION_REGEX = r"\d+(\.\d+)*" + +class UnsupportedReleasePageError(Exception): + "Raised when a firefox release page is not supported" + pass + +class InvalidPageVariantError(Exception): + "Raised when an invalid variant is passed to get_version_and_date" + pass + +def format_date(unformatted_date: str) -> str: + """ Format date from July 11, 2002 to 2002-07-11 """ + date = re.sub(r'(\d)(st|nd|rd|th)', r'\1', unformatted_date) + formats = ["%b %d, %Y", "%B %d, %Y"] + for f in formats: + try: + return datetime.strptime(date, f).strftime("%Y-%m-%d") + except ValueError: + pass + return "" + +def get_version_and_date_varant_1(soup: BeautifulSoup) -> Tuple[str, str]: + """ Version matching for firefox versions >= 28.0 (usually) """ + # get version + version = soup.find("div", class_="c-release-version").get_text() + + # get date + unformatted_date = soup.find("p", class_="c-release-date").get_text() + date = format_date(unformatted_date) + + return (version, date) + +def get_version_and_date_variant_2(soup: BeautifulSoup) -> Tuple[str, str]: + """ Version matching for firefox versions >= 10.0 (usually) """ + release_info = soup.find("h2").find("small").text + + # get version + version_match = re.search(VERSION_REGEX, soup.select('div#nav-access a')[0].get("href")) + if version_match is None: + raise InvalidPageVariantError("Unable to find version") + version = version_match.group() + + # get date + unformatted_date_match = re.search(DATE_REGEX, release_info) + if unformatted_date_match is None: + raise InvalidPageVariantError("Unable to find date") + unformatted_date = unformatted_date_match.group() + date = format_date(unformatted_date) + + return (version, date) + +def get_version_and_date_variant_3(soup: BeautifulSoup) -> Tuple[str, str]: + """ Version matching for firefox versions >= 3.0 (usually) """ + release_info = soup.select('div#main-feature p em')[0].get_text() + + # get version + version_match = re.search(VERSION_REGEX, release_info) + if version_match is None: + raise InvalidPageVariantError("Unable to find version") + version = version_match.group() + + # get date + unformatted_date_match = re.search(DATE_REGEX, release_info) + if unformatted_date_match is None: + raise InvalidPageVariantError("Unable to find date") + unformatted_date = unformatted_date_match.group() + date = format_date(unformatted_date) + + return (version, date) + +def get_version_and_date(release_page: str, release_version: str) -> Tuple[str, str]: + """ Get version and date from the given release page """ + major = int(release_version.split(".")[0]) + + # firefox release pages for versions <3.0 don't include release dates so we + # can't match these versions for now. + # example: https://www.mozilla.org/en-US/firefox/2.0/releasenotes/ + if major < 3: + raise UnsupportedReleasePageError("Unsupported release page: %s" % release_page) + + # Firefox release pages come in 3 different variants. Unforunately, there is no + # consistent way to determine which variant a page is (say, by version number), so + # we have to try each variant until we find one that works. + functions = [get_version_and_date_varant_1, get_version_and_date_variant_2, get_version_and_date_variant_3] + soup = make_bs_request(release_page) + + for function in functions: + try: + return function(soup) + except (InvalidPageVariantError, AttributeError, IndexError): + pass + + raise UnsupportedReleasePageError("Unable to find version and date for %s" % release_page) + +def make_bs_request(url: str) -> BeautifulSoup: + """ Make a request to the given url and return a BeautifulSoup object """ + headers = {"user-agent": "mozilla"} + req = urllib.request.Request(url, headers=headers) + res = urllib.request.urlopen(req, timeout=5) + return BeautifulSoup(res.read(), features="html5lib") + +def fetch_releases(): + releases = {} + soup = make_bs_request(URL) + + ff_releases = soup.find_all("ol", class_="c-release-list") + with concurrent.futures.ThreadPoolExecutor() as executor: + future_to_url = { + executor.submit( + get_version_and_date, + requests.compat.urljoin(URL, p.get("href")), + p.get_text()): p.get("href") for p in ff_releases[0].find_all("a") + } + + for future in concurrent.futures.as_completed(future_to_url): + try: + (version, date) = future.result() + print("%s: %s" % (version, date)) + releases[version] = date + except UnsupportedReleasePageError: + print("Unsupported release page: %s" % future_to_url[future]) + + return releases + +def main(): + print(f"::group::{PRODUCT}") + + releases = fetch_releases() + with open(f"releases/{PRODUCT}.json", "w") as f: + f.write(json.dumps(dict( + # sort by date then version (desc) + sorted(releases.items(), key=lambda x: (x[1], x[0]), reverse=True) + ), indent=2)) + + print("::endgroup::") + + +if __name__ == '__main__': + main()