Refactor scripts arguments handling (#456)
- remove the use of environment variables to get directory paths, - make use of arguments / argparse instead of environment variables in `update.py` and `report.py`, - automatically guess the data directory in `latest.py` based on the script's location, - propagate log level to auto scripts, - move `list_configs_from_argv` from `endoflife` module to `releasedata` module, - use `list_products` in `latest.py` to load the product's frontmatters.
This commit is contained in:
2
.github/workflows/update.yml
vendored
2
.github/workflows/update.yml
vendored
@@ -61,7 +61,7 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
continue-on-error: true # commit even if the data was not fully updated
|
continue-on-error: true # commit even if the data was not fully updated
|
||||||
run: python update.py
|
run: python update.py -p 'website/products'
|
||||||
|
|
||||||
- name: Commit changes
|
- name: Commit changes
|
||||||
uses: stefanzweifel/git-auto-commit-action@v6
|
uses: stefanzweifel/git-auto-commit-action@v6
|
||||||
|
|||||||
112
README.md
112
README.md
@@ -20,7 +20,7 @@ Common Release Data for various projects in a consistent and easy-to-parse forma
|
|||||||
|
|
||||||
## Currently Updated
|
## Currently Updated
|
||||||
|
|
||||||
As of 2025-05-17, 308 of the 379 products tracked by endoflife.date have automatically tracked releases:
|
As of 2025-06-28, 316 of the 383 products tracked by endoflife.date have automatically tracked releases:
|
||||||
|
|
||||||
| Product | Permalink | Auto | Method(s) |
|
| Product | Permalink | Auto | Method(s) |
|
||||||
|---------|-----------|------|-----------|
|
|---------|-----------|------|-----------|
|
||||||
@@ -31,18 +31,18 @@ As of 2025-05-17, 308 of the 379 products tracked by endoflife.date have automat
|
|||||||
| Alpine Linux | [`/alpine-linux`](https://endoflife.date/alpine-linux) | ✔️ | git, release_table |
|
| Alpine Linux | [`/alpine-linux`](https://endoflife.date/alpine-linux) | ✔️ | git, release_table |
|
||||||
| Amazon CDK | [`/amazon-cdk`](https://endoflife.date/amazon-cdk) | ✔️ | git |
|
| Amazon CDK | [`/amazon-cdk`](https://endoflife.date/amazon-cdk) | ✔️ | git |
|
||||||
| Amazon Corretto | [`/amazon-corretto`](https://endoflife.date/amazon-corretto) | ✔️ | github_releases |
|
| Amazon Corretto | [`/amazon-corretto`](https://endoflife.date/amazon-corretto) | ✔️ | github_releases |
|
||||||
| Amazon EKS | [`/amazon-eks`](https://endoflife.date/amazon-eks) | ✔️ | custom, release_table |
|
| Amazon EKS | [`/amazon-eks`](https://endoflife.date/amazon-eks) | ✔️ | amazon-eks, release_table |
|
||||||
| Amazon Glue | [`/amazon-glue`](https://endoflife.date/amazon-glue) | ❌ | |
|
| Amazon Glue | [`/amazon-glue`](https://endoflife.date/amazon-glue) | ❌ | |
|
||||||
| Amazon Linux | [`/amazon-linux`](https://endoflife.date/amazon-linux) | ✔️ | docker_hub |
|
| Amazon Linux | [`/amazon-linux`](https://endoflife.date/amazon-linux) | ✔️ | docker_hub |
|
||||||
| Amazon Neptune | [`/amazon-neptune`](https://endoflife.date/amazon-neptune) | ✔️ | custom, release_table |
|
| Amazon Neptune | [`/amazon-neptune`](https://endoflife.date/amazon-neptune) | ✔️ | amazon-neptune, release_table |
|
||||||
| Amazon RDS for MariaDB | [`/amazon-rds-mariadb`](https://endoflife.date/amazon-rds-mariadb) | ✔️ | custom, release_table |
|
| Amazon RDS for MariaDB | [`/amazon-rds-mariadb`](https://endoflife.date/amazon-rds-mariadb) | ✔️ | rds, release_table |
|
||||||
| Amazon RDS for MySQL | [`/amazon-rds-mysql`](https://endoflife.date/amazon-rds-mysql) | ✔️ | custom, release_table |
|
| Amazon RDS for MySQL | [`/amazon-rds-mysql`](https://endoflife.date/amazon-rds-mysql) | ✔️ | rds, release_table |
|
||||||
| Amazon RDS for PostgreSQL | [`/amazon-rds-postgresql`](https://endoflife.date/amazon-rds-postgresql) | ✔️ | custom, release_table |
|
| Amazon RDS for PostgreSQL | [`/amazon-rds-postgresql`](https://endoflife.date/amazon-rds-postgresql) | ✔️ | rds, release_table |
|
||||||
| Android OS | [`/android`](https://endoflife.date/android) | ❌ | |
|
| Android OS | [`/android`](https://endoflife.date/android) | ❌ | |
|
||||||
| Angular | [`/angular`](https://endoflife.date/angular) | ✔️ | git, release_table |
|
| Angular | [`/angular`](https://endoflife.date/angular) | ✔️ | git, release_table |
|
||||||
| AngularJS | [`/angularjs`](https://endoflife.date/angularjs) | ✔️ | npm |
|
| AngularJS | [`/angularjs`](https://endoflife.date/angularjs) | ✔️ | npm |
|
||||||
| Ansible-core | [`/ansible-core`](https://endoflife.date/ansible-core) | ✔️ | git, release_table |
|
|
||||||
| Ansible | [`/ansible`](https://endoflife.date/ansible) | ✔️ | pypi |
|
| Ansible | [`/ansible`](https://endoflife.date/ansible) | ✔️ | pypi |
|
||||||
|
| Ansible-core | [`/ansible-core`](https://endoflife.date/ansible-core) | ✔️ | git, release_table |
|
||||||
| antiX Linux | [`/antix`](https://endoflife.date/antix) | ✔️ | distrowatch |
|
| antiX Linux | [`/antix`](https://endoflife.date/antix) | ✔️ | distrowatch |
|
||||||
| Apache ActiveMQ | [`/apache-activemq`](https://endoflife.date/apache-activemq) | ✔️ | git |
|
| Apache ActiveMQ | [`/apache-activemq`](https://endoflife.date/apache-activemq) | ✔️ | git |
|
||||||
| Apache Airflow | [`/apache-airflow`](https://endoflife.date/apache-airflow) | ✔️ | pypi, release_table |
|
| Apache Airflow | [`/apache-airflow`](https://endoflife.date/apache-airflow) | ✔️ | pypi, release_table |
|
||||||
@@ -55,7 +55,7 @@ As of 2025-05-17, 308 of the 379 products tracked by endoflife.date have automat
|
|||||||
| Apache Groovy | [`/apache-groovy`](https://endoflife.date/apache-groovy) | ✔️ | maven |
|
| Apache Groovy | [`/apache-groovy`](https://endoflife.date/apache-groovy) | ✔️ | maven |
|
||||||
| Apache Hadoop | [`/apache-hadoop`](https://endoflife.date/apache-hadoop) | ✔️ | git |
|
| Apache Hadoop | [`/apache-hadoop`](https://endoflife.date/apache-hadoop) | ✔️ | git |
|
||||||
| Apache Hop | [`/apache-hop`](https://endoflife.date/apache-hop) | ✔️ | maven |
|
| Apache Hop | [`/apache-hop`](https://endoflife.date/apache-hop) | ✔️ | maven |
|
||||||
| Apache HTTP Server | [`/apache-http-server`](https://endoflife.date/apache-http-server) | ✔️ | custom |
|
| Apache HTTP Server | [`/apache-http-server`](https://endoflife.date/apache-http-server) | ✔️ | apache-http-server |
|
||||||
| Apache Kafka | [`/apache-kafka`](https://endoflife.date/apache-kafka) | ✔️ | git, release_table |
|
| Apache Kafka | [`/apache-kafka`](https://endoflife.date/apache-kafka) | ✔️ | git, release_table |
|
||||||
| Apache Lucene | [`/apache-lucene`](https://endoflife.date/apache-lucene) | ✔️ | maven |
|
| Apache Lucene | [`/apache-lucene`](https://endoflife.date/apache-lucene) | ✔️ | maven |
|
||||||
| Apache Maven | [`/apache-maven`](https://endoflife.date/apache-maven) | ✔️ | maven |
|
| Apache Maven | [`/apache-maven`](https://endoflife.date/apache-maven) | ✔️ | maven |
|
||||||
@@ -63,14 +63,14 @@ As of 2025-05-17, 308 of the 379 products tracked by endoflife.date have automat
|
|||||||
| Apache Pulsar | [`/apache-pulsar`](https://endoflife.date/apache-pulsar) | ✔️ | github_releases, release_table |
|
| Apache Pulsar | [`/apache-pulsar`](https://endoflife.date/apache-pulsar) | ✔️ | github_releases, release_table |
|
||||||
| Apache Spark | [`/apache-spark`](https://endoflife.date/apache-spark) | ✔️ | git |
|
| Apache Spark | [`/apache-spark`](https://endoflife.date/apache-spark) | ✔️ | git |
|
||||||
| Apache Struts | [`/apache-struts`](https://endoflife.date/apache-struts) | ✔️ | maven |
|
| Apache Struts | [`/apache-struts`](https://endoflife.date/apache-struts) | ✔️ | maven |
|
||||||
| Apache Subversion | [`/apache-subversion`](https://endoflife.date/apache-subversion) | ✔️ | custom |
|
| Apache Subversion | [`/apache-subversion`](https://endoflife.date/apache-subversion) | ✔️ | apache-subversion |
|
||||||
| API Platform | [`/api-platform`](https://endoflife.date/api-platform) | ✔️ | git |
|
| API Platform | [`/api-platform`](https://endoflife.date/api-platform) | ✔️ | git |
|
||||||
| Apple tvOS | [`/tvos`](https://endoflife.date/tvos) | ✔️ | apple |
|
| Apple tvOS | [`/tvos`](https://endoflife.date/tvos) | ✔️ | apple |
|
||||||
| Apple Watch | [`/apple-watch`](https://endoflife.date/apple-watch) | ❌ | |
|
| Apple Watch | [`/apple-watch`](https://endoflife.date/apple-watch) | ❌ | |
|
||||||
| ArangoDB | [`/arangodb`](https://endoflife.date/arangodb) | ✔️ | git |
|
| ArangoDB | [`/arangodb`](https://endoflife.date/arangodb) | ✔️ | git |
|
||||||
| Argo CD | [`/argo-cd`](https://endoflife.date/argo-cd) | ✔️ | git |
|
| Argo CD | [`/argo-cd`](https://endoflife.date/argo-cd) | ✔️ | git |
|
||||||
| Artifactory | [`/artifactory`](https://endoflife.date/artifactory) | ✔️ | custom |
|
| Artifactory | [`/artifactory`](https://endoflife.date/artifactory) | ✔️ | artifactory |
|
||||||
| AWS Lambda | [`/aws-lambda`](https://endoflife.date/aws-lambda) | ✔️ | custom |
|
| AWS Lambda | [`/aws-lambda`](https://endoflife.date/aws-lambda) | ✔️ | aws-lambda |
|
||||||
| Azul Zulu | [`/azul-zulu`](https://endoflife.date/azul-zulu) | ❌ | |
|
| Azul Zulu | [`/azul-zulu`](https://endoflife.date/azul-zulu) | ❌ | |
|
||||||
| Azure DevOps Server | [`/azure-devops-server`](https://endoflife.date/azure-devops-server) | ❌ | |
|
| Azure DevOps Server | [`/azure-devops-server`](https://endoflife.date/azure-devops-server) | ❌ | |
|
||||||
| Azure Kubernetes Service | [`/azure-kubernetes-service`](https://endoflife.date/azure-kubernetes-service) | ❌ | |
|
| Azure Kubernetes Service | [`/azure-kubernetes-service`](https://endoflife.date/azure-kubernetes-service) | ❌ | |
|
||||||
@@ -88,14 +88,14 @@ As of 2025-05-17, 308 of the 379 products tracked by endoflife.date have automat
|
|||||||
| caddy | [`/caddy`](https://endoflife.date/caddy) | ✔️ | git |
|
| caddy | [`/caddy`](https://endoflife.date/caddy) | ✔️ | git |
|
||||||
| CakePHP | [`/cakephp`](https://endoflife.date/cakephp) | ✔️ | git |
|
| CakePHP | [`/cakephp`](https://endoflife.date/cakephp) | ✔️ | git |
|
||||||
| Calico | [`/calico`](https://endoflife.date/calico) | ✔️ | git |
|
| Calico | [`/calico`](https://endoflife.date/calico) | ✔️ | git |
|
||||||
| CentOS Stream | [`/centos-stream`](https://endoflife.date/centos-stream) | ❌ | |
|
|
||||||
| CentOS | [`/centos`](https://endoflife.date/centos) | ❌ | |
|
| CentOS | [`/centos`](https://endoflife.date/centos) | ❌ | |
|
||||||
|
| CentOS Stream | [`/centos-stream`](https://endoflife.date/centos-stream) | ❌ | |
|
||||||
| Centreon | [`/centreon`](https://endoflife.date/centreon) | ✔️ | git, release_table |
|
| Centreon | [`/centreon`](https://endoflife.date/centreon) | ✔️ | git, release_table |
|
||||||
| cert-manager | [`/cert-manager`](https://endoflife.date/cert-manager) | ✔️ | git |
|
| cert-manager | [`/cert-manager`](https://endoflife.date/cert-manager) | ✔️ | git |
|
||||||
| CFEngine | [`/cfengine`](https://endoflife.date/cfengine) | ✔️ | git |
|
| CFEngine | [`/cfengine`](https://endoflife.date/cfengine) | ✔️ | git |
|
||||||
| Chef Infra Client | [`/chef-infra-client`](https://endoflife.date/chef-infra-client) | ✔️ | custom |
|
| Chef Infra Client | [`/chef-infra-client`](https://endoflife.date/chef-infra-client) | ✔️ | chef-infra |
|
||||||
| Chef Infra Server | [`/chef-infra-server`](https://endoflife.date/chef-infra-server) | ✔️ | custom |
|
| Chef Infra Server | [`/chef-infra-server`](https://endoflife.date/chef-infra-server) | ✔️ | chef-infra |
|
||||||
| Chef InSpec | [`/chef-inspec`](https://endoflife.date/chef-inspec) | ✔️ | custom |
|
| Chef InSpec | [`/chef-inspec`](https://endoflife.date/chef-inspec) | ✔️ | chef-inspec |
|
||||||
| Citrix Virtual Apps and Desktops | [`/citrix-vad`](https://endoflife.date/citrix-vad) | ❌ | |
|
| Citrix Virtual Apps and Desktops | [`/citrix-vad`](https://endoflife.date/citrix-vad) | ❌ | |
|
||||||
| CKEditor | [`/ckeditor`](https://endoflife.date/ckeditor) | ❌ | |
|
| CKEditor | [`/ckeditor`](https://endoflife.date/ckeditor) | ❌ | |
|
||||||
| ClamAV | [`/clamav`](https://endoflife.date/clamav) | ✔️ | git |
|
| ClamAV | [`/clamav`](https://endoflife.date/clamav) | ✔️ | git |
|
||||||
@@ -110,12 +110,12 @@ As of 2025-05-17, 308 of the 379 products tracked by endoflife.date have automat
|
|||||||
| Contao | [`/contao`](https://endoflife.date/contao) | ✔️ | git |
|
| Contao | [`/contao`](https://endoflife.date/contao) | ✔️ | git |
|
||||||
| Contour | [`/contour`](https://endoflife.date/contour) | ✔️ | git |
|
| Contour | [`/contour`](https://endoflife.date/contour) | ✔️ | git |
|
||||||
| Control-M | [`/controlm`](https://endoflife.date/controlm) | ❌ | |
|
| Control-M | [`/controlm`](https://endoflife.date/controlm) | ❌ | |
|
||||||
| Google Container-Optimized OS (COS) | [`/cos`](https://endoflife.date/cos) | ✔️ | custom |
|
| Google Container-Optimized OS (COS) | [`/cos`](https://endoflife.date/cos) | ✔️ | cos |
|
||||||
| Couchbase Server | [`/couchbase-server`](https://endoflife.date/couchbase-server) | ✔️ | custom, release_table |
|
| Couchbase Server | [`/couchbase-server`](https://endoflife.date/couchbase-server) | ✔️ | couchbase-server, release_table |
|
||||||
| Craft CMS | [`/craft-cms`](https://endoflife.date/craft-cms) | ✔️ | git, release_table |
|
| Craft CMS | [`/craft-cms`](https://endoflife.date/craft-cms) | ✔️ | git, release_table |
|
||||||
| dbt Core | [`/dbt-core`](https://endoflife.date/dbt-core) | ✔️ | git |
|
| dbt Core | [`/dbt-core`](https://endoflife.date/dbt-core) | ✔️ | git |
|
||||||
| DaoCloud Enterprise | [`/dce`](https://endoflife.date/dce) | ❌ | |
|
| DaoCloud Enterprise | [`/dce`](https://endoflife.date/dce) | ❌ | |
|
||||||
| Debian | [`/debian`](https://endoflife.date/debian) | ✔️ | custom, release_table |
|
| Debian | [`/debian`](https://endoflife.date/debian) | ✔️ | debian, release_table |
|
||||||
| Deno | [`/deno`](https://endoflife.date/deno) | ✔️ | git |
|
| Deno | [`/deno`](https://endoflife.date/deno) | ✔️ | git |
|
||||||
| Dependency-Track | [`/dependency-track`](https://endoflife.date/dependency-track) | ✔️ | git |
|
| Dependency-Track | [`/dependency-track`](https://endoflife.date/dependency-track) | ✔️ | git |
|
||||||
| Devuan | [`/devuan`](https://endoflife.date/devuan) | ✔️ | distrowatch |
|
| Devuan | [`/devuan`](https://endoflife.date/devuan) | ✔️ | distrowatch |
|
||||||
@@ -142,7 +142,7 @@ As of 2025-05-17, 308 of the 379 products tracked by endoflife.date have automat
|
|||||||
| Fedora Linux | [`/fedora`](https://endoflife.date/fedora) | ✔️ | distrowatch |
|
| Fedora Linux | [`/fedora`](https://endoflife.date/fedora) | ✔️ | distrowatch |
|
||||||
| FFmpeg | [`/ffmpeg`](https://endoflife.date/ffmpeg) | ✔️ | git |
|
| FFmpeg | [`/ffmpeg`](https://endoflife.date/ffmpeg) | ✔️ | git |
|
||||||
| FileMaker Platform | [`/filemaker`](https://endoflife.date/filemaker) | ✔️ | release_table |
|
| FileMaker Platform | [`/filemaker`](https://endoflife.date/filemaker) | ✔️ | release_table |
|
||||||
| Firefox | [`/firefox`](https://endoflife.date/firefox) | ✔️ | custom |
|
| Firefox | [`/firefox`](https://endoflife.date/firefox) | ✔️ | firefox |
|
||||||
| Fluent Bit | [`/fluent-bit`](https://endoflife.date/fluent-bit) | ✔️ | git |
|
| Fluent Bit | [`/fluent-bit`](https://endoflife.date/fluent-bit) | ✔️ | git |
|
||||||
| Flux | [`/flux`](https://endoflife.date/flux) | ✔️ | git |
|
| Flux | [`/flux`](https://endoflife.date/flux) | ✔️ | git |
|
||||||
| Forgejo | [`/forgejo`](https://endoflife.date/forgejo) | ✔️ | git, release_table |
|
| Forgejo | [`/forgejo`](https://endoflife.date/forgejo) | ✔️ | git, release_table |
|
||||||
@@ -150,34 +150,34 @@ As of 2025-05-17, 308 of the 379 products tracked by endoflife.date have automat
|
|||||||
| FreeBSD | [`/freebsd`](https://endoflife.date/freebsd) | ❌ | |
|
| FreeBSD | [`/freebsd`](https://endoflife.date/freebsd) | ❌ | |
|
||||||
| Gatekeeper | [`/gatekeeper`](https://endoflife.date/gatekeeper) | ✔️ | git |
|
| Gatekeeper | [`/gatekeeper`](https://endoflife.date/gatekeeper) | ✔️ | git |
|
||||||
| Gerrit | [`/gerrit`](https://endoflife.date/gerrit) | ✔️ | git |
|
| Gerrit | [`/gerrit`](https://endoflife.date/gerrit) | ✔️ | git |
|
||||||
| Glasgow Haskell Compiler (GHC) | [`/ghc`](https://endoflife.date/ghc) | ✔️ | custom, git |
|
| Glasgow Haskell Compiler (GHC) | [`/ghc`](https://endoflife.date/ghc) | ✔️ | git |
|
||||||
| GitLab | [`/gitlab`](https://endoflife.date/gitlab) | ✔️ | git, release_table |
|
| GitLab | [`/gitlab`](https://endoflife.date/gitlab) | ✔️ | git, release_table |
|
||||||
| Go | [`/go`](https://endoflife.date/go) | ✔️ | git |
|
| Go | [`/go`](https://endoflife.date/go) | ✔️ | git |
|
||||||
| GoAccess | [`/goaccess`](https://endoflife.date/goaccess) | ✔️ | git |
|
| GoAccess | [`/goaccess`](https://endoflife.date/goaccess) | ✔️ | git |
|
||||||
| Godot | [`/godot`](https://endoflife.date/godot) | ✔️ | git |
|
| Godot | [`/godot`](https://endoflife.date/godot) | ✔️ | git |
|
||||||
| Google Kubernetes Engine | [`/google-kubernetes-engine`](https://endoflife.date/google-kubernetes-engine) | ✔️ | custom |
|
| Google Kubernetes Engine | [`/google-kubernetes-engine`](https://endoflife.date/google-kubernetes-engine) | ✔️ | google-kubernetes-engine |
|
||||||
| Google Nexus | [`/google-nexus`](https://endoflife.date/google-nexus) | ❌ | |
|
| Google Nexus | [`/google-nexus`](https://endoflife.date/google-nexus) | ❌ | |
|
||||||
| Gorilla Toolkit | [`/gorilla`](https://endoflife.date/gorilla) | ❌ | |
|
| Gorilla Toolkit | [`/gorilla`](https://endoflife.date/gorilla) | ❌ | |
|
||||||
| GraalVM | [`/graalvm`](https://endoflife.date/graalvm) | ✔️ | custom |
|
| GraalVM Community Edition | [`/graalvm-ce`](https://endoflife.date/graalvm-ce) | ✔️ | graalvm |
|
||||||
| Gradle | [`/gradle`](https://endoflife.date/gradle) | ✔️ | git |
|
| Gradle | [`/gradle`](https://endoflife.date/gradle) | ✔️ | git |
|
||||||
| Grafana Loki | [`/grafana-loki`](https://endoflife.date/grafana-loki) | ✔️ | git |
|
|
||||||
| Grafana | [`/grafana`](https://endoflife.date/grafana) | ✔️ | github_releases, release_table |
|
| Grafana | [`/grafana`](https://endoflife.date/grafana) | ✔️ | github_releases, release_table |
|
||||||
|
| Grafana Loki | [`/grafana-loki`](https://endoflife.date/grafana-loki) | ✔️ | git |
|
||||||
| Grails Framework | [`/grails`](https://endoflife.date/grails) | ✔️ | git |
|
| Grails Framework | [`/grails`](https://endoflife.date/grails) | ✔️ | git |
|
||||||
| Graylog | [`/graylog`](https://endoflife.date/graylog) | ✔️ | git |
|
| Graylog | [`/graylog`](https://endoflife.date/graylog) | ✔️ | git |
|
||||||
| Greenlight | [`/greenlight`](https://endoflife.date/greenlight) | ✔️ | git |
|
| Greenlight | [`/greenlight`](https://endoflife.date/greenlight) | ✔️ | git |
|
||||||
| Grunt | [`/grunt`](https://endoflife.date/grunt) | ✔️ | git |
|
| Grunt | [`/grunt`](https://endoflife.date/grunt) | ✔️ | git |
|
||||||
| GStreamer | [`/gstreamer`](https://endoflife.date/gstreamer) | ✔️ | git |
|
| GStreamer | [`/gstreamer`](https://endoflife.date/gstreamer) | ✔️ | git |
|
||||||
| Guzzle | [`/guzzle`](https://endoflife.date/guzzle) | ✔️ | git |
|
| Guzzle | [`/guzzle`](https://endoflife.date/guzzle) | ✔️ | git |
|
||||||
| HAProxy | [`/haproxy`](https://endoflife.date/haproxy) | ✔️ | custom |
|
| HAProxy | [`/haproxy`](https://endoflife.date/haproxy) | ✔️ | haproxy |
|
||||||
| Harbor | [`/harbor`](https://endoflife.date/harbor) | ✔️ | git |
|
| Harbor | [`/harbor`](https://endoflife.date/harbor) | ✔️ | git |
|
||||||
| Hashicorp Packer | [`/hashicorp-packer`](https://endoflife.date/hashicorp-packer) | ✔️ | git |
|
| Hashicorp Packer | [`/hashicorp-packer`](https://endoflife.date/hashicorp-packer) | ✔️ | git |
|
||||||
| Hashicorp Vault | [`/hashicorp-vault`](https://endoflife.date/hashicorp-vault) | ✔️ | git |
|
| Hashicorp Vault | [`/hashicorp-vault`](https://endoflife.date/hashicorp-vault) | ✔️ | git |
|
||||||
| Apache HBase | [`/hbase`](https://endoflife.date/hbase) | ✔️ | git |
|
| Apache HBase | [`/hbase`](https://endoflife.date/hbase) | ✔️ | git |
|
||||||
| IBM AIX | [`/ibm-aix`](https://endoflife.date/ibm-aix) | ✔️ | custom, release_table |
|
| IBM AIX | [`/ibm-aix`](https://endoflife.date/ibm-aix) | ✔️ | ibm-aix, release_table |
|
||||||
| IBM iSeries | [`/ibm-i`](https://endoflife.date/ibm-i) | ✔️ | release_table |
|
| IBM iSeries | [`/ibm-i`](https://endoflife.date/ibm-i) | ✔️ | release_table |
|
||||||
| IBM Semeru Runtime | [`/ibm-semeru-runtime`](https://endoflife.date/ibm-semeru-runtime) | ✔️ | github_releases, release_table |
|
| IBM Semeru Runtime | [`/ibm-semeru-runtime`](https://endoflife.date/ibm-semeru-runtime) | ✔️ | github_releases, release_table |
|
||||||
| Icinga Web | [`/icinga-web`](https://endoflife.date/icinga-web) | ✔️ | git |
|
|
||||||
| Icinga | [`/icinga`](https://endoflife.date/icinga) | ✔️ | git |
|
| Icinga | [`/icinga`](https://endoflife.date/icinga) | ✔️ | git |
|
||||||
|
| Icinga Web | [`/icinga-web`](https://endoflife.date/icinga-web) | ✔️ | git |
|
||||||
| Intel Processors | [`/intel-processors`](https://endoflife.date/intel-processors) | ❌ | |
|
| Intel Processors | [`/intel-processors`](https://endoflife.date/intel-processors) | ❌ | |
|
||||||
| Internet Explorer | [`/internet-explorer`](https://endoflife.date/internet-explorer) | ❌ | |
|
| Internet Explorer | [`/internet-explorer`](https://endoflife.date/internet-explorer) | ❌ | |
|
||||||
| Ionic Framework | [`/ionic`](https://endoflife.date/ionic) | ✔️ | git, release_table |
|
| Ionic Framework | [`/ionic`](https://endoflife.date/ionic) | ✔️ | git, release_table |
|
||||||
@@ -192,8 +192,8 @@ As of 2025-05-17, 308 of the 379 products tracked by endoflife.date have automat
|
|||||||
| JHipster | [`/jhipster`](https://endoflife.date/jhipster) | ✔️ | npm |
|
| JHipster | [`/jhipster`](https://endoflife.date/jhipster) | ✔️ | npm |
|
||||||
| Jira Software | [`/jira-software`](https://endoflife.date/jira-software) | ✔️ | atlassian_eol, atlassian_versions |
|
| Jira Software | [`/jira-software`](https://endoflife.date/jira-software) | ✔️ | atlassian_eol, atlassian_versions |
|
||||||
| Joomla! | [`/joomla`](https://endoflife.date/joomla) | ✔️ | git |
|
| Joomla! | [`/joomla`](https://endoflife.date/joomla) | ✔️ | git |
|
||||||
| jQuery UI | [`/jquery-ui`](https://endoflife.date/jquery-ui) | ✔️ | git |
|
|
||||||
| jQuery | [`/jquery`](https://endoflife.date/jquery) | ✔️ | git |
|
| jQuery | [`/jquery`](https://endoflife.date/jquery) | ✔️ | git |
|
||||||
|
| jQuery UI | [`/jquery-ui`](https://endoflife.date/jquery-ui) | ✔️ | git |
|
||||||
| JReleaser | [`/jreleaser`](https://endoflife.date/jreleaser) | ✔️ | maven |
|
| JReleaser | [`/jreleaser`](https://endoflife.date/jreleaser) | ✔️ | maven |
|
||||||
| Julia | [`/julia`](https://endoflife.date/julia) | ✔️ | git |
|
| Julia | [`/julia`](https://endoflife.date/julia) | ✔️ | git |
|
||||||
| KDE Plasma | [`/kde-plasma`](https://endoflife.date/kde-plasma) | ✔️ | git |
|
| KDE Plasma | [`/kde-plasma`](https://endoflife.date/kde-plasma) | ✔️ | git |
|
||||||
@@ -204,21 +204,22 @@ As of 2025-05-17, 308 of the 379 products tracked by endoflife.date have automat
|
|||||||
| Kirby | [`/kirby`](https://endoflife.date/kirby) | ✔️ | git |
|
| Kirby | [`/kirby`](https://endoflife.date/kirby) | ✔️ | git |
|
||||||
| Kong Gateway | [`/kong-gateway`](https://endoflife.date/kong-gateway) | ✔️ | git |
|
| Kong Gateway | [`/kong-gateway`](https://endoflife.date/kong-gateway) | ✔️ | git |
|
||||||
| Kotlin | [`/kotlin`](https://endoflife.date/kotlin) | ✔️ | github_releases |
|
| Kotlin | [`/kotlin`](https://endoflife.date/kotlin) | ✔️ | github_releases |
|
||||||
|
| Kubernetes | [`/kubernetes`](https://endoflife.date/kubernetes) | ✔️ | git |
|
||||||
| Kubernetes CSI Node Driver Registrar | [`/kubernetes-csi-node-driver-registrar`](https://endoflife.date/kubernetes-csi-node-driver-registrar) | ✔️ | git |
|
| Kubernetes CSI Node Driver Registrar | [`/kubernetes-csi-node-driver-registrar`](https://endoflife.date/kubernetes-csi-node-driver-registrar) | ✔️ | git |
|
||||||
| Kubernetes Node Feature Discovery | [`/kubernetes-node-feature-discovery`](https://endoflife.date/kubernetes-node-feature-discovery) | ✔️ | github_releases |
|
| Kubernetes Node Feature Discovery | [`/kubernetes-node-feature-discovery`](https://endoflife.date/kubernetes-node-feature-discovery) | ✔️ | github_releases |
|
||||||
| Kubernetes | [`/kubernetes`](https://endoflife.date/kubernetes) | ✔️ | git |
|
| Kuma | [`/kuma`](https://endoflife.date/kuma) | ✔️ | git, kuma |
|
||||||
| Kuma | [`/kuma`](https://endoflife.date/kuma) | ✔️ | git |
|
|
||||||
| Kyverno | [`/kyverno`](https://endoflife.date/kyverno) | ✔️ | git |
|
| Kyverno | [`/kyverno`](https://endoflife.date/kyverno) | ✔️ | git |
|
||||||
| Laravel | [`/laravel`](https://endoflife.date/laravel) | ✔️ | git, release_table |
|
| Laravel | [`/laravel`](https://endoflife.date/laravel) | ✔️ | git, release_table |
|
||||||
| LDAP Account Manager | [`/ldap-account-manager`](https://endoflife.date/ldap-account-manager) | ✔️ | git |
|
| LDAP Account Manager | [`/ldap-account-manager`](https://endoflife.date/ldap-account-manager) | ✔️ | git |
|
||||||
| LibreOffice | [`/libreoffice`](https://endoflife.date/libreoffice) | ✔️ | custom |
|
| LibreOffice | [`/libreoffice`](https://endoflife.date/libreoffice) | ✔️ | libreoffice |
|
||||||
| LineageOS | [`/lineageos`](https://endoflife.date/lineageos) | ❌ | |
|
| LineageOS | [`/lineageos`](https://endoflife.date/lineageos) | ❌ | |
|
||||||
| Linux Kernel | [`/linux`](https://endoflife.date/linux) | ✔️ | github_tags |
|
| Linux Kernel | [`/linux`](https://endoflife.date/linux) | ✔️ | github_tags |
|
||||||
| Linux Mint | [`/linuxmint`](https://endoflife.date/linuxmint) | ✔️ | release_table |
|
| Linux Mint | [`/linuxmint`](https://endoflife.date/linuxmint) | ✔️ | release_table |
|
||||||
|
| Liquibase | [`/liquibase`](https://endoflife.date/liquibase) | ✔️ | maven |
|
||||||
| Apache Log4j | [`/log4j`](https://endoflife.date/log4j) | ✔️ | maven |
|
| Apache Log4j | [`/log4j`](https://endoflife.date/log4j) | ✔️ | maven |
|
||||||
| Logstash | [`/logstash`](https://endoflife.date/logstash) | ✔️ | git |
|
| Logstash | [`/logstash`](https://endoflife.date/logstash) | ✔️ | git |
|
||||||
| Looker | [`/looker`](https://endoflife.date/looker) | ✔️ | custom |
|
| Looker | [`/looker`](https://endoflife.date/looker) | ✔️ | looker |
|
||||||
| Lua | [`/lua`](https://endoflife.date/lua) | ✔️ | custom |
|
| Lua | [`/lua`](https://endoflife.date/lua) | ✔️ | lua |
|
||||||
| Apple macOS | [`/macos`](https://endoflife.date/macos) | ✔️ | apple |
|
| Apple macOS | [`/macos`](https://endoflife.date/macos) | ✔️ | apple |
|
||||||
| Mageia | [`/mageia`](https://endoflife.date/mageia) | ✔️ | distrowatch |
|
| Mageia | [`/mageia`](https://endoflife.date/mageia) | ✔️ | distrowatch |
|
||||||
| Magento | [`/magento`](https://endoflife.date/magento) | ✔️ | git |
|
| Magento | [`/magento`](https://endoflife.date/magento) | ✔️ | git |
|
||||||
@@ -244,7 +245,7 @@ As of 2025-05-17, 308 of the 379 products tracked by endoflife.date have automat
|
|||||||
| Neo4j | [`/neo4j`](https://endoflife.date/neo4j) | ✔️ | git, release_table |
|
| Neo4j | [`/neo4j`](https://endoflife.date/neo4j) | ✔️ | git, release_table |
|
||||||
| Neos | [`/neos`](https://endoflife.date/neos) | ✔️ | git |
|
| Neos | [`/neos`](https://endoflife.date/neos) | ✔️ | git |
|
||||||
| NetApp ONTAP | [`/netapp-ontap`](https://endoflife.date/netapp-ontap) | ❌ | |
|
| NetApp ONTAP | [`/netapp-ontap`](https://endoflife.date/netapp-ontap) | ❌ | |
|
||||||
| NetBSD | [`/netbsd`](https://endoflife.date/netbsd) | ✔️ | custom |
|
| NetBSD | [`/netbsd`](https://endoflife.date/netbsd) | ✔️ | netbsd |
|
||||||
| Nextcloud | [`/nextcloud`](https://endoflife.date/nextcloud) | ✔️ | git, release_table |
|
| Nextcloud | [`/nextcloud`](https://endoflife.date/nextcloud) | ✔️ | git, release_table |
|
||||||
| Next.js | [`/nextjs`](https://endoflife.date/nextjs) | ✔️ | npm |
|
| Next.js | [`/nextjs`](https://endoflife.date/nextjs) | ✔️ | npm |
|
||||||
| Nexus Repository | [`/nexus`](https://endoflife.date/nexus) | ✔️ | git, release_table |
|
| Nexus Repository | [`/nexus`](https://endoflife.date/nexus) | ✔️ | git, release_table |
|
||||||
@@ -254,6 +255,7 @@ As of 2025-05-17, 308 of the 379 products tracked by endoflife.date have automat
|
|||||||
| Node.js | [`/nodejs`](https://endoflife.date/nodejs) | ✔️ | git |
|
| Node.js | [`/nodejs`](https://endoflife.date/nodejs) | ✔️ | git |
|
||||||
| Nokia Mobile | [`/nokia`](https://endoflife.date/nokia) | ❌ | |
|
| Nokia Mobile | [`/nokia`](https://endoflife.date/nokia) | ❌ | |
|
||||||
| Nomad | [`/nomad`](https://endoflife.date/nomad) | ✔️ | git |
|
| Nomad | [`/nomad`](https://endoflife.date/nomad) | ✔️ | git |
|
||||||
|
| Notepad++ | [`/notepad-plus-plus`](https://endoflife.date/notepad-plus-plus) | ✔️ | git |
|
||||||
| NumPy | [`/numpy`](https://endoflife.date/numpy) | ✔️ | pypi |
|
| NumPy | [`/numpy`](https://endoflife.date/numpy) | ✔️ | pypi |
|
||||||
| Nutanix AOS | [`/nutanix-aos`](https://endoflife.date/nutanix-aos) | ✔️ | nutanix |
|
| Nutanix AOS | [`/nutanix-aos`](https://endoflife.date/nutanix-aos) | ✔️ | nutanix |
|
||||||
| Nutanix Files | [`/nutanix-files`](https://endoflife.date/nutanix-files) | ✔️ | nutanix |
|
| Nutanix Files | [`/nutanix-files`](https://endoflife.date/nutanix-files) | ✔️ | nutanix |
|
||||||
@@ -277,7 +279,8 @@ As of 2025-05-17, 308 of the 379 products tracked by endoflife.date have automat
|
|||||||
| OPNsense | [`/opnsense`](https://endoflife.date/opnsense) | ✔️ | git |
|
| OPNsense | [`/opnsense`](https://endoflife.date/opnsense) | ✔️ | git |
|
||||||
| Oracle APEX | [`/oracle-apex`](https://endoflife.date/oracle-apex) | ✔️ | release_table |
|
| Oracle APEX | [`/oracle-apex`](https://endoflife.date/oracle-apex) | ✔️ | release_table |
|
||||||
| Oracle Database | [`/oracle-database`](https://endoflife.date/oracle-database) | ✔️ | release_table |
|
| Oracle Database | [`/oracle-database`](https://endoflife.date/oracle-database) | ✔️ | release_table |
|
||||||
| Oracle JDK | [`/oracle-jdk`](https://endoflife.date/oracle-jdk) | ✔️ | custom, release_table |
|
| Oracle GraalVM | [`/oracle-graalvm`](https://endoflife.date/oracle-graalvm) | ✔️ | graalvm, release_table |
|
||||||
|
| Oracle JDK | [`/oracle-jdk`](https://endoflife.date/oracle-jdk) | ✔️ | oracle-jdk, release_table |
|
||||||
| Oracle Linux | [`/oracle-linux`](https://endoflife.date/oracle-linux) | ✔️ | distrowatch |
|
| Oracle Linux | [`/oracle-linux`](https://endoflife.date/oracle-linux) | ✔️ | distrowatch |
|
||||||
| Oracle Solaris | [`/oracle-solaris`](https://endoflife.date/oracle-solaris) | ❌ | |
|
| Oracle Solaris | [`/oracle-solaris`](https://endoflife.date/oracle-solaris) | ❌ | |
|
||||||
| oVirt | [`/ovirt`](https://endoflife.date/ovirt) | ✔️ | git |
|
| oVirt | [`/ovirt`](https://endoflife.date/ovirt) | ✔️ | git |
|
||||||
@@ -286,12 +289,12 @@ As of 2025-05-17, 308 of the 379 products tracked by endoflife.date have automat
|
|||||||
| Palo Alto Networks PAN-OS | [`/panos`](https://endoflife.date/panos) | ✔️ | release_table |
|
| Palo Alto Networks PAN-OS | [`/panos`](https://endoflife.date/panos) | ✔️ | release_table |
|
||||||
| PCI-DSS | [`/pci-dss`](https://endoflife.date/pci-dss) | ❌ | |
|
| PCI-DSS | [`/pci-dss`](https://endoflife.date/pci-dss) | ❌ | |
|
||||||
| Perl | [`/perl`](https://endoflife.date/perl) | ✔️ | git |
|
| Perl | [`/perl`](https://endoflife.date/perl) | ✔️ | git |
|
||||||
| PHP | [`/php`](https://endoflife.date/php) | ✔️ | custom |
|
| PHP | [`/php`](https://endoflife.date/php) | ✔️ | php |
|
||||||
| phpBB | [`/phpbb`](https://endoflife.date/phpbb) | ✔️ | git |
|
| phpBB | [`/phpbb`](https://endoflife.date/phpbb) | ✔️ | git |
|
||||||
| phpMyAdmin | [`/phpmyadmin`](https://endoflife.date/phpmyadmin) | ✔️ | git |
|
| phpMyAdmin | [`/phpmyadmin`](https://endoflife.date/phpmyadmin) | ✔️ | git |
|
||||||
| Google Pixel Watch | [`/pixel-watch`](https://endoflife.date/pixel-watch) | ❌ | |
|
|
||||||
| Google Pixel | [`/pixel`](https://endoflife.date/pixel) | ❌ | |
|
| Google Pixel | [`/pixel`](https://endoflife.date/pixel) | ❌ | |
|
||||||
| Plesk | [`/plesk`](https://endoflife.date/plesk) | ✔️ | custom |
|
| Google Pixel Watch | [`/pixel-watch`](https://endoflife.date/pixel-watch) | ❌ | |
|
||||||
|
| Plesk | [`/plesk`](https://endoflife.date/plesk) | ✔️ | plesk |
|
||||||
| Plone | [`/plone`](https://endoflife.date/plone) | ✔️ | git, release_table |
|
| Plone | [`/plone`](https://endoflife.date/plone) | ✔️ | git, release_table |
|
||||||
| pnpm | [`/pnpm`](https://endoflife.date/pnpm) | ✔️ | npm |
|
| pnpm | [`/pnpm`](https://endoflife.date/pnpm) | ✔️ | npm |
|
||||||
| Podman | [`/podman`](https://endoflife.date/podman) | ✔️ | git |
|
| Podman | [`/podman`](https://endoflife.date/podman) | ✔️ | git |
|
||||||
@@ -313,39 +316,40 @@ As of 2025-05-17, 308 of the 379 products tracked by endoflife.date have automat
|
|||||||
| RabbitMQ | [`/rabbitmq`](https://endoflife.date/rabbitmq) | ✔️ | git |
|
| RabbitMQ | [`/rabbitmq`](https://endoflife.date/rabbitmq) | ✔️ | git |
|
||||||
| Rancher | [`/rancher`](https://endoflife.date/rancher) | ✔️ | git |
|
| Rancher | [`/rancher`](https://endoflife.date/rancher) | ✔️ | git |
|
||||||
| Raspberry Pi | [`/raspberry-pi`](https://endoflife.date/raspberry-pi) | ❌ | |
|
| Raspberry Pi | [`/raspberry-pi`](https://endoflife.date/raspberry-pi) | ❌ | |
|
||||||
| React Native | [`/react-native`](https://endoflife.date/react-native) | ✔️ | npm |
|
|
||||||
| React | [`/react`](https://endoflife.date/react) | ✔️ | npm |
|
| React | [`/react`](https://endoflife.date/react) | ✔️ | npm |
|
||||||
|
| React Native | [`/react-native`](https://endoflife.date/react-native) | ✔️ | npm |
|
||||||
| Red Hat build of OpenJDK | [`/redhat-build-of-openjdk`](https://endoflife.date/redhat-build-of-openjdk) | ✔️ | redhat_lifecycles |
|
| Red Hat build of OpenJDK | [`/redhat-build-of-openjdk`](https://endoflife.date/redhat-build-of-openjdk) | ✔️ | redhat_lifecycles |
|
||||||
| Red Hat JBoss Enterprise Application Platform | [`/redhat-jboss-eap`](https://endoflife.date/redhat-jboss-eap) | ✔️ | custom, redhat_lifecycles |
|
| Red Hat JBoss Enterprise Application Platform | [`/redhat-jboss-eap`](https://endoflife.date/redhat-jboss-eap) | ✔️ | red-hat-jboss-eap-7, red-hat-jboss-eap-8, redhat_lifecycles |
|
||||||
| Red Hat OpenShift | [`/red-hat-openshift`](https://endoflife.date/red-hat-openshift) | ✔️ | custom |
|
| Red Hat OpenShift | [`/red-hat-openshift`](https://endoflife.date/red-hat-openshift) | ✔️ | red-hat-openshift |
|
||||||
| Red Hat Satellite | [`/redhat-satellite`](https://endoflife.date/redhat-satellite) | ✔️ | custom |
|
| Red Hat Satellite | [`/redhat-satellite`](https://endoflife.date/redhat-satellite) | ✔️ | red-hat-satellite |
|
||||||
| Redis | [`/redis`](https://endoflife.date/redis) | ✔️ | git, release_table |
|
| Redis | [`/redis`](https://endoflife.date/redis) | ✔️ | git, release_table |
|
||||||
| Redmine | [`/redmine`](https://endoflife.date/redmine) | ✔️ | git |
|
| Redmine | [`/redmine`](https://endoflife.date/redmine) | ✔️ | git |
|
||||||
| Red Hat Enterprise Linux | [`/rhel`](https://endoflife.date/rhel) | ✔️ | redhat_lifecycles |
|
| Red Hat Enterprise Linux | [`/rhel`](https://endoflife.date/rhel) | ✔️ | redhat_lifecycles |
|
||||||
| Robo | [`/robo`](https://endoflife.date/robo) | ✔️ | git, release_table |
|
| Robo | [`/robo`](https://endoflife.date/robo) | ✔️ | git, release_table |
|
||||||
| Rocket.Chat | [`/rocket-chat`](https://endoflife.date/rocket-chat) | ✔️ | git |
|
| Rocket.Chat | [`/rocket-chat`](https://endoflife.date/rocket-chat) | ✔️ | git |
|
||||||
| Rocky Linux | [`/rocky-linux`](https://endoflife.date/rocky-linux) | ✔️ | custom, release_table |
|
| Rocky Linux | [`/rocky-linux`](https://endoflife.date/rocky-linux) | ✔️ | release_table, rocky-linux |
|
||||||
|
| ROS | [`/ros`](https://endoflife.date/ros) | ✔️ | ros |
|
||||||
| ROS 2 | [`/ros-2`](https://endoflife.date/ros-2) | ❌ | |
|
| ROS 2 | [`/ros-2`](https://endoflife.date/ros-2) | ❌ | |
|
||||||
| ROS | [`/ros`](https://endoflife.date/ros) | ❌ | |
|
|
||||||
| Roundcube Webmail | [`/roundcube`](https://endoflife.date/roundcube) | ✔️ | git |
|
| Roundcube Webmail | [`/roundcube`](https://endoflife.date/roundcube) | ✔️ | git |
|
||||||
| rtpengine | [`/rtpengine`](https://endoflife.date/rtpengine) | ✔️ | git |
|
| rtpengine | [`/rtpengine`](https://endoflife.date/rtpengine) | ✔️ | git |
|
||||||
| Ruby on Rails | [`/rails`](https://endoflife.date/rails) | ✔️ | git |
|
|
||||||
| Ruby | [`/ruby`](https://endoflife.date/ruby) | ✔️ | git |
|
| Ruby | [`/ruby`](https://endoflife.date/ruby) | ✔️ | git |
|
||||||
|
| Ruby on Rails | [`/rails`](https://endoflife.date/rails) | ✔️ | git |
|
||||||
| Rust | [`/rust`](https://endoflife.date/rust) | ✔️ | git |
|
| Rust | [`/rust`](https://endoflife.date/rust) | ✔️ | git |
|
||||||
| Salt | [`/salt`](https://endoflife.date/salt) | ✔️ | git, release_table |
|
| Salt | [`/salt`](https://endoflife.date/salt) | ✔️ | git, release_table |
|
||||||
|
| Samsung Galaxy Tab | [`/samsung-galaxy-tab`](https://endoflife.date/samsung-galaxy-tab) | ✔️ | samsung-security |
|
||||||
| Samsung Galaxy Watch | [`/samsung-galaxy-watch`](https://endoflife.date/samsung-galaxy-watch) | ❌ | |
|
| Samsung Galaxy Watch | [`/samsung-galaxy-watch`](https://endoflife.date/samsung-galaxy-watch) | ❌ | |
|
||||||
| Samsung Mobile | [`/samsung-mobile`](https://endoflife.date/samsung-mobile) | ✔️ | custom |
|
| Samsung Mobile | [`/samsung-mobile`](https://endoflife.date/samsung-mobile) | ✔️ | samsung-security |
|
||||||
| SapMachine | [`/sapmachine`](https://endoflife.date/sapmachine) | ✔️ | github_releases |
|
| SapMachine | [`/sapmachine`](https://endoflife.date/sapmachine) | ✔️ | github_releases |
|
||||||
| Scala | [`/scala`](https://endoflife.date/scala) | ✔️ | github_releases |
|
| Scala | [`/scala`](https://endoflife.date/scala) | ✔️ | github_releases |
|
||||||
| Microsoft SharePoint | [`/sharepoint`](https://endoflife.date/sharepoint) | ❌ | |
|
| Microsoft SharePoint | [`/sharepoint`](https://endoflife.date/sharepoint) | ❌ | |
|
||||||
| Shopware | [`/shopware`](https://endoflife.date/shopware) | ✔️ | git |
|
| Shopware | [`/shopware`](https://endoflife.date/shopware) | ✔️ | git |
|
||||||
| Silverstripe CMS | [`/silverstripe`](https://endoflife.date/silverstripe) | ✔️ | git, release_table |
|
| Silverstripe CMS | [`/silverstripe`](https://endoflife.date/silverstripe) | ✔️ | git, release_table |
|
||||||
| Slackware Linux | [`/slackware`](https://endoflife.date/slackware) | ✔️ | distrowatch |
|
| Slackware Linux | [`/slackware`](https://endoflife.date/slackware) | ✔️ | distrowatch |
|
||||||
| SUSE Linux Enterprise Server | [`/sles`](https://endoflife.date/sles) | ❌ | |
|
| SUSE Linux Enterprise Server | [`/sles`](https://endoflife.date/sles) | ✔️ | sles |
|
||||||
| Apache Solr | [`/solr`](https://endoflife.date/solr) | ✔️ | git |
|
| Apache Solr | [`/solr`](https://endoflife.date/solr) | ✔️ | git |
|
||||||
| SonarQube | [`/sonar`](https://endoflife.date/sonar) | ✔️ | git |
|
| SonarQube | [`/sonar`](https://endoflife.date/sonar) | ✔️ | git |
|
||||||
| Sourcegraph | [`/sourcegraph`](https://endoflife.date/sourcegraph) | ✔️ | git |
|
| Sourcegraph | [`/sourcegraph`](https://endoflife.date/sourcegraph) | ✔️ | git |
|
||||||
| Splunk | [`/splunk`](https://endoflife.date/splunk) | ✔️ | custom |
|
| Splunk | [`/splunk`](https://endoflife.date/splunk) | ✔️ | splunk |
|
||||||
| Spring Boot | [`/spring-boot`](https://endoflife.date/spring-boot) | ✔️ | git, release_table |
|
| Spring Boot | [`/spring-boot`](https://endoflife.date/spring-boot) | ✔️ | git, release_table |
|
||||||
| Spring Framework | [`/spring-framework`](https://endoflife.date/spring-framework) | ✔️ | git, release_table |
|
| Spring Framework | [`/spring-framework`](https://endoflife.date/spring-framework) | ✔️ | git, release_table |
|
||||||
| SQLite | [`/sqlite`](https://endoflife.date/sqlite) | ✔️ | git |
|
| SQLite | [`/sqlite`](https://endoflife.date/sqlite) | ✔️ | git |
|
||||||
@@ -362,7 +366,7 @@ As of 2025-05-17, 308 of the 379 products tracked by endoflife.date have automat
|
|||||||
| Apache Tomcat | [`/tomcat`](https://endoflife.date/tomcat) | ✔️ | maven |
|
| Apache Tomcat | [`/tomcat`](https://endoflife.date/tomcat) | ✔️ | maven |
|
||||||
| Traefik | [`/traefik`](https://endoflife.date/traefik) | ✔️ | git, release_table |
|
| Traefik | [`/traefik`](https://endoflife.date/traefik) | ✔️ | git, release_table |
|
||||||
| Twig | [`/twig`](https://endoflife.date/twig) | ✔️ | git |
|
| Twig | [`/twig`](https://endoflife.date/twig) | ✔️ | git |
|
||||||
| TYPO3 | [`/typo3`](https://endoflife.date/typo3) | ✔️ | custom |
|
| TYPO3 | [`/typo3`](https://endoflife.date/typo3) | ✔️ | typo3 |
|
||||||
| Ubuntu | [`/ubuntu`](https://endoflife.date/ubuntu) | ✔️ | distrowatch |
|
| Ubuntu | [`/ubuntu`](https://endoflife.date/ubuntu) | ✔️ | distrowatch |
|
||||||
| Umbraco CMS | [`/umbraco`](https://endoflife.date/umbraco) | ✔️ | git, release_table |
|
| Umbraco CMS | [`/umbraco`](https://endoflife.date/umbraco) | ✔️ | git, release_table |
|
||||||
| Unity | [`/unity`](https://endoflife.date/unity) | ❌ | |
|
| Unity | [`/unity`](https://endoflife.date/unity) | ❌ | |
|
||||||
@@ -372,10 +376,10 @@ As of 2025-05-17, 308 of the 379 products tracked by endoflife.date have automat
|
|||||||
| Veeam Backup & Replication | [`/veeam-backup-and-replication`](https://endoflife.date/veeam-backup-and-replication) | ✔️ | veeam |
|
| Veeam Backup & Replication | [`/veeam-backup-and-replication`](https://endoflife.date/veeam-backup-and-replication) | ✔️ | veeam |
|
||||||
| Veeam Backup for Microsoft 365 | [`/veeam-backup-for-microsoft-365`](https://endoflife.date/veeam-backup-for-microsoft-365) | ✔️ | veeam |
|
| Veeam Backup for Microsoft 365 | [`/veeam-backup-for-microsoft-365`](https://endoflife.date/veeam-backup-for-microsoft-365) | ✔️ | veeam |
|
||||||
| Veeam ONE | [`/veeam-one`](https://endoflife.date/veeam-one) | ✔️ | veeam |
|
| Veeam ONE | [`/veeam-one`](https://endoflife.date/veeam-one) | ✔️ | veeam |
|
||||||
| VirtualBox | [`/virtualbox`](https://endoflife.date/virtualbox) | ❌ | |
|
| VirtualBox | [`/virtualbox`](https://endoflife.date/virtualbox) | ✔️ | virtualbox |
|
||||||
| Apple visionOS | [`/visionos`](https://endoflife.date/visionos) | ✔️ | apple |
|
| Apple visionOS | [`/visionos`](https://endoflife.date/visionos) | ✔️ | apple |
|
||||||
| Visual COBOL | [`/visual-cobol`](https://endoflife.date/visual-cobol) | ❌ | |
|
| Visual COBOL | [`/visual-cobol`](https://endoflife.date/visual-cobol) | ❌ | |
|
||||||
| Microsoft Visual Studio | [`/visual-studio`](https://endoflife.date/visual-studio) | ✔️ | custom |
|
| Microsoft Visual Studio | [`/visual-studio`](https://endoflife.date/visual-studio) | ✔️ | visual-studio |
|
||||||
| Vitess | [`/vitess`](https://endoflife.date/vitess) | ✔️ | git |
|
| Vitess | [`/vitess`](https://endoflife.date/vitess) | ✔️ | git |
|
||||||
| VMware Cloud Foundation | [`/vmware-cloud-foundation`](https://endoflife.date/vmware-cloud-foundation) | ❌ | |
|
| VMware Cloud Foundation | [`/vmware-cloud-foundation`](https://endoflife.date/vmware-cloud-foundation) | ❌ | |
|
||||||
| VMware ESXi | [`/esxi`](https://endoflife.date/esxi) | ❌ | |
|
| VMware ESXi | [`/esxi`](https://endoflife.date/esxi) | ❌ | |
|
||||||
@@ -389,19 +393,19 @@ As of 2025-05-17, 308 of the 379 products tracked by endoflife.date have automat
|
|||||||
| Apple watchOS | [`/watchos`](https://endoflife.date/watchos) | ✔️ | apple |
|
| Apple watchOS | [`/watchos`](https://endoflife.date/watchos) | ✔️ | apple |
|
||||||
| Weakforced | [`/weakforced`](https://endoflife.date/weakforced) | ✔️ | git |
|
| Weakforced | [`/weakforced`](https://endoflife.date/weakforced) | ✔️ | git |
|
||||||
| WeeChat | [`/weechat`](https://endoflife.date/weechat) | ✔️ | git |
|
| WeeChat | [`/weechat`](https://endoflife.date/weechat) | ✔️ | git |
|
||||||
|
| Microsoft Windows | [`/windows`](https://endoflife.date/windows) | ❌ | |
|
||||||
| Microsoft Windows Embedded | [`/windows-embedded`](https://endoflife.date/windows-embedded) | ❌ | |
|
| Microsoft Windows Embedded | [`/windows-embedded`](https://endoflife.date/windows-embedded) | ❌ | |
|
||||||
| Microsoft Nano Server | [`/windows-nano-server`](https://endoflife.date/windows-nano-server) | ❌ | |
|
| Microsoft Nano Server | [`/windows-nano-server`](https://endoflife.date/windows-nano-server) | ❌ | |
|
||||||
| Microsoft Windows Server Core | [`/windows-server-core`](https://endoflife.date/windows-server-core) | ❌ | |
|
|
||||||
| Microsoft Windows Server | [`/windows-server`](https://endoflife.date/windows-server) | ❌ | |
|
| Microsoft Windows Server | [`/windows-server`](https://endoflife.date/windows-server) | ❌ | |
|
||||||
| Microsoft Windows | [`/windows`](https://endoflife.date/windows) | ❌ | |
|
| Microsoft Windows Server Core | [`/windows-server-core`](https://endoflife.date/windows-server-core) | ❌ | |
|
||||||
| Wireshark | [`/wireshark`](https://endoflife.date/wireshark) | ✔️ | git |
|
| Wireshark | [`/wireshark`](https://endoflife.date/wireshark) | ✔️ | git |
|
||||||
| WordPress | [`/wordpress`](https://endoflife.date/wordpress) | ✔️ | git |
|
| WordPress | [`/wordpress`](https://endoflife.date/wordpress) | ✔️ | git |
|
||||||
| XCP-ng | [`/xcp-ng`](https://endoflife.date/xcp-ng) | ✔️ | git, release_table |
|
| XCP-ng | [`/xcp-ng`](https://endoflife.date/xcp-ng) | ✔️ | git, release_table |
|
||||||
| Yarn | [`/yarn`](https://endoflife.date/yarn) | ✔️ | npm |
|
| Yarn | [`/yarn`](https://endoflife.date/yarn) | ✔️ | npm |
|
||||||
| Yocto Project | [`/yocto`](https://endoflife.date/yocto) | ✔️ | git |
|
| Yocto Project | [`/yocto`](https://endoflife.date/yocto) | ✔️ | git |
|
||||||
| Zabbix | [`/zabbix`](https://endoflife.date/zabbix) | ✔️ | git, release_table |
|
| Zabbix | [`/zabbix`](https://endoflife.date/zabbix) | ✔️ | git |
|
||||||
| Zentyal | [`/zentyal`](https://endoflife.date/zentyal) | ✔️ | release_table |
|
| Zentyal | [`/zentyal`](https://endoflife.date/zentyal) | ✔️ | release_table |
|
||||||
| Zerto | [`/zerto`](https://endoflife.date/zerto) | ❌ | |
|
| Zerto | [`/zerto`](https://endoflife.date/zerto) | ✔️ | release_table |
|
||||||
| Apache ZooKeeper | [`/zookeeper`](https://endoflife.date/zookeeper) | ✔️ | maven |
|
| Apache ZooKeeper | [`/zookeeper`](https://endoflife.date/zookeeper) | ✔️ | maven |
|
||||||
|
|
||||||
This table has been generated by [report.py](/report.py).
|
This table has been generated by [report.py](/report.py).
|
||||||
|
|||||||
12
latest.py
12
latest.py
@@ -11,7 +11,9 @@ from ruamel.yaml import YAML
|
|||||||
from ruamel.yaml.representer import RoundTripRepresenter
|
from ruamel.yaml.representer import RoundTripRepresenter
|
||||||
from ruamel.yaml.resolver import Resolver
|
from ruamel.yaml.resolver import Resolver
|
||||||
|
|
||||||
|
from src.common.endoflife import list_products
|
||||||
from src.common.gha import GitHubOutput
|
from src.common.gha import GitHubOutput
|
||||||
|
from src.common.releasedata import DATA_DIR
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Updates the `release`, `latest` and `latestReleaseDate` property in automatically updated pages
|
Updates the `release`, `latest` and `latestReleaseDate` property in automatically updated pages
|
||||||
@@ -243,7 +245,6 @@ if __name__ == "__main__":
|
|||||||
parser = argparse.ArgumentParser(description='Update product releases.')
|
parser = argparse.ArgumentParser(description='Update product releases.')
|
||||||
parser.add_argument('product', nargs='?', help='restrict update to the given product')
|
parser.add_argument('product', nargs='?', help='restrict update to the given product')
|
||||||
parser.add_argument('-p', '--product-dir', required=True, help='path to the product directory')
|
parser.add_argument('-p', '--product-dir', required=True, help='path to the product directory')
|
||||||
parser.add_argument('-d', '--data-dir', required=True, help='path to the release data directory')
|
|
||||||
parser.add_argument('-v', '--verbose', action='store_true', help='enable verbose logging')
|
parser.add_argument('-v', '--verbose', action='store_true', help='enable verbose logging')
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
@@ -257,10 +258,11 @@ if __name__ == "__main__":
|
|||||||
RoundTripRepresenter.ignore_aliases = lambda x, y: True # NOQA: ARG005
|
RoundTripRepresenter.ignore_aliases = lambda x, y: True # NOQA: ARG005
|
||||||
|
|
||||||
products_dir = Path(args.product_dir)
|
products_dir = Path(args.product_dir)
|
||||||
product_names = [args.product] if args.product else [p.stem for p in products_dir.glob("*.md")]
|
data_dir = Path(__file__).resolve().parent / DATA_DIR
|
||||||
|
products = list_products(products_dir, args.product)
|
||||||
|
|
||||||
github_output = GitHubOutput("warning")
|
github_output = GitHubOutput("warning")
|
||||||
with github_output:
|
with github_output:
|
||||||
for product_name in sorted(product_names):
|
for product in products:
|
||||||
logging.debug(f"Processing {product_name}")
|
logging.debug(f"Processing {product.name}")
|
||||||
update_product(product_name, products_dir, Path(args.data_dir), github_output)
|
update_product(product.name, products_dir, data_dir, github_output)
|
||||||
|
|||||||
26
report.py
26
report.py
@@ -1,20 +1,28 @@
|
|||||||
|
import argparse
|
||||||
import time
|
import time
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
from src.common import endoflife
|
from src.common import endoflife
|
||||||
|
|
||||||
products = endoflife.list_products()
|
if __name__ == "__main__":
|
||||||
count_auto = len([product for product in products if product.auto_configs()])
|
parser = argparse.ArgumentParser(description='Create report on product automation.')
|
||||||
|
parser.add_argument('-p', '--product-dir', required=True, help='path to the product directory')
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
print(f"As of {time.strftime('%Y-%m-%d')}, {count_auto} of the {len(products)} products"
|
products_dir = Path(args.product_dir)
|
||||||
|
products = endoflife.list_products(products_dir)
|
||||||
|
count_auto = len([product for product in products if product.auto_configs()])
|
||||||
|
|
||||||
|
print(f"As of {time.strftime('%Y-%m-%d')}, {count_auto} of the {len(products)} products"
|
||||||
f" tracked by endoflife.date have automatically tracked releases:")
|
f" tracked by endoflife.date have automatically tracked releases:")
|
||||||
print()
|
print()
|
||||||
print('| Product | Permalink | Auto | Method(s) |')
|
print('| Product | Permalink | Auto | Method(s) |')
|
||||||
print('|---------|-----------|------|-----------|')
|
print('|---------|-----------|------|-----------|')
|
||||||
for product in products:
|
for product in products:
|
||||||
title = product.get_title()
|
title = product.get_title()
|
||||||
permalink = product.get_permalink()
|
permalink = product.get_permalink()
|
||||||
auto = '✔️' if product.has_auto_configs() else '❌'
|
auto = '✔️' if product.has_auto_configs() else '❌'
|
||||||
methods = ', '.join(sorted({config.method for config in product.auto_configs()}))
|
methods = ', '.join(sorted({config.method for config in product.auto_configs()}))
|
||||||
print(f"| {title} | [`{permalink}`](https://endoflife.date{permalink}) | {auto} | {methods} |")
|
print(f"| {title} | [`{permalink}`](https://endoflife.date{permalink}) | {auto} | {methods} |")
|
||||||
print()
|
print()
|
||||||
print('This table has been generated by [report.py](/report.py).')
|
print('This table has been generated by [report.py](/report.py).')
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from common import dates, endoflife, http, releasedata
|
from common import dates, http, releasedata
|
||||||
|
|
||||||
"""Fetches EKS versions from AWS docs.
|
"""Fetches EKS versions from AWS docs.
|
||||||
Now that AWS no longer publishes docs on GitHub, we use the Web Archive to get the older versions."""
|
Now that AWS no longer publishes docs on GitHub, we use the Web Archive to get the older versions."""
|
||||||
|
|
||||||
for config in endoflife.list_configs_from_argv():
|
for config in releasedata.list_configs_from_argv():
|
||||||
with releasedata.ProductData(config.product) as product_data:
|
with releasedata.ProductData(config.product) as product_data:
|
||||||
html = http.fetch_html(config.url)
|
html = http.fetch_html(config.url)
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from common import dates, endoflife, http, releasedata
|
from common import dates, http, releasedata
|
||||||
|
|
||||||
"""Fetches Amazon Neptune versions from its RSS feed on docs.aws.amazon.com."""
|
"""Fetches Amazon Neptune versions from its RSS feed on docs.aws.amazon.com."""
|
||||||
|
|
||||||
for config in endoflife.list_configs_from_argv():
|
for config in releasedata.list_configs_from_argv():
|
||||||
with releasedata.ProductData(config.product) as product_data:
|
with releasedata.ProductData(config.product) as product_data:
|
||||||
rss = http.fetch_xml(config.url)
|
rss = http.fetch_xml(config.url)
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
from common import dates, endoflife, releasedata
|
from common import dates, releasedata
|
||||||
from common.git import Git
|
from common.git import Git
|
||||||
|
|
||||||
"""Fetches Apache HTTP Server versions and release date from its git repository
|
"""Fetches Apache HTTP Server versions and release date from its git repository
|
||||||
by looking at the STATUS file of each <major>.<minor>.x branch."""
|
by looking at the STATUS file of each <major>.<minor>.x branch."""
|
||||||
|
|
||||||
for config in endoflife.list_configs_from_argv():
|
for config in releasedata.list_configs_from_argv():
|
||||||
with releasedata.ProductData(config.product) as product_data:
|
with releasedata.ProductData(config.product) as product_data:
|
||||||
git = Git(config.url)
|
git = Git(config.url)
|
||||||
git.setup()
|
git.setup()
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from common import dates, endoflife, http, releasedata
|
from common import dates, http, releasedata
|
||||||
|
|
||||||
for config in endoflife.list_configs_from_argv():
|
for config in releasedata.list_configs_from_argv():
|
||||||
with releasedata.ProductData(config.product) as product_data:
|
with releasedata.ProductData(config.product) as product_data:
|
||||||
html = http.fetch_html(config.url)
|
html = http.fetch_html(config.url)
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import logging
|
|||||||
import re
|
import re
|
||||||
|
|
||||||
from bs4 import BeautifulSoup
|
from bs4 import BeautifulSoup
|
||||||
from common import dates, endoflife, http, releasedata
|
from common import dates, http, releasedata
|
||||||
|
|
||||||
"""Fetches and parses version and release date information from Apple's support website."""
|
"""Fetches and parses version and release date information from Apple's support website."""
|
||||||
|
|
||||||
@@ -22,7 +22,7 @@ URLS = [
|
|||||||
|
|
||||||
DATE_PATTERN = re.compile(r"\b\d+\s[A-Za-z]+\s\d+\b")
|
DATE_PATTERN = re.compile(r"\b\d+\s[A-Za-z]+\s\d+\b")
|
||||||
|
|
||||||
for config in endoflife.list_configs_from_argv():
|
for config in releasedata.list_configs_from_argv():
|
||||||
with releasedata.ProductData(config.product) as product_data:
|
with releasedata.ProductData(config.product) as product_data:
|
||||||
# URLs are cached to avoid rate limiting by support.apple.com.
|
# URLs are cached to avoid rate limiting by support.apple.com.
|
||||||
soups = [BeautifulSoup(response.text, features="html5lib") for response in http.fetch_urls(URLS)]
|
soups = [BeautifulSoup(response.text, features="html5lib") for response in http.fetch_urls(URLS)]
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
from bs4 import BeautifulSoup
|
from bs4 import BeautifulSoup
|
||||||
from common import dates, endoflife, http, releasedata
|
from common import dates, http, releasedata
|
||||||
|
|
||||||
"""Fetches Artifactory versions from https://jfrog.com, using requests_html because JavaScript is
|
"""Fetches Artifactory versions from https://jfrog.com, using requests_html because JavaScript is
|
||||||
needed to render the page."""
|
needed to render the page."""
|
||||||
|
|
||||||
for config in endoflife.list_configs_from_argv():
|
for config in releasedata.list_configs_from_argv():
|
||||||
with releasedata.ProductData(config.product) as product_data:
|
with releasedata.ProductData(config.product) as product_data:
|
||||||
content = http.fetch_javascript_url(config.url, wait_until = 'networkidle')
|
content = http.fetch_javascript_url(config.url, wait_until = 'networkidle')
|
||||||
soup = BeautifulSoup(content, 'html.parser')
|
soup = BeautifulSoup(content, 'html.parser')
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from bs4 import BeautifulSoup
|
from bs4 import BeautifulSoup
|
||||||
from common import dates, endoflife, http, releasedata
|
from common import dates, http, releasedata
|
||||||
|
|
||||||
"""Fetches EOL dates from Atlassian EOL page.
|
"""Fetches EOL dates from Atlassian EOL page.
|
||||||
|
|
||||||
@@ -9,7 +9,7 @@ This script takes a selector argument which is the product title identifier on t
|
|||||||
`AtlassianSupportEndofLifePolicy-JiraSoftware`.
|
`AtlassianSupportEndofLifePolicy-JiraSoftware`.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
for config in endoflife.list_configs_from_argv():
|
for config in releasedata.list_configs_from_argv():
|
||||||
with releasedata.ProductData(config.product) as product_data:
|
with releasedata.ProductData(config.product) as product_data:
|
||||||
content = http.fetch_javascript_url(config.url)
|
content = http.fetch_javascript_url(config.url)
|
||||||
soup = BeautifulSoup(content, features="html5lib")
|
soup = BeautifulSoup(content, features="html5lib")
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
from bs4 import BeautifulSoup
|
from bs4 import BeautifulSoup
|
||||||
from common import dates, endoflife, http, releasedata
|
from common import dates, http, releasedata
|
||||||
|
|
||||||
"""Fetches versions from Atlassian download-archives pages.
|
"""Fetches versions from Atlassian download-archives pages.
|
||||||
|
|
||||||
@@ -7,7 +7,7 @@ This script takes a single argument which is the url of the product's download-a
|
|||||||
`https://www.atlassian.com/software/confluence/download-archives`.
|
`https://www.atlassian.com/software/confluence/download-archives`.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
for config in endoflife.list_configs_from_argv():
|
for config in releasedata.list_configs_from_argv():
|
||||||
with releasedata.ProductData(config.product) as product_data:
|
with releasedata.ProductData(config.product) as product_data:
|
||||||
content = http.fetch_javascript_url(config.url, wait_until='networkidle')
|
content = http.fetch_javascript_url(config.url, wait_until='networkidle')
|
||||||
soup = BeautifulSoup(content, 'html5lib')
|
soup = BeautifulSoup(content, 'html5lib')
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from common import dates, endoflife, http, releasedata
|
from common import dates, http, releasedata
|
||||||
|
|
||||||
"""Fetches AWS lambda runtimes with their support / EOL dates from https://docs.aws.amazon.com."""
|
"""Fetches AWS lambda runtimes with their support / EOL dates from https://docs.aws.amazon.com."""
|
||||||
|
|
||||||
for config in endoflife.list_configs_from_argv():
|
for config in releasedata.list_configs_from_argv():
|
||||||
with releasedata.ProductData(config.product) as product_data:
|
with releasedata.ProductData(config.product) as product_data:
|
||||||
html = http.fetch_html(config.url)
|
html = http.fetch_html(config.url)
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
from common import dates, endoflife, http, releasedata
|
from common import dates, http, releasedata
|
||||||
|
|
||||||
"""Fetches versions from repositories managed with cgit, such as the Linux kernel repository.
|
"""Fetches versions from repositories managed with cgit, such as the Linux kernel repository.
|
||||||
Ideally we would want to use the git repository directly, but cgit-managed repositories don't support partial clone."""
|
Ideally we would want to use the git repository directly, but cgit-managed repositories don't support partial clone."""
|
||||||
|
|
||||||
for config in endoflife.list_configs_from_argv():
|
for config in releasedata.list_configs_from_argv():
|
||||||
with releasedata.ProductData(config.product) as product_data:
|
with releasedata.ProductData(config.product) as product_data:
|
||||||
html = http.fetch_html(config.url + '/refs/tags')
|
html = http.fetch_html(config.url + '/refs/tags')
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
from common import dates, endoflife, http, releasedata
|
from common import dates, http, releasedata
|
||||||
from common.git import Git
|
from common.git import Git
|
||||||
|
|
||||||
"""Fetch released versions from docs.chef.io and retrieve their date from GitHub.
|
"""Fetch released versions from docs.chef.io and retrieve their date from GitHub.
|
||||||
@@ -7,7 +7,7 @@ docs.chef.io needs to be scraped because not all tagged versions are actually re
|
|||||||
More context on https://github.com/endoflife-date/endoflife.date/pull/4425#discussion_r1447932411.
|
More context on https://github.com/endoflife-date/endoflife.date/pull/4425#discussion_r1447932411.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
for config in endoflife.list_configs_from_argv():
|
for config in releasedata.list_configs_from_argv():
|
||||||
with releasedata.ProductData(config.product) as product_data:
|
with releasedata.ProductData(config.product) as product_data:
|
||||||
html = http.fetch_html(config.url)
|
html = http.fetch_html(config.url)
|
||||||
released_versions = [h2.get('id') for h2 in html.find_all('h2', id=True) if h2.get('id')]
|
released_versions = [h2.get('id') for h2 in html.find_all('h2', id=True) if h2.get('id')]
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
from common import dates, endoflife, github, http, releasedata
|
from common import dates, github, http, releasedata
|
||||||
|
|
||||||
"""Fetch released versions from docs.chef.io and retrieve their date from GitHub.
|
"""Fetch released versions from docs.chef.io and retrieve their date from GitHub.
|
||||||
docs.chef.io needs to be scraped because not all tagged versions are actually released.
|
docs.chef.io needs to be scraped because not all tagged versions are actually released.
|
||||||
@@ -6,7 +6,7 @@ docs.chef.io needs to be scraped because not all tagged versions are actually re
|
|||||||
More context on https://github.com/endoflife-date/endoflife.date/pull/4425#discussion_r1447932411.
|
More context on https://github.com/endoflife-date/endoflife.date/pull/4425#discussion_r1447932411.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
for config in endoflife.list_configs_from_argv():
|
for config in releasedata.list_configs_from_argv():
|
||||||
with releasedata.ProductData(config.product) as product_data:
|
with releasedata.ProductData(config.product) as product_data:
|
||||||
html = http.fetch_html(config.url)
|
html = http.fetch_html(config.url)
|
||||||
released_versions = [h2.get('id') for h2 in html.find_all('h2', id=True) if h2.get('id')]
|
released_versions = [h2.get('id') for h2 in html.find_all('h2', id=True) if h2.get('id')]
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import re
|
import re
|
||||||
|
|
||||||
from common import dates, endoflife, http, releasedata
|
from common import dates, http, releasedata
|
||||||
|
|
||||||
"""Fetches versions from Adobe ColdFusion release notes on helpx.adobe.com.
|
"""Fetches versions from Adobe ColdFusion release notes on helpx.adobe.com.
|
||||||
|
|
||||||
@@ -21,7 +21,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/
|
||||||
}
|
}
|
||||||
|
|
||||||
for config in endoflife.list_configs_from_argv():
|
for config in releasedata.list_configs_from_argv():
|
||||||
with releasedata.ProductData(config.product) as product_data:
|
with releasedata.ProductData(config.product) as product_data:
|
||||||
html = http.fetch_html(config.url)
|
html = http.fetch_html(config.url)
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
import itertools
|
|
||||||
import logging
|
import logging
|
||||||
import os
|
|
||||||
import re
|
import re
|
||||||
import sys
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
@@ -15,8 +12,6 @@ DEFAULT_VERSION_REGEX = r"^v?(?P<major>[1-9]\d*)\.(?P<minor>\d+)(\.(?P<patch>\d+
|
|||||||
DEFAULT_VERSION_PATTERN = re.compile(DEFAULT_VERSION_REGEX)
|
DEFAULT_VERSION_PATTERN = re.compile(DEFAULT_VERSION_REGEX)
|
||||||
DEFAULT_VERSION_TEMPLATE = "{{major}}{% if minor %}.{{minor}}{% if patch %}.{{patch}}{% if tiny %}.{{tiny}}{% endif %}{% endif %}{% endif %}"
|
DEFAULT_VERSION_TEMPLATE = "{{major}}{% if minor %}.{{minor}}{% if patch %}.{{patch}}{% if tiny %}.{{tiny}}{% endif %}{% endif %}{% endif %}"
|
||||||
|
|
||||||
PRODUCTS_PATH = Path(os.environ.get("PRODUCTS_PATH", "website/products"))
|
|
||||||
|
|
||||||
|
|
||||||
class AutoConfig:
|
class AutoConfig:
|
||||||
def __init__(self, product: str, data: dict) -> None:
|
def __init__(self, product: str, data: dict) -> None:
|
||||||
@@ -58,9 +53,9 @@ class AutoConfig:
|
|||||||
|
|
||||||
|
|
||||||
class ProductFrontmatter:
|
class ProductFrontmatter:
|
||||||
def __init__(self, name: str) -> None:
|
def __init__(self, path: Path) -> None:
|
||||||
self.name: str = name
|
self.path: Path = path
|
||||||
self.path: Path = PRODUCTS_PATH / f"{name}.md"
|
self.name: str = path.stem
|
||||||
|
|
||||||
self.data = None
|
self.data = None
|
||||||
if self.path.is_file():
|
if self.path.is_file():
|
||||||
@@ -109,37 +104,19 @@ class ProductFrontmatter:
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def list_products(products_filter: str = None) -> list[ProductFrontmatter]:
|
def list_products(products_dir: Path, product_name: str = None) -> list[ProductFrontmatter]:
|
||||||
"""Return a list of products that are using the same given update method."""
|
product_names = [product_name] if product_name else sorted([p.stem for p in products_dir.glob("*.md")])
|
||||||
|
|
||||||
products = []
|
products = []
|
||||||
|
for product_name in product_names:
|
||||||
for product_file in sorted(PRODUCTS_PATH.glob("*.md")):
|
|
||||||
product_name = product_file.stem
|
|
||||||
if products_filter and product_name != products_filter:
|
|
||||||
continue
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
products.append(ProductFrontmatter(product_name))
|
products.append(ProductFrontmatter(products_dir / f"{product_name}.md"))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.exception(f"failed to load product data for {product_name}: {e}")
|
logging.exception(f"failed to load product data for {product_name}: {e}")
|
||||||
|
|
||||||
return products
|
return products
|
||||||
|
|
||||||
|
|
||||||
def list_configs(products_filter: str = None, methods_filter: str = None, urls_filter: str = None) -> list[AutoConfig]:
|
|
||||||
"""Return a list of auto configs, filtering by product name, method, and URL."""
|
|
||||||
products = list_products(products_filter)
|
|
||||||
configs_by_product = [p.auto_configs(methods_filter, urls_filter) for p in products]
|
|
||||||
return list(itertools.chain.from_iterable(configs_by_product)) # flatten the list of lists
|
|
||||||
|
|
||||||
|
|
||||||
def list_configs_from_argv() -> list[AutoConfig]:
|
|
||||||
products_filter = sys.argv[1] if len(sys.argv) > 1 else None
|
|
||||||
methods_filter = sys.argv[2] if len(sys.argv) > 1 else None
|
|
||||||
urls_filter = sys.argv[3] if len(sys.argv) > 2 else None
|
|
||||||
return list_configs(products_filter, methods_filter, urls_filter)
|
|
||||||
|
|
||||||
|
|
||||||
def to_identifier(s: str) -> str:
|
def to_identifier(s: str) -> str:
|
||||||
"""Convert a string to a valid endoflife.date identifier."""
|
"""Convert a string to a valid endoflife.date identifier."""
|
||||||
identifier = s.strip().lower()
|
identifier = s.strip().lower()
|
||||||
|
|||||||
@@ -1,15 +1,16 @@
|
|||||||
|
import argparse
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import os
|
import sys
|
||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from types import TracebackType
|
from types import TracebackType
|
||||||
from typing import Optional, Type
|
from typing import Optional, Type
|
||||||
|
|
||||||
# Do not update the format: it's also used to declare groups in the GitHub Actions logs.
|
from . import endoflife
|
||||||
logging.basicConfig(format="%(message)s", level=logging.INFO)
|
|
||||||
|
|
||||||
VERSIONS_PATH = Path(os.environ.get("VERSIONS_PATH", "releases"))
|
SRC_DIR = Path('src')
|
||||||
|
DATA_DIR = Path('releases')
|
||||||
|
|
||||||
|
|
||||||
class ProductUpdateError(Exception):
|
class ProductUpdateError(Exception):
|
||||||
@@ -108,7 +109,7 @@ class ProductVersion:
|
|||||||
class ProductData:
|
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 = DATA_DIR / f"{name}.json"
|
||||||
self.releases = {}
|
self.releases = {}
|
||||||
self.versions: dict[str, ProductVersion] = {}
|
self.versions: dict[str, ProductVersion] = {}
|
||||||
self.updated = False
|
self.updated = False
|
||||||
@@ -190,3 +191,21 @@ class ProductData:
|
|||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
|
|
||||||
|
def list_configs_from_argv() -> list[endoflife.AutoConfig]:
|
||||||
|
return parse_argv()[1]
|
||||||
|
|
||||||
|
def parse_argv() -> tuple[endoflife.ProductFrontmatter, list[endoflife.AutoConfig]]:
|
||||||
|
parser = argparse.ArgumentParser(description=sys.argv[0])
|
||||||
|
parser.add_argument('-p', '--product', required=True, help='path to the product')
|
||||||
|
parser.add_argument('-m', '--method', required=True, help='method to filter by')
|
||||||
|
parser.add_argument('-u', '--url', required=True, help='url to filter by')
|
||||||
|
parser.add_argument('-v', '--verbose', action='store_true', help='enable verbose logging')
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
# Do not update the format: it's also used to declare groups in the GitHub Actions logs.
|
||||||
|
logging.basicConfig(format="%(message)s", level=(logging.DEBUG if args.verbose else logging.INFO))
|
||||||
|
|
||||||
|
product = endoflife.ProductFrontmatter(Path(args.product))
|
||||||
|
return product, product.auto_configs(args.method, args.url)
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import datetime
|
|||||||
import re
|
import re
|
||||||
|
|
||||||
from bs4 import BeautifulSoup
|
from bs4 import BeautifulSoup
|
||||||
from common import dates, endoflife, http, releasedata
|
from common import dates, http, releasedata
|
||||||
|
|
||||||
MILESTONE_PATTERN = re.compile(r'COS \d+ LTS')
|
MILESTONE_PATTERN = re.compile(r'COS \d+ LTS')
|
||||||
VERSION_PATTERN = re.compile(r"^(cos-\d+-\d+-\d+-\d+)")
|
VERSION_PATTERN = re.compile(r"^(cos-\d+-\d+-\d+-\d+)")
|
||||||
@@ -14,7 +14,7 @@ def parse_date(date_text: str) -> datetime:
|
|||||||
return dates.parse_date(date_text)
|
return dates.parse_date(date_text)
|
||||||
|
|
||||||
|
|
||||||
for config in endoflife.list_configs_from_argv():
|
for config in releasedata.list_configs_from_argv():
|
||||||
with releasedata.ProductData(config.product) as product_data:
|
with releasedata.ProductData(config.product) as product_data:
|
||||||
main = http.fetch_url(config.url)
|
main = http.fetch_url(config.url)
|
||||||
main_soup = BeautifulSoup(main.text, features="html5lib")
|
main_soup = BeautifulSoup(main.text, features="html5lib")
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from bs4 import BeautifulSoup
|
from bs4 import BeautifulSoup
|
||||||
from common import dates, endoflife, http, releasedata
|
from common import dates, http, releasedata
|
||||||
|
|
||||||
"""Fetches versions from release notes of each minor version on docs.couchbase.com.
|
"""Fetches versions from release notes of each minor version on docs.couchbase.com.
|
||||||
|
|
||||||
@@ -16,7 +16,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/
|
||||||
}
|
}
|
||||||
|
|
||||||
for config in endoflife.list_configs_from_argv():
|
for config in releasedata.list_configs_from_argv():
|
||||||
with releasedata.ProductData(config.product) as product_data:
|
with releasedata.ProductData(config.product) as product_data:
|
||||||
html = http.fetch_html(f"{config.url}/current/install/install-intro.html")
|
html = http.fetch_html(f"{config.url}/current/install/install-intro.html")
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from subprocess import run
|
from subprocess import run
|
||||||
|
|
||||||
from common import dates, endoflife, releasedata
|
from common import dates, releasedata
|
||||||
from common.git import Git
|
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."""
|
||||||
@@ -40,7 +40,7 @@ def extract_point_versions(p: releasedata.ProductData, repo_dir: Path) -> None:
|
|||||||
(date, version) = line.split(' ')
|
(date, version) = line.split(' ')
|
||||||
p.declare_version(version, dates.parse_date(date))
|
p.declare_version(version, dates.parse_date(date))
|
||||||
|
|
||||||
for config in endoflife.list_configs_from_argv():
|
for config in releasedata.list_configs_from_argv():
|
||||||
with releasedata.ProductData(config.product) as product_data:
|
with releasedata.ProductData(config.product) as product_data:
|
||||||
git = Git(config.url)
|
git = Git(config.url)
|
||||||
git.setup()
|
git.setup()
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
from common import dates, endoflife, http, releasedata
|
from common import dates, http, releasedata
|
||||||
|
|
||||||
for config in endoflife.list_configs_from_argv():
|
for config in releasedata.list_configs_from_argv():
|
||||||
with releasedata.ProductData(config.product) as product_data:
|
with releasedata.ProductData(config.product) as product_data:
|
||||||
html = http.fetch_html(f"https://distrowatch.com/index.php?distribution={config.url}")
|
html = http.fetch_html(f"https://distrowatch.com/index.php?distribution={config.url}")
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,6 @@ def fetch_releases(p: releasedata.ProductData, c: endoflife.AutoConfig, url: str
|
|||||||
fetch_releases(p, c, data["next"])
|
fetch_releases(p, c, data["next"])
|
||||||
|
|
||||||
|
|
||||||
for config in endoflife.list_configs_from_argv():
|
for config in releasedata.list_configs_from_argv():
|
||||||
with releasedata.ProductData(config.product) as product_data:
|
with releasedata.ProductData(config.product) as product_data:
|
||||||
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")
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import urllib.parse
|
import urllib.parse
|
||||||
|
|
||||||
from bs4 import BeautifulSoup
|
from bs4 import BeautifulSoup
|
||||||
from common import dates, endoflife, http, releasedata
|
from common import dates, http, releasedata
|
||||||
|
|
||||||
"""Fetch Firefox versions with their dates from https://www.mozilla.org/.
|
"""Fetch Firefox versions with their dates from https://www.mozilla.org/.
|
||||||
|
|
||||||
@@ -20,7 +20,7 @@ The script will need to be updated if someday those conditions are not met."""
|
|||||||
|
|
||||||
MAX_VERSIONS_LIMIT = 100
|
MAX_VERSIONS_LIMIT = 100
|
||||||
|
|
||||||
for config in endoflife.list_configs_from_argv():
|
for config in releasedata.list_configs_from_argv():
|
||||||
with releasedata.ProductData(config.product) as product_data:
|
with releasedata.ProductData(config.product) as product_data:
|
||||||
releases_page = http.fetch_url(config.url)
|
releases_page = http.fetch_url(config.url)
|
||||||
releases_soup = BeautifulSoup(releases_page.text, features="html5lib")
|
releases_soup = BeautifulSoup(releases_page.text, features="html5lib")
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ References:
|
|||||||
import re
|
import re
|
||||||
from typing import Any, Generator, Iterator
|
from typing import Any, Generator, Iterator
|
||||||
|
|
||||||
from common import dates, endoflife, http, releasedata
|
from common import dates, http, releasedata
|
||||||
|
|
||||||
|
|
||||||
def parse_markdown_tables(lineiter: Iterator[str]) -> Generator[list[list[Any]], Any, None]:
|
def parse_markdown_tables(lineiter: Iterator[str]) -> Generator[list[list[Any]], Any, None]:
|
||||||
@@ -50,7 +50,7 @@ def maybe_markdown_table_row(line: str) -> list[str] | None:
|
|||||||
return None
|
return None
|
||||||
return [x.strip() for x in line.strip('|').split('|')]
|
return [x.strip() for x in line.strip('|').split('|')]
|
||||||
|
|
||||||
for config in endoflife.list_configs_from_argv():
|
for config in releasedata.list_configs_from_argv():
|
||||||
with releasedata.ProductData(config.product) as product:
|
with releasedata.ProductData(config.product) as product:
|
||||||
resp = http.fetch_url(config.url)
|
resp = http.fetch_url(config.url)
|
||||||
resp.raise_for_status()
|
resp.raise_for_status()
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
from common import dates, endoflife, releasedata
|
from common import dates, releasedata
|
||||||
from common.git import Git
|
from common.git import Git
|
||||||
|
|
||||||
"""Fetches versions from tags in a git repository. This replace the old update.rb script."""
|
"""Fetches versions from tags in a git repository. This replace the old update.rb script."""
|
||||||
|
|
||||||
for config in endoflife.list_configs_from_argv():
|
for config in releasedata.list_configs_from_argv():
|
||||||
with releasedata.ProductData(config.product) as product_data:
|
with releasedata.ProductData(config.product) as product_data:
|
||||||
git = Git(config.url)
|
git = Git(config.url)
|
||||||
git.setup(bare=True)
|
git.setup(bare=True)
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
from common import dates, endoflife, github, releasedata
|
from common import dates, github, releasedata
|
||||||
|
|
||||||
"""Fetches versions from GitHub releases using the GraphQL API and the GitHub CLI.
|
"""Fetches versions from GitHub releases using the GraphQL API and the GitHub CLI.
|
||||||
|
|
||||||
Note: GraphQL API and GitHub CLI are used because it's simpler: no need to manage pagination and authentication.
|
Note: GraphQL API and GitHub CLI are used because it's simpler: no need to manage pagination and authentication.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
for config in endoflife.list_configs_from_argv():
|
for config in releasedata.list_configs_from_argv():
|
||||||
with releasedata.ProductData(config.product) as product_data:
|
with releasedata.ProductData(config.product) as product_data:
|
||||||
for release in github.fetch_releases(config.url):
|
for release in github.fetch_releases(config.url):
|
||||||
if release.is_prerelease:
|
if release.is_prerelease:
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
from common import dates, endoflife, github, releasedata
|
from common import dates, github, releasedata
|
||||||
|
|
||||||
"""Fetches versions from GitHub tags using the GraphQL API and the GitHub CLI.
|
"""Fetches versions from GitHub tags using the GraphQL API and the GitHub CLI.
|
||||||
|
|
||||||
Note: GraphQL API and GitHub CLI are used because it's simpler: no need to manage pagination and authentication.
|
Note: GraphQL API and GitHub CLI are used because it's simpler: no need to manage pagination and authentication.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
for config in endoflife.list_configs_from_argv():
|
for config in releasedata.list_configs_from_argv():
|
||||||
with releasedata.ProductData(config.product) as product_data:
|
with releasedata.ProductData(config.product) as product_data:
|
||||||
for tag in github.fetch_tags(config.url):
|
for tag in github.fetch_tags(config.url):
|
||||||
version_str = tag.name
|
version_str = tag.name
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import re
|
import re
|
||||||
|
|
||||||
from common import dates, endoflife, http, releasedata
|
from common import dates, http, releasedata
|
||||||
|
|
||||||
# https://regex101.com/r/zPxBqT/1
|
# https://regex101.com/r/zPxBqT/1
|
||||||
VERSION_PATTERN = re.compile(r"\d.\d+\.\d+-gke\.\d+")
|
VERSION_PATTERN = re.compile(r"\d.\d+\.\d+-gke\.\d+")
|
||||||
@@ -11,7 +11,7 @@ URL_BY_PRODUCT = {
|
|||||||
"google-kubernetes-engine-rapid": "https://cloud.google.com/kubernetes-engine/docs/release-notes-rapid",
|
"google-kubernetes-engine-rapid": "https://cloud.google.com/kubernetes-engine/docs/release-notes-rapid",
|
||||||
}
|
}
|
||||||
|
|
||||||
for config in endoflife.list_configs_from_argv(): # noqa: B007 multiple JSON produced for historical reasons
|
for config in releasedata.list_configs_from_argv(): # noqa: B007 multiple JSON produced for historical reasons
|
||||||
for product_name, url in URL_BY_PRODUCT.items():
|
for product_name, url in URL_BY_PRODUCT.items():
|
||||||
with releasedata.ProductData(product_name) as product_data:
|
with releasedata.ProductData(product_name) as product_data:
|
||||||
html = http.fetch_html(url)
|
html = http.fetch_html(url)
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from common import dates, endoflife, http, releasedata
|
from common import dates, http, releasedata
|
||||||
|
|
||||||
for config in endoflife.list_configs_from_argv():
|
for config in releasedata.list_configs_from_argv():
|
||||||
with releasedata.ProductData(config.product) as product_data:
|
with releasedata.ProductData(config.product) as product_data:
|
||||||
html = http.fetch_html(config.url)
|
html = http.fetch_html(config.url)
|
||||||
table_selector = config.data.get("table_selector", "#previous-releases + table").strip()
|
table_selector = config.data.get("table_selector", "#previous-releases + table").strip()
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import re
|
import re
|
||||||
|
|
||||||
from common import dates, endoflife, http, releasedata
|
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
|
||||||
|
|
||||||
for config in endoflife.list_configs_from_argv():
|
for config in releasedata.list_configs_from_argv():
|
||||||
with releasedata.ProductData(config.product) as product_data:
|
with releasedata.ProductData(config.product) as product_data:
|
||||||
# First, get all minor releases from the download page
|
# First, get all minor releases from the download page
|
||||||
download_html = http.fetch_html(config.url)
|
download_html = http.fetch_html(config.url)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
from common import dates, endoflife, http, releasedata
|
from common import dates, http, releasedata
|
||||||
|
|
||||||
for config in endoflife.list_configs_from_argv():
|
for config in releasedata.list_configs_from_argv():
|
||||||
with releasedata.ProductData(config.product) as product_data:
|
with releasedata.ProductData(config.product) as product_data:
|
||||||
html = http.fetch_html(config.url)
|
html = http.fetch_html(config.url)
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from common import dates, endoflife, http, releasedata
|
from common import dates, http, releasedata
|
||||||
|
|
||||||
"""Fetch version data for Kuma from https://raw.githubusercontent.com/kumahq/kuma/master/versions.yml.
|
"""Fetch version data for Kuma from https://raw.githubusercontent.com/kumahq/kuma/master/versions.yml.
|
||||||
"""
|
"""
|
||||||
@@ -9,7 +9,7 @@ RELEASE_FIELD = 'release'
|
|||||||
RELEASE_DATE_FIELD = 'releaseDate'
|
RELEASE_DATE_FIELD = 'releaseDate'
|
||||||
EOL_FIELD = 'endOfLifeDate'
|
EOL_FIELD = 'endOfLifeDate'
|
||||||
|
|
||||||
for config in endoflife.list_configs_from_argv():
|
for config in releasedata.list_configs_from_argv():
|
||||||
with releasedata.ProductData(config.product) as product_data:
|
with releasedata.ProductData(config.product) as product_data:
|
||||||
versions_data = http.fetch_yaml(config.url)
|
versions_data = http.fetch_yaml(config.url)
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from common import dates, endoflife, http, releasedata
|
from common import dates, http, releasedata
|
||||||
|
|
||||||
"""Fetches LibreOffice versions from https://downloadarchive.documentfoundation.org/libreoffice/old/"""
|
"""Fetches LibreOffice versions from https://downloadarchive.documentfoundation.org/libreoffice/old/"""
|
||||||
|
|
||||||
for config in endoflife.list_configs_from_argv():
|
for config in releasedata.list_configs_from_argv():
|
||||||
with releasedata.ProductData(config.product) as product_data:
|
with releasedata.ProductData(config.product) as product_data:
|
||||||
html = http.fetch_html(config.url)
|
html = http.fetch_html(config.url)
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
import re
|
import re
|
||||||
|
|
||||||
from bs4 import BeautifulSoup
|
from bs4 import BeautifulSoup
|
||||||
from common import dates, endoflife, http, releasedata
|
from common import dates, http, releasedata
|
||||||
|
|
||||||
"""Fetch Looker versions from the Google Cloud release notes RSS feed.
|
"""Fetch Looker versions from the Google Cloud release notes RSS feed.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
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)
|
||||||
|
|
||||||
for config in endoflife.list_configs_from_argv():
|
for config in releasedata.list_configs_from_argv():
|
||||||
with releasedata.ProductData(config.product) as product_data:
|
with releasedata.ProductData(config.product) as product_data:
|
||||||
rss = http.fetch_xml(config.url)
|
rss = http.fetch_xml(config.url)
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import re
|
import re
|
||||||
|
|
||||||
from common import dates, endoflife, http, releasedata
|
from common import dates, http, releasedata
|
||||||
|
|
||||||
"""Fetches Lua releases from lua.org."""
|
"""Fetches Lua releases from lua.org."""
|
||||||
|
|
||||||
RELEASED_AT_PATTERN = re.compile(r"Lua\s*(?P<release>\d+\.\d+)\s*was\s*released\s*on\s*(?P<release_date>\d+\s*\w+\s*\d{4})")
|
RELEASED_AT_PATTERN = re.compile(r"Lua\s*(?P<release>\d+\.\d+)\s*was\s*released\s*on\s*(?P<release_date>\d+\s*\w+\s*\d{4})")
|
||||||
VERSION_PATTERN = re.compile(r"(?P<version>\d+\.\d+\.\d+),\s*released\s*on\s*(?P<version_date>\d+\s*\w+\s*\d{4})")
|
VERSION_PATTERN = re.compile(r"(?P<version>\d+\.\d+\.\d+),\s*released\s*on\s*(?P<version_date>\d+\s*\w+\s*\d{4})")
|
||||||
|
|
||||||
for config in endoflife.list_configs_from_argv():
|
for config in releasedata.list_configs_from_argv():
|
||||||
with releasedata.ProductData(config.product) as product_data:
|
with releasedata.ProductData(config.product) as product_data:
|
||||||
html = http.fetch_html(config.url, features = 'html.parser')
|
html = http.fetch_html(config.url, features = 'html.parser')
|
||||||
page_text = html.text # HTML is broken, no way to parse it with beautifulsoup
|
page_text = html.text # HTML is broken, no way to parse it with beautifulsoup
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
|
|
||||||
from common import endoflife, http, releasedata
|
from common import http, releasedata
|
||||||
|
|
||||||
for config in endoflife.list_configs_from_argv():
|
for config in releasedata.list_configs_from_argv():
|
||||||
with releasedata.ProductData(config.product) as product_data:
|
with releasedata.ProductData(config.product) as product_data:
|
||||||
start = 0
|
start = 0
|
||||||
group_id, artifact_id = config.url.split("/")
|
group_id, artifact_id = config.url.split("/")
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from common import dates, endoflife, http, releasedata
|
from common import dates, http, releasedata
|
||||||
|
|
||||||
"""Fetches NetBSD versions and EOL information from https://www.netbsd.org/."""
|
"""Fetches NetBSD versions and EOL information from https://www.netbsd.org/."""
|
||||||
|
|
||||||
for config in endoflife.list_configs_from_argv():
|
for config in releasedata.list_configs_from_argv():
|
||||||
with releasedata.ProductData(config.product) as product_data:
|
with releasedata.ProductData(config.product) as product_data:
|
||||||
html = http.fetch_html(config.url)
|
html = http.fetch_html(config.url)
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
from common import dates, endoflife, http, releasedata
|
from common import dates, http, releasedata
|
||||||
|
|
||||||
for config in endoflife.list_configs_from_argv():
|
for config in releasedata.list_configs_from_argv():
|
||||||
with releasedata.ProductData(config.product) as product_data:
|
with releasedata.ProductData(config.product) as product_data:
|
||||||
data = http.fetch_json(f"https://registry.npmjs.org/{config.url}")
|
data = http.fetch_json(f"https://registry.npmjs.org/{config.url}")
|
||||||
for version_str in data["versions"]:
|
for version_str in data["versions"]:
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
from common import dates, endoflife, http, releasedata
|
from common import dates, http, releasedata
|
||||||
|
|
||||||
"""Fetch Nutanix products versions from https://portal.nutanix.com/api/v1."""
|
"""Fetch Nutanix products versions from https://portal.nutanix.com/api/v1."""
|
||||||
|
|
||||||
for config in endoflife.list_configs_from_argv():
|
for config in releasedata.list_configs_from_argv():
|
||||||
with releasedata.ProductData(config.product) as product_data:
|
with releasedata.ProductData(config.product) as product_data:
|
||||||
data = http.fetch_json(f"https://portal.nutanix.com/api/v1/eol/find?type={config.url}")
|
data = http.fetch_json(f"https://portal.nutanix.com/api/v1/eol/find?type={config.url}")
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
from bs4 import BeautifulSoup
|
from bs4 import BeautifulSoup
|
||||||
from common import dates, endoflife, http, releasedata
|
from common import dates, http, releasedata
|
||||||
|
|
||||||
"""Fetch Java versions from https://www.java.com/releases/.
|
"""Fetch Java versions from https://www.java.com/releases/.
|
||||||
|
|
||||||
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."""
|
||||||
|
|
||||||
for config in endoflife.list_configs_from_argv():
|
for config in releasedata.list_configs_from_argv():
|
||||||
with releasedata.ProductData(config.product) as product_data:
|
with releasedata.ProductData(config.product) as product_data:
|
||||||
content = http.fetch_javascript_url(config.url, wait_until='networkidle')
|
content = http.fetch_javascript_url(config.url, wait_until='networkidle')
|
||||||
soup = BeautifulSoup(content, 'html.parser')
|
soup = BeautifulSoup(content, 'html.parser')
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
from common import dates, endoflife, http, releasedata
|
from common import dates, http, releasedata
|
||||||
|
|
||||||
"""Fetches pan-os versions from https://github.com/mrjcap/panos-versions/."""
|
"""Fetches pan-os versions from https://github.com/mrjcap/panos-versions/."""
|
||||||
|
|
||||||
for config in endoflife.list_configs_from_argv():
|
for config in releasedata.list_configs_from_argv():
|
||||||
with releasedata.ProductData(config.product) as product_data:
|
with releasedata.ProductData(config.product) as product_data:
|
||||||
versions = http.fetch_json(config.url)
|
versions = http.fetch_json(config.url)
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
from common import dates, endoflife, http, releasedata
|
from common import dates, http, releasedata
|
||||||
|
|
||||||
for config in endoflife.list_configs_from_argv():
|
for config in releasedata.list_configs_from_argv():
|
||||||
with releasedata.ProductData(config.product) as product_data:
|
with releasedata.ProductData(config.product) as product_data:
|
||||||
# Fetch major versions
|
# Fetch major versions
|
||||||
latest_by_major = http.fetch_url(config.url).json()
|
latest_by_major = http.fetch_url(config.url).json()
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
from common import dates, endoflife, http, releasedata
|
from common import dates, http, releasedata
|
||||||
|
|
||||||
"""Fetches versions from Plesk's change log.
|
"""Fetches versions from Plesk's change log.
|
||||||
|
|
||||||
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."""
|
||||||
|
|
||||||
for config in endoflife.list_configs_from_argv():
|
for config in releasedata.list_configs_from_argv():
|
||||||
with releasedata.ProductData(config.product) as product_data:
|
with releasedata.ProductData(config.product) as product_data:
|
||||||
html = http.fetch_html(config.url)
|
html = http.fetch_html(config.url)
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
from common import dates, endoflife, http, releasedata
|
from common import dates, http, releasedata
|
||||||
|
|
||||||
for config in endoflife.list_configs_from_argv():
|
for config in releasedata.list_configs_from_argv():
|
||||||
with releasedata.ProductData(config.product) as product_data:
|
with releasedata.ProductData(config.product) as product_data:
|
||||||
data = http.fetch_json(f"https://pypi.org/pypi/{config.url}/json")
|
data = http.fetch_json(f"https://pypi.org/pypi/{config.url}/json")
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from common import dates, endoflife, http, releasedata
|
from common import dates, http, releasedata
|
||||||
|
|
||||||
"""Fetches Amazon RDS versions from the version management pages on AWS docs.
|
"""Fetches Amazon RDS versions from the version management pages on AWS docs.
|
||||||
|
|
||||||
@@ -8,7 +8,7 @@ Pages parsed by this script are expected to have version tables with a version i
|
|||||||
in the third column (usually named 'RDS release date').
|
in the third column (usually named 'RDS release date').
|
||||||
"""
|
"""
|
||||||
|
|
||||||
for config in endoflife.list_configs_from_argv():
|
for config in releasedata.list_configs_from_argv():
|
||||||
with releasedata.ProductData(config.product) as product_data:
|
with releasedata.ProductData(config.product) as product_data:
|
||||||
html = http.fetch_html(config.url)
|
html = http.fetch_html(config.url)
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from common import dates, endoflife, http, releasedata
|
from common import dates, http, releasedata
|
||||||
|
|
||||||
"""Fetches RedHat JBoss EAP version data for JBoss 7"""
|
"""Fetches RedHat JBoss EAP version data for JBoss 7"""
|
||||||
|
|
||||||
for config in endoflife.list_configs_from_argv():
|
for config in releasedata.list_configs_from_argv():
|
||||||
with releasedata.ProductData(config.product) as product_data:
|
with releasedata.ProductData(config.product) as product_data:
|
||||||
html = http.fetch_html(config.url)
|
html = http.fetch_html(config.url)
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import re
|
import re
|
||||||
|
|
||||||
from common import dates, endoflife, http, releasedata
|
from common import dates, http, releasedata
|
||||||
|
|
||||||
"""Fetches the latest RedHat JBoss EAP version data for JBoss 8.0"""
|
"""Fetches the latest RedHat JBoss EAP version data for JBoss 8.0"""
|
||||||
|
|
||||||
for config in endoflife.list_configs_from_argv():
|
for config in releasedata.list_configs_from_argv():
|
||||||
with releasedata.ProductData(config.product) as product_data:
|
with releasedata.ProductData(config.product) as product_data:
|
||||||
xml = http.fetch_xml(config.url)
|
xml = http.fetch_xml(config.url)
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import re
|
import re
|
||||||
|
|
||||||
from common import dates, endoflife, releasedata
|
from common import dates, releasedata
|
||||||
from common.git import Git
|
from common.git import Git
|
||||||
|
|
||||||
"""Fetches Red Hat OpenShift versions from the documentation's git repository"""
|
"""Fetches Red Hat OpenShift versions from the documentation's git repository"""
|
||||||
@@ -10,7 +10,7 @@ VERSION_AND_DATE_PATTERN = re.compile(
|
|||||||
re.MULTILINE,
|
re.MULTILINE,
|
||||||
)
|
)
|
||||||
|
|
||||||
for config in endoflife.list_configs_from_argv():
|
for config in releasedata.list_configs_from_argv():
|
||||||
with releasedata.ProductData(config.product) as product_data:
|
with releasedata.ProductData(config.product) as product_data:
|
||||||
git = Git(config.url)
|
git = Git(config.url)
|
||||||
git.setup()
|
git.setup()
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from common import dates, endoflife, http, releasedata
|
from common import dates, http, releasedata
|
||||||
|
|
||||||
"""Fetches Satellite versions from access.redhat.com.
|
"""Fetches Satellite versions from access.redhat.com.
|
||||||
|
|
||||||
A few of the older versions, such as 'Satellite 6.1 GA Release (Build 6.1.1)', were ignored because too hard to parse."""
|
A few of the older versions, such as 'Satellite 6.1 GA Release (Build 6.1.1)', were ignored because too hard to parse."""
|
||||||
|
|
||||||
for config in endoflife.list_configs_from_argv():
|
for config in releasedata.list_configs_from_argv():
|
||||||
with releasedata.ProductData(config.product) as product_data:
|
with releasedata.ProductData(config.product) as product_data:
|
||||||
html = http.fetch_html(config.url)
|
html = http.fetch_html(config.url)
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import logging
|
import logging
|
||||||
import urllib.parse
|
import urllib.parse
|
||||||
|
|
||||||
from common import dates, endoflife, http, releasedata
|
from common import dates, http, releasedata
|
||||||
|
|
||||||
"""Fetches EOL dates from the Red Hat Product Life Cycle Data API.
|
"""Fetches EOL dates from the Red Hat Product Life Cycle Data API.
|
||||||
|
|
||||||
@@ -17,7 +17,7 @@ class Mapping:
|
|||||||
def get_field_for(self, phase_name: str) -> str | None:
|
def get_field_for(self, phase_name: str) -> str | None:
|
||||||
return self.fields_by_phase.get(phase_name.lower(), None)
|
return self.fields_by_phase.get(phase_name.lower(), None)
|
||||||
|
|
||||||
for config in endoflife.list_configs_from_argv():
|
for config in releasedata.list_configs_from_argv():
|
||||||
with releasedata.ProductData(config.product) as product_data:
|
with releasedata.ProductData(config.product) as product_data:
|
||||||
name = urllib.parse.quote(config.url)
|
name = urllib.parse.quote(config.url)
|
||||||
mapping = Mapping(config.data["fields"])
|
mapping = Mapping(config.data["fields"])
|
||||||
|
|||||||
@@ -150,7 +150,7 @@ class Field:
|
|||||||
return f"{self.name}({self.column})"
|
return f"{self.name}({self.column})"
|
||||||
|
|
||||||
|
|
||||||
for config in endoflife.list_configs_from_argv():
|
for config in releasedata.list_configs_from_argv():
|
||||||
with releasedata.ProductData(config.product) as product_data:
|
with releasedata.ProductData(config.product) as product_data:
|
||||||
render_javascript = config.data.get("render_javascript", False)
|
render_javascript = config.data.get("render_javascript", False)
|
||||||
render_javascript_click_selector = config.data.get("render_javascript_click_selector", None)
|
render_javascript_click_selector = config.data.get("render_javascript_click_selector", None)
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import re
|
import re
|
||||||
|
|
||||||
from common import dates, endoflife, http, releasedata
|
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)?")
|
||||||
|
|
||||||
for config in endoflife.list_configs_from_argv():
|
for config in releasedata.list_configs_from_argv():
|
||||||
with releasedata.ProductData(config.product) as product_data:
|
with releasedata.ProductData(config.product) as product_data:
|
||||||
html = http.fetch_html(config.url)
|
html = http.fetch_html(config.url)
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
from common import dates, endoflife, http, releasedata
|
from common import dates, http, releasedata
|
||||||
|
|
||||||
for config in endoflife.list_configs_from_argv():
|
for config in releasedata.list_configs_from_argv():
|
||||||
with releasedata.ProductData(config.product) as product_data:
|
with releasedata.ProductData(config.product) as product_data:
|
||||||
response = http.fetch_url(config.url)
|
response = http.fetch_url(config.url)
|
||||||
for line in response.text.strip().split('\n'):
|
for line in response.text.strip().split('\n'):
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from common import dates, endoflife, http, releasedata
|
from common import dates, http, releasedata
|
||||||
|
|
||||||
for config in endoflife.list_configs_from_argv():
|
for config in releasedata.list_configs_from_argv():
|
||||||
with releasedata.ProductData(config.product) as product_data:
|
with releasedata.ProductData(config.product) as product_data:
|
||||||
html = http.fetch_html(config.url)
|
html = http.fetch_html(config.url)
|
||||||
|
|
||||||
|
|||||||
@@ -12,9 +12,9 @@ it retains the date and use it as the model's EOL date.
|
|||||||
|
|
||||||
TODAY = dates.today()
|
TODAY = dates.today()
|
||||||
|
|
||||||
for config in endoflife.list_configs_from_argv():
|
frontmatter, configs = releasedata.parse_argv()
|
||||||
|
for config in configs:
|
||||||
with releasedata.ProductData(config.product) as product_data:
|
with releasedata.ProductData(config.product) as product_data:
|
||||||
frontmatter = endoflife.ProductFrontmatter(product_data.name)
|
|
||||||
frontmatter_release_names = frontmatter.get_release_names()
|
frontmatter_release_names = frontmatter.get_release_names()
|
||||||
|
|
||||||
# Copy EOL dates from frontmatter to product data
|
# Copy EOL dates from frontmatter to product data
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from common import dates, endoflife, http, releasedata
|
from common import dates, http, releasedata
|
||||||
|
|
||||||
for config in endoflife.list_configs_from_argv():
|
for config in releasedata.list_configs_from_argv():
|
||||||
with releasedata.ProductData(config.product) as product_data:
|
with releasedata.ProductData(config.product) as product_data:
|
||||||
html = http.fetch_html(config.url)
|
html = http.fetch_html(config.url)
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import re
|
import re
|
||||||
|
|
||||||
from common import dates, endoflife, http, releasedata
|
from common import dates, http, releasedata
|
||||||
|
|
||||||
VERSION_DATE_PATTERN = re.compile(r"Splunk Enterprise (?P<version>\d+\.\d+(?:\.\d+)*) was (?:first )?released on (?P<date>\w+\s\d\d?,\s\d{4})\.", re.MULTILINE)
|
VERSION_DATE_PATTERN = re.compile(r"Splunk Enterprise (?P<version>\d+\.\d+(?:\.\d+)*) was (?:first )?released on (?P<date>\w+\s\d\d?,\s\d{4})\.", re.MULTILINE)
|
||||||
|
|
||||||
@@ -29,7 +29,7 @@ def get_latest_minor_versions(versions: list[str]) -> list[str]:
|
|||||||
return latest_versions
|
return latest_versions
|
||||||
|
|
||||||
|
|
||||||
for config in endoflife.list_configs_from_argv():
|
for config in releasedata.list_configs_from_argv():
|
||||||
with releasedata.ProductData(config.product) as product_data:
|
with releasedata.ProductData(config.product) as product_data:
|
||||||
html = http.fetch_html(config.url)
|
html = http.fetch_html(config.url)
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
from common import dates, endoflife, http, releasedata
|
from common import dates, http, releasedata
|
||||||
|
|
||||||
for config in endoflife.list_configs_from_argv():
|
for config in releasedata.list_configs_from_argv():
|
||||||
with releasedata.ProductData(config.product) as product_data:
|
with releasedata.ProductData(config.product) as product_data:
|
||||||
data = http.fetch_json(config.url)
|
data = http.fetch_json(config.url)
|
||||||
for v in data:
|
for v in data:
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
from common import dates, endoflife, http, releasedata
|
from common import dates, http, releasedata
|
||||||
|
|
||||||
"""Fetches the Unity LTS releases from the Unity website. Non-LTS releases are not listed there, so this automation
|
"""Fetches the Unity LTS releases from the Unity website. Non-LTS releases are not listed there, so this automation
|
||||||
is only partial.
|
is only partial.
|
||||||
@@ -16,7 +16,7 @@ Note that it was assumed that:
|
|||||||
|
|
||||||
The script will need to be updated if someday those conditions are not met."""
|
The script will need to be updated if someday those conditions are not met."""
|
||||||
|
|
||||||
for config in endoflife.list_configs_from_argv():
|
for config in releasedata.list_configs_from_argv():
|
||||||
with releasedata.ProductData(config.product) as product_data:
|
with releasedata.ProductData(config.product) as product_data:
|
||||||
html = http.fetch_html(config.url)
|
html = http.fetch_html(config.url)
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import re
|
import re
|
||||||
|
|
||||||
from common import dates, endoflife, http, releasedata
|
from common import dates, http, releasedata
|
||||||
|
|
||||||
DATE_PATTERN = re.compile(r"\d{4}-\d{2}-\d{2}")
|
DATE_PATTERN = re.compile(r"\d{4}-\d{2}-\d{2}")
|
||||||
|
|
||||||
for config in endoflife.list_configs_from_argv():
|
for config in releasedata.list_configs_from_argv():
|
||||||
with releasedata.ProductData(config.product) as product_data:
|
with releasedata.ProductData(config.product) as product_data:
|
||||||
wikicode = http.fetch_markdown(config.url)
|
wikicode = http.fetch_markdown(config.url)
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import logging
|
import logging
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from common import dates, endoflife, http, releasedata
|
from common import dates, http, releasedata
|
||||||
|
|
||||||
"""Fetches Veeam products versions from https://www.veeam.com.
|
"""Fetches Veeam products versions from https://www.veeam.com.
|
||||||
|
|
||||||
@@ -9,7 +9,7 @@ This script takes a single argument which is the url of the versions page on htt
|
|||||||
such as `https://www.veeam.com/kb2680`.
|
such as `https://www.veeam.com/kb2680`.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
for config in endoflife.list_configs_from_argv():
|
for config in releasedata.list_configs_from_argv():
|
||||||
with releasedata.ProductData(config.product) as product_data:
|
with releasedata.ProductData(config.product) as product_data:
|
||||||
html = http.fetch_html(config.url)
|
html = http.fetch_html(config.url)
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import logging
|
import logging
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from common import dates, endoflife, http, releasedata
|
from common import dates, http, releasedata
|
||||||
|
|
||||||
"""Fetches releases from VirtualBox download page."""
|
"""Fetches releases from VirtualBox download page."""
|
||||||
|
|
||||||
EOL_REGEX = re.compile(r"^\(no longer supported, support ended (?P<value>\d{4}/\d{2})\)$")
|
EOL_REGEX = re.compile(r"^\(no longer supported, support ended (?P<value>\d{4}/\d{2})\)$")
|
||||||
|
|
||||||
for config in endoflife.list_configs_from_argv():
|
for config in releasedata.list_configs_from_argv():
|
||||||
with releasedata.ProductData(config.product) as product_data:
|
with releasedata.ProductData(config.product) as product_data:
|
||||||
html = http.fetch_html(config.url)
|
html = http.fetch_html(config.url)
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
from common import dates, endoflife, http, releasedata
|
from common import dates, http, releasedata
|
||||||
|
|
||||||
for config in endoflife.list_configs_from_argv():
|
for config in releasedata.list_configs_from_argv():
|
||||||
with releasedata.ProductData(config.product) as product_data:
|
with releasedata.ProductData(config.product) as product_data:
|
||||||
html = http.fetch_html(config.url)
|
html = http.fetch_html(config.url)
|
||||||
|
|
||||||
|
|||||||
45
update.py
45
update.py
@@ -1,3 +1,4 @@
|
|||||||
|
import argparse
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import subprocess
|
import subprocess
|
||||||
@@ -9,9 +10,7 @@ from deepdiff import DeepDiff
|
|||||||
|
|
||||||
from src.common.endoflife import AutoConfig, ProductFrontmatter, list_products
|
from src.common.endoflife import AutoConfig, ProductFrontmatter, list_products
|
||||||
from src.common.gha import GitHubGroup, GitHubOutput, GitHubStepSummary
|
from src.common.gha import GitHubGroup, GitHubOutput, GitHubStepSummary
|
||||||
|
from src.common.releasedata import DATA_DIR, SRC_DIR
|
||||||
SRC_DIR = Path('src')
|
|
||||||
DATA_DIR = Path('releases')
|
|
||||||
|
|
||||||
|
|
||||||
class ScriptExecutionSummary:
|
class ScriptExecutionSummary:
|
||||||
@@ -66,7 +65,7 @@ def install_playwright() -> None:
|
|||||||
|
|
||||||
|
|
||||||
def __delete_data(product: ProductFrontmatter) -> None:
|
def __delete_data(product: ProductFrontmatter) -> None:
|
||||||
release_data_path = DATA_DIR / f"{product.name}.json"
|
release_data_path = Path(__file__).resolve().parent / DATA_DIR / f"{product.name}.json"
|
||||||
if not release_data_path.exists() or product.is_auto_update_cumulative():
|
if not release_data_path.exists() or product.is_auto_update_cumulative():
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -75,19 +74,23 @@ def __delete_data(product: ProductFrontmatter) -> None:
|
|||||||
|
|
||||||
|
|
||||||
def __revert_data(product: ProductFrontmatter) -> None:
|
def __revert_data(product: ProductFrontmatter) -> None:
|
||||||
release_data_path = DATA_DIR / f"{product.name}.json"
|
release_data_path = Path(__file__).resolve().parent / DATA_DIR / f"{product.name}.json"
|
||||||
# check=False because the command fails if the file did not exist before
|
# check=False because the command fails if the file did not exist before
|
||||||
subprocess.run(f'git checkout HEAD -- {release_data_path}', timeout=10, check=False, shell=True)
|
subprocess.run(f'git checkout HEAD -- {release_data_path}', timeout=10, check=False, shell=True)
|
||||||
logging.warning(f"reverted changes in {release_data_path}")
|
logging.warning(f"reverted changes in {release_data_path}")
|
||||||
|
|
||||||
|
|
||||||
def __run_script(product: ProductFrontmatter, config: AutoConfig, summary: ScriptExecutionSummary) -> bool:
|
def __run_script(product: ProductFrontmatter, config: AutoConfig, summary: ScriptExecutionSummary) -> bool:
|
||||||
script = SRC_DIR / config.script
|
script = Path(__file__).resolve().parent / SRC_DIR / config.script
|
||||||
|
|
||||||
logging.info(f"start running {script} for {config}")
|
logging.info(f"start running {script} for {config}")
|
||||||
start = time.perf_counter()
|
start = time.perf_counter()
|
||||||
|
|
||||||
# timeout is handled in child scripts
|
# timeout is handled in child scripts
|
||||||
child = subprocess.run([sys.executable, script, config.product, str(config.method), str(config.url)])
|
script_args = [sys.executable, script, "-p", product.path, "-m", str(config.method), "-u", str(config.url)]
|
||||||
|
script_args = script_args + ["-v"] if logging.getLogger().isEnabledFor(logging.DEBUG) else script_args
|
||||||
|
child = subprocess.run(script_args)
|
||||||
|
|
||||||
success = child.returncode == 0
|
success = child.returncode == 0
|
||||||
elapsed_seconds = time.perf_counter() - start
|
elapsed_seconds = time.perf_counter() - start
|
||||||
|
|
||||||
@@ -98,13 +101,10 @@ def __run_script(product: ProductFrontmatter, config: AutoConfig, summary: Scrip
|
|||||||
return success
|
return success
|
||||||
|
|
||||||
|
|
||||||
def run_scripts(summary: GitHubStepSummary, product_filter: str) -> bool:
|
def run_scripts(summary: GitHubStepSummary, products: list[ProductFrontmatter]) -> bool:
|
||||||
exec_summary = ScriptExecutionSummary()
|
exec_summary = ScriptExecutionSummary()
|
||||||
|
|
||||||
with GitHubGroup("Load Product Data"):
|
for product in products:
|
||||||
product_list = list_products(product_filter)
|
|
||||||
|
|
||||||
for product in product_list:
|
|
||||||
if not product.has_auto_configs():
|
if not product.has_auto_configs():
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@@ -166,14 +166,21 @@ def generate_commit_message(old_content: dict[Path, dict], new_content: dict[Pat
|
|||||||
commit_message.println("")
|
commit_message.println("")
|
||||||
summary.println("")
|
summary.println("")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
parser = argparse.ArgumentParser(description='Update product releases.')
|
||||||
|
parser.add_argument('product', nargs='?', help='restrict update to the given product')
|
||||||
|
parser.add_argument('-p', '--product-dir', required=True, help='path to the product directory')
|
||||||
|
parser.add_argument('-v', '--verbose', action='store_true', help='enable verbose logging')
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
logging.basicConfig(format="%(message)s", level=logging.INFO)
|
logging.basicConfig(format=logging.BASIC_FORMAT, level=(logging.DEBUG if args.verbose else logging.INFO))
|
||||||
p_filter = sys.argv[1] if len(sys.argv) > 1 else None
|
|
||||||
|
|
||||||
|
|
||||||
with GitHubStepSummary() as step_summary:
|
|
||||||
install_playwright()
|
install_playwright()
|
||||||
some_script_failed = run_scripts(step_summary, p_filter)
|
|
||||||
|
products_dir = Path(args.product_dir)
|
||||||
|
products_list = list_products(products_dir, args.product)
|
||||||
|
|
||||||
|
with GitHubStepSummary() as step_summary:
|
||||||
|
some_script_failed = run_scripts(step_summary, products_list)
|
||||||
updated_products = get_updated_products()
|
updated_products = get_updated_products()
|
||||||
|
|
||||||
step_summary.println("## Update summary\n")
|
step_summary.println("## Update summary\n")
|
||||||
@@ -186,4 +193,4 @@ with GitHubStepSummary() as step_summary:
|
|||||||
else:
|
else:
|
||||||
step_summary.println("No update")
|
step_summary.println("No update")
|
||||||
|
|
||||||
sys.exit(1 if some_script_failed else 0)
|
sys.exit(1 if some_script_failed else 0)
|
||||||
|
|||||||
Reference in New Issue
Block a user