Loading .gitlab-ci.d/get-versions.py 0 → 100755 +78 −0 Original line number Diff line number Diff line #!/usr/bin/env python3 # # $pip install requests lxml packaging # from __future__ import annotations import gzip import sys from collections import defaultdict from collections.abc import Iterator from typing import IO from typing import cast import requests from lxml import etree from packaging.version import InvalidVersion from packaging.version import Version WP_RELEASES_URL = "https://wordpress.org/download/releases/" def get_doc() -> etree._ElementTree: """ Return the WP releases page as an element tree """ with requests.Session() as session: session.hooks["response"].append(lambda r, *_, **__: r.raise_for_status()) resp = session.get(WP_RELEASES_URL, stream=True) match resp.headers.get("Content-Encoding"): case "gzip": stream = cast(IO[bytes], gzip.GzipFile(fileobj=resp.raw)) case _: stream = resp.raw return etree.parse(stream, etree.HTMLParser()) # type: ignore def get_releases(doc: etree._ElementTree) -> Iterator[Version]: """ Iteratively yield release versions from the given releases page """ path = '//div[@id and starts-with(@id, "branch-")]//th[@scope="row"]' elements = doc.xpath(path) assert isinstance(elements, list) for elem in elements: assert isinstance(elem, etree._Element) if elem.text is None: continue try: yield Version(elem.text) except InvalidVersion: continue def get_latest_patches(doc: etree._ElementTree) -> Iterator[tuple[int, int, Version]]: """ For each feature release, yield the major and minor numbers and the latest patch version """ versions = defaultdict(list) for v in get_releases(doc): versions[v.major, v.minor].append(v) for (major, minor), releases in sorted(versions.items(), key=lambda i: i[0], reverse=True): yield major, minor, max(releases) def main() -> None: """ Command line entrypoint """ num = int(sys.argv[1]) for idx, (*_, version) in enumerate(get_latest_patches(get_doc())): if idx >= num: break sys.stdout.write(f"{version}\n") if __name__ == "__main__": main() .gitlab-ci.yml +33 −8 Original line number Diff line number Diff line Loading @@ -52,14 +52,39 @@ Build Images: strategy: depend Build Releases: Prepare Release Builds: stage: build extends: [Build Images] image: python:3.10 rules: - if: $BUILD_RELEASE artifacts: paths: [build.yml] script: | pip install requests lxml packaging tee build.yml <<END_YAML Build Releases: stage: build parallel: matrix: - TARGET: [nginx] RELEASE: [$NGINX_VERSION] - TARGET: [fastcgi] RELEASE: [$WORDPRESS_VERSION] RELEASE: [`python .gitlab-ci.d/get-versions.py 3 | tr '\n' ','`] trigger: include: - project: dom/project-templates file: /pipeline-templates/build-image.yml strategy: depend END_YAML Run Builds: stage: build rules: - if: $BUILD_RELEASE needs: [Prepare Release Builds] trigger: include: - artifact: build.yml job: Prepare Release Builds strategy: depend .lint.cfg +1 −0 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ format = pylint select = C,D,E,ET,F,SFS,T,W,WT per-file-ignores = .gitlab-ci.d/*.py: D100 **/__init__.py: D104 **/__main__.py: D100, E702 Loading .pre-commit-config.yaml +3 −2 Original line number Diff line number Diff line Loading @@ -40,7 +40,7 @@ repos: - id: check-executable-modes - id: check-for-squash - id: copyright-notice exclude: ^data/|^scripts/(compile-|install-) exclude: ^data/|^scripts/(compile-|install-)|^.gitlab-ci.d/ - id: protect-first-parent - repo: https://github.com/pre-commit/pygrep-hooks Loading Loading @@ -93,7 +93,8 @@ repos: rev: v0.982 hooks: - id: mypy args: ["--config-file=.lint.cfg", "--python-version=3.9"] args: ["--config-file=.lint.cfg", "--python-version=3.10"] additional_dependencies: - lxml-stubs - types-requests - behave-utils ~=0.3.2 Loading
.gitlab-ci.d/get-versions.py 0 → 100755 +78 −0 Original line number Diff line number Diff line #!/usr/bin/env python3 # # $pip install requests lxml packaging # from __future__ import annotations import gzip import sys from collections import defaultdict from collections.abc import Iterator from typing import IO from typing import cast import requests from lxml import etree from packaging.version import InvalidVersion from packaging.version import Version WP_RELEASES_URL = "https://wordpress.org/download/releases/" def get_doc() -> etree._ElementTree: """ Return the WP releases page as an element tree """ with requests.Session() as session: session.hooks["response"].append(lambda r, *_, **__: r.raise_for_status()) resp = session.get(WP_RELEASES_URL, stream=True) match resp.headers.get("Content-Encoding"): case "gzip": stream = cast(IO[bytes], gzip.GzipFile(fileobj=resp.raw)) case _: stream = resp.raw return etree.parse(stream, etree.HTMLParser()) # type: ignore def get_releases(doc: etree._ElementTree) -> Iterator[Version]: """ Iteratively yield release versions from the given releases page """ path = '//div[@id and starts-with(@id, "branch-")]//th[@scope="row"]' elements = doc.xpath(path) assert isinstance(elements, list) for elem in elements: assert isinstance(elem, etree._Element) if elem.text is None: continue try: yield Version(elem.text) except InvalidVersion: continue def get_latest_patches(doc: etree._ElementTree) -> Iterator[tuple[int, int, Version]]: """ For each feature release, yield the major and minor numbers and the latest patch version """ versions = defaultdict(list) for v in get_releases(doc): versions[v.major, v.minor].append(v) for (major, minor), releases in sorted(versions.items(), key=lambda i: i[0], reverse=True): yield major, minor, max(releases) def main() -> None: """ Command line entrypoint """ num = int(sys.argv[1]) for idx, (*_, version) in enumerate(get_latest_patches(get_doc())): if idx >= num: break sys.stdout.write(f"{version}\n") if __name__ == "__main__": main()
.gitlab-ci.yml +33 −8 Original line number Diff line number Diff line Loading @@ -52,14 +52,39 @@ Build Images: strategy: depend Build Releases: Prepare Release Builds: stage: build extends: [Build Images] image: python:3.10 rules: - if: $BUILD_RELEASE artifacts: paths: [build.yml] script: | pip install requests lxml packaging tee build.yml <<END_YAML Build Releases: stage: build parallel: matrix: - TARGET: [nginx] RELEASE: [$NGINX_VERSION] - TARGET: [fastcgi] RELEASE: [$WORDPRESS_VERSION] RELEASE: [`python .gitlab-ci.d/get-versions.py 3 | tr '\n' ','`] trigger: include: - project: dom/project-templates file: /pipeline-templates/build-image.yml strategy: depend END_YAML Run Builds: stage: build rules: - if: $BUILD_RELEASE needs: [Prepare Release Builds] trigger: include: - artifact: build.yml job: Prepare Release Builds strategy: depend
.lint.cfg +1 −0 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ format = pylint select = C,D,E,ET,F,SFS,T,W,WT per-file-ignores = .gitlab-ci.d/*.py: D100 **/__init__.py: D104 **/__main__.py: D100, E702 Loading
.pre-commit-config.yaml +3 −2 Original line number Diff line number Diff line Loading @@ -40,7 +40,7 @@ repos: - id: check-executable-modes - id: check-for-squash - id: copyright-notice exclude: ^data/|^scripts/(compile-|install-) exclude: ^data/|^scripts/(compile-|install-)|^.gitlab-ci.d/ - id: protect-first-parent - repo: https://github.com/pre-commit/pygrep-hooks Loading Loading @@ -93,7 +93,8 @@ repos: rev: v0.982 hooks: - id: mypy args: ["--config-file=.lint.cfg", "--python-version=3.9"] args: ["--config-file=.lint.cfg", "--python-version=3.10"] additional_dependencies: - lxml-stubs - types-requests - behave-utils ~=0.3.2