-
Notifications
You must be signed in to change notification settings - Fork 6.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Refactor and expand download_hash.py #11513
Refactor and expand download_hash.py #11513
Conversation
The script is currently limited to one hardcoded URL for kubernetes related binaries, and a fixed set of architectures. The solution is three-fold: 1. Use an url template dictionary for each download -> this allow to easily add support for new downloads. 2. Source the architectures to search from the existing data 3. Enumerate the existing versions in the data and start searching from the last one until no newer version is found (newer in the version order sense, irrespective of actual age)
runc upstream does not provide one hash file per assets in their releases, but one file with all the hashes. To handle this (and/or any arbitrary format from upstreams), add a dictionary mapping the name of the download to a lambda function which transform the file provided by upstream into a dictionary of hashes, keyed by architecture.
Allow the script to be called with a list of components, to only download new versions checksums for those. By default, we get new versions checksums for all supported (by the script) components.
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: VannTen The full list of commands accepted by this bot can be found here. The pull request process is described here
Needs approval from an approver in each of these files:
Approvers can indicate their approval by writing |
/ok-to-test |
This allows to reuse http connection and be more efficient. From rough measuring it saves around 25-30% of execution time.
This avoid re-downloading the same file for different arch and re-parsing it
cfe1b31
to
230cb37
Compare
/cherrypick release-1.26
/cherrypick release-1.25
/cherrypick release-1.24
|
@VannTen: once the present PR merges, I will cherry-pick it on top of release-1.26 in a new PR and assign it to you. In response to this:
Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here is an initial review from a first look, overall I agree with the direction this is going but I left some comments to improve readability inline. Thanks!
for minor, patches in groupby(versions.copy().keys(), lambda v : '.'.join(v.split('.')[:-1])): | ||
for version in (f"{minor}.{patch}" for patch in | ||
count(start=int(max(patches, key=version_compare).split('.')[-1]), | ||
step=1)): | ||
# Those barbaric generators do the following: | ||
# Group all patches versions by minor number, take the newest and start from that | ||
# to find new versions |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could these generators be independent functions? it would probably be much more readable that way IMO
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hum, I might be able to convert this two loops into one, since I don't use minor
of patches
expect in the sub-loop, and then put that in a distinct fonction.
for download, url in (downloads if only_downloads == [] | ||
else {k:downloads[k] for k in downloads.keys() & only_downloads}).items(): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Having such complex expression directly in the for loop makes it super hard to read IMO, could you refactor this in a few lines before the for loop?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You mean something like for instance ?
for download, url in (downloads if only_downloads == [] | |
else {k:downloads[k] for k in downloads.keys() & only_downloads}).items(): | |
if only_downloads != []: | |
downloads = {k:downloads[k] for k in downloads.keys() & only_downloads} | |
for download, url in downloads.items(): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yep 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
btw maybe you have a good reason for it but in general I think this:
if not only_downloads:
instead of:
if only_downloads != []:
is slightly more idiomatic (although it's not technically checking the same thing strictly speaking) but that's more a nit feel free to do what you prefer
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not a specific reason, it's just I tend to avoid "implicit conversion to boolean" even in Python, I find it confusing (for later)
if download in download_hash_extract: | ||
hashes = _get_hash_by_arch(download, version) | ||
if hashes == None: | ||
break | ||
sha256sum = hashes.get(arch) | ||
if sha256sum == None: | ||
break | ||
else: | ||
hash_file = s.get(downloads[download].format( | ||
version = version, | ||
os = "linux", | ||
arch = arch | ||
), | ||
allow_redirects=True) | ||
if hash_file.status_code == 404: | ||
print(f"Unable to find {download} hash file for version {version} (arch: {arch}) at {hash_file.url}") | ||
break | ||
hash_file.raise_for_status() | ||
sha256sum = hash_file.content.decode().split()[0] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel like there is some unnecessary duplicated code here and you could find a way to do the file download/error checking in a common function and/or still rely on _get_hash_by_arch
and handle the if download in download_hash_extract
there somehow
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This comes from the need to cache memoize _get_hash_by_arch (to avoid redundant HTTP calls). functools.cache
memoize by parameters, so I can't pass arch
to _get_hash_by_arch because it would break the memoization. OTOH, I need it for "simple hash" download .
I could extract the GET + check 404 + raise in it's own little function though 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ahh ok I see, hmm well if you find some elegant way to not duplicate this piece of code that could be nice but otherwise feel free to resolve that comment then
I'm also looking if I can just drop all the "guess version and handle 404" by leveraging the GitHub graphql api to get a complete list of the versions needed (with a single api call thanks to graphql).
So far it appears promising, so I may end up dropping the unreadable generators all together o/
|
Thanks @VannTen Greate Refactor |
@VannTen: cannot checkout In response to this:
Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. |
@VannTen: cannot checkout In response to this:
Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. |
@VannTen: cannot checkout In response to this:
Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. |
/cherrypick release-2.26 |
@tico88612: new pull request created: #11538 In response to this:
Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. |
/cherrypick release-2.25 |
@tico88612: new pull request created: #11539 In response to this:
Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. |
commit 656ed79 Author: janosbabik <[email protected]> Date: Thu Sep 26 10:12:01 2024 +0200 [etcd] make etcd 3.5.16 default (kubernetes-sigs#11572) * [etcd] make etcd 3.5.16 default * Update etcd binary checksums for version 3.5.16 and lower commit e355bef Author: Bakke <[email protected]> Date: Thu Sep 26 07:22:02 2024 +0000 fix: vsphere image repositories, tags and docs (kubernetes-sigs#11564) The old repository for these has been deleted, leaving the previous configuration not possible to deploy, and even currently running clusters fail after a restart as the DeameonSet has ImagePullPolicy: Always. More details can be found here: kubernetes-sigs/vsphere-csi-driver#3053 As of writing, only CSI driver versions 3.1.2 to 3.3.1 is available in this registry. This "officially" supports Kubernetes 1.26 to 1.30. Since older drivers are not available, I have removed some feature-gating for those unavailable versions while I was at it. For the cloud provider, the `latest` image is now missing, and only 1.28.0 to 1.31.0 are available. I've set the latest of these as the new default. I also updated the documented default versions, as they were all out of date and not aligned with actual code defaults. commit 15bb5b0 Author: Philip Sabri <[email protected]> Date: Wed Sep 25 06:10:01 2024 +0200 [kubernetes] Support kubernetes 1.31.1 (kubernetes-sigs#11533) commit ebdc599 Author: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue Sep 24 02:52:00 2024 +0100 Bump tox from 4.18.1 to 4.20.0 (kubernetes-sigs#11562) Bumps [tox](https://github.com/tox-dev/tox) from 4.18.1 to 4.20.0. - [Release notes](https://github.com/tox-dev/tox/releases) - [Changelog](https://github.com/tox-dev/tox/blob/main/docs/changelog.rst) - [Commits](tox-dev/tox@4.18.1...4.20.0) --- updated-dependencies: - dependency-name: tox dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> commit 1c0718b Author: Baargav <[email protected]> Date: Mon Sep 23 10:31:59 2024 -0400 update containerd 1.7.22 (kubernetes-sigs#11554) commit 03a055c Merge: e9d406e fe60832 Author: Kubernetes Prow Robot <[email protected]> Date: Mon Sep 23 14:16:00 2024 +0100 Merge pull request kubernetes-sigs#10643 from VannTen/cleanup/k8s_node_templates Refactor kubernetes/node templates commit e9d406e Merge: 99c6a88 1818993 Author: Kubernetes Prow Robot <[email protected]> Date: Mon Sep 23 10:08:00 2024 +0100 Merge pull request kubernetes-sigs#11559 from VannTen/cleanup/less_inventory_boilerplate Only require minimum structure in inventory, compute the rest commit 99c6a88 Author: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon Sep 23 09:32:00 2024 +0100 Bump ansible-lint from 24.9.0 to 24.9.2 (kubernetes-sigs#11563) Bumps [ansible-lint](https://github.com/ansible/ansible-lint) from 24.9.0 to 24.9.2. - [Release notes](https://github.com/ansible/ansible-lint/releases) - [Commits](ansible/ansible-lint@v24.9.0...v24.9.2) --- updated-dependencies: - dependency-name: ansible-lint dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> commit 1818993 Author: Max Gautier <[email protected]> Date: Sat Sep 21 15:10:59 2024 +0200 CI: for upgrade testing, checkout old version before provisioning Otherwise, modifying the test inventory + it's expected structure will always fail. commit 88b6f08 Author: Max Gautier <[email protected]> Date: Sat Sep 21 14:33:45 2024 +0200 Documentation of k8s_cluster auto-defined Also remove the group from the example inventory, since it should not be needed anymore. commit 7580e59 Author: Max Gautier <[email protected]> Date: Sat Sep 21 14:16:17 2024 +0200 Define k8s_cluster dynamically This allows inventories to not define the k8s_cluster group manually. commit 2ec1c93 Author: Max Gautier <[email protected]> Date: Sat Sep 21 14:09:09 2024 +0200 Test group membership with group_names Testing for group membership with group names makes Kubespray more tolerant towards the structure of the inventory. Where 'inventory_hostname in groups["some_group"] would fail if "some_group" is not defined, '"some_group" in group_names' would not. commit 89ff071 Author: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri Sep 20 10:45:58 2024 +0100 Bump ansible-lint from 24.7.0 to 24.9.0 (kubernetes-sigs#11541) Bumps [ansible-lint](https://github.com/ansible/ansible-lint) from 24.7.0 to 24.9.0. - [Release notes](https://github.com/ansible/ansible-lint/releases) - [Commits](ansible/ansible-lint@v24.7.0...v24.9.0) --- updated-dependencies: - dependency-name: ansible-lint dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> commit 1fa4bb7 Author: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri Sep 20 09:33:39 2024 +0100 Bump ansible from 9.8.0 to 9.10.0 (kubernetes-sigs#11540) Bumps [ansible](https://github.com/ansible-community/ansible-build-data) from 9.8.0 to 9.10.0. - [Changelog](https://github.com/ansible-community/ansible-build-data/blob/main/docs/release-process.md) - [Commits](ansible-community/ansible-build-data@9.8.0...9.10.0) --- updated-dependencies: - dependency-name: ansible dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> commit 93ee122 Merge: 4323e5d d54356e Author: Kubernetes Prow Robot <[email protected]> Date: Fri Sep 20 01:05:14 2024 +0100 Merge pull request kubernetes-sigs#11521 from VannTen/cleanup/deduplicate_kubeadm_control_plane Use in inventory variables rather than patch files for kubeadm_patches commit 4323e5d Author: Leeon Fu <[email protected]> Date: Fri Sep 20 02:14:37 2024 +0800 Reset operation, disable service enabled and clean container's logs. (kubernetes-sigs#11501) commit 1636979 Merge: 893e9cb 4bf2d7a Author: Kubernetes Prow Robot <[email protected]> Date: Thu Sep 19 13:46:45 2024 +0100 Merge pull request kubernetes-sigs#11527 from VannTen/feat/vagrant_multi_inv Cleanup Vagrantfile and allow to use multiples inventories commit 893e9cb Merge: 76c42b4 5b057c7 Author: Kubernetes Prow Robot <[email protected]> Date: Wed Sep 18 13:18:44 2024 +0100 Merge pull request kubernetes-sigs#11471 from VannTen/feat/config_plugin_list Update the list of admission plugins which needs config commit 76c42b4 Author: Max Gautier <[email protected]> Date: Wed Sep 18 14:04:50 2024 +0200 CI: cleanup '-scale' tests infra (kubernetes-sigs#11535) There is actually no test using this since ad6fece, so there is no reason to keep that infra in our tests scripts. commit b3b0077 Author: Max Gautier <[email protected]> Date: Wed Sep 18 14:04:44 2024 +0200 Remove unused test infrastructure (kubernetes-sigs#11529) These two files haven't been touched since 2016 and don't appear to be referenced anywhere else commit e550118 Author: Qasim Mehmood <[email protected]> Date: Wed Sep 18 15:54:44 2024 +0500 Allow setting annotations on ingress-nginx service (kubernetes-sigs#11544) commit c3de25c Author: Max Gautier <[email protected]> Date: Wed Sep 18 02:34:45 2024 +0200 Move the CRI endpoint setting to kubelet config (kubernetes-sigs#11550) The `--container-runtime-endpoint` kubelet argument is deprecated in favor of the config file alternative. commit 59dd713 Author: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon Sep 16 08:31:14 2024 +0100 Bump molecule from 24.8.0 to 24.9.0 (kubernetes-sigs#11542) Bumps [molecule](https://github.com/ansible-community/molecule) from 24.8.0 to 24.9.0. - [Release notes](https://github.com/ansible-community/molecule/releases) - [Commits](ansible/molecule@v24.8.0...v24.9.0) --- updated-dependencies: - dependency-name: molecule dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> commit e5d2452 Author: Kay Yan <[email protected]> Date: Sun Sep 15 20:59:13 2024 +0800 fix elastx cleanup security groups (kubernetes-sigs#11411) commit 8cb081a Author: ChengHao Yang <[email protected]> Date: Sun Sep 15 00:35:13 2024 +0800 Fix: download hash crictl link (kubernetes-sigs#11534) Signed-off-by: ChengHao Yang <[email protected]> commit 4bf2d7a Author: Max Gautier <[email protected]> Date: Thu Sep 12 16:44:30 2024 +0200 Rework vagrant documentation - Use proper syntax highlighting for config.rb examples - Consistent shell style ($ as prompt) - Use only one way to do things - Remove OS specific details commit 1e769b7 Author: Max Gautier <[email protected]> Date: Thu Sep 12 13:56:12 2024 +0200 Rework vagrant inventory handling + support for multiples inventories The current way to handle a custom inventory in vagrant is a bit hackish, copy files around and can break Vagrantfile parsing in cornercase scenarios (removing vagrant inventories, or the inventory copied into vagrant inventory). Instead, simply pass additional inventories to the ansible-playbook command lines as raw arguments with `-i`. This also makes supporting multiples inventories trivial, so we add a new `$inventories` variable for that purpose. commit 8d8d063 Author: Max Gautier <[email protected]> Date: Fri Sep 13 03:31:12 2024 +0000 Remove useless file (kubernetes-sigs#11526) This was somehow copied from vagrant docs, not sure why it's there... commit c601c8f Author: Kevin Huang <[email protected]> Date: Thu Sep 12 23:21:12 2024 +0200 fix: Swap kubespray-defaults & boostrap-os (kubernetes-sigs#11441) - Execute boostrap-os before so that Python is installed for kubespray-defaults - Remove outdated kubespray-defaults dependency on boostrap-os commit 5ae433b Author: Max Gautier <[email protected]> Date: Thu Sep 12 10:01:13 2024 +0000 Remove refs to rhel7 in Vagrantfile (kubernetes-sigs#11525) commit d54356e Author: Max Gautier <[email protected]> Date: Tue Sep 10 17:38:56 2024 +0200 Add testcase for kubeadm_patches commit c87097f Author: Max Gautier <[email protected]> Date: Tue Sep 10 17:34:04 2024 +0200 Document how to use kubeadm patches commit 4e6ae04 Merge: d54cfba 230cb37 Author: Kubernetes Prow Robot <[email protected]> Date: Thu Sep 12 06:51:12 2024 +0100 Merge pull request kubernetes-sigs#11513 from VannTen/feat/python_download Refactor and expand download_hash.py commit 8e254ec Author: Max Gautier <[email protected]> Date: Tue Sep 10 12:00:26 2024 +0200 kubeadm: allow to provide patch inline in inventories Specifying one directory for kubeadm patches is not ideal: 1. It does not allow working with multiples inventories easily 2. No ansible templating of the patch 3. Ansible path searching can sometimes be confusing Instead, provide the patch directly in a variable, and add some quality of life to handle components targeting and patch ordering more explicitly (`target` and `type` which are translated to the kubeadm scheme which is based on the file name) commit a8b66fd Author: Max Gautier <[email protected]> Date: Tue Sep 10 13:43:02 2024 +0200 Deduplicate kubeadm_patches tasks kubernetes/control-plane and kubernetes/kubeadm roles both push kubeadm patches in the same way. Extract that code and make it a dependency of both. This is safe because it's only configuration for kubeadm, which only takes effect when kubeadm is run. commit 230cb37 Author: Max Gautier <[email protected]> Date: Sun Sep 8 11:12:24 2024 +0200 download_hash: document usage commit dec4e71 Author: Max Gautier <[email protected]> Date: Fri Sep 6 16:21:20 2024 +0200 download_hash: cache request for 'multi-hash' files This avoid re-downloading the same file for different arch and re-parsing it commit 1b1045c Author: Max Gautier <[email protected]> Date: Fri Sep 6 15:25:53 2024 +0200 download_hash: use persistent session This allows to reuse http connection and be more efficient. From rough measuring it saves around 25-30% of execution time. commit 86855be Author: Max Gautier <[email protected]> Date: Fri Sep 6 10:56:03 2024 +0200 download_hash: document missing support commit b2e64ae Author: Max Gautier <[email protected]> Date: Thu Sep 5 17:15:12 2024 +0200 download_hash: support 'multi-hash' components commit a2644c7 Author: Max Gautier <[email protected]> Date: Thu Sep 5 16:19:09 2024 +0200 download_hash: add support for 'simple hash' components commit e256f74 Author: Max Gautier <[email protected]> Date: Thu Sep 5 16:39:04 2024 +0200 download_hash: propagate new patch versions to all archs commit 2710e98 Author: Max Gautier <[email protected]> Date: Thu Sep 5 15:58:36 2024 +0200 download_hash: argument handling with argparse Allow the script to be called with a list of components, to only download new versions checksums for those. By default, we get new versions checksums for all supported (by the script) components. commit da0e445 Author: Max Gautier <[email protected]> Date: Fri Feb 2 20:48:08 2024 +0100 download_hash.py: support for 'multi-hash' file + runc runc upstream does not provide one hash file per assets in their releases, but one file with all the hashes. To handle this (and/or any arbitrary format from upstreams), add a dictionary mapping the name of the download to a lambda function which transform the file provided by upstream into a dictionary of hashes, keyed by architecture. commit a761623 Author: Max Gautier <[email protected]> Date: Fri Feb 2 16:01:14 2024 +0100 download_hash.py: generalized and data-driven The script is currently limited to one hardcoded URL for kubernetes related binaries, and a fixed set of architectures. The solution is three-fold: 1. Use an url template dictionary for each download -> this allow to easily add support for new downloads. 2. Source the architectures to search from the existing data 3. Enumerate the existing versions in the data and start searching from the last one until no newer version is found (newer in the version order sense, irrespective of actual age) commit fe60832 Author: Max Gautier <[email protected]> Date: Thu Nov 23 21:13:55 2023 +0100 Remove kubelet_node_{custom_flags,config_extra_args} There is no need to have an extra variables for this, just use different values per host (using Ansible group_vars, for example) commit 1bc61c9 Author: Max Gautier <[email protected]> Date: Thu Nov 23 17:18:47 2023 +0100 Simplify kubelet-config template Remove system|kube_master_<resource>_reserved variables. Those variables are unnecessary because users can simply use the variables in group_vars if they which to differentiate control plane nodes from other nodes. Set conservative defaults for ephemeral-storage and pids for both kube and system reserved resources. commit 872d717 Author: Max Gautier <[email protected]> Date: Wed Sep 4 13:44:11 2024 +0200 Add kube|system_reserved CI testing commit 1533d40 Author: Max Gautier <[email protected]> Date: Wed Sep 4 14:02:53 2024 +0200 Fix kube_reserved_cgroups_for_service_slice The default value is used across kubespray but only defined in kubernetes/node. Move it to kubespray-defaults commit 5b057c7 Author: Max Gautier <[email protected]> Date: Mon Aug 26 14:53:20 2024 +0200 Update list of admission plugins with a config file commit d340273 Author: Max Gautier <[email protected]> Date: Mon Aug 26 14:43:00 2024 +0200 Remove special case for PodNodeSelector This is already handled by the previous task. commit 47c3949 Author: Max Gautier <[email protected]> Date: Mon Aug 26 14:42:04 2024 +0200 Change plugins_needs_config list format Makes easier diff when adding or removing plugins.
What type of PR is this?
/kind feature
What this PR does / why we need it:
This reworks the Python version of the download script making it much more
powerful and versatile and supporting most of the components we download.
It's intended to be the first step in completely replacing download_hash.sh.
It's missing some things for that (see 86855be) that I will address in later PRs.
It has the following advantages over download_hash.sh in particular:
downloads
variable, which allow to avoid lots of '0' value.This means it will have to handle different hash format, and crypto signing, certainly (gpg + cosign for k8s binaries).
I've also started asking upstream project which don't provide any of these if they would kindly do so (that's just youki for now)
$ time python download_hash.py
...
real 1m26.888s
user 0m0.827s
sys 0m0.058s
$ time python download_hash.sh
...
real 14m32.278s
user 0m41.258s
sys 0m29.733s
Special notes for your reviewer:
My python code is pretty functional (in the Haskell sense, lambdas and
comprehensions all other the place), you've been warned ^.
I didn't mark this as draft because it does not affect end-users, but there is still things I'm unsure about:
Does this PR introduce a user-facing change?:
/label tide/merge-method-merge
/cc @MrFreezeex
/cc @mzaian