From 4e0ad0a71564fd1d6db16f90a6ab5924f44e57af Mon Sep 17 00:00:00 2001 From: Meni Yakove Date: Thu, 21 Nov 2024 14:28:16 +0200 Subject: [PATCH 01/31] Set owners per folder --- OWNERS | 11 +- README.md | 11 +- pyproject.toml | 3 +- uv.lock | 257 ++++++++++---------- webhook_server_container/OWNERS | 4 + webhook_server_container/libs/github_api.py | 65 ++--- 6 files changed, 176 insertions(+), 175 deletions(-) create mode 100644 webhook_server_container/OWNERS diff --git a/OWNERS b/OWNERS index aa367105..83a727f9 100644 --- a/OWNERS +++ b/OWNERS @@ -2,12 +2,5 @@ approvers: - myakove - rnetser reviewers: - any: - - myakove - - rnetser - files: - Dockerfile: - - myakove - folders: - github-webhook-server: - - myakove + - myakove + - rnetser diff --git a/README.md b/README.md index 431222bb..83983dee 100644 --- a/README.md +++ b/README.md @@ -242,15 +242,8 @@ approvers: - myakove - rnetser reviewers: - any: # will be added to all pull requests - - myakove - - rnetser - files: # will be added to pull requests if files in the list are changed - Dockerfile: - - myakove - folders: # will be added to pull requests if folders in the list are changed - webhook_server_container/libs: # path is relative to the repository root - - myakove + - myakove + - rnetser ``` ### Supported user actions via adding comment diff --git a/pyproject.toml b/pyproject.toml index b1745247..9e51db72 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -25,7 +25,7 @@ dev-dependencies = ["ipdb>=0.13.13", "ipython>=8.12.3"] [project] name = "github-webhook-server" version = "2.0.0" -requires-python = ">=3.9" +requires-python = ">=3.9, <3.12" description = "A webhook server to manage Github reposotories and pull requests." readme = "README.md" license = "Apache-2.0" @@ -38,6 +38,7 @@ dependencies = [ "colorama>=0.4.6", "colorlog>=6.8.2", "fastapi>=0.115.0", + "ipdb>=0.13.13", "jira>=3.8.0", "pygithub>=2.4.0", "pyhelper-utils>=0.0.42", diff --git a/uv.lock b/uv.lock index 67300700..d01c5eb5 100644 --- a/uv.lock +++ b/uv.lock @@ -1,9 +1,8 @@ version = 1 -requires-python = ">=3.9" +requires-python = ">=3.9, <3.12" resolution-markers = [ "python_full_version < '3.11'", - "python_full_version >= '3.11' and python_full_version < '3.13'", - "python_full_version >= '3.13'", + "python_full_version >= '3.11'", ] [[package]] @@ -44,36 +43,34 @@ wheels = [ [[package]] name = "bcrypt" -version = "4.2.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e4/7e/d95e7d96d4828e965891af92e43b52a4cd3395dc1c1ef4ee62748d0471d0/bcrypt-4.2.0.tar.gz", hash = "sha256:cf69eaf5185fd58f268f805b505ce31f9b9fc2d64b376642164e9244540c1221", size = 24294 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a9/81/4e8f5bc0cd947e91fb720e1737371922854da47a94bc9630454e7b2845f8/bcrypt-4.2.0-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:096a15d26ed6ce37a14c1ac1e48119660f21b24cba457f160a4b830f3fe6b5cb", size = 471568 }, - { url = "https://files.pythonhosted.org/packages/05/d2/1be1e16aedec04bcf8d0156e01b987d16a2063d38e64c3f28030a3427d61/bcrypt-4.2.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c02d944ca89d9b1922ceb8a46460dd17df1ba37ab66feac4870f6862a1533c00", size = 277372 }, - { url = "https://files.pythonhosted.org/packages/e3/96/7a654027638ad9b7589effb6db77eb63eba64319dfeaf9c0f4ca953e5f76/bcrypt-4.2.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1d84cf6d877918620b687b8fd1bf7781d11e8a0998f576c7aa939776b512b98d", size = 273488 }, - { url = "https://files.pythonhosted.org/packages/46/54/dc7b58abeb4a3d95bab653405935e27ba32f21b812d8ff38f271fb6f7f55/bcrypt-4.2.0-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:1bb429fedbe0249465cdd85a58e8376f31bb315e484f16e68ca4c786dcc04291", size = 277759 }, - { url = "https://files.pythonhosted.org/packages/ac/be/da233c5f11fce3f8adec05e8e532b299b64833cc962f49331cdd0e614fa9/bcrypt-4.2.0-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:655ea221910bcac76ea08aaa76df427ef8625f92e55a8ee44fbf7753dbabb328", size = 273796 }, - { url = "https://files.pythonhosted.org/packages/b0/b8/8b4add88d55a263cf1c6b8cf66c735280954a04223fcd2880120cc767ac3/bcrypt-4.2.0-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:1ee38e858bf5d0287c39b7a1fc59eec64bbf880c7d504d3a06a96c16e14058e7", size = 311082 }, - { url = "https://files.pythonhosted.org/packages/7b/76/2aa660679abbdc7f8ee961552e4bb6415a81b303e55e9374533f22770203/bcrypt-4.2.0-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:0da52759f7f30e83f1e30a888d9163a81353ef224d82dc58eb5bb52efcabc399", size = 305912 }, - { url = "https://files.pythonhosted.org/packages/00/03/2af7c45034aba6002d4f2b728c1a385676b4eab7d764410e34fd768009f2/bcrypt-4.2.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:3698393a1b1f1fd5714524193849d0c6d524d33523acca37cd28f02899285060", size = 325185 }, - { url = "https://files.pythonhosted.org/packages/dc/5d/6843443ce4ab3af40bddb6c7c085ed4a8418b3396f7a17e60e6d9888416c/bcrypt-4.2.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:762a2c5fb35f89606a9fde5e51392dad0cd1ab7ae64149a8b935fe8d79dd5ed7", size = 335188 }, - { url = "https://files.pythonhosted.org/packages/cb/4c/ff8ca83d816052fba36def1d24e97d9a85739b9bbf428c0d0ecd296a07c8/bcrypt-4.2.0-cp37-abi3-win32.whl", hash = "sha256:5a1e8aa9b28ae28020a3ac4b053117fb51c57a010b9f969603ed885f23841458", size = 156481 }, - { url = "https://files.pythonhosted.org/packages/65/f1/e09626c88a56cda488810fb29d5035f1662873777ed337880856b9d204ae/bcrypt-4.2.0-cp37-abi3-win_amd64.whl", hash = "sha256:8f6ede91359e5df88d1f5c1ef47428a4420136f3ce97763e31b86dd8280fbdf5", size = 151336 }, - { url = "https://files.pythonhosted.org/packages/96/86/8c6a84daed4dd878fbab094400c9174c43d9b838ace077a2f8ee8bc3ae12/bcrypt-4.2.0-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:c52aac18ea1f4a4f65963ea4f9530c306b56ccd0c6f8c8da0c06976e34a6e841", size = 472414 }, - { url = "https://files.pythonhosted.org/packages/f6/05/e394515f4e23c17662e5aeb4d1859b11dc651be01a3bd03c2e919a155901/bcrypt-4.2.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3bbbfb2734f0e4f37c5136130405332640a1e46e6b23e000eeff2ba8d005da68", size = 277599 }, - { url = "https://files.pythonhosted.org/packages/4b/3b/ad784eac415937c53da48983756105d267b91e56aa53ba8a1b2014b8d930/bcrypt-4.2.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3413bd60460f76097ee2e0a493ccebe4a7601918219c02f503984f0a7ee0aebe", size = 273491 }, - { url = "https://files.pythonhosted.org/packages/cc/14/b9ff8e0218bee95e517b70e91130effb4511e8827ac1ab00b4e30943a3f6/bcrypt-4.2.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:8d7bb9c42801035e61c109c345a28ed7e84426ae4865511eb82e913df18f58c2", size = 277934 }, - { url = "https://files.pythonhosted.org/packages/3e/d0/31938bb697600a04864246acde4918c4190a938f891fd11883eaaf41327a/bcrypt-4.2.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3d3a6d28cb2305b43feac298774b997e372e56c7c7afd90a12b3dc49b189151c", size = 273804 }, - { url = "https://files.pythonhosted.org/packages/e7/c3/dae866739989e3f04ae304e1201932571708cb292a28b2f1b93283e2dcd8/bcrypt-4.2.0-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:9c1c4ad86351339c5f320ca372dfba6cb6beb25e8efc659bedd918d921956bae", size = 311275 }, - { url = "https://files.pythonhosted.org/packages/5d/2c/019bc2c63c6125ddf0483ee7d914a405860327767d437913942b476e9c9b/bcrypt-4.2.0-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:27fe0f57bb5573104b5a6de5e4153c60814c711b29364c10a75a54bb6d7ff48d", size = 306355 }, - { url = "https://files.pythonhosted.org/packages/75/fe/9e137727f122bbe29771d56afbf4e0dbc85968caa8957806f86404a5bfe1/bcrypt-4.2.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:8ac68872c82f1add6a20bd489870c71b00ebacd2e9134a8aa3f98a0052ab4b0e", size = 325381 }, - { url = "https://files.pythonhosted.org/packages/1a/d4/586b9c18a327561ea4cd336ff4586cca1a7aa0f5ee04e23a8a8bb9ca64f1/bcrypt-4.2.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:cb2a8ec2bc07d3553ccebf0746bbf3d19426d1c6d1adbd4fa48925f66af7b9e8", size = 335685 }, - { url = "https://files.pythonhosted.org/packages/24/55/1a7127faf4576138bb278b91e9c75307490178979d69c8e6e273f74b974f/bcrypt-4.2.0-cp39-abi3-win32.whl", hash = "sha256:77800b7147c9dc905db1cba26abe31e504d8247ac73580b4aa179f98e6608f34", size = 155857 }, - { url = "https://files.pythonhosted.org/packages/1c/2a/c74052e54162ec639266d91539cca7cbf3d1d3b8b36afbfeaee0ea6a1702/bcrypt-4.2.0-cp39-abi3-win_amd64.whl", hash = "sha256:61ed14326ee023917ecd093ee6ef422a72f3aec6f07e21ea5f10622b735538a9", size = 151717 }, - { url = "https://files.pythonhosted.org/packages/09/97/01026e7b1b7f8aeb41514408eca1137c0f8aef9938335e3bc713f82c282e/bcrypt-4.2.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:39e1d30c7233cfc54f5c3f2c825156fe044efdd3e0b9d309512cc514a263ec2a", size = 275924 }, - { url = "https://files.pythonhosted.org/packages/ca/46/03eb26ea3e9c12ca18d1f3bf06199f7d72ce52e68f2a1ebcfd8acff9c472/bcrypt-4.2.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:f4f4acf526fcd1c34e7ce851147deedd4e26e6402369304220250598b26448db", size = 272242 }, - { url = "https://files.pythonhosted.org/packages/73/5a/811c3c7af3be99888f39ee8845ddf849d2a03a83049d63ece5dfb4612f4d/bcrypt-4.2.0-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:1ff39b78a52cf03fdf902635e4c81e544714861ba3f0efc56558979dd4f09170", size = 278107 }, - { url = "https://files.pythonhosted.org/packages/8b/79/76a139d1b9f11aa4afcb7ceb882d2e81003667681711f2fe8a302c4c10ca/bcrypt-4.2.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:373db9abe198e8e2c70d12b479464e0d5092cc122b20ec504097b5f2297ed184", size = 274081 }, +version = "4.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/56/8c/dd696962612e4cd83c40a9e6b3db77bfe65a830f4b9af44098708584686c/bcrypt-4.2.1.tar.gz", hash = "sha256:6765386e3ab87f569b276988742039baab087b2cdb01e809d74e74503c2faafe", size = 24427 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bc/ca/e17b08c523adb93d5f07a226b2bd45a7c6e96b359e31c1e99f9db58cb8c3/bcrypt-4.2.1-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:1340411a0894b7d3ef562fb233e4b6ed58add185228650942bdc885362f32c17", size = 489982 }, + { url = "https://files.pythonhosted.org/packages/6a/be/e7c6e0fd6087ee8fc6d77d8d9e817e9339d879737509019b9a9012a1d96f/bcrypt-4.2.1-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1ee315739bc8387aa36ff127afc99120ee452924e0df517a8f3e4c0187a0f5f", size = 273108 }, + { url = "https://files.pythonhosted.org/packages/d6/53/ac084b7d985aee1a5f2b086d501f550862596dbf73220663b8c17427e7f2/bcrypt-4.2.1-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8dbd0747208912b1e4ce730c6725cb56c07ac734b3629b60d4398f082ea718ad", size = 278733 }, + { url = "https://files.pythonhosted.org/packages/8e/ab/b8710a3d6231c587e575ead0b1c45bb99f5454f9f579c9d7312c17b069cc/bcrypt-4.2.1-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:aaa2e285be097050dba798d537b6efd9b698aa88eef52ec98d23dcd6d7cf6fea", size = 273856 }, + { url = "https://files.pythonhosted.org/packages/9d/e5/2fd1ea6395358ffdfd4afe370d5b52f71408f618f781772a48971ef3b92b/bcrypt-4.2.1-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:76d3e352b32f4eeb34703370e370997065d28a561e4a18afe4fef07249cb4396", size = 279067 }, + { url = "https://files.pythonhosted.org/packages/4e/ef/f2cb7a0f7e1ed800a604f8ab256fb0afcf03c1540ad94ff771ce31e794aa/bcrypt-4.2.1-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:b7703ede632dc945ed1172d6f24e9f30f27b1b1a067f32f68bf169c5f08d0425", size = 306851 }, + { url = "https://files.pythonhosted.org/packages/de/cb/578b0023c6a5ca16a177b9044ba6bd6032277bd3ef020fb863eccd22e49b/bcrypt-4.2.1-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:89df2aea2c43be1e1fa066df5f86c8ce822ab70a30e4c210968669565c0f4685", size = 310793 }, + { url = "https://files.pythonhosted.org/packages/98/bc/9d501ee9d754f63d4b1086b64756c284facc3696de9b556c146279a124a5/bcrypt-4.2.1-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:04e56e3fe8308a88b77e0afd20bec516f74aecf391cdd6e374f15cbed32783d6", size = 320957 }, + { url = "https://files.pythonhosted.org/packages/a1/25/2ec4ce5740abc43182bfc064b9acbbf5a493991246985e8b2bfe231ead64/bcrypt-4.2.1-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:cfdf3d7530c790432046c40cda41dfee8c83e29482e6a604f8930b9930e94139", size = 339958 }, + { url = "https://files.pythonhosted.org/packages/6d/64/fd67788f64817727897d31e9cdeeeba3941eaad8540733c05c7eac4aa998/bcrypt-4.2.1-cp37-abi3-win32.whl", hash = "sha256:adadd36274510a01f33e6dc08f5824b97c9580583bd4487c564fc4617b328005", size = 160912 }, + { url = "https://files.pythonhosted.org/packages/00/8f/fe834eaa54abbd7cab8607e5020fa3a0557e929555b9e4ca404b4adaab06/bcrypt-4.2.1-cp37-abi3-win_amd64.whl", hash = "sha256:8c458cd103e6c5d1d85cf600e546a639f234964d0228909d8f8dbeebff82d526", size = 152981 }, + { url = "https://files.pythonhosted.org/packages/4a/57/23b46933206daf5384b5397d9878746d2249fe9d45efaa8e1467c87d3048/bcrypt-4.2.1-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:8ad2f4528cbf0febe80e5a3a57d7a74e6635e41af1ea5675282a33d769fba413", size = 489842 }, + { url = "https://files.pythonhosted.org/packages/fd/28/3ea8a39ddd4938b6c6b6136816d72ba5e659e2d82b53d843c8c53455ac4d/bcrypt-4.2.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:909faa1027900f2252a9ca5dfebd25fc0ef1417943824783d1c8418dd7d6df4a", size = 272500 }, + { url = "https://files.pythonhosted.org/packages/77/7f/b43622999f5d4de06237a195ac5501ac83516adf571b907228cd14bac8fe/bcrypt-4.2.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cde78d385d5e93ece5479a0a87f73cd6fa26b171c786a884f955e165032b262c", size = 278368 }, + { url = "https://files.pythonhosted.org/packages/50/68/f2e3959014b4d8874c747e6e171d46d3e63a3a39aaca8417a8d837eda0a8/bcrypt-4.2.1-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:533e7f3bcf2f07caee7ad98124fab7499cb3333ba2274f7a36cf1daee7409d99", size = 273335 }, + { url = "https://files.pythonhosted.org/packages/d6/c3/4b4bad4da852924427c651589d464ad1aa624f94dd904ddda8493b0a35e5/bcrypt-4.2.1-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:687cf30e6681eeda39548a93ce9bfbb300e48b4d445a43db4298d2474d2a1e54", size = 278614 }, + { url = "https://files.pythonhosted.org/packages/6e/5a/ee107961e84c41af2ac201d0460f962b6622ff391255ffd46429e9e09dc1/bcrypt-4.2.1-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:041fa0155c9004eb98a232d54da05c0b41d4b8e66b6fc3cb71b4b3f6144ba837", size = 306464 }, + { url = "https://files.pythonhosted.org/packages/5c/72/916e14fa12d2b1d1fc6c26ea195337419da6dd23d0bf53ac61ef3739e5c5/bcrypt-4.2.1-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:f85b1ffa09240c89aa2e1ae9f3b1c687104f7b2b9d2098da4e923f1b7082d331", size = 310674 }, + { url = "https://files.pythonhosted.org/packages/97/92/3dc76d8bfa23300591eec248e950f85bd78eb608c96bd4747ce4cc06acdb/bcrypt-4.2.1-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:c6f5fa3775966cca251848d4d5393ab016b3afed251163c1436fefdec3b02c84", size = 320577 }, + { url = "https://files.pythonhosted.org/packages/5d/ab/a6c0da5c2cf86600f74402a72b06dfe365e1a1d30783b1bbeec460fd57d1/bcrypt-4.2.1-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:807261df60a8b1ccd13e6599c779014a362ae4e795f5c59747f60208daddd96d", size = 339836 }, + { url = "https://files.pythonhosted.org/packages/b4/b4/e75b6e9a72a030a04362034022ebe317c5b735d04db6ad79237101ae4a5c/bcrypt-4.2.1-cp39-abi3-win32.whl", hash = "sha256:b588af02b89d9fad33e5f98f7838bf590d6d692df7153647724a7f20c186f6bf", size = 160911 }, + { url = "https://files.pythonhosted.org/packages/76/b9/d51d34e6cd6d887adddb28a8680a1d34235cc45b9d6e238ce39b98199ca0/bcrypt-4.2.1-cp39-abi3-win_amd64.whl", hash = "sha256:e84e0e6f8e40a242b11bce56c313edc2be121cec3e0ec2d76fce01f6af33c07c", size = 153078 }, + { url = "https://files.pythonhosted.org/packages/4e/6e/7193067042de23af3d71882f898c8c0bd2b18e6ee44a4f76e395dfadb5a8/bcrypt-4.2.1-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:76132c176a6d9953cdc83c296aeaed65e1a708485fd55abf163e0d9f8f16ce0e", size = 270069 }, + { url = "https://files.pythonhosted.org/packages/3b/05/2546085c6dc07a45627460a39e6291b82382b434fff2bd0167ff3bc31eb1/bcrypt-4.2.1-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:e158009a54c4c8bc91d5e0da80920d048f918c61a581f0a63e4e93bb556d362f", size = 274652 }, ] [[package]] @@ -408,6 +405,7 @@ dependencies = [ { name = "colorama" }, { name = "colorlog" }, { name = "fastapi" }, + { name = "ipdb" }, { name = "jira" }, { name = "pygithub" }, { name = "pyhelper-utils" }, @@ -436,6 +434,7 @@ requires-dist = [ { name = "colorama", specifier = ">=0.4.6" }, { name = "colorlog", specifier = ">=6.8.2" }, { name = "fastapi", specifier = ">=0.115.0" }, + { name = "ipdb", specifier = ">=0.13.13" }, { name = "jira", specifier = ">=3.8.0" }, { name = "pygithub", specifier = ">=2.4.0" }, { name = "pyhelper-utils", specifier = ">=0.0.42" }, @@ -799,103 +798,113 @@ wheels = [ [[package]] name = "pydantic" -version = "2.9.2" +version = "2.10.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "annotated-types" }, { name = "pydantic-core" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a9/b7/d9e3f12af310e1120c21603644a1cd86f59060e040ec5c3a80b8f05fae30/pydantic-2.9.2.tar.gz", hash = "sha256:d155cef71265d1e9807ed1c32b4c8deec042a44a50a4188b25ac67ecd81a9c0f", size = 769917 } +sdist = { url = "https://files.pythonhosted.org/packages/e9/78/58c36d0cf331b659d0ccd99175e3523c457b4f8e67cb92a8fdc22ec1667c/pydantic-2.10.0.tar.gz", hash = "sha256:0aca0f045ff6e2f097f1fe89521115335f15049eeb8a7bef3dafe4b19a74e289", size = 781980 } wheels = [ - { url = "https://files.pythonhosted.org/packages/df/e4/ba44652d562cbf0bf320e0f3810206149c8a4e99cdbf66da82e97ab53a15/pydantic-2.9.2-py3-none-any.whl", hash = "sha256:f048cec7b26778210e28a0459867920654d48e5e62db0958433636cde4254f12", size = 434928 }, + { url = "https://files.pythonhosted.org/packages/9e/ee/255cbfdbf5c47650de70ac8a5425107511f505ed0366c29d537f7f1842e1/pydantic-2.10.0-py3-none-any.whl", hash = "sha256:5e7807ba9201bdf61b1b58aa6eb690916c40a47acfb114b1b4fef3e7fd5b30fc", size = 454346 }, ] [[package]] name = "pydantic-core" -version = "2.23.4" +version = "2.27.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e2/aa/6b6a9b9f8537b872f552ddd46dd3da230367754b6f707b8e1e963f515ea3/pydantic_core-2.23.4.tar.gz", hash = "sha256:2584f7cf844ac4d970fba483a717dbe10c1c1c96a969bf65d61ffe94df1b2863", size = 402156 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5c/8b/d3ae387f66277bd8104096d6ec0a145f4baa2966ebb2cad746c0920c9526/pydantic_core-2.23.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:b10bd51f823d891193d4717448fab065733958bdb6a6b351967bd349d48d5c9b", size = 1867835 }, - { url = "https://files.pythonhosted.org/packages/46/76/f68272e4c3a7df8777798282c5e47d508274917f29992d84e1898f8908c7/pydantic_core-2.23.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4fc714bdbfb534f94034efaa6eadd74e5b93c8fa6315565a222f7b6f42ca1166", size = 1776689 }, - { url = "https://files.pythonhosted.org/packages/cc/69/5f945b4416f42ea3f3bc9d2aaec66c76084a6ff4ff27555bf9415ab43189/pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63e46b3169866bd62849936de036f901a9356e36376079b05efa83caeaa02ceb", size = 1800748 }, - { url = "https://files.pythonhosted.org/packages/50/ab/891a7b0054bcc297fb02d44d05c50e68154e31788f2d9d41d0b72c89fdf7/pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed1a53de42fbe34853ba90513cea21673481cd81ed1be739f7f2efb931b24916", size = 1806469 }, - { url = "https://files.pythonhosted.org/packages/31/7c/6e3fa122075d78f277a8431c4c608f061881b76c2b7faca01d317ee39b5d/pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cfdd16ab5e59fc31b5e906d1a3f666571abc367598e3e02c83403acabc092e07", size = 2002246 }, - { url = "https://files.pythonhosted.org/packages/ad/6f/22d5692b7ab63fc4acbc74de6ff61d185804a83160adba5e6cc6068e1128/pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:255a8ef062cbf6674450e668482456abac99a5583bbafb73f9ad469540a3a232", size = 2659404 }, - { url = "https://files.pythonhosted.org/packages/11/ac/1e647dc1121c028b691028fa61a4e7477e6aeb5132628fde41dd34c1671f/pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a7cd62e831afe623fbb7aabbb4fe583212115b3ef38a9f6b71869ba644624a2", size = 2053940 }, - { url = "https://files.pythonhosted.org/packages/91/75/984740c17f12c3ce18b5a2fcc4bdceb785cce7df1511a4ce89bca17c7e2d/pydantic_core-2.23.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f09e2ff1f17c2b51f2bc76d1cc33da96298f0a036a137f5440ab3ec5360b624f", size = 1921437 }, - { url = "https://files.pythonhosted.org/packages/a0/74/13c5f606b64d93f0721e7768cd3e8b2102164866c207b8cd6f90bb15d24f/pydantic_core-2.23.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e38e63e6f3d1cec5a27e0afe90a085af8b6806ee208b33030e65b6516353f1a3", size = 1966129 }, - { url = "https://files.pythonhosted.org/packages/18/03/9c4aa5919457c7b57a016c1ab513b1a926ed9b2bb7915bf8e506bf65c34b/pydantic_core-2.23.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0dbd8dbed2085ed23b5c04afa29d8fd2771674223135dc9bc937f3c09284d071", size = 2110908 }, - { url = "https://files.pythonhosted.org/packages/92/2c/053d33f029c5dc65e5cf44ff03ceeefb7cce908f8f3cca9265e7f9b540c8/pydantic_core-2.23.4-cp310-none-win32.whl", hash = "sha256:6531b7ca5f951d663c339002e91aaebda765ec7d61b7d1e3991051906ddde119", size = 1735278 }, - { url = "https://files.pythonhosted.org/packages/de/81/7dfe464eca78d76d31dd661b04b5f2036ec72ea8848dd87ab7375e185c23/pydantic_core-2.23.4-cp310-none-win_amd64.whl", hash = "sha256:7c9129eb40958b3d4500fa2467e6a83356b3b61bfff1b414c7361d9220f9ae8f", size = 1917453 }, - { url = "https://files.pythonhosted.org/packages/5d/30/890a583cd3f2be27ecf32b479d5d615710bb926d92da03e3f7838ff3e58b/pydantic_core-2.23.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:77733e3892bb0a7fa797826361ce8a9184d25c8dffaec60b7ffe928153680ba8", size = 1865160 }, - { url = "https://files.pythonhosted.org/packages/1d/9a/b634442e1253bc6889c87afe8bb59447f106ee042140bd57680b3b113ec7/pydantic_core-2.23.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b84d168f6c48fabd1f2027a3d1bdfe62f92cade1fb273a5d68e621da0e44e6d", size = 1776777 }, - { url = "https://files.pythonhosted.org/packages/75/9a/7816295124a6b08c24c96f9ce73085032d8bcbaf7e5a781cd41aa910c891/pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df49e7a0861a8c36d089c1ed57d308623d60416dab2647a4a17fe050ba85de0e", size = 1799244 }, - { url = "https://files.pythonhosted.org/packages/a9/8f/89c1405176903e567c5f99ec53387449e62f1121894aa9fc2c4fdc51a59b/pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ff02b6d461a6de369f07ec15e465a88895f3223eb75073ffea56b84d9331f607", size = 1805307 }, - { url = "https://files.pythonhosted.org/packages/d5/a5/1a194447d0da1ef492e3470680c66048fef56fc1f1a25cafbea4bc1d1c48/pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:996a38a83508c54c78a5f41456b0103c30508fed9abcad0a59b876d7398f25fd", size = 2000663 }, - { url = "https://files.pythonhosted.org/packages/13/a5/1df8541651de4455e7d587cf556201b4f7997191e110bca3b589218745a5/pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d97683ddee4723ae8c95d1eddac7c192e8c552da0c73a925a89fa8649bf13eea", size = 2655941 }, - { url = "https://files.pythonhosted.org/packages/44/31/a3899b5ce02c4316865e390107f145089876dff7e1dfc770a231d836aed8/pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:216f9b2d7713eb98cb83c80b9c794de1f6b7e3145eef40400c62e86cee5f4e1e", size = 2052105 }, - { url = "https://files.pythonhosted.org/packages/1b/aa/98e190f8745d5ec831f6d5449344c48c0627ac5fed4e5340a44b74878f8e/pydantic_core-2.23.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6f783e0ec4803c787bcea93e13e9932edab72068f68ecffdf86a99fd5918878b", size = 1919967 }, - { url = "https://files.pythonhosted.org/packages/ae/35/b6e00b6abb2acfee3e8f85558c02a0822e9a8b2f2d812ea8b9079b118ba0/pydantic_core-2.23.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d0776dea117cf5272382634bd2a5c1b6eb16767c223c6a5317cd3e2a757c61a0", size = 1964291 }, - { url = "https://files.pythonhosted.org/packages/13/46/7bee6d32b69191cd649bbbd2361af79c472d72cb29bb2024f0b6e350ba06/pydantic_core-2.23.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d5f7a395a8cf1621939692dba2a6b6a830efa6b3cee787d82c7de1ad2930de64", size = 2109666 }, - { url = "https://files.pythonhosted.org/packages/39/ef/7b34f1b122a81b68ed0a7d0e564da9ccdc9a2924c8d6c6b5b11fa3a56970/pydantic_core-2.23.4-cp311-none-win32.whl", hash = "sha256:74b9127ffea03643e998e0c5ad9bd3811d3dac8c676e47db17b0ee7c3c3bf35f", size = 1732940 }, - { url = "https://files.pythonhosted.org/packages/2f/76/37b7e76c645843ff46c1d73e046207311ef298d3f7b2f7d8f6ac60113071/pydantic_core-2.23.4-cp311-none-win_amd64.whl", hash = "sha256:98d134c954828488b153d88ba1f34e14259284f256180ce659e8d83e9c05eaa3", size = 1916804 }, - { url = "https://files.pythonhosted.org/packages/74/7b/8e315f80666194b354966ec84b7d567da77ad927ed6323db4006cf915f3f/pydantic_core-2.23.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f3e0da4ebaef65158d4dfd7d3678aad692f7666877df0002b8a522cdf088f231", size = 1856459 }, - { url = "https://files.pythonhosted.org/packages/14/de/866bdce10ed808323d437612aca1ec9971b981e1c52e5e42ad9b8e17a6f6/pydantic_core-2.23.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f69a8e0b033b747bb3e36a44e7732f0c99f7edd5cea723d45bc0d6e95377ffee", size = 1770007 }, - { url = "https://files.pythonhosted.org/packages/dc/69/8edd5c3cd48bb833a3f7ef9b81d7666ccddd3c9a635225214e044b6e8281/pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:723314c1d51722ab28bfcd5240d858512ffd3116449c557a1336cbe3919beb87", size = 1790245 }, - { url = "https://files.pythonhosted.org/packages/80/33/9c24334e3af796ce80d2274940aae38dd4e5676298b4398eff103a79e02d/pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bb2802e667b7051a1bebbfe93684841cc9351004e2badbd6411bf357ab8d5ac8", size = 1801260 }, - { url = "https://files.pythonhosted.org/packages/a5/6f/e9567fd90104b79b101ca9d120219644d3314962caa7948dd8b965e9f83e/pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d18ca8148bebe1b0a382a27a8ee60350091a6ddaf475fa05ef50dc35b5df6327", size = 1996872 }, - { url = "https://files.pythonhosted.org/packages/2d/ad/b5f0fe9e6cfee915dd144edbd10b6e9c9c9c9d7a56b69256d124b8ac682e/pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33e3d65a85a2a4a0dc3b092b938a4062b1a05f3a9abde65ea93b233bca0e03f2", size = 2661617 }, - { url = "https://files.pythonhosted.org/packages/06/c8/7d4b708f8d05a5cbfda3243aad468052c6e99de7d0937c9146c24d9f12e9/pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:128585782e5bfa515c590ccee4b727fb76925dd04a98864182b22e89a4e6ed36", size = 2071831 }, - { url = "https://files.pythonhosted.org/packages/89/4d/3079d00c47f22c9a9a8220db088b309ad6e600a73d7a69473e3a8e5e3ea3/pydantic_core-2.23.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:68665f4c17edcceecc112dfed5dbe6f92261fb9d6054b47d01bf6371a6196126", size = 1917453 }, - { url = "https://files.pythonhosted.org/packages/e9/88/9df5b7ce880a4703fcc2d76c8c2d8eb9f861f79d0c56f4b8f5f2607ccec8/pydantic_core-2.23.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:20152074317d9bed6b7a95ade3b7d6054845d70584216160860425f4fbd5ee9e", size = 1968793 }, - { url = "https://files.pythonhosted.org/packages/e3/b9/41f7efe80f6ce2ed3ee3c2dcfe10ab7adc1172f778cc9659509a79518c43/pydantic_core-2.23.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9261d3ce84fa1d38ed649c3638feefeae23d32ba9182963e465d58d62203bd24", size = 2116872 }, - { url = "https://files.pythonhosted.org/packages/63/08/b59b7a92e03dd25554b0436554bf23e7c29abae7cce4b1c459cd92746811/pydantic_core-2.23.4-cp312-none-win32.whl", hash = "sha256:4ba762ed58e8d68657fc1281e9bb72e1c3e79cc5d464be146e260c541ec12d84", size = 1738535 }, - { url = "https://files.pythonhosted.org/packages/88/8d/479293e4d39ab409747926eec4329de5b7129beaedc3786eca070605d07f/pydantic_core-2.23.4-cp312-none-win_amd64.whl", hash = "sha256:97df63000f4fea395b2824da80e169731088656d1818a11b95f3b173747b6cd9", size = 1917992 }, - { url = "https://files.pythonhosted.org/packages/ad/ef/16ee2df472bf0e419b6bc68c05bf0145c49247a1095e85cee1463c6a44a1/pydantic_core-2.23.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7530e201d10d7d14abce4fb54cfe5b94a0aefc87da539d0346a484ead376c3cc", size = 1856143 }, - { url = "https://files.pythonhosted.org/packages/da/fa/bc3dbb83605669a34a93308e297ab22be82dfb9dcf88c6cf4b4f264e0a42/pydantic_core-2.23.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:df933278128ea1cd77772673c73954e53a1c95a4fdf41eef97c2b779271bd0bd", size = 1770063 }, - { url = "https://files.pythonhosted.org/packages/4e/48/e813f3bbd257a712303ebdf55c8dc46f9589ec74b384c9f652597df3288d/pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cb3da3fd1b6a5d0279a01877713dbda118a2a4fc6f0d821a57da2e464793f05", size = 1790013 }, - { url = "https://files.pythonhosted.org/packages/b4/e0/56eda3a37929a1d297fcab1966db8c339023bcca0b64c5a84896db3fcc5c/pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:42c6dcb030aefb668a2b7009c85b27f90e51e6a3b4d5c9bc4c57631292015b0d", size = 1801077 }, - { url = "https://files.pythonhosted.org/packages/04/be/5e49376769bfbf82486da6c5c1683b891809365c20d7c7e52792ce4c71f3/pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:696dd8d674d6ce621ab9d45b205df149399e4bb9aa34102c970b721554828510", size = 1996782 }, - { url = "https://files.pythonhosted.org/packages/bc/24/e3ee6c04f1d58cc15f37bcc62f32c7478ff55142b7b3e6d42ea374ea427c/pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2971bb5ffe72cc0f555c13e19b23c85b654dd2a8f7ab493c262071377bfce9f6", size = 2661375 }, - { url = "https://files.pythonhosted.org/packages/c1/f8/11a9006de4e89d016b8de74ebb1db727dc100608bb1e6bbe9d56a3cbbcce/pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8394d940e5d400d04cad4f75c0598665cbb81aecefaca82ca85bd28264af7f9b", size = 2071635 }, - { url = "https://files.pythonhosted.org/packages/7c/45/bdce5779b59f468bdf262a5bc9eecbae87f271c51aef628d8c073b4b4b4c/pydantic_core-2.23.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0dff76e0602ca7d4cdaacc1ac4c005e0ce0dcfe095d5b5259163a80d3a10d327", size = 1916994 }, - { url = "https://files.pythonhosted.org/packages/d8/fa/c648308fe711ee1f88192cad6026ab4f925396d1293e8356de7e55be89b5/pydantic_core-2.23.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7d32706badfe136888bdea71c0def994644e09fff0bfe47441deaed8e96fdbc6", size = 1968877 }, - { url = "https://files.pythonhosted.org/packages/16/16/b805c74b35607d24d37103007f899abc4880923b04929547ae68d478b7f4/pydantic_core-2.23.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ed541d70698978a20eb63d8c5d72f2cc6d7079d9d90f6b50bad07826f1320f5f", size = 2116814 }, - { url = "https://files.pythonhosted.org/packages/d1/58/5305e723d9fcdf1c5a655e6a4cc2a07128bf644ff4b1d98daf7a9dbf57da/pydantic_core-2.23.4-cp313-none-win32.whl", hash = "sha256:3d5639516376dce1940ea36edf408c554475369f5da2abd45d44621cb616f769", size = 1738360 }, - { url = "https://files.pythonhosted.org/packages/a5/ae/e14b0ff8b3f48e02394d8acd911376b7b66e164535687ef7dc24ea03072f/pydantic_core-2.23.4-cp313-none-win_amd64.whl", hash = "sha256:5a1504ad17ba4210df3a045132a7baeeba5a200e930f57512ee02909fc5c4cb5", size = 1919411 }, - { url = "https://files.pythonhosted.org/packages/7a/04/2580b2deaae37b3e30fc30c54298be938b973990b23612d6b61c7bdd01c7/pydantic_core-2.23.4-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:a4fa4fc04dff799089689f4fd502ce7d59de529fc2f40a2c8836886c03e0175a", size = 1868200 }, - { url = "https://files.pythonhosted.org/packages/39/6e/e311bd0751505350f0cdcee3077841eb1f9253c5a1ddbad048cd9fbf7c6e/pydantic_core-2.23.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0a7df63886be5e270da67e0966cf4afbae86069501d35c8c1b3b6c168f42cb36", size = 1749316 }, - { url = "https://files.pythonhosted.org/packages/d0/b4/95b5eb47c6dc8692508c3ca04a1f8d6f0884c9dacb34cf3357595cbe73be/pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dcedcd19a557e182628afa1d553c3895a9f825b936415d0dbd3cd0bbcfd29b4b", size = 1800880 }, - { url = "https://files.pythonhosted.org/packages/da/79/41c4f817acd7f42d94cd1e16526c062a7b089f66faed4bd30852314d9a66/pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f54b118ce5de9ac21c363d9b3caa6c800341e8c47a508787e5868c6b79c9323", size = 1807077 }, - { url = "https://files.pythonhosted.org/packages/fb/53/d13d1eb0a97d5c06cf7a225935d471e9c241afd389a333f40c703f214973/pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:86d2f57d3e1379a9525c5ab067b27dbb8a0642fb5d454e17a9ac434f9ce523e3", size = 2002859 }, - { url = "https://files.pythonhosted.org/packages/53/7d/6b8a1eff453774b46cac8c849e99455b27167971a003212f668e94bc4c9c/pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:de6d1d1b9e5101508cb37ab0d972357cac5235f5c6533d1071964c47139257df", size = 2661437 }, - { url = "https://files.pythonhosted.org/packages/6c/ea/8820f57f0b46e6148ee42d8216b15e8fe3b360944284bbc705bf34fac888/pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1278e0d324f6908e872730c9102b0112477a7f7cf88b308e4fc36ce1bdb6d58c", size = 2054404 }, - { url = "https://files.pythonhosted.org/packages/0f/36/d4ae869e473c3c7868e1cd1e2a1b9e13bce5cd1a7d287f6ac755a0b1575e/pydantic_core-2.23.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9a6b5099eeec78827553827f4c6b8615978bb4b6a88e5d9b93eddf8bb6790f55", size = 1921680 }, - { url = "https://files.pythonhosted.org/packages/0d/f8/eed5c65b80c4ac4494117e2101973b45fc655774ef647d17dde40a70f7d2/pydantic_core-2.23.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:e55541f756f9b3ee346b840103f32779c695a19826a4c442b7954550a0972040", size = 1966093 }, - { url = "https://files.pythonhosted.org/packages/e8/c8/1d42ce51d65e571ab53d466cae83434325a126811df7ce4861d9d97bee4b/pydantic_core-2.23.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a5c7ba8ffb6d6f8f2ab08743be203654bb1aaa8c9dcb09f82ddd34eadb695605", size = 2111437 }, - { url = "https://files.pythonhosted.org/packages/aa/c9/7fea9d13383c2ec6865919e09cffe44ab77e911eb281b53a4deaafd4c8e8/pydantic_core-2.23.4-cp39-none-win32.whl", hash = "sha256:37b0fe330e4a58d3c58b24d91d1eb102aeec675a3db4c292ec3928ecd892a9a6", size = 1735049 }, - { url = "https://files.pythonhosted.org/packages/98/95/dd7045c4caa2b73d0bf3b989d66b23cfbb7a0ef14ce99db15677a000a953/pydantic_core-2.23.4-cp39-none-win_amd64.whl", hash = "sha256:1498bec4c05c9c787bde9125cfdcc63a41004ff167f495063191b863399b1a29", size = 1920180 }, - { url = "https://files.pythonhosted.org/packages/13/a9/5d582eb3204464284611f636b55c0a7410d748ff338756323cb1ce721b96/pydantic_core-2.23.4-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f455ee30a9d61d3e1a15abd5068827773d6e4dc513e795f380cdd59932c782d5", size = 1857135 }, - { url = "https://files.pythonhosted.org/packages/2c/57/faf36290933fe16717f97829eabfb1868182ac495f99cf0eda9f59687c9d/pydantic_core-2.23.4-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:1e90d2e3bd2c3863d48525d297cd143fe541be8bbf6f579504b9712cb6b643ec", size = 1740583 }, - { url = "https://files.pythonhosted.org/packages/91/7c/d99e3513dc191c4fec363aef1bf4c8af9125d8fa53af7cb97e8babef4e40/pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e203fdf807ac7e12ab59ca2bfcabb38c7cf0b33c41efeb00f8e5da1d86af480", size = 1793637 }, - { url = "https://files.pythonhosted.org/packages/29/18/812222b6d18c2d13eebbb0f7cdc170a408d9ced65794fdb86147c77e1982/pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e08277a400de01bc72436a0ccd02bdf596631411f592ad985dcee21445bd0068", size = 1941963 }, - { url = "https://files.pythonhosted.org/packages/0f/36/c1f3642ac3f05e6bb4aec3ffc399fa3f84895d259cf5f0ce3054b7735c29/pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f220b0eea5965dec25480b6333c788fb72ce5f9129e8759ef876a1d805d00801", size = 1915332 }, - { url = "https://files.pythonhosted.org/packages/f7/ca/9c0854829311fb446020ebb540ee22509731abad886d2859c855dd29b904/pydantic_core-2.23.4-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:d06b0c8da4f16d1d1e352134427cb194a0a6e19ad5db9161bf32b2113409e728", size = 1957926 }, - { url = "https://files.pythonhosted.org/packages/c0/1c/7836b67c42d0cd4441fcd9fafbf6a027ad4b79b6559f80cf11f89fd83648/pydantic_core-2.23.4-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:ba1a0996f6c2773bd83e63f18914c1de3c9dd26d55f4ac302a7efe93fb8e7433", size = 2100342 }, - { url = "https://files.pythonhosted.org/packages/a9/f9/b6bcaf874f410564a78908739c80861a171788ef4d4f76f5009656672dfe/pydantic_core-2.23.4-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:9a5bce9d23aac8f0cf0836ecfc033896aa8443b501c58d0602dbfd5bd5b37753", size = 1920344 }, - { url = "https://files.pythonhosted.org/packages/32/fd/ac9cdfaaa7cf2d32590b807d900612b39acb25e5527c3c7e482f0553025b/pydantic_core-2.23.4-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:78ddaaa81421a29574a682b3179d4cf9e6d405a09b99d93ddcf7e5239c742e21", size = 1857850 }, - { url = "https://files.pythonhosted.org/packages/08/fe/038f4b2bcae325ea643c8ad353191187a4c92a9c3b913b139289a6f2ef04/pydantic_core-2.23.4-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:883a91b5dd7d26492ff2f04f40fbb652de40fcc0afe07e8129e8ae779c2110eb", size = 1740265 }, - { url = "https://files.pythonhosted.org/packages/51/14/b215c9c3cbd1edaaea23014d4b3304260823f712d3fdee52549b19b25d62/pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88ad334a15b32a791ea935af224b9de1bf99bcd62fabf745d5f3442199d86d59", size = 1793912 }, - { url = "https://files.pythonhosted.org/packages/62/de/2c3ad79b63ba564878cbce325be725929ba50089cd5156f89ea5155cb9b3/pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:233710f069d251feb12a56da21e14cca67994eab08362207785cf8c598e74577", size = 1942870 }, - { url = "https://files.pythonhosted.org/packages/cb/55/c222af19e4644c741b3f3fe4fd8bbb6b4cdca87d8a49258b61cf7826b19e/pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:19442362866a753485ba5e4be408964644dd6a09123d9416c54cd49171f50744", size = 1915610 }, - { url = "https://files.pythonhosted.org/packages/c4/7a/9a8760692a6f76bb54bcd43f245ff3d8b603db695899bbc624099c00af80/pydantic_core-2.23.4-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:624e278a7d29b6445e4e813af92af37820fafb6dcc55c012c834f9e26f9aaaef", size = 1958403 }, - { url = "https://files.pythonhosted.org/packages/4c/91/9b03166feb914bb5698e2f6499e07c2617e2eebf69f9374d0358d7eb2009/pydantic_core-2.23.4-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f5ef8f42bec47f21d07668a043f077d507e5bf4e668d5c6dfe6aaba89de1a5b8", size = 2101154 }, - { url = "https://files.pythonhosted.org/packages/1d/d9/1d7ecb98318da4cb96986daaf0e20d66f1651d0aeb9e2d4435b916ce031d/pydantic_core-2.23.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:aea443fffa9fbe3af1a9ba721a87f926fe548d32cab71d188a6ede77d0ff244e", size = 1920855 }, +sdist = { url = "https://files.pythonhosted.org/packages/d1/cd/8331ae216bcc5a3f2d4c6b941c9f63de647e2700d38133f4f7e0132a00c4/pydantic_core-2.27.0.tar.gz", hash = "sha256:f57783fbaf648205ac50ae7d646f27582fc706be3977e87c3c124e7a92407b10", size = 412675 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ff/97/8a42e9c17c305516c0d956a2887d616d3a1b0531b0053ac95a917e4a1ab7/pydantic_core-2.27.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:cd2ac6b919f7fed71b17fe0b4603c092a4c9b5bae414817c9c81d3c22d1e1bcc", size = 1893954 }, + { url = "https://files.pythonhosted.org/packages/5b/09/ff3ce866f769ebbae2abdcd742247dc2bd6967d646daf54a562ceee6abdb/pydantic_core-2.27.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e015833384ca3e1a0565a79f5d953b0629d9138021c27ad37c92a9fa1af7623c", size = 1807944 }, + { url = "https://files.pythonhosted.org/packages/88/d7/e04d06ca71a0bd7f4cac24e6aa562129969c91117e5fad2520ede865c8cb/pydantic_core-2.27.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:db72e40628967f6dc572020d04b5f800d71264e0531c6da35097e73bdf38b003", size = 1829151 }, + { url = "https://files.pythonhosted.org/packages/14/24/90b0babb61b68ecc471ce5becad8f7fc5f7835c601774e5de577b051b7ad/pydantic_core-2.27.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:df45c4073bed486ea2f18757057953afed8dd77add7276ff01bccb79982cf46c", size = 1849502 }, + { url = "https://files.pythonhosted.org/packages/fc/34/62612e655b4d693a6ec515fd0ddab4bfc0cc6759076e09c23fc6966bd07b/pydantic_core-2.27.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:836a4bfe0cc6d36dc9a9cc1a7b391265bf6ce9d1eb1eac62ac5139f5d8d9a6fa", size = 2035489 }, + { url = "https://files.pythonhosted.org/packages/12/7d/0ff62235adda41b87c495c1b95c84d4debfecb91cfd62e3100abad9754fa/pydantic_core-2.27.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4bf1340ae507f6da6360b24179c2083857c8ca7644aab65807023cf35404ea8d", size = 2774949 }, + { url = "https://files.pythonhosted.org/packages/7f/ac/e1867e2b808a668f32ad9012eaeac0b0ee377eee8157ab93720f48ee609b/pydantic_core-2.27.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5ab325fc86fbc077284c8d7f996d904d30e97904a87d6fb303dce6b3de7ebba9", size = 2130123 }, + { url = "https://files.pythonhosted.org/packages/2f/04/5006f2dbf655052826ac8d03d51b9a122de709fed76eb1040aa21772f530/pydantic_core-2.27.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1da0c98a85a6c6ed702d5556db3b09c91f9b0b78de37b7593e2de8d03238807a", size = 1981988 }, + { url = "https://files.pythonhosted.org/packages/80/8b/bdbe875c4758282402e3cc75fa6bf2f0c8ffac1874f384190034786d3cbc/pydantic_core-2.27.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7b0202ebf2268954090209a84f9897345719e46a57c5f2c9b7b250ca0a9d3e63", size = 1992043 }, + { url = "https://files.pythonhosted.org/packages/2f/2d/4e46981cfcf4ca4c2ff7734dec08162e398dc598c6c0687454b05a82dc2f/pydantic_core-2.27.0-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:35380671c3c921fe8adf31ad349dc6f7588b7e928dbe44e1093789734f607399", size = 2087309 }, + { url = "https://files.pythonhosted.org/packages/d2/43/56ef2e72360d909629a54198d2bc7ef60f19fde8ceb5c90d7749120d0b61/pydantic_core-2.27.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6b4c19525c3538fbc0bbda6229f9682fb8199ce9ac37395880e6952798e00373", size = 2140517 }, + { url = "https://files.pythonhosted.org/packages/61/40/81e5d8f84ab070cf091d072bb61b6021ff79d7110b2d0145fe3171b6107b/pydantic_core-2.27.0-cp310-none-win32.whl", hash = "sha256:333c840a1303d1474f491e7be0b718226c730a39ead0f7dab2c7e6a2f3855555", size = 1814120 }, + { url = "https://files.pythonhosted.org/packages/05/64/e543d342b991d38426bcb841bc0b4b95b9bd2191367ba0cc75f258e3d583/pydantic_core-2.27.0-cp310-none-win_amd64.whl", hash = "sha256:99b2863c1365f43f74199c980a3d40f18a218fbe683dd64e470199db426c4d6a", size = 1972268 }, + { url = "https://files.pythonhosted.org/packages/85/ba/5ed9583a44d9fbd6fbc028df8e3eae574a3ef4761d7f56bb4e0eb428d5ce/pydantic_core-2.27.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:4523c4009c3f39d948e01962223c9f5538602e7087a628479b723c939fab262d", size = 1891468 }, + { url = "https://files.pythonhosted.org/packages/50/1e/58baa0fde14aafccfcc09a8b45bdc11eb941b58a69536729d832e383bdbd/pydantic_core-2.27.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:84af1cf7bfdcbc6fcf5a5f70cc9896205e0350306e4dd73d54b6a18894f79386", size = 1807103 }, + { url = "https://files.pythonhosted.org/packages/7d/87/0422a653ddfcf68763eb56d6e4e2ad19df6d5e006f3f4b854fda06ce2ba3/pydantic_core-2.27.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e65466b31be1070b4a5b7dbfbd14b247884cb8e8b79c64fb0f36b472912dbaea", size = 1827446 }, + { url = "https://files.pythonhosted.org/packages/a4/48/8e431b7732695c93ded79214299a83ac04249d748243b8ba6644ab076574/pydantic_core-2.27.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a5c022bb0d453192426221605efc865373dde43b17822a264671c53b068ac20c", size = 1847798 }, + { url = "https://files.pythonhosted.org/packages/98/7d/e1f28e12a26035d7c8b7678830400e5b94129c9ccb74636235a2eeeee40f/pydantic_core-2.27.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6bb69bf3b6500f195c3deb69c1205ba8fc3cb21d1915f1f158a10d6b1ef29b6a", size = 2033797 }, + { url = "https://files.pythonhosted.org/packages/89/b4/ad5bc2b43b7ca8fd5f5068eca7f195565f53911d9ae69925f7f21859a929/pydantic_core-2.27.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0aa4d1b2eba9a325897308b3124014a142cdccb9f3e016f31d3ebee6b5ea5e75", size = 2767592 }, + { url = "https://files.pythonhosted.org/packages/3e/a6/7fb0725eaf1122518c018bfe38aaf4ad3d512e8598e2c08419b9a270f4bf/pydantic_core-2.27.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e96ca781e0c01e32115912ebdf7b3fb0780ce748b80d7d28a0802fa9fbaf44e", size = 2130244 }, + { url = "https://files.pythonhosted.org/packages/a1/2c/453e52a866947a153bb575bbbb6b14db344f07a73b2ad820ff8f40e9807b/pydantic_core-2.27.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b872c86d8d71827235c7077461c502feb2db3f87d9d6d5a9daa64287d75e4fa0", size = 1979626 }, + { url = "https://files.pythonhosted.org/packages/7a/43/1faa8601085dab2a37dfaca8d48605b76e38aeefcde58bf95534ab96b135/pydantic_core-2.27.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:82e1ad4ca170e8af4c928b67cff731b6296e6a0a0981b97b2eb7c275cc4e15bd", size = 1990741 }, + { url = "https://files.pythonhosted.org/packages/dd/ef/21f25f5964979b7e6f9102074083b5448c22c871da438d91db09601e6634/pydantic_core-2.27.0-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:eb40f828bc2f73f777d1eb8fee2e86cd9692a4518b63b6b5aa8af915dfd3207b", size = 2086325 }, + { url = "https://files.pythonhosted.org/packages/8a/f9/81e5f910571a20655dd7bf10e6d6db8c279e250bfbdb5ab1a09ce3e0eb82/pydantic_core-2.27.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9a8fbf506fde1529a1e3698198fe64bfbe2e0c09557bc6a7dcf872e7c01fec40", size = 2138839 }, + { url = "https://files.pythonhosted.org/packages/59/c4/27917b73d0631098b91f2ec303e1becb823fead0628ee9055fca78ec1e2e/pydantic_core-2.27.0-cp311-none-win32.whl", hash = "sha256:24f984fc7762ed5f806d9e8c4c77ea69fdb2afd987b4fd319ef06c87595a8c55", size = 1809514 }, + { url = "https://files.pythonhosted.org/packages/ea/48/a30c67d62b8f39095edc3dab6abe69225e8c57186f31cc59a1ab984ea8e6/pydantic_core-2.27.0-cp311-none-win_amd64.whl", hash = "sha256:68950bc08f9735306322bfc16a18391fcaac99ded2509e1cc41d03ccb6013cfe", size = 1971838 }, + { url = "https://files.pythonhosted.org/packages/4e/9e/3798b901cf331058bae0ba4712a52fb0106c39f913830aaf71f01fd10d45/pydantic_core-2.27.0-cp311-none-win_arm64.whl", hash = "sha256:3eb8849445c26b41c5a474061032c53e14fe92a11a5db969f722a2716cd12206", size = 1862174 }, + { url = "https://files.pythonhosted.org/packages/82/99/43149b127559f3152cd28cb7146592c6547cfe47d528761954e2e8fcabaf/pydantic_core-2.27.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:8117839a9bdbba86e7f9df57018fe3b96cec934c3940b591b0fd3fbfb485864a", size = 1887064 }, + { url = "https://files.pythonhosted.org/packages/7e/dd/989570c76334aa55ccb4ee8b5e0e6881a513620c6172d93b2f3b77e10f81/pydantic_core-2.27.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a291d0b4243a259c8ea7e2b84eb9ccb76370e569298875a7c5e3e71baf49057a", size = 1804405 }, + { url = "https://files.pythonhosted.org/packages/3e/b5/bce1d6d6fb71d916c74bf988b7d0cd7fc0c23da5e08bc0d6d6e08c12bf36/pydantic_core-2.27.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:84e35afd9e10b2698e6f2f32256678cb23ca6c1568d02628033a837638b3ed12", size = 1822595 }, + { url = "https://files.pythonhosted.org/packages/35/93/a6e5e04625ac8fcbed523d7b741e91cc3a37ed1e04e16f8f2f34269bbe53/pydantic_core-2.27.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:58ab0d979c969983cdb97374698d847a4acffb217d543e172838864636ef10d9", size = 1848701 }, + { url = "https://files.pythonhosted.org/packages/3a/74/56ead1436e3f6513b59b3a442272578a6ec09a39ab95abd5ee321bcc8c95/pydantic_core-2.27.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0d06b667e53320332be2bf6f9461f4a9b78092a079b8ce8634c9afaa7e10cd9f", size = 2031878 }, + { url = "https://files.pythonhosted.org/packages/e1/4d/8905b2710ef653c0da27224bfb6a084b5873ad6fdb975dda837943e5639d/pydantic_core-2.27.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:78f841523729e43e3928a364ec46e2e3f80e6625a4f62aca5c345f3f626c6e8a", size = 2673386 }, + { url = "https://files.pythonhosted.org/packages/1d/f0/abe1511f11756d12ce18d016f3555cb47211590e4849ee02e7adfdd1684e/pydantic_core-2.27.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:400bf470e4327e920883b51e255617dfe4496d4e80c3fea0b5a5d0bf2c404dd4", size = 2152867 }, + { url = "https://files.pythonhosted.org/packages/c7/90/1c588d4d93ce53e1f5ab0cea2d76151fcd36613446bf99b670d7da9ddf89/pydantic_core-2.27.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:951e71da6c89d354572098bada5ba5b5dc3a9390c933af8a614e37755d3d1840", size = 1986595 }, + { url = "https://files.pythonhosted.org/packages/a3/9c/27d06369f39375966836cde5c8aec0a66dc2f532c13d9aa1a6c370131fbd/pydantic_core-2.27.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:2a51ce96224eadd1845150b204389623c8e129fde5a67a84b972bd83a85c6c40", size = 1995731 }, + { url = "https://files.pythonhosted.org/packages/26/4e/b039e52b7f4c51d9fae6715d5d2e47a57c369b8e0cb75838974a193aae40/pydantic_core-2.27.0-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:483c2213a609e7db2c592bbc015da58b6c75af7360ca3c981f178110d9787bcf", size = 2085771 }, + { url = "https://files.pythonhosted.org/packages/01/93/2796bd116a93e7e4e10baca4c55266c4d214b3b4e5ee7f0e9add69c184af/pydantic_core-2.27.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:359e7951f04ad35111b5ddce184db3391442345d0ab073aa63a95eb8af25a5ef", size = 2150452 }, + { url = "https://files.pythonhosted.org/packages/0f/93/e57562d6ea961557174c3afa481a73ce0e2d8b823e0eb2b320bfb00debbe/pydantic_core-2.27.0-cp312-none-win32.whl", hash = "sha256:ee7d9d5537daf6d5c74a83b38a638cc001b648096c1cae8ef695b0c919d9d379", size = 1830767 }, + { url = "https://files.pythonhosted.org/packages/44/00/4f121ca5dd06420813e7858395b5832603ed0074a5b74ef3104c8dbc2fd5/pydantic_core-2.27.0-cp312-none-win_amd64.whl", hash = "sha256:2be0ad541bb9f059954ccf8877a49ed73877f862529575ff3d54bf4223e4dd61", size = 1973909 }, + { url = "https://files.pythonhosted.org/packages/c3/c7/36f87c0dabbde9c0dd59b9024e4bf117a5122515c864ddbe685ed8301670/pydantic_core-2.27.0-cp312-none-win_arm64.whl", hash = "sha256:6e19401742ed7b69e51d8e4df3c03ad5ec65a83b36244479fd70edde2828a5d9", size = 1877037 }, + { url = "https://files.pythonhosted.org/packages/9d/b2/740159bdfe532d856e340510246aa1fd723b97cadf1a38153bdfb52efa28/pydantic_core-2.27.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:5f2b19b8d6fca432cb3acf48cf5243a7bf512988029b6e6fd27e9e8c0a204d85", size = 1886935 }, + { url = "https://files.pythonhosted.org/packages/ca/2a/2f435d9fd591c912ca227f29c652a93775d35d54677b57c3157bbad823b5/pydantic_core-2.27.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c86679f443e7085ea55a7376462553996c688395d18ef3f0d3dbad7838f857a2", size = 1805318 }, + { url = "https://files.pythonhosted.org/packages/ba/f2/755b628009530b19464bb95c60f829b47a6ef7930f8ca1d87dac90fd2848/pydantic_core-2.27.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:510b11e9c3b1a852876d1ccd8d5903684336d635214148637ceb27366c75a467", size = 1822284 }, + { url = "https://files.pythonhosted.org/packages/3d/c2/a12744628b1b55c5384bd77657afa0780868484a92c37a189fb460d1cfe7/pydantic_core-2.27.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eb704155e73b833801c247f39d562229c0303f54770ca14fb1c053acb376cf10", size = 1848522 }, + { url = "https://files.pythonhosted.org/packages/60/1d/dfcb8ab94a4637d4cf682550a2bf94695863988e7bcbd6f4d83c04178e17/pydantic_core-2.27.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9ce048deb1e033e7a865ca384770bccc11d44179cf09e5193a535c4c2f497bdc", size = 2031678 }, + { url = "https://files.pythonhosted.org/packages/ee/c8/f9cbcab0275e031c4312223c75d999b61fba60995003cd89dc4866300059/pydantic_core-2.27.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:58560828ee0951bb125c6f2862fbc37f039996d19ceb6d8ff1905abf7da0bf3d", size = 2672948 }, + { url = "https://files.pythonhosted.org/packages/41/f9/c613546237cf58ed7a7fa9158410c14d0e7e0cbbf95f83a905c9424bb074/pydantic_core-2.27.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:abb4785894936d7682635726613c44578c420a096729f1978cd061a7e72d5275", size = 2152419 }, + { url = "https://files.pythonhosted.org/packages/49/71/b951b03a271678b1d1b79481dac38cf8bce8a4e178f36ada0e9aff65a679/pydantic_core-2.27.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2883b260f7a93235488699d39cbbd94fa7b175d3a8063fbfddd3e81ad9988cb2", size = 1986408 }, + { url = "https://files.pythonhosted.org/packages/9a/2c/07b0d5b5e1cdaa07b7c23e758354377d294ff0395116d39c9fa734e5d89e/pydantic_core-2.27.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c6fcb3fa3855d583aa57b94cf146f7781d5d5bc06cb95cb3afece33d31aac39b", size = 1995895 }, + { url = "https://files.pythonhosted.org/packages/63/09/c21e0d7438c7e742209cc8603607c8d389df96018396c8a2577f6e24c5c5/pydantic_core-2.27.0-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:e851a051f7260e6d688267eb039c81f05f23a19431bd7dfa4bf5e3cb34c108cd", size = 2085914 }, + { url = "https://files.pythonhosted.org/packages/68/e4/5ed8f09d92655dcd0a86ee547e509adb3e396cef0a48f5c31e3b060bb9d0/pydantic_core-2.27.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:edb1bfd45227dec8d50bc7c7d86463cd8728bcc574f9b07de7369880de4626a3", size = 2150217 }, + { url = "https://files.pythonhosted.org/packages/cd/e6/a202f0e1b81c729130404e82d9de90dc4418ec01df35000d48d027c38501/pydantic_core-2.27.0-cp313-none-win32.whl", hash = "sha256:678f66462058dd978702db17eb6a3633d634f7aa0deaea61e0a674152766d3fc", size = 1830973 }, + { url = "https://files.pythonhosted.org/packages/06/3d/21ed0f308e6618ce6c5c6bfb9e71734a9a3256d5474a53c8e5aaaba498ca/pydantic_core-2.27.0-cp313-none-win_amd64.whl", hash = "sha256:d28ca7066d6cdd347a50d8b725dc10d9a1d6a1cce09836cf071ea6a2d4908be0", size = 1974853 }, + { url = "https://files.pythonhosted.org/packages/d7/18/e5744a132b81f98b9f92e15f33f03229a1d254ce7af942b1422ec2ac656f/pydantic_core-2.27.0-cp313-none-win_arm64.whl", hash = "sha256:6f4a53af9e81d757756508b57cae1cf28293f0f31b9fa2bfcb416cc7fb230f9d", size = 1877469 }, + { url = "https://files.pythonhosted.org/packages/00/e4/4d6d9193a33c964920bf56fcbe11fa30511d3d900a81c740b0157579b122/pydantic_core-2.27.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:4148dc9184ab79e356dc00a4199dc0ee8647973332cb385fc29a7cced49b9f9c", size = 1894360 }, + { url = "https://files.pythonhosted.org/packages/f4/46/9d27771309609126678dee81e8e93188dbd0515a543b27e0a01a806c1893/pydantic_core-2.27.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5fc72fbfebbf42c0856a824b8b0dc2b5cd2e4a896050281a21cfa6fed8879cb1", size = 1773921 }, + { url = "https://files.pythonhosted.org/packages/a0/3a/3a6a4cee7bc11bcb3f8859a63c6b4d88b8df66ad7c9c9e6d667dd894b439/pydantic_core-2.27.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:185ef205256cd8b38431205698531026979db89a79587725c1e55c59101d64e9", size = 1829480 }, + { url = "https://files.pythonhosted.org/packages/2b/aa/ecf0fcee9031eef516cef2e336d403a61bd8df75ab17a856bc29f3eb07d4/pydantic_core-2.27.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:395e3e1148fa7809016231f8065f30bb0dc285a97b4dc4360cd86e17bab58af7", size = 1849759 }, + { url = "https://files.pythonhosted.org/packages/b6/17/8953bbbe7d3c015bdfa34171ba1738a43682d770e68c87171dd8887035c3/pydantic_core-2.27.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:33d14369739c5d07e2e7102cdb0081a1fa46ed03215e07f097b34e020b83b1ae", size = 2035679 }, + { url = "https://files.pythonhosted.org/packages/ec/19/514fdf2f684003961b6f34543f0bdf3be2e0f17b8b25cd8d44c343521148/pydantic_core-2.27.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e7820bb0d65e3ce1e3e70b6708c2f66143f55912fa02f4b618d0f08b61575f12", size = 2773208 }, + { url = "https://files.pythonhosted.org/packages/9a/37/2cdd48b7367fbf0576d16402837212d2b1798aa4ea887f1795f8ddbace07/pydantic_core-2.27.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:43b61989068de9ce62296cde02beffabcadb65672207fc51e7af76dca75e6636", size = 2130616 }, + { url = "https://files.pythonhosted.org/packages/3a/6c/fa100356e1c8f749797d88401a1d5ed8d458705d43e259931681b5b96ab4/pydantic_core-2.27.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:15e350efb67b855cd014c218716feea4986a149ed1f42a539edd271ee074a196", size = 1981857 }, + { url = "https://files.pythonhosted.org/packages/0f/3d/36c0c832c1fd1351c495bf1495b61b2e40248c54f7874e6df439e6ffb9a5/pydantic_core-2.27.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:433689845288f9a1ee5714444e65957be26d30915f7745091ede4a83cfb2d7bb", size = 1992515 }, + { url = "https://files.pythonhosted.org/packages/99/12/ee67e29369b368c404c6aead492e1528ec887609d388a7a30b675b969b82/pydantic_core-2.27.0-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:3fd8bc2690e7c39eecdf9071b6a889ce7b22b72073863940edc2a0a23750ca90", size = 2087604 }, + { url = "https://files.pythonhosted.org/packages/0e/6c/72ca869aabe190e4cd36b03226286e430a1076c367097c77cb0704b1cbb3/pydantic_core-2.27.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:884f1806609c2c66564082540cffc96868c5571c7c3cf3a783f63f2fb49bd3cd", size = 2141000 }, + { url = "https://files.pythonhosted.org/packages/5c/b8/e7499cfa6f1e46e92a645e74198b7bb9ce3d49e82f626a02726dc917fc74/pydantic_core-2.27.0-cp39-none-win32.whl", hash = "sha256:bf37b72834e7239cf84d4a0b2c050e7f9e48bced97bad9bdf98d26b8eb72e846", size = 1813857 }, + { url = "https://files.pythonhosted.org/packages/2e/27/81203aa6cbf68772afd9c3877ce2e35878f434e824aad4047e7cfd3bc14d/pydantic_core-2.27.0-cp39-none-win_amd64.whl", hash = "sha256:31a2cae5f059329f9cfe3d8d266d3da1543b60b60130d186d9b6a3c20a346361", size = 1974744 }, + { url = "https://files.pythonhosted.org/packages/d3/ad/c1dc814ab524cb247ceb6cb25236895a5cae996c438baf504db610fd6c92/pydantic_core-2.27.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:4fb49cfdb53af5041aba909be00cccfb2c0d0a2e09281bf542371c5fd36ad04c", size = 1889233 }, + { url = "https://files.pythonhosted.org/packages/24/bb/069a9dd910e6c09aab90a118c08d3cb30dc5738550e9f2d21f3b086352c2/pydantic_core-2.27.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:49633583eb7dc5cba61aaf7cdb2e9e662323ad394e543ee77af265736bcd3eaa", size = 1768419 }, + { url = "https://files.pythonhosted.org/packages/cb/a1/f9b4e625ee8c7f683c8295c85d11f79a538eb53719f326646112a7800bda/pydantic_core-2.27.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:153017e3d6cd3ce979de06d84343ca424bb6092727375eba1968c8b4693c6ecb", size = 1822870 }, + { url = "https://files.pythonhosted.org/packages/12/07/04abaeeabf212650de3edc300b2ab89fb17da9bc4408ef4e01a62efc87dc/pydantic_core-2.27.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff63a92f6e249514ef35bc795de10745be0226eaea06eb48b4bbeaa0c8850a4a", size = 1977039 }, + { url = "https://files.pythonhosted.org/packages/0f/9d/99bbeb21d5be1d5affdc171e0e84603a757056f9f4293ef236e41af0a5bc/pydantic_core-2.27.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5982048129f40b082c2654de10c0f37c67a14f5ff9d37cf35be028ae982f26df", size = 1974317 }, + { url = "https://files.pythonhosted.org/packages/5f/78/815aa74db1591a9ad4086bc1bf98e2126686245a956d76cd4e72bf9841ad/pydantic_core-2.27.0-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:91bc66f878557313c2a6bcf396e7befcffe5ab4354cfe4427318968af31143c3", size = 1985101 }, + { url = "https://files.pythonhosted.org/packages/d9/a8/9c1557d5282108916448415e85f829b70ba99d97f03cee0e40a296e58a65/pydantic_core-2.27.0-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:68ef5377eb582fa4343c9d0b57a5b094046d447b4c73dd9fbd9ffb216f829e7d", size = 2073399 }, + { url = "https://files.pythonhosted.org/packages/ca/b0/5296273d652fa9aa140771b3f4bb574edd3cbf397090625b988f6a57b02b/pydantic_core-2.27.0-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:c5726eec789ee38f2c53b10b1821457b82274f81f4f746bb1e666d8741fcfadb", size = 2129499 }, + { url = "https://files.pythonhosted.org/packages/e9/fd/7f39ff702fdca954f26c84b40d9bf744733bb1a50ca6b7569822b9cbb7f4/pydantic_core-2.27.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c0c431e4be5c1a0c6654e0c31c661cd89e0ca956ef65305c3c3fd96f4e72ca39", size = 1997246 }, + { url = "https://files.pythonhosted.org/packages/bb/4f/76f1ac16a0c277a3a8be2b5b52b0a09929630e794fb1938c4cd85396c34f/pydantic_core-2.27.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:8e21d927469d04b39386255bf00d0feedead16f6253dcc85e9e10ddebc334084", size = 1889486 }, + { url = "https://files.pythonhosted.org/packages/f3/96/4ff5a8ec0c457afcd87334d4e2f6fd25df6642b4ff8bf587316dd6eccd59/pydantic_core-2.27.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:4b51f964fcbb02949fc546022e56cdb16cda457af485e9a3e8b78ac2ecf5d77e", size = 1768718 }, + { url = "https://files.pythonhosted.org/packages/52/21/e7bab7b9674d5b1a8cf06939929991753e4b814b01bae29321a8739990b3/pydantic_core-2.27.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25a7fd4de38f7ff99a37e18fa0098c3140286451bc823d1746ba80cec5b433a1", size = 1823291 }, + { url = "https://files.pythonhosted.org/packages/1d/68/d1868a78ce0d776c3e04179fbfa6272e72d4363c49f9bdecfe4b2007dd75/pydantic_core-2.27.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fda87808429c520a002a85d6e7cdadbf58231d60e96260976c5b8f9a12a8e13", size = 1977040 }, + { url = "https://files.pythonhosted.org/packages/68/7b/2e361ff81f60c4c28f65b53670436849ec716366d4f1635ea243a31903a2/pydantic_core-2.27.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8a150392102c402c538190730fda06f3bce654fc498865579a9f2c1d2b425833", size = 1973909 }, + { url = "https://files.pythonhosted.org/packages/a8/44/a4a3718f3b148526baccdb9a0bc8e6b7aa840c796e637805c04aaf1a74c3/pydantic_core-2.27.0-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:c9ed88b398ba7e3bad7bd64d66cc01dcde9cfcb7ec629a6fd78a82fa0b559d78", size = 1985091 }, + { url = "https://files.pythonhosted.org/packages/3a/79/2cdf503e8aac926a99d64b2a02642ab1377146999f9a68536c54bd8b2c46/pydantic_core-2.27.0-pp39-pypy39_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:9fe94d9d2a2b4edd7a4b22adcd45814b1b59b03feb00e56deb2e89747aec7bfe", size = 2073484 }, + { url = "https://files.pythonhosted.org/packages/e8/15/74c61b7ea348b252fe97a32e5b531fdde331710db80e9b0fae1302023414/pydantic_core-2.27.0-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:d8b5ee4ae9170e2775d495b81f414cc20268041c42571530513496ba61e94ba3", size = 2129473 }, + { url = "https://files.pythonhosted.org/packages/57/81/0e9ebcc80b107e1dfacc677ad7c2ab0202cc0e10ba76b23afbb147ac32fb/pydantic_core-2.27.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:d29e235ce13c91902ef3efc3d883a677655b3908b1cbc73dee816e5e1f8f7739", size = 1997389 }, ] [[package]] @@ -1164,11 +1173,11 @@ wheels = [ [[package]] name = "setuptools" -version = "75.5.0" +version = "75.6.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c8/db/722a42ffdc226e950c4757b3da7b56ff5c090bb265dccd707f7b8a3c6fee/setuptools-75.5.0.tar.gz", hash = "sha256:5c4ccb41111392671f02bb5f8436dfc5a9a7185e80500531b133f5775c4163ef", size = 1336032 } +sdist = { url = "https://files.pythonhosted.org/packages/43/54/292f26c208734e9a7f067aea4a7e282c080750c4546559b58e2e45413ca0/setuptools-75.6.0.tar.gz", hash = "sha256:8199222558df7c86216af4f84c30e9b34a61d8ba19366cc914424cdbd28252f6", size = 1337429 } wheels = [ - { url = "https://files.pythonhosted.org/packages/fe/df/88ccbee85aefbca071db004fdc8f8d2507d55d5a9dc27ebb93c92edb1bd8/setuptools-75.5.0-py3-none-any.whl", hash = "sha256:87cb777c3b96d638ca02031192d40390e0ad97737e27b6b4fa831bea86f2f829", size = 1222710 }, + { url = "https://files.pythonhosted.org/packages/55/21/47d163f615df1d30c094f6c8bbb353619274edccf0327b185cc2493c2c33/setuptools-75.6.0-py3-none-any.whl", hash = "sha256:ce74b49e8f7110f9bf04883b730f4765b774ef3ef28f722cce7c273d253aaf7d", size = 1224032 }, ] [[package]] @@ -1214,15 +1223,15 @@ wheels = [ [[package]] name = "starlette" -version = "0.41.2" +version = "0.41.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, { name = "typing-extensions", marker = "python_full_version < '3.10'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/3e/da/1fb4bdb72ae12b834becd7e1e7e47001d32f91ec0ce8d7bc1b618d9f0bd9/starlette-0.41.2.tar.gz", hash = "sha256:9834fd799d1a87fd346deb76158668cfa0b0d56f85caefe8268e2d97c3468b62", size = 2573867 } +sdist = { url = "https://files.pythonhosted.org/packages/1a/4c/9b5764bd22eec91c4039ef4c55334e9187085da2d8a2df7bd570869aae18/starlette-0.41.3.tar.gz", hash = "sha256:0e4ab3d16522a255be6b28260b938eae2482f98ce5cc934cb08dce8dc3ba5835", size = 2574159 } wheels = [ - { url = "https://files.pythonhosted.org/packages/54/43/f185bfd0ca1d213beb4293bed51d92254df23d8ceaf6c0e17146d508a776/starlette-0.41.2-py3-none-any.whl", hash = "sha256:fbc189474b4731cf30fcef52f18a8d070e3f3b46c6a04c97579e85e6ffca942d", size = 73259 }, + { url = "https://files.pythonhosted.org/packages/96/00/2b325970b3060c7cecebab6d295afe763365822b1306a12eeab198f74323/starlette-0.41.3-py3-none-any.whl", hash = "sha256:44cedb2b7c77a9de33a8b74b2b90e9f50d11fcf25d8270ea525ad71a25374ff7", size = 73225 }, ] [[package]] @@ -1295,16 +1304,16 @@ wheels = [ [[package]] name = "uvicorn" -version = "0.32.0" +version = "0.32.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, { name = "h11" }, { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e0/fc/1d785078eefd6945f3e5bab5c076e4230698046231eb0f3747bc5c8fa992/uvicorn-0.32.0.tar.gz", hash = "sha256:f78b36b143c16f54ccdb8190d0a26b5f1901fe5a3c777e1ab29f26391af8551e", size = 77564 } +sdist = { url = "https://files.pythonhosted.org/packages/6a/3c/21dba3e7d76138725ef307e3d7ddd29b763119b3aa459d02cc05fefcff75/uvicorn-0.32.1.tar.gz", hash = "sha256:ee9519c246a72b1c084cea8d3b44ed6026e78a4a309cbedae9c37e4cb9fbb175", size = 77630 } wheels = [ - { url = "https://files.pythonhosted.org/packages/eb/14/78bd0e95dd2444b6caacbca2b730671d4295ccb628ef58b81bee903629df/uvicorn-0.32.0-py3-none-any.whl", hash = "sha256:60b8f3a5ac027dcd31448f411ced12b5ef452c646f76f02f8cc3f25d8d26fd82", size = 63723 }, + { url = "https://files.pythonhosted.org/packages/50/c1/2d27b0a15826c2b71dcf6e2f5402181ef85acf439617bb2f1453125ce1f3/uvicorn-0.32.1-py3-none-any.whl", hash = "sha256:82ad92fd58da0d12af7482ecdb5f2470a04c9c9a53ced65b9bbb4a205377602e", size = 63828 }, ] [[package]] diff --git a/webhook_server_container/OWNERS b/webhook_server_container/OWNERS new file mode 100644 index 00000000..22ec2c44 --- /dev/null +++ b/webhook_server_container/OWNERS @@ -0,0 +1,4 @@ +approvers: + - myakove +reviewers: + - rnetser diff --git a/webhook_server_container/libs/github_api.py b/webhook_server_container/libs/github_api.py index 2a8ced00..effc09c1 100644 --- a/webhook_server_container/libs/github_api.py +++ b/webhook_server_container/libs/github_api.py @@ -192,7 +192,7 @@ def process(self) -> None: return event_log: str = f"Event type: {self.github_event}. event ID: {self.x_github_delivery}" - self.owners_content = self.get_owners_content() + self.approvers_and_reviewers = self.get_approvers_and_reviewers() try: self.pull_request = self._get_pull_request() @@ -575,9 +575,10 @@ def _error(_out: str, _err: str) -> None: """ self.send_slack_message(message=message, webhook_url=self.slack_webhook_url) - def get_owners_content(self) -> Dict[str, Any]: + def get_owners_content(self, folder_path: str = "") -> Dict[str, Any]: try: - owners_content: list[ContentFile] | ContentFile = self.repository.get_contents("OWNERS") + owners_path = f"{folder_path}/OWNERS" if folder_path else "OWNERS" + owners_content: list[ContentFile] | ContentFile = self.repository.get_contents(owners_path) if isinstance(owners_content, list): self.logger.debug(f"{self.log_prefix} Found more than one OWNERS file, using the first one") owners_content = owners_content[0] @@ -592,34 +593,15 @@ def get_owners_content(self) -> Dict[str, Any]: @property def reviewers(self) -> List[str]: - bc_reviewers: List[str] = self.owners_content.get("reviewers", []) - if isinstance(bc_reviewers, dict): - _reviewers: List[str] = self.owners_content.get("reviewers", {}).get("any", []) - else: - _reviewers = bc_reviewers - + _reviewers = self.approvers_and_reviewers.get("OWNERS", {}).get("reviewers", []) self.logger.debug(f"{self.log_prefix} Reviewers: {_reviewers}") return _reviewers - @property - def files_reviewers(self) -> Dict[str, str]: - _reviewers = self.owners_content.get("reviewers", {}) - if isinstance(_reviewers, dict): - return _reviewers.get("files", {}) - - return {} - - @property - def folders_reviewers(self) -> Dict[str, str]: - _reviewers = self.owners_content.get("reviewers", {}) - if isinstance(_reviewers, dict): - return _reviewers.get("folders", {}) - - return {} - @property def approvers(self) -> List[str]: - return self.owners_content.get("approvers", []) + _approvers = self.approvers_and_reviewers.get("OWNERS", {}).get("approvers", []) + self.logger.debug(f"{self.log_prefix} Approvers: {_approvers}") + return _approvers def list_changed_commit_files(self) -> list[str]: return [fd["filename"] for fd in self.last_commit.raw_data["files"]] @@ -628,12 +610,12 @@ def assign_reviewers(self) -> None: self.logger.info(f"{self.log_prefix} Assign reviewers") changed_files = self.list_changed_commit_files() reviewers_to_add = self.reviewers - for _file, _file_reviewers in self.files_reviewers.items(): - if _file in changed_files: - reviewers_to_add.extend(_file_reviewers) - for _folder, _folder_reviewers in self.folders_reviewers.items(): - if any(cf for cf in changed_files if _folder in str(Path(cf).parent)): - reviewers_to_add.extend(_folder_reviewers) + changed_folders = [str(Path(cf).parent) for cf in changed_files] + + for changed_folder_path in changed_folders: + for owners_path, owners_data in self.approvers_and_reviewers.items(): + if owners_path in changed_folder_path: + reviewers_to_add.extend(owners_data.get("reviewers", [])) _to_add: List[str] = list(set(reviewers_to_add)) self.logger.debug(f"{self.log_prefix} Reviewers to add: {', '.join(_to_add)}") @@ -2071,3 +2053,22 @@ def run_podman_command(self, command: str, pipe: bool = False) -> Tuple[bool, st return run_command(command=command, log_prefix=self.log_prefix, pipe=pipe) return rc, out, err + + def get_approvers_and_reviewers(self) -> dict[str, dict[str, list[str]]]: + # owners hole mapping of OWNERS file full path: dict with `approvers` and `reviewers` each hold a list of names + _owners: dict[str, dict[str, list[str]]] = {} + + for obj in self.repository._requester.requestJsonAndCheck( + "GET", f"{self.repository.url}/git/trees/{self.pull_request_branch}?recursive=1" + ): + for content in obj.get("tree", []): + if content_path := content.get("path", ""): + if content.get("type") == "blob" and content_path.endswith("OWNERS"): + _path = self.repository.get_contents(content_path) + if isinstance(_path, list): + _path = _path[0] + + _owners[content_path] = yaml.safe_load(_path.decoded_content) + + self.logger.debug(f"{self.log_prefix} Owners file mapping: {_owners}") + return _owners From ccd98f04423b07bb845339af839113a95586adb9 Mon Sep 17 00:00:00 2001 From: Meni Yakove Date: Thu, 21 Nov 2024 14:42:21 +0200 Subject: [PATCH 02/31] Set minimum python to 3.10 --- Dockerfile | 13 +++++--- pyproject.toml | 2 +- uv.lock | 91 +------------------------------------------------- 3 files changed, 11 insertions(+), 95 deletions(-) diff --git a/Dockerfile b/Dockerfile index 6e11ee3b..c26e4d6e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,10 +8,6 @@ ENV BIN_DIR="$HOME_DIR/.local/bin" ENV PATH="$PATH:$BIN_DIR" ENV DATA_DIR="$HOME_DIR/data" ENV APP_DIR="$HOME_DIR/github-webhook-server" -ENV UV_PYTHON=python3.13 -ENV UV_COMPILE_BYTECODE=1 -ENV UV_NO_SYNC=1 -ENV UV_CACHE_DIR=${APP_DIR}/.cache RUN dnf -y install dnf-plugins-core \ && dnf -y update \ @@ -21,6 +17,10 @@ RUN dnf -y install dnf-plugins-core \ unzip \ gcc \ python3-devel \ + python3.10-devel \ + python3.11-devel \ + python3.12-devel \ + python3.13-devel \ clang \ cargo \ && dnf clean all \ @@ -41,6 +41,11 @@ RUN usermod --add-subuids 100000-165535 --add-subgids 100000-165535 $USERNAME \ USER $USERNAME WORKDIR $HOME_DIR +ENV UV_PYTHON=python3.12 +ENV UV_COMPILE_BYTECODE=1 +ENV UV_NO_SYNC=1 +ENV UV_CACHE_DIR=${APP_DIR}/.cache + COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx ${BIN_DIR}/ RUN set -x \ diff --git a/pyproject.toml b/pyproject.toml index 9e51db72..08ec2db0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -25,7 +25,7 @@ dev-dependencies = ["ipdb>=0.13.13", "ipython>=8.12.3"] [project] name = "github-webhook-server" version = "2.0.0" -requires-python = ">=3.9, <3.12" +requires-python = ">=3.10, <3.13" description = "A webhook server to manage Github reposotories and pull requests." readme = "README.md" license = "Apache-2.0" diff --git a/uv.lock b/uv.lock index d01c5eb5..8444a67d 100644 --- a/uv.lock +++ b/uv.lock @@ -1,5 +1,5 @@ version = 1 -requires-python = ">=3.9, <3.12" +requires-python = ">=3.10, <3.13" resolution-markers = [ "python_full_version < '3.11'", "python_full_version >= '3.11'", @@ -153,18 +153,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f1/47/d7145bf2dc04684935d57d67dff9d6d795b2ba2796806bb109864be3a151/cffi-1.17.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9", size = 488469 }, { url = "https://files.pythonhosted.org/packages/bf/ee/f94057fa6426481d663b88637a9a10e859e492c73d0384514a17d78ee205/cffi-1.17.1-cp313-cp313-win32.whl", hash = "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d", size = 172475 }, { url = "https://files.pythonhosted.org/packages/7c/fc/6a8cb64e5f0324877d503c854da15d76c1e50eb722e320b15345c4d0c6de/cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a", size = 182009 }, - { url = "https://files.pythonhosted.org/packages/b9/ea/8bb50596b8ffbc49ddd7a1ad305035daa770202a6b782fc164647c2673ad/cffi-1.17.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b2ab587605f4ba0bf81dc0cb08a41bd1c0a5906bd59243d56bad7668a6fc6c16", size = 182220 }, - { url = "https://files.pythonhosted.org/packages/ae/11/e77c8cd24f58285a82c23af484cf5b124a376b32644e445960d1a4654c3a/cffi-1.17.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:28b16024becceed8c6dfbc75629e27788d8a3f9030691a1dbf9821a128b22c36", size = 178605 }, - { url = "https://files.pythonhosted.org/packages/ed/65/25a8dc32c53bf5b7b6c2686b42ae2ad58743f7ff644844af7cdb29b49361/cffi-1.17.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1d599671f396c4723d016dbddb72fe8e0397082b0a77a4fab8028923bec050e8", size = 424910 }, - { url = "https://files.pythonhosted.org/packages/42/7a/9d086fab7c66bd7c4d0f27c57a1b6b068ced810afc498cc8c49e0088661c/cffi-1.17.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca74b8dbe6e8e8263c0ffd60277de77dcee6c837a3d0881d8c1ead7268c9e576", size = 447200 }, - { url = "https://files.pythonhosted.org/packages/da/63/1785ced118ce92a993b0ec9e0d0ac8dc3e5dbfbcaa81135be56c69cabbb6/cffi-1.17.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7f5baafcc48261359e14bcd6d9bff6d4b28d9103847c9e136694cb0501aef87", size = 454565 }, - { url = "https://files.pythonhosted.org/packages/74/06/90b8a44abf3556599cdec107f7290277ae8901a58f75e6fe8f970cd72418/cffi-1.17.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98e3969bcff97cae1b2def8ba499ea3d6f31ddfdb7635374834cf89a1a08ecf0", size = 435635 }, - { url = "https://files.pythonhosted.org/packages/bd/62/a1f468e5708a70b1d86ead5bab5520861d9c7eacce4a885ded9faa7729c3/cffi-1.17.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cdf5ce3acdfd1661132f2a9c19cac174758dc2352bfe37d98aa7512c6b7178b3", size = 445218 }, - { url = "https://files.pythonhosted.org/packages/5b/95/b34462f3ccb09c2594aa782d90a90b045de4ff1f70148ee79c69d37a0a5a/cffi-1.17.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9755e4345d1ec879e3849e62222a18c7174d65a6a92d5b346b1863912168b595", size = 460486 }, - { url = "https://files.pythonhosted.org/packages/fc/fc/a1e4bebd8d680febd29cf6c8a40067182b64f00c7d105f8f26b5bc54317b/cffi-1.17.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f1e22e8c4419538cb197e4dd60acc919d7696e5ef98ee4da4e01d3f8cfa4cc5a", size = 437911 }, - { url = "https://files.pythonhosted.org/packages/e6/c3/21cab7a6154b6a5ea330ae80de386e7665254835b9e98ecc1340b3a7de9a/cffi-1.17.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c03e868a0b3bc35839ba98e74211ed2b05d2119be4e8a0f224fba9384f1fe02e", size = 460632 }, - { url = "https://files.pythonhosted.org/packages/cb/b5/fd9f8b5a84010ca169ee49f4e4ad6f8c05f4e3545b72ee041dbbcb159882/cffi-1.17.1-cp39-cp39-win32.whl", hash = "sha256:e31ae45bc2e29f6b2abd0de1cc3b9d5205aa847cafaecb8af1476a609a2f6eb7", size = 171820 }, - { url = "https://files.pythonhosted.org/packages/8c/52/b08750ce0bce45c143e1b5d7357ee8c55341b52bdef4b0f081af1eb248c2/cffi-1.17.1-cp39-cp39-win_amd64.whl", hash = "sha256:d016c76bdd850f3c626af19b0542c9677ba156e4ee4fccfdd7848803533ef662", size = 181290 }, ] [[package]] @@ -233,21 +221,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d8/90/6af4cd042066a4adad58ae25648a12c09c879efa4849c705719ba1b23d8c/charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ffc519621dce0c767e96b9c53f09c5d215578e10b02c285809f76509a3931482", size = 144970 }, { url = "https://files.pythonhosted.org/packages/cc/67/e5e7e0cbfefc4ca79025238b43cdf8a2037854195b37d6417f3d0895c4c2/charset_normalizer-3.4.0-cp313-cp313-win32.whl", hash = "sha256:f19c1585933c82098c2a520f8ec1227f20e339e33aca8fa6f956f6691b784e67", size = 94973 }, { url = "https://files.pythonhosted.org/packages/65/97/fc9bbc54ee13d33dc54a7fcf17b26368b18505500fc01e228c27b5222d80/charset_normalizer-3.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:707b82d19e65c9bd28b81dde95249b07bf9f5b90ebe1ef17d9b57473f8a64b7b", size = 102308 }, - { url = "https://files.pythonhosted.org/packages/54/2f/28659eee7f5d003e0f5a3b572765bf76d6e0fe6601ab1f1b1dd4cba7e4f1/charset_normalizer-3.4.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:980b4f289d1d90ca5efcf07958d3eb38ed9c0b7676bf2831a54d4f66f9c27dfa", size = 196326 }, - { url = "https://files.pythonhosted.org/packages/d1/18/92869d5c0057baa973a3ee2af71573be7b084b3c3d428fe6463ce71167f8/charset_normalizer-3.4.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f28f891ccd15c514a0981f3b9db9aa23d62fe1a99997512b0491d2ed323d229a", size = 125614 }, - { url = "https://files.pythonhosted.org/packages/d6/27/327904c5a54a7796bb9f36810ec4173d2df5d88b401d2b95ef53111d214e/charset_normalizer-3.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8aacce6e2e1edcb6ac625fb0f8c3a9570ccc7bfba1f63419b3769ccf6a00ed0", size = 120450 }, - { url = "https://files.pythonhosted.org/packages/a4/23/65af317914a0308495133b2d654cf67b11bbd6ca16637c4e8a38f80a5a69/charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd7af3717683bea4c87acd8c0d3d5b44d56120b26fd3f8a692bdd2d5260c620a", size = 140135 }, - { url = "https://files.pythonhosted.org/packages/f2/41/6190102ad521a8aa888519bb014a74251ac4586cde9b38e790901684f9ab/charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5ff2ed8194587faf56555927b3aa10e6fb69d931e33953943bc4f837dfee2242", size = 150413 }, - { url = "https://files.pythonhosted.org/packages/7b/ab/f47b0159a69eab9bd915591106859f49670c75f9a19082505ff16f50efc0/charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e91f541a85298cf35433bf66f3fab2a4a2cff05c127eeca4af174f6d497f0d4b", size = 142992 }, - { url = "https://files.pythonhosted.org/packages/28/89/60f51ad71f63aaaa7e51a2a2ad37919985a341a1d267070f212cdf6c2d22/charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:309a7de0a0ff3040acaebb35ec45d18db4b28232f21998851cfa709eeff49d62", size = 144871 }, - { url = "https://files.pythonhosted.org/packages/0c/48/0050550275fea585a6e24460b42465020b53375017d8596c96be57bfabca/charset_normalizer-3.4.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:285e96d9d53422efc0d7a17c60e59f37fbf3dfa942073f666db4ac71e8d726d0", size = 146756 }, - { url = "https://files.pythonhosted.org/packages/dc/b5/47f8ee91455946f745e6c9ddbb0f8f50314d2416dd922b213e7d5551ad09/charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:5d447056e2ca60382d460a604b6302d8db69476fd2015c81e7c35417cfabe4cd", size = 141034 }, - { url = "https://files.pythonhosted.org/packages/84/79/5c731059ebab43e80bf61fa51666b9b18167974b82004f18c76378ed31a3/charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:20587d20f557fe189b7947d8e7ec5afa110ccf72a3128d61a2a387c3313f46be", size = 149434 }, - { url = "https://files.pythonhosted.org/packages/ca/f3/0719cd09fc4dc42066f239cb3c48ced17fc3316afca3e2a30a4756fe49ab/charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:130272c698667a982a5d0e626851ceff662565379baf0ff2cc58067b81d4f11d", size = 152443 }, - { url = "https://files.pythonhosted.org/packages/f7/0e/c6357297f1157c8e8227ff337e93fd0a90e498e3d6ab96b2782204ecae48/charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:ab22fbd9765e6954bc0bcff24c25ff71dcbfdb185fcdaca49e81bac68fe724d3", size = 150294 }, - { url = "https://files.pythonhosted.org/packages/54/9a/acfa96dc4ea8c928040b15822b59d0863d6e1757fba8bd7de3dc4f761c13/charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:7782afc9b6b42200f7362858f9e73b1f8316afb276d316336c0ec3bd73312742", size = 145314 }, - { url = "https://files.pythonhosted.org/packages/73/1c/b10a63032eaebb8d7bcb8544f12f063f41f5f463778ac61da15d9985e8b6/charset_normalizer-3.4.0-cp39-cp39-win32.whl", hash = "sha256:2de62e8801ddfff069cd5c504ce3bc9672b23266597d4e4f50eda28846c322f2", size = 94724 }, - { url = "https://files.pythonhosted.org/packages/c5/77/3a78bf28bfaa0863f9cfef278dbeadf55efe064eafff8c7c424ae3c4c1bf/charset_normalizer-3.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:95c3c157765b031331dd4db3c775e58deaee050a3042fcad72cbc4189d7c8dca", size = 102159 }, { url = "https://files.pythonhosted.org/packages/bf/9b/08c0432272d77b04803958a4598a51e2a4b51c06640af8b8f0f908c18bf2/charset_normalizer-3.4.0-py3-none-any.whl", hash = "sha256:fe9f97feb71aa9896b81973a7bbada8c49501dc73e58a10fcef6663af95e5079", size = 49446 }, ] @@ -328,10 +301,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/93/90/116edd5f8ec23b2dc879f7a42443e073cdad22950d3c8ee834e3b8124543/cryptography-43.0.3-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:a2a431ee15799d6db9fe80c82b055bae5a752bef645bba795e8e52687c69efe3", size = 3679828 }, { url = "https://files.pythonhosted.org/packages/d8/32/1e1d78b316aa22c0ba6493cc271c1c309969e5aa5c22c830a1d7ce3471e6/cryptography-43.0.3-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:281c945d0e28c92ca5e5930664c1cefd85efe80e5c0d2bc58dd63383fda29f83", size = 3908132 }, { url = "https://files.pythonhosted.org/packages/91/bb/cd2c13be3332e7af3cdf16154147952d39075b9f61ea5e6b5241bf4bf436/cryptography-43.0.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:f18c716be16bc1fea8e95def49edf46b82fccaa88587a45f8dc0ff6ab5d8e0a7", size = 2988811 }, - { url = "https://files.pythonhosted.org/packages/cc/fc/ff7c76afdc4f5933b5e99092528d4783d3d1b131960fc8b31eb38e076ca8/cryptography-43.0.3-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:4a02ded6cd4f0a5562a8887df8b3bd14e822a90f97ac5e544c162899bc467664", size = 3146844 }, - { url = "https://files.pythonhosted.org/packages/d7/29/a233efb3e98b13d9175dcb3c3146988ec990896c8fa07e8467cce27d5a80/cryptography-43.0.3-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:53a583b6637ab4c4e3591a15bc9db855b8d9dee9a669b550f311480acab6eb08", size = 3681997 }, - { url = "https://files.pythonhosted.org/packages/c0/cf/c9eea7791b961f279fb6db86c3355cfad29a73141f46427af71852b23b95/cryptography-43.0.3-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:1ec0bcf7e17c0c5669d881b1cd38c4972fade441b27bda1051665faaa89bdcaa", size = 3905208 }, - { url = "https://files.pythonhosted.org/packages/21/ea/6c38ca546d5b6dab3874c2b8fc6b1739baac29bacdea31a8c6c0513b3cfa/cryptography-43.0.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2ce6fae5bdad59577b44e4dfed356944fbf1d925269114c28be377692643b4ff", size = 2989787 }, ] [[package]] @@ -525,7 +494,6 @@ dependencies = [ { name = "pygments" }, { name = "stack-data" }, { name = "traitlets" }, - { name = "typing-extensions", marker = "python_full_version < '3.10'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/b1/b9/3ba6c45a6df813c09a48bac313c22ff83efa26cbb55011218d925a46e2ad/ipython-8.18.1.tar.gz", hash = "sha256:ca6f079bb33457c66e233e4580ebfc4128855b4cf6370dddd73842a9563e8a27", size = 5486330 } wheels = [ @@ -724,17 +692,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d6/b9/fb620dd47fc7cc9678af8f8bd8c772034ca4977237049287e99dda360b66/pillow-11.0.0-cp313-cp313t-win32.whl", hash = "sha256:607bbe123c74e272e381a8d1957083a9463401f7bd01287f50521ecb05a313f8", size = 2253197 }, { url = "https://files.pythonhosted.org/packages/df/86/25dde85c06c89d7fc5db17940f07aae0a56ac69aa9ccb5eb0f09798862a8/pillow-11.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:5c39ed17edea3bc69c743a8dd3e9853b7509625c2462532e62baa0732163a904", size = 2572169 }, { url = "https://files.pythonhosted.org/packages/51/85/9c33f2517add612e17f3381aee7c4072779130c634921a756c97bc29fb49/pillow-11.0.0-cp313-cp313t-win_arm64.whl", hash = "sha256:75acbbeb05b86bc53cbe7b7e6fe00fbcf82ad7c684b3ad82e3d711da9ba287d3", size = 2256828 }, - { url = "https://files.pythonhosted.org/packages/f3/8b/01849a820686bf309b7d79a935d57bcafbfd016f1d78fc3d37ed2ba00f96/pillow-11.0.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:2e46773dc9f35a1dd28bd6981332fd7f27bec001a918a72a79b4133cf5291dba", size = 3154738 }, - { url = "https://files.pythonhosted.org/packages/35/e8/ff71a40ca8e24cfd6bb333cc4ca8cc24ebecb6942bb4ad1e5ec61f33d1b8/pillow-11.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2679d2258b7f1192b378e2893a8a0a0ca472234d4c2c0e6bdd3380e8dfa21b6a", size = 2979272 }, - { url = "https://files.pythonhosted.org/packages/09/4f/2280ad43f5639174a0227920a59664fb78c5096a0b3fd865fee5184d4526/pillow-11.0.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eda2616eb2313cbb3eebbe51f19362eb434b18e3bb599466a1ffa76a033fb916", size = 4179756 }, - { url = "https://files.pythonhosted.org/packages/14/b1/c8f428bae932a27ce9c87e7b21aba8ea3e820aa11413c5a795868c37e039/pillow-11.0.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:20ec184af98a121fb2da42642dea8a29ec80fc3efbaefb86d8fdd2606619045d", size = 4280488 }, - { url = "https://files.pythonhosted.org/packages/78/66/7c5e44ab2c0123710a5d4692a4ee5931ac438efd7730ac395e305902346e/pillow-11.0.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:8594f42df584e5b4bb9281799698403f7af489fba84c34d53d1c4bfb71b7c4e7", size = 4192772 }, - { url = "https://files.pythonhosted.org/packages/36/5d/a9a00f8251ce93144f0250c0f0aece31b83ff33ffc243cdf987a8d584818/pillow-11.0.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:c12b5ae868897c7338519c03049a806af85b9b8c237b7d675b8c5e089e4a618e", size = 4363533 }, - { url = "https://files.pythonhosted.org/packages/fd/21/d8182fc1f3233078eb744f9f2950992f537655174febb8b3f7bdc61847b1/pillow-11.0.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:70fbbdacd1d271b77b7721fe3cdd2d537bbbd75d29e6300c672ec6bb38d9672f", size = 4275415 }, - { url = "https://files.pythonhosted.org/packages/c9/ee/93e02e8c29210ba7383843405b8b39bd19a164770f14d8569096dd123781/pillow-11.0.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:5178952973e588b3f1360868847334e9e3bf49d19e169bbbdfaf8398002419ae", size = 4407081 }, - { url = "https://files.pythonhosted.org/packages/6e/77/8cda03af2b5177a18d645ad4a7446cda6c1292d1a2fb6e772a06fa9fc86b/pillow-11.0.0-cp39-cp39-win32.whl", hash = "sha256:8c676b587da5673d3c75bd67dd2a8cdfeb282ca38a30f37950511766b26858c4", size = 2249213 }, - { url = "https://files.pythonhosted.org/packages/9f/e4/c90bf7889489f3a14803bd00d3645945dd476020ab67579985af8233ab30/pillow-11.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:94f3e1780abb45062287b4614a5bc0874519c86a777d4a7ad34978e86428b8dd", size = 2566862 }, - { url = "https://files.pythonhosted.org/packages/27/a6/77d2ed085055237581d6276ac1e85f562f1b1848614647d8427e49d83c03/pillow-11.0.0-cp39-cp39-win_arm64.whl", hash = "sha256:290f2cc809f9da7d6d622550bbf4c1e57518212da51b6a30fe8e0a270a5b78bd", size = 2254605 }, { url = "https://files.pythonhosted.org/packages/36/57/42a4dd825eab762ba9e690d696d894ba366e06791936056e26e099398cda/pillow-11.0.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:1187739620f2b365de756ce086fdb3604573337cc28a0d3ac4a01ab6b2d2a6d2", size = 3119239 }, { url = "https://files.pythonhosted.org/packages/98/f7/25f9f9e368226a1d6cf3507081a1a7944eddd3ca7821023377043f5a83c8/pillow-11.0.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:fbbcb7b57dc9c794843e3d1258c0fbf0f48656d46ffe9e09b63bbd6e8cd5d0a2", size = 2950803 }, { url = "https://files.pythonhosted.org/packages/59/01/98ead48a6c2e31e6185d4c16c978a67fe3ccb5da5c2ff2ba8475379bb693/pillow-11.0.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d203af30149ae339ad1b4f710d9844ed8796e97fda23ffbc4cc472968a47d0b", size = 3281098 }, @@ -742,10 +699,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/0e/75/689b4ec0483c42bfc7d1aacd32ade7a226db4f4fac57c6fdcdf90c0731e3/pillow-11.0.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:73853108f56df97baf2bb8b522f3578221e56f646ba345a372c78326710d3830", size = 3310533 }, { url = "https://files.pythonhosted.org/packages/3d/30/38bd6149cf53da1db4bad304c543ade775d225961c4310f30425995cb9ec/pillow-11.0.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:e58876c91f97b0952eb766123bfef372792ab3f4e3e1f1a2267834c2ab131734", size = 3414886 }, { url = "https://files.pythonhosted.org/packages/ec/3d/c32a51d848401bd94cabb8767a39621496491ee7cd5199856b77da9b18ad/pillow-11.0.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:224aaa38177597bb179f3ec87eeefcce8e4f85e608025e9cfac60de237ba6316", size = 2567508 }, - { url = "https://files.pythonhosted.org/packages/67/21/fbb4222399f72d6e9c828818ff4ef8391c1e8e71623368295c8dbc789bd1/pillow-11.0.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:5bd2d3bdb846d757055910f0a59792d33b555800813c3b39ada1829c372ccb06", size = 2950706 }, - { url = "https://files.pythonhosted.org/packages/a2/b6/6aeb6e018b705ea4076db50aac078c9db8715a901f4c65698edc31375d0f/pillow-11.0.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:375b8dd15a1f5d2feafff536d47e22f69625c1aa92f12b339ec0b2ca40263273", size = 3323524 }, - { url = "https://files.pythonhosted.org/packages/48/26/36cc90e9932c5fe7c8876c32d6091ef5a09e8137e8e0633045bd35085fdd/pillow-11.0.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:daffdf51ee5db69a82dd127eabecce20729e21f7a3680cf7cbb23f0829189790", size = 3414787 }, - { url = "https://files.pythonhosted.org/packages/44/5c/089154029fcca7729ae142ac820057f74ca4b0b59617734276c31281af15/pillow-11.0.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7326a1787e3c7b0429659e0a944725e1b03eeaa10edd945a86dead1913383944", size = 2567664 }, ] [[package]] @@ -874,19 +827,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/cd/e6/a202f0e1b81c729130404e82d9de90dc4418ec01df35000d48d027c38501/pydantic_core-2.27.0-cp313-none-win32.whl", hash = "sha256:678f66462058dd978702db17eb6a3633d634f7aa0deaea61e0a674152766d3fc", size = 1830973 }, { url = "https://files.pythonhosted.org/packages/06/3d/21ed0f308e6618ce6c5c6bfb9e71734a9a3256d5474a53c8e5aaaba498ca/pydantic_core-2.27.0-cp313-none-win_amd64.whl", hash = "sha256:d28ca7066d6cdd347a50d8b725dc10d9a1d6a1cce09836cf071ea6a2d4908be0", size = 1974853 }, { url = "https://files.pythonhosted.org/packages/d7/18/e5744a132b81f98b9f92e15f33f03229a1d254ce7af942b1422ec2ac656f/pydantic_core-2.27.0-cp313-none-win_arm64.whl", hash = "sha256:6f4a53af9e81d757756508b57cae1cf28293f0f31b9fa2bfcb416cc7fb230f9d", size = 1877469 }, - { url = "https://files.pythonhosted.org/packages/00/e4/4d6d9193a33c964920bf56fcbe11fa30511d3d900a81c740b0157579b122/pydantic_core-2.27.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:4148dc9184ab79e356dc00a4199dc0ee8647973332cb385fc29a7cced49b9f9c", size = 1894360 }, - { url = "https://files.pythonhosted.org/packages/f4/46/9d27771309609126678dee81e8e93188dbd0515a543b27e0a01a806c1893/pydantic_core-2.27.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5fc72fbfebbf42c0856a824b8b0dc2b5cd2e4a896050281a21cfa6fed8879cb1", size = 1773921 }, - { url = "https://files.pythonhosted.org/packages/a0/3a/3a6a4cee7bc11bcb3f8859a63c6b4d88b8df66ad7c9c9e6d667dd894b439/pydantic_core-2.27.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:185ef205256cd8b38431205698531026979db89a79587725c1e55c59101d64e9", size = 1829480 }, - { url = "https://files.pythonhosted.org/packages/2b/aa/ecf0fcee9031eef516cef2e336d403a61bd8df75ab17a856bc29f3eb07d4/pydantic_core-2.27.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:395e3e1148fa7809016231f8065f30bb0dc285a97b4dc4360cd86e17bab58af7", size = 1849759 }, - { url = "https://files.pythonhosted.org/packages/b6/17/8953bbbe7d3c015bdfa34171ba1738a43682d770e68c87171dd8887035c3/pydantic_core-2.27.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:33d14369739c5d07e2e7102cdb0081a1fa46ed03215e07f097b34e020b83b1ae", size = 2035679 }, - { url = "https://files.pythonhosted.org/packages/ec/19/514fdf2f684003961b6f34543f0bdf3be2e0f17b8b25cd8d44c343521148/pydantic_core-2.27.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e7820bb0d65e3ce1e3e70b6708c2f66143f55912fa02f4b618d0f08b61575f12", size = 2773208 }, - { url = "https://files.pythonhosted.org/packages/9a/37/2cdd48b7367fbf0576d16402837212d2b1798aa4ea887f1795f8ddbace07/pydantic_core-2.27.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:43b61989068de9ce62296cde02beffabcadb65672207fc51e7af76dca75e6636", size = 2130616 }, - { url = "https://files.pythonhosted.org/packages/3a/6c/fa100356e1c8f749797d88401a1d5ed8d458705d43e259931681b5b96ab4/pydantic_core-2.27.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:15e350efb67b855cd014c218716feea4986a149ed1f42a539edd271ee074a196", size = 1981857 }, - { url = "https://files.pythonhosted.org/packages/0f/3d/36c0c832c1fd1351c495bf1495b61b2e40248c54f7874e6df439e6ffb9a5/pydantic_core-2.27.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:433689845288f9a1ee5714444e65957be26d30915f7745091ede4a83cfb2d7bb", size = 1992515 }, - { url = "https://files.pythonhosted.org/packages/99/12/ee67e29369b368c404c6aead492e1528ec887609d388a7a30b675b969b82/pydantic_core-2.27.0-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:3fd8bc2690e7c39eecdf9071b6a889ce7b22b72073863940edc2a0a23750ca90", size = 2087604 }, - { url = "https://files.pythonhosted.org/packages/0e/6c/72ca869aabe190e4cd36b03226286e430a1076c367097c77cb0704b1cbb3/pydantic_core-2.27.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:884f1806609c2c66564082540cffc96868c5571c7c3cf3a783f63f2fb49bd3cd", size = 2141000 }, - { url = "https://files.pythonhosted.org/packages/5c/b8/e7499cfa6f1e46e92a645e74198b7bb9ce3d49e82f626a02726dc917fc74/pydantic_core-2.27.0-cp39-none-win32.whl", hash = "sha256:bf37b72834e7239cf84d4a0b2c050e7f9e48bced97bad9bdf98d26b8eb72e846", size = 1813857 }, - { url = "https://files.pythonhosted.org/packages/2e/27/81203aa6cbf68772afd9c3877ce2e35878f434e824aad4047e7cfd3bc14d/pydantic_core-2.27.0-cp39-none-win_amd64.whl", hash = "sha256:31a2cae5f059329f9cfe3d8d266d3da1543b60b60130d186d9b6a3c20a346361", size = 1974744 }, { url = "https://files.pythonhosted.org/packages/d3/ad/c1dc814ab524cb247ceb6cb25236895a5cae996c438baf504db610fd6c92/pydantic_core-2.27.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:4fb49cfdb53af5041aba909be00cccfb2c0d0a2e09281bf542371c5fd36ad04c", size = 1889233 }, { url = "https://files.pythonhosted.org/packages/24/bb/069a9dd910e6c09aab90a118c08d3cb30dc5738550e9f2d21f3b086352c2/pydantic_core-2.27.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:49633583eb7dc5cba61aaf7cdb2e9e662323ad394e543ee77af265736bcd3eaa", size = 1768419 }, { url = "https://files.pythonhosted.org/packages/cb/a1/f9b4e625ee8c7f683c8295c85d11f79a538eb53719f326646112a7800bda/pydantic_core-2.27.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:153017e3d6cd3ce979de06d84343ca424bb6092727375eba1968c8b4693c6ecb", size = 1822870 }, @@ -896,15 +836,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d9/a8/9c1557d5282108916448415e85f829b70ba99d97f03cee0e40a296e58a65/pydantic_core-2.27.0-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:68ef5377eb582fa4343c9d0b57a5b094046d447b4c73dd9fbd9ffb216f829e7d", size = 2073399 }, { url = "https://files.pythonhosted.org/packages/ca/b0/5296273d652fa9aa140771b3f4bb574edd3cbf397090625b988f6a57b02b/pydantic_core-2.27.0-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:c5726eec789ee38f2c53b10b1821457b82274f81f4f746bb1e666d8741fcfadb", size = 2129499 }, { url = "https://files.pythonhosted.org/packages/e9/fd/7f39ff702fdca954f26c84b40d9bf744733bb1a50ca6b7569822b9cbb7f4/pydantic_core-2.27.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c0c431e4be5c1a0c6654e0c31c661cd89e0ca956ef65305c3c3fd96f4e72ca39", size = 1997246 }, - { url = "https://files.pythonhosted.org/packages/bb/4f/76f1ac16a0c277a3a8be2b5b52b0a09929630e794fb1938c4cd85396c34f/pydantic_core-2.27.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:8e21d927469d04b39386255bf00d0feedead16f6253dcc85e9e10ddebc334084", size = 1889486 }, - { url = "https://files.pythonhosted.org/packages/f3/96/4ff5a8ec0c457afcd87334d4e2f6fd25df6642b4ff8bf587316dd6eccd59/pydantic_core-2.27.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:4b51f964fcbb02949fc546022e56cdb16cda457af485e9a3e8b78ac2ecf5d77e", size = 1768718 }, - { url = "https://files.pythonhosted.org/packages/52/21/e7bab7b9674d5b1a8cf06939929991753e4b814b01bae29321a8739990b3/pydantic_core-2.27.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25a7fd4de38f7ff99a37e18fa0098c3140286451bc823d1746ba80cec5b433a1", size = 1823291 }, - { url = "https://files.pythonhosted.org/packages/1d/68/d1868a78ce0d776c3e04179fbfa6272e72d4363c49f9bdecfe4b2007dd75/pydantic_core-2.27.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fda87808429c520a002a85d6e7cdadbf58231d60e96260976c5b8f9a12a8e13", size = 1977040 }, - { url = "https://files.pythonhosted.org/packages/68/7b/2e361ff81f60c4c28f65b53670436849ec716366d4f1635ea243a31903a2/pydantic_core-2.27.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8a150392102c402c538190730fda06f3bce654fc498865579a9f2c1d2b425833", size = 1973909 }, - { url = "https://files.pythonhosted.org/packages/a8/44/a4a3718f3b148526baccdb9a0bc8e6b7aa840c796e637805c04aaf1a74c3/pydantic_core-2.27.0-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:c9ed88b398ba7e3bad7bd64d66cc01dcde9cfcb7ec629a6fd78a82fa0b559d78", size = 1985091 }, - { url = "https://files.pythonhosted.org/packages/3a/79/2cdf503e8aac926a99d64b2a02642ab1377146999f9a68536c54bd8b2c46/pydantic_core-2.27.0-pp39-pypy39_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:9fe94d9d2a2b4edd7a4b22adcd45814b1b59b03feb00e56deb2e89747aec7bfe", size = 2073484 }, - { url = "https://files.pythonhosted.org/packages/e8/15/74c61b7ea348b252fe97a32e5b531fdde331710db80e9b0fae1302023414/pydantic_core-2.27.0-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:d8b5ee4ae9170e2775d495b81f414cc20268041c42571530513496ba61e94ba3", size = 2129473 }, - { url = "https://files.pythonhosted.org/packages/57/81/0e9ebcc80b107e1dfacc677ad7c2ab0202cc0e10ba76b23afbb147ac32fb/pydantic_core-2.27.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:d29e235ce13c91902ef3efc3d883a677655b3908b1cbc73dee816e5e1f8f7739", size = 1997389 }, ] [[package]] @@ -1081,15 +1012,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", size = 751597 }, { url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527 }, { url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446 }, - { url = "https://files.pythonhosted.org/packages/65/d8/b7a1db13636d7fb7d4ff431593c510c8b8fca920ade06ca8ef20015493c5/PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d", size = 184777 }, - { url = "https://files.pythonhosted.org/packages/0a/02/6ec546cd45143fdf9840b2c6be8d875116a64076218b61d68e12548e5839/PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f", size = 172318 }, - { url = "https://files.pythonhosted.org/packages/0e/9a/8cc68be846c972bda34f6c2a93abb644fb2476f4dcc924d52175786932c9/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290", size = 720891 }, - { url = "https://files.pythonhosted.org/packages/e9/6c/6e1b7f40181bc4805e2e07f4abc10a88ce4648e7e95ff1abe4ae4014a9b2/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12", size = 722614 }, - { url = "https://files.pythonhosted.org/packages/3d/32/e7bd8535d22ea2874cef6a81021ba019474ace0d13a4819c2a4bce79bd6a/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19", size = 737360 }, - { url = "https://files.pythonhosted.org/packages/d7/12/7322c1e30b9be969670b672573d45479edef72c9a0deac3bb2868f5d7469/PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e", size = 699006 }, - { url = "https://files.pythonhosted.org/packages/82/72/04fcad41ca56491995076630c3ec1e834be241664c0c09a64c9a2589b507/PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725", size = 723577 }, - { url = "https://files.pythonhosted.org/packages/ed/5e/46168b1f2757f1fcd442bc3029cd8767d88a98c9c05770d8b420948743bb/PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631", size = 144593 }, - { url = "https://files.pythonhosted.org/packages/19/87/5124b1c1f2412bb95c59ec481eaf936cd32f0fe2a7b16b97b81c4c017a6a/PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8", size = 162312 }, ] [[package]] @@ -1227,7 +1149,6 @@ version = "0.41.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, - { name = "typing-extensions", marker = "python_full_version < '3.10'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/1a/4c/9b5764bd22eec91c4039ef4c55334e9187085da2d8a2df7bd570869aae18/starlette-0.41.3.tar.gz", hash = "sha256:0e4ab3d16522a255be6b28260b938eae2482f98ce5cc934cb08dce8dc3ba5835", size = 2574159 } wheels = [ @@ -1367,16 +1288,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/14/26/93a9fa02c6f257df54d7570dfe8011995138118d11939a4ecd82cb849613/wrapt-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:418abb18146475c310d7a6dc71143d6f7adec5b004ac9ce08dc7a34e2babdc5c", size = 91738 }, { url = "https://files.pythonhosted.org/packages/a2/5b/4660897233eb2c8c4de3dc7cefed114c61bacb3c28327e64150dc44ee2f6/wrapt-1.16.0-cp312-cp312-win32.whl", hash = "sha256:685f568fa5e627e93f3b52fda002c7ed2fa1800b50ce51f6ed1d572d8ab3e7fc", size = 35568 }, { url = "https://files.pythonhosted.org/packages/5c/cc/8297f9658506b224aa4bd71906447dea6bb0ba629861a758c28f67428b91/wrapt-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:dcdba5c86e368442528f7060039eda390cc4091bfd1dca41e8046af7c910dda8", size = 37653 }, - { url = "https://files.pythonhosted.org/packages/70/cc/b92e1da2cad6a9f8ee481000ece07a35e3b24e041e60ff8b850c079f0ebf/wrapt-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9b201ae332c3637a42f02d1045e1d0cccfdc41f1f2f801dafbaa7e9b4797bfc2", size = 37314 }, - { url = "https://files.pythonhosted.org/packages/4a/cc/3402bcc897978be00fef608cd9e3e39ec8869c973feeb5e1e277670e5ad2/wrapt-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2076fad65c6736184e77d7d4729b63a6d1ae0b70da4868adeec40989858eb3fb", size = 38162 }, - { url = "https://files.pythonhosted.org/packages/28/d3/4f079f649c515727c127c987b2ec2e0816b80d95784f2d28d1a57d2a1029/wrapt-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5cd603b575ebceca7da5a3a251e69561bec509e0b46e4993e1cac402b7247b8", size = 80235 }, - { url = "https://files.pythonhosted.org/packages/a3/1c/226c2a4932e578a2241dcb383f425995f80224b446f439c2e112eb51c3a6/wrapt-1.16.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b47cfad9e9bbbed2339081f4e346c93ecd7ab504299403320bf85f7f85c7d46c", size = 72553 }, - { url = "https://files.pythonhosted.org/packages/b1/e7/459a8a4f40f2fa65eb73cb3f339e6d152957932516d18d0e996c7ae2d7ae/wrapt-1.16.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8212564d49c50eb4565e502814f694e240c55551a5f1bc841d4fcaabb0a9b8a", size = 80129 }, - { url = "https://files.pythonhosted.org/packages/da/6f/6d0b3c4983f1fc764a422989dabc268ee87d937763246cd48aa92f1eed1e/wrapt-1.16.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5f15814a33e42b04e3de432e573aa557f9f0f56458745c2074952f564c50e664", size = 84550 }, - { url = "https://files.pythonhosted.org/packages/96/e8/27ef35cf61e5147c1c3abcb89cfbb8d691b2bb8364803fcc950140bc14d8/wrapt-1.16.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db2e408d983b0e61e238cf579c09ef7020560441906ca990fe8412153e3b291f", size = 77352 }, - { url = "https://files.pythonhosted.org/packages/b6/ad/7a0766341081bfd9f18a7049e4d6d45586ae5c5bb0a640f05e2f558e849c/wrapt-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:edfad1d29c73f9b863ebe7082ae9321374ccb10879eeabc84ba3b69f2579d537", size = 84626 }, - { url = "https://files.pythonhosted.org/packages/09/43/b26852e9c45a1aac0d14b1080b25b612fa840ba99739c5fc55db07b7ce08/wrapt-1.16.0-cp39-cp39-win32.whl", hash = "sha256:ed867c42c268f876097248e05b6117a65bcd1e63b779e916fe2e33cd6fd0d3c3", size = 35327 }, - { url = "https://files.pythonhosted.org/packages/74/f2/96ed140b08743f7f68d5bda35a2a589600781366c3da96f056043d258b1a/wrapt-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:eb1b046be06b0fce7249f1d025cd359b4b80fc1c3e24ad9eca33e0dcdb2e4a35", size = 37526 }, { url = "https://files.pythonhosted.org/packages/ff/21/abdedb4cdf6ff41ebf01a74087740a709e2edb146490e4d9beea054b0b7a/wrapt-1.16.0-py3-none-any.whl", hash = "sha256:6906c4100a8fcbf2fa735f6059214bb13b97f75b1a61777fcf6432121ef12ef1", size = 23362 }, ] From f561cba5f9473cbd0568bdddc4654305a38f7527 Mon Sep 17 00:00:00 2001 From: Meni Yakove Date: Thu, 21 Nov 2024 15:25:41 +0200 Subject: [PATCH 03/31] Fix get fils branch name --- webhook_server_container/app.py | 3 ++- webhook_server_container/libs/github_api.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/webhook_server_container/app.py b/webhook_server_container/app.py index 26999192..024f8002 100644 --- a/webhook_server_container/app.py +++ b/webhook_server_container/app.py @@ -44,7 +44,8 @@ async def process_webhook(request: Request) -> Dict[str, Any]: api.process() return {"status": requests.codes.ok, "message": "process success", "log_prefix": delivery_headers} - except Exception as _: + except Exception as exp: + logger.error(f"Error: {exp}") exc_type, exc_obj, exc_tb = sys.exc_info() # noqa: F841 msg = f"Error: {exc_type}" diff --git a/webhook_server_container/libs/github_api.py b/webhook_server_container/libs/github_api.py index effc09c1..ccfe91be 100644 --- a/webhook_server_container/libs/github_api.py +++ b/webhook_server_container/libs/github_api.py @@ -192,7 +192,6 @@ def process(self) -> None: return event_log: str = f"Event type: {self.github_event}. event ID: {self.x_github_delivery}" - self.approvers_and_reviewers = self.get_approvers_and_reviewers() try: self.pull_request = self._get_pull_request() @@ -202,6 +201,7 @@ def process(self) -> None: self.parent_committer = self.pull_request.user.login self.last_committer = getattr(self.last_commit.committer, "login", self.parent_committer) self.pull_request_branch = self.pull_request.base.ref + self.approvers_and_reviewers = self.get_approvers_and_reviewers() if self.jira_enabled_repository: self.set_jira_in_pull_request() From c96bec06cd1762f6d98cbc3e9688f3bbae22afc4 Mon Sep 17 00:00:00 2001 From: Meni Yakove Date: Thu, 21 Nov 2024 15:41:03 +0200 Subject: [PATCH 04/31] Address comments --- pyproject.toml | 1 - uv.lock | 4 +-- webhook_server_container/libs/github_api.py | 28 +++++++++------------ 3 files changed, 13 insertions(+), 20 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 08ec2db0..0c5420cc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -38,7 +38,6 @@ dependencies = [ "colorama>=0.4.6", "colorlog>=6.8.2", "fastapi>=0.115.0", - "ipdb>=0.13.13", "jira>=3.8.0", "pygithub>=2.4.0", "pyhelper-utils>=0.0.42", diff --git a/uv.lock b/uv.lock index 8444a67d..510aded3 100644 --- a/uv.lock +++ b/uv.lock @@ -374,7 +374,6 @@ dependencies = [ { name = "colorama" }, { name = "colorlog" }, { name = "fastapi" }, - { name = "ipdb" }, { name = "jira" }, { name = "pygithub" }, { name = "pyhelper-utils" }, @@ -403,7 +402,6 @@ requires-dist = [ { name = "colorama", specifier = ">=0.4.6" }, { name = "colorlog", specifier = ">=6.8.2" }, { name = "fastapi", specifier = ">=0.115.0" }, - { name = "ipdb", specifier = ">=0.13.13" }, { name = "jira", specifier = ">=3.8.0" }, { name = "pygithub", specifier = ">=2.4.0" }, { name = "pyhelper-utils", specifier = ">=0.0.42" }, @@ -449,7 +447,7 @@ name = "importlib-metadata" version = "8.5.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "zipp" }, + { name = "zipp", marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/cd/12/33e59336dca5be0c398a7482335911a33aa0e20776128f038019f1a95f1b/importlib_metadata-8.5.0.tar.gz", hash = "sha256:71522656f0abace1d072b9e5481a48f07c138e00f079c38c8f883823f9c26bd7", size = 55304 } wheels = [ diff --git a/webhook_server_container/libs/github_api.py b/webhook_server_container/libs/github_api.py index ccfe91be..f2de54ad 100644 --- a/webhook_server_container/libs/github_api.py +++ b/webhook_server_container/libs/github_api.py @@ -610,11 +610,12 @@ def assign_reviewers(self) -> None: self.logger.info(f"{self.log_prefix} Assign reviewers") changed_files = self.list_changed_commit_files() reviewers_to_add = self.reviewers - changed_folders = [str(Path(cf).parent) for cf in changed_files] + changed_folders = [Path(cf).parent for cf in changed_files] for changed_folder_path in changed_folders: for owners_path, owners_data in self.approvers_and_reviewers.items(): - if owners_path in changed_folder_path: + owners_dir = Path(owners_path).parent + if owners_dir == changed_folder_path or owners_dir in changed_folder_path.parents: reviewers_to_add.extend(owners_data.get("reviewers", [])) _to_add: List[str] = list(set(reviewers_to_add)) @@ -2055,20 +2056,15 @@ def run_podman_command(self, command: str, pipe: bool = False) -> Tuple[bool, st return rc, out, err def get_approvers_and_reviewers(self) -> dict[str, dict[str, list[str]]]: - # owners hole mapping of OWNERS file full path: dict with `approvers` and `reviewers` each hold a list of names + # Dictionary mapping OWNERS file paths to their approvers and reviewers _owners: dict[str, dict[str, list[str]]] = {} - - for obj in self.repository._requester.requestJsonAndCheck( - "GET", f"{self.repository.url}/git/trees/{self.pull_request_branch}?recursive=1" - ): - for content in obj.get("tree", []): - if content_path := content.get("path", ""): - if content.get("type") == "blob" and content_path.endswith("OWNERS"): - _path = self.repository.get_contents(content_path) - if isinstance(_path, list): - _path = _path[0] - - _owners[content_path] = yaml.safe_load(_path.decoded_content) - + tree = self.repository.get_git_tree(self.pull_request_branch, recursive=True) + for element in tree.tree: + if element.type == "blob" and element.path.endswith("OWNERS"): + content_path = element.path + _path = self.repository.get_contents(content_path) + if isinstance(_path, list): + _path = _path[0] + _owners[content_path] = yaml.safe_load(_path.decoded_content) self.logger.debug(f"{self.log_prefix} Owners file mapping: {_owners}") return _owners From 2d8fb13521c7f4c83a3c9f832705e1ce2d041471 Mon Sep 17 00:00:00 2001 From: Meni Yakove Date: Thu, 21 Nov 2024 15:47:08 +0200 Subject: [PATCH 05/31] Remove =0.13.13", "ipython>=8.12.3"] [project] name = "github-webhook-server" version = "2.0.0" -requires-python = ">=3.10, <3.13" +requires-python = ">=3.10" description = "A webhook server to manage Github reposotories and pull requests." readme = "README.md" license = "Apache-2.0" diff --git a/uv.lock b/uv.lock index 510aded3..867158ee 100644 --- a/uv.lock +++ b/uv.lock @@ -1,5 +1,5 @@ version = 1 -requires-python = ">=3.10, <3.13" +requires-python = ">=3.10" resolution-markers = [ "python_full_version < '3.11'", "python_full_version >= '3.11'", @@ -447,7 +447,7 @@ name = "importlib-metadata" version = "8.5.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "zipp", marker = "python_full_version < '3.11'" }, + { name = "zipp" }, ] sdist = { url = "https://files.pythonhosted.org/packages/cd/12/33e59336dca5be0c398a7482335911a33aa0e20776128f038019f1a95f1b/importlib_metadata-8.5.0.tar.gz", hash = "sha256:71522656f0abace1d072b9e5481a48f07c138e00f079c38c8f883823f9c26bd7", size = 55304 } wheels = [ From 48eda4548832392e1588efae3ac165dfde379207 Mon Sep 17 00:00:00 2001 From: Meni Yakove Date: Thu, 21 Nov 2024 15:54:52 +0200 Subject: [PATCH 06/31] Address comments --- webhook_server_container/libs/github_api.py | 30 ++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/webhook_server_container/libs/github_api.py b/webhook_server_container/libs/github_api.py index f2de54ad..6f2d7a64 100644 --- a/webhook_server_container/libs/github_api.py +++ b/webhook_server_container/libs/github_api.py @@ -576,6 +576,10 @@ def _error(_out: str, _err: str) -> None: self.send_slack_message(message=message, webhook_url=self.slack_webhook_url) def get_owners_content(self, folder_path: str = "") -> Dict[str, Any]: + if folder_path and ((".." in folder_path) or folder_path.startswith("/")): + self.logger.error(f"{self.log_prefix} Invalid folder path: {folder_path}") + return {} + try: owners_path = f"{folder_path}/OWNERS" if folder_path else "OWNERS" owners_content: list[ContentFile] | ContentFile = self.repository.get_contents(owners_path) @@ -2058,13 +2062,37 @@ def run_podman_command(self, command: str, pipe: bool = False) -> Tuple[bool, st def get_approvers_and_reviewers(self) -> dict[str, dict[str, list[str]]]: # Dictionary mapping OWNERS file paths to their approvers and reviewers _owners: dict[str, dict[str, list[str]]] = {} + + max_owners_files = 1000 # Configurable limit + owners_count = 0 + tree = self.repository.get_git_tree(self.pull_request_branch, recursive=True) for element in tree.tree: if element.type == "blob" and element.path.endswith("OWNERS"): + owners_count += 1 + if owners_count > max_owners_files: + self.logger.error(f"{self.log_prefix} Too many OWNERS files (>{max_owners_files})") + break + content_path = element.path _path = self.repository.get_contents(content_path) if isinstance(_path, list): _path = _path[0] - _owners[content_path] = yaml.safe_load(_path.decoded_content) + + try: + content = yaml.safe_load(_path.decoded_content) + # Validate structure + if not isinstance(content, dict): + raise ValueError("OWNERS file must contain a dictionary") + + for key in ["approvers", "reviewers"]: + if key in content and not isinstance(content[key], list): + raise ValueError(f"{key} must be a list") + + _owners[content_path] = content + except (yaml.YAMLError, ValueError) as e: + self.logger.error(f"{self.log_prefix} Invalid OWNERS file {content_path}: {e}") + continue + self.logger.debug(f"{self.log_prefix} Owners file mapping: {_owners}") return _owners From 1fa96234bfe9b8f7a825fd6ad2769793bca54c13 Mon Sep 17 00:00:00 2001 From: Meni Yakove Date: Thu, 21 Nov 2024 16:41:33 +0200 Subject: [PATCH 07/31] rename approvers/reviewers functions to root_ --- webhook_server_container/libs/github_api.py | 53 ++++++++++++--------- 1 file changed, 31 insertions(+), 22 deletions(-) diff --git a/webhook_server_container/libs/github_api.py b/webhook_server_container/libs/github_api.py index 6f2d7a64..3907b9e9 100644 --- a/webhook_server_container/libs/github_api.py +++ b/webhook_server_container/libs/github_api.py @@ -531,7 +531,7 @@ def _error(_out: str, _err: str) -> None: self.logger.error(f"{self.log_prefix} {err} - {_err}, {_out}") self.repository.create_issue( title=_err, - assignee=self.approvers[0] if self.approvers else "", + assignee=self.root_approvers[0] if self.root_approvers else "", body=f""" stdout: `{_out}` stderr: `{_err}` @@ -576,9 +576,17 @@ def _error(_out: str, _err: str) -> None: self.send_slack_message(message=message, webhook_url=self.slack_webhook_url) def get_owners_content(self, folder_path: str = "") -> Dict[str, Any]: - if folder_path and ((".." in folder_path) or folder_path.startswith("/")): - self.logger.error(f"{self.log_prefix} Invalid folder path: {folder_path}") - return {} + if folder_path: + # Normalize path and check for directory traversal + norm_path = os.path.normpath(folder_path) + if ( + norm_path.startswith("/") + or norm_path.startswith("\\") + or ".." in norm_path + or not all(part.isalnum() or part in "-_" for part in norm_path.split(os.path.sep)) + ): + self.logger.error(f"{self.log_prefix} Invalid folder path: {folder_path}") + return {} try: owners_path = f"{folder_path}/OWNERS" if folder_path else "OWNERS" @@ -596,13 +604,13 @@ def get_owners_content(self, folder_path: str = "") -> Dict[str, Any]: return {} @property - def reviewers(self) -> List[str]: + def root_reviewers(self) -> List[str]: _reviewers = self.approvers_and_reviewers.get("OWNERS", {}).get("reviewers", []) self.logger.debug(f"{self.log_prefix} Reviewers: {_reviewers}") return _reviewers @property - def approvers(self) -> List[str]: + def root_approvers(self) -> List[str]: _approvers = self.approvers_and_reviewers.get("OWNERS", {}).get("approvers", []) self.logger.debug(f"{self.log_prefix} Approvers: {_approvers}") return _approvers @@ -613,13 +621,13 @@ def list_changed_commit_files(self) -> list[str]: def assign_reviewers(self) -> None: self.logger.info(f"{self.log_prefix} Assign reviewers") changed_files = self.list_changed_commit_files() - reviewers_to_add = self.reviewers + reviewers_to_add = self.root_reviewers changed_folders = [Path(cf).parent for cf in changed_files] for changed_folder_path in changed_folders: - for owners_path, owners_data in self.approvers_and_reviewers.items(): - owners_dir = Path(owners_path).parent - if owners_dir == changed_folder_path or owners_dir in changed_folder_path.parents: + for owners_dir, owners_data in self.approvers_and_reviewers.items(): + _owners_dir = Path(owners_dir) + if _owners_dir == changed_folder_path or _owners_dir in changed_folder_path.parents: reviewers_to_add.extend(owners_data.get("reviewers", [])) _to_add: List[str] = list(set(reviewers_to_add)) @@ -947,7 +955,7 @@ def process_pull_request_webhook_data(self) -> None: _reviewer = labeled.split(CHANGED_REQUESTED_BY_LABEL_PREFIX)[-1] _approved_output: Dict[str, Any] = {"title": "Approved", "summary": "", "text": ""} - if _reviewer in self.approvers: + if _reviewer in self.root_approvers: _check_for_merge = True _approved_output["text"] += f"Approved by {_reviewer}.\n" @@ -1003,7 +1011,7 @@ def manage_reviewed_by_label(self, review_state: str, action: str, reviewed_user label_prefix: str = "" label_to_remove: str = "" - if reviewed_user in self.approvers: + if reviewed_user in self.root_approvers: approved_lgtm_label = APPROVED_BY_LABEL_PREFIX else: approved_lgtm_label = LGTM_BY_LABEL_PREFIX @@ -1162,7 +1170,7 @@ def user_commands(self, command: str, reviewed_user: str, issue_comment_id: int) elif _command == HOLD_LABEL_STR: self.create_comment_reaction(issue_comment_id=issue_comment_id, reaction=REACTIONS.ok) - if reviewed_user not in self.approvers: + if reviewed_user not in self.root_approvers: self.pull_request.create_issue_comment( f"{reviewed_user} is not part of the approver, only approvers can mark pull request as hold" ) @@ -1367,7 +1375,7 @@ def check_if_can_be_merged(self) -> None: for _label in _labels: if CHANGED_REQUESTED_BY_LABEL_PREFIX.lower() in _label.lower(): change_request_user = _label.split("-")[-1] - if change_request_user in self.approvers: + if change_request_user in self.root_approvers: failure_output += "PR has changed requests from approvers\n" missing_required_labels = [] @@ -1382,12 +1390,12 @@ def check_if_can_be_merged(self) -> None: for _label in _labels: if APPROVED_BY_LABEL_PREFIX.lower() in _label.lower(): approved_user = _label.split("-")[-1] - if approved_user in self.approvers and self.parent_committer != approved_user: + if approved_user in self.root_approvers and self.parent_committer != approved_user: pr_approved = True break if not pr_approved: - missing_approvers = [approver for approver in self.approvers if approver != self.parent_committer] + missing_approvers = [approver for approver in self.root_approvers if approver != self.parent_committer] failure_output += f"Missing lgtm/approved from approvers: {', '.join(missing_approvers)}\n" if not failure_output: @@ -1787,7 +1795,7 @@ def set_wip_label_based_on_title(self) -> None: def set_jira_in_pull_request(self) -> None: if self.jira_enabled_repository: - reviewers_and_approvers = self.reviewers + self.approvers + reviewers_and_approvers = self.root_reviewers + self.root_approvers if self.parent_committer in reviewers_and_approvers: self.jira_assignee = self.jira_user_mapping.get(self.parent_committer) if not self.jira_assignee: @@ -2002,8 +2010,8 @@ def add_pull_request_owner_as_assingee(self) -> None: self.logger.info(f"{self.log_prefix} Adding PR owner as assignee") self.pull_request.add_to_assignees() except Exception: - if self.approvers: - self.pull_request.add_to_assignees(self.approvers[0]) + if self.root_approvers: + self.pull_request.add_to_assignees(self.root_approvers[0]) def set_pull_request_automerge(self) -> None: if self.parent_committer in self.auto_verified_and_merged_users: @@ -2089,9 +2097,10 @@ def get_approvers_and_reviewers(self) -> dict[str, dict[str, list[str]]]: if key in content and not isinstance(content[key], list): raise ValueError(f"{key} must be a list") - _owners[content_path] = content - except (yaml.YAMLError, ValueError) as e: - self.logger.error(f"{self.log_prefix} Invalid OWNERS file {content_path}: {e}") + _owners[str(Path(content_path).parent)] = content + + except (yaml.YAMLError, ValueError) as exp: + self.logger.error(f"{self.log_prefix} Invalid OWNERS file {content_path}: {exp}") continue self.logger.debug(f"{self.log_prefix} Owners file mapping: {_owners}") From 11f1c9fc231dc6d65e94655cac10d95a94ff0823 Mon Sep 17 00:00:00 2001 From: Meni Yakove Date: Thu, 21 Nov 2024 16:46:16 +0200 Subject: [PATCH 08/31] Fix get reviewer/approvers from owners dict --- webhook_server_container/libs/github_api.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/webhook_server_container/libs/github_api.py b/webhook_server_container/libs/github_api.py index 3907b9e9..3347fbe1 100644 --- a/webhook_server_container/libs/github_api.py +++ b/webhook_server_container/libs/github_api.py @@ -605,13 +605,13 @@ def get_owners_content(self, folder_path: str = "") -> Dict[str, Any]: @property def root_reviewers(self) -> List[str]: - _reviewers = self.approvers_and_reviewers.get("OWNERS", {}).get("reviewers", []) + _reviewers = self.approvers_and_reviewers.get(".", {}).get("reviewers", []) self.logger.debug(f"{self.log_prefix} Reviewers: {_reviewers}") return _reviewers @property def root_approvers(self) -> List[str]: - _approvers = self.approvers_and_reviewers.get("OWNERS", {}).get("approvers", []) + _approvers = self.approvers_and_reviewers.get(".", {}).get("approvers", []) self.logger.debug(f"{self.log_prefix} Approvers: {_approvers}") return _approvers From 01adc56f3286f57a6cfd8f5bac07215a25e930e7 Mon Sep 17 00:00:00 2001 From: Meni Yakove Date: Thu, 21 Nov 2024 16:52:34 +0200 Subject: [PATCH 09/31] Add debug logs for add_reviewers --- webhook_server_container/libs/github_api.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/webhook_server_container/libs/github_api.py b/webhook_server_container/libs/github_api.py index 3347fbe1..a3cb5eda 100644 --- a/webhook_server_container/libs/github_api.py +++ b/webhook_server_container/libs/github_api.py @@ -628,7 +628,9 @@ def assign_reviewers(self) -> None: for owners_dir, owners_data in self.approvers_and_reviewers.items(): _owners_dir = Path(owners_dir) if _owners_dir == changed_folder_path or _owners_dir in changed_folder_path.parents: - reviewers_to_add.extend(owners_data.get("reviewers", [])) + _reviewers = owners_data.get("reviewers", []) + reviewers_to_add.extend(_reviewers) + self.logger.debug(f"{self.log_prefix} Found reviewers for {owners_dir}: {_reviewers}") _to_add: List[str] = list(set(reviewers_to_add)) self.logger.debug(f"{self.log_prefix} Reviewers to add: {', '.join(_to_add)}") From 330fd05fdde6002bb086046ed4299d32a2c730d2 Mon Sep 17 00:00:00 2001 From: Meni Yakove Date: Thu, 21 Nov 2024 16:56:15 +0200 Subject: [PATCH 10/31] Add debug logs for add_reviewers --- webhook_server_container/libs/github_api.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/webhook_server_container/libs/github_api.py b/webhook_server_container/libs/github_api.py index a3cb5eda..f09d3b87 100644 --- a/webhook_server_container/libs/github_api.py +++ b/webhook_server_container/libs/github_api.py @@ -606,13 +606,11 @@ def get_owners_content(self, folder_path: str = "") -> Dict[str, Any]: @property def root_reviewers(self) -> List[str]: _reviewers = self.approvers_and_reviewers.get(".", {}).get("reviewers", []) - self.logger.debug(f"{self.log_prefix} Reviewers: {_reviewers}") return _reviewers @property def root_approvers(self) -> List[str]: _approvers = self.approvers_and_reviewers.get(".", {}).get("approvers", []) - self.logger.debug(f"{self.log_prefix} Approvers: {_approvers}") return _approvers def list_changed_commit_files(self) -> list[str]: @@ -627,6 +625,7 @@ def assign_reviewers(self) -> None: for changed_folder_path in changed_folders: for owners_dir, owners_data in self.approvers_and_reviewers.items(): _owners_dir = Path(owners_dir) + if _owners_dir == changed_folder_path or _owners_dir in changed_folder_path.parents: _reviewers = owners_data.get("reviewers", []) reviewers_to_add.extend(_reviewers) From 2a411ad4865fc35d25f11cdcab831173d24978db Mon Sep 17 00:00:00 2001 From: Meni Yakove Date: Thu, 21 Nov 2024 17:03:16 +0200 Subject: [PATCH 11/31] Add debug logs for add_reviewers --- webhook_server_container/libs/github_api.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/webhook_server_container/libs/github_api.py b/webhook_server_container/libs/github_api.py index f09d3b87..5f3e076b 100644 --- a/webhook_server_container/libs/github_api.py +++ b/webhook_server_container/libs/github_api.py @@ -606,11 +606,13 @@ def get_owners_content(self, folder_path: str = "") -> Dict[str, Any]: @property def root_reviewers(self) -> List[str]: _reviewers = self.approvers_and_reviewers.get(".", {}).get("reviewers", []) + self.logger.debug(f"{self.log_prefix} ROOT Reviewers: {_reviewers}") return _reviewers @property def root_approvers(self) -> List[str]: _approvers = self.approvers_and_reviewers.get(".", {}).get("approvers", []) + self.logger.debug(f"{self.log_prefix} ROOT Approvers: {_approvers}") return _approvers def list_changed_commit_files(self) -> list[str]: @@ -628,8 +630,8 @@ def assign_reviewers(self) -> None: if _owners_dir == changed_folder_path or _owners_dir in changed_folder_path.parents: _reviewers = owners_data.get("reviewers", []) - reviewers_to_add.extend(_reviewers) self.logger.debug(f"{self.log_prefix} Found reviewers for {owners_dir}: {_reviewers}") + reviewers_to_add.extend(_reviewers) _to_add: List[str] = list(set(reviewers_to_add)) self.logger.debug(f"{self.log_prefix} Reviewers to add: {', '.join(_to_add)}") From 23459a918d69cec01ee7955f9c2ccac129b0c075 Mon Sep 17 00:00:00 2001 From: Meni Yakove Date: Thu, 21 Nov 2024 17:28:40 +0200 Subject: [PATCH 12/31] refactore all reviewers.approvers code --- webhook_server_container/libs/github_api.py | 51 +++++++++++++-------- 1 file changed, 33 insertions(+), 18 deletions(-) diff --git a/webhook_server_container/libs/github_api.py b/webhook_server_container/libs/github_api.py index 5f3e076b..b7634a4e 100644 --- a/webhook_server_container/libs/github_api.py +++ b/webhook_server_container/libs/github_api.py @@ -202,6 +202,8 @@ def process(self) -> None: self.last_committer = getattr(self.last_commit.committer, "login", self.parent_committer) self.pull_request_branch = self.pull_request.base.ref self.approvers_and_reviewers = self.get_approvers_and_reviewers() + self.all_approvers = self.get_all_approvers() + self.all_reviewers = self.get_all_reviewers() if self.jira_enabled_repository: self.set_jira_in_pull_request() @@ -620,20 +622,8 @@ def list_changed_commit_files(self) -> list[str]: def assign_reviewers(self) -> None: self.logger.info(f"{self.log_prefix} Assign reviewers") - changed_files = self.list_changed_commit_files() - reviewers_to_add = self.root_reviewers - changed_folders = [Path(cf).parent for cf in changed_files] - - for changed_folder_path in changed_folders: - for owners_dir, owners_data in self.approvers_and_reviewers.items(): - _owners_dir = Path(owners_dir) - if _owners_dir == changed_folder_path or _owners_dir in changed_folder_path.parents: - _reviewers = owners_data.get("reviewers", []) - self.logger.debug(f"{self.log_prefix} Found reviewers for {owners_dir}: {_reviewers}") - reviewers_to_add.extend(_reviewers) - - _to_add: List[str] = list(set(reviewers_to_add)) + _to_add: List[str] = list(set(self.all_reviewers)) self.logger.debug(f"{self.log_prefix} Reviewers to add: {', '.join(_to_add)}") for reviewer in _to_add: @@ -958,7 +948,7 @@ def process_pull_request_webhook_data(self) -> None: _reviewer = labeled.split(CHANGED_REQUESTED_BY_LABEL_PREFIX)[-1] _approved_output: Dict[str, Any] = {"title": "Approved", "summary": "", "text": ""} - if _reviewer in self.root_approvers: + if _reviewer in self.all_approvers: _check_for_merge = True _approved_output["text"] += f"Approved by {_reviewer}.\n" @@ -1014,7 +1004,7 @@ def manage_reviewed_by_label(self, review_state: str, action: str, reviewed_user label_prefix: str = "" label_to_remove: str = "" - if reviewed_user in self.root_approvers: + if reviewed_user in self.all_approvers: approved_lgtm_label = APPROVED_BY_LABEL_PREFIX else: approved_lgtm_label = LGTM_BY_LABEL_PREFIX @@ -1173,7 +1163,7 @@ def user_commands(self, command: str, reviewed_user: str, issue_comment_id: int) elif _command == HOLD_LABEL_STR: self.create_comment_reaction(issue_comment_id=issue_comment_id, reaction=REACTIONS.ok) - if reviewed_user not in self.root_approvers: + if reviewed_user not in self.all_approvers: self.pull_request.create_issue_comment( f"{reviewed_user} is not part of the approver, only approvers can mark pull request as hold" ) @@ -1378,7 +1368,7 @@ def check_if_can_be_merged(self) -> None: for _label in _labels: if CHANGED_REQUESTED_BY_LABEL_PREFIX.lower() in _label.lower(): change_request_user = _label.split("-")[-1] - if change_request_user in self.root_approvers: + if change_request_user in self.all_approvers: failure_output += "PR has changed requests from approvers\n" missing_required_labels = [] @@ -1398,7 +1388,7 @@ def check_if_can_be_merged(self) -> None: break if not pr_approved: - missing_approvers = [approver for approver in self.root_approvers if approver != self.parent_committer] + missing_approvers = [approver for approver in self.all_approvers if approver != self.parent_committer] failure_output += f"Missing lgtm/approved from approvers: {', '.join(missing_approvers)}\n" if not failure_output: @@ -2108,3 +2098,28 @@ def get_approvers_and_reviewers(self) -> dict[str, dict[str, list[str]]]: self.logger.debug(f"{self.log_prefix} Owners file mapping: {_owners}") return _owners + + def get_all_approvers(self) -> list[str]: + return self.root_approvers + self.owners_data_for_changed_files()["approvers"] + + def get_all_reviewers(self) -> list[str]: + return self.root_reviewers + self.owners_data_for_changed_files()["reviewers"] + + def owners_data_for_changed_files(self) -> dict[str, list[str]]: + data: dict[str, list[str]] = {"approvers": [], "reviewers": []} + changed_files = self.list_changed_commit_files() + changed_folders = [Path(cf).parent for cf in changed_files] + + for changed_folder_path in changed_folders: + for owners_dir, owners_data in self.approvers_and_reviewers.items(): + _owners_dir = Path(owners_dir) + + if _owners_dir == changed_folder_path or _owners_dir in changed_folder_path.parents: + _reviewers = owners_data.get("reviewers", []) + self.logger.debug(f"{self.log_prefix} Found reviewers for {owners_dir}: {_reviewers}") + data["reviewers"].extend(_reviewers) + _approvers = owners_data.get("approvers", []) + self.logger.debug(f"{self.log_prefix} Found approvers for {owners_dir}: {_approvers}") + data["approvers"].extend(_approvers) + + return data From 29ce366a10703b8ad1b1cefd35ba111aaf9eb433 Mon Sep 17 00:00:00 2001 From: Meni Yakove Date: Thu, 21 Nov 2024 17:44:03 +0200 Subject: [PATCH 13/31] Address comments --- webhook_server_container/libs/github_api.py | 48 +++++++++++++++------ 1 file changed, 34 insertions(+), 14 deletions(-) diff --git a/webhook_server_container/libs/github_api.py b/webhook_server_container/libs/github_api.py index b7634a4e..866c68ee 100644 --- a/webhook_server_container/libs/github_api.py +++ b/webhook_server_container/libs/github_api.py @@ -2002,8 +2002,11 @@ def add_pull_request_owner_as_assingee(self) -> None: try: self.logger.info(f"{self.log_prefix} Adding PR owner as assignee") self.pull_request.add_to_assignees() - except Exception: + except Exception as exp: + self.logger.debug(f"{self.log_prefix} Exception while adding PR owner as assignee: {exp}") + if self.root_approvers: + self.logger.debug(f"{self.log_prefix} Falling back to first approver as assignee") self.pull_request.add_to_assignees(self.root_approvers[0]) def set_pull_request_automerge(self) -> None: @@ -2082,17 +2085,14 @@ def get_approvers_and_reviewers(self) -> dict[str, dict[str, list[str]]]: try: content = yaml.safe_load(_path.decoded_content) - # Validate structure - if not isinstance(content, dict): - raise ValueError("OWNERS file must contain a dictionary") - - for key in ["approvers", "reviewers"]: - if key in content and not isinstance(content[key], list): - raise ValueError(f"{key} must be a list") - - _owners[str(Path(content_path).parent)] = content - - except (yaml.YAMLError, ValueError) as exp: + if self._validate_owners_content(content, content_path): + # Use Path for consistent path handling + parent_path = str(Path(content_path).parent) + if not parent_path: + parent_path = "." + _owners[parent_path] = content + + except yaml.YAMLError as exp: self.logger.error(f"{self.log_prefix} Invalid OWNERS file {content_path}: {exp}") continue @@ -2100,10 +2100,10 @@ def get_approvers_and_reviewers(self) -> dict[str, dict[str, list[str]]]: return _owners def get_all_approvers(self) -> list[str]: - return self.root_approvers + self.owners_data_for_changed_files()["approvers"] + return list(set(self.root_approvers + self.owners_data_for_changed_files()["approvers"])) def get_all_reviewers(self) -> list[str]: - return self.root_reviewers + self.owners_data_for_changed_files()["reviewers"] + return list(set(self.root_reviewers + self.owners_data_for_changed_files()["reviewers"])) def owners_data_for_changed_files(self) -> dict[str, list[str]]: data: dict[str, list[str]] = {"approvers": [], "reviewers": []} @@ -2123,3 +2123,23 @@ def owners_data_for_changed_files(self) -> dict[str, list[str]]: data["approvers"].extend(_approvers) return data + + def _validate_owners_content(self, content: Any, path: str) -> bool: + """Validate OWNERS file content structure.""" + try: + if not isinstance(content, dict): + raise ValueError("OWNERS file must contain a dictionary") + + for key in ["approvers", "reviewers"]: + if key in content: + if not isinstance(content[key], list): + raise ValueError(f"{key} must be a list") + + if not all(isinstance(_elm, str) for _elm in content[key]): + raise ValueError(f"All {key} must be strings") + + return True + + except ValueError as e: + self.logger.error(f"{self.log_prefix} Invalid OWNERS file {path}: {e}") + return False From 49b26d2d5764da247c219c0a3bf4e6fcdcaa858d Mon Sep 17 00:00:00 2001 From: Meni Yakove Date: Thu, 21 Nov 2024 17:55:19 +0200 Subject: [PATCH 14/31] Get pr changed file sonce --- webhook_server_container/libs/github_api.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/webhook_server_container/libs/github_api.py b/webhook_server_container/libs/github_api.py index 866c68ee..38b9ceaa 100644 --- a/webhook_server_container/libs/github_api.py +++ b/webhook_server_container/libs/github_api.py @@ -200,6 +200,7 @@ def process(self) -> None: self.last_commit = self._get_last_commit() self.parent_committer = self.pull_request.user.login self.last_committer = getattr(self.last_commit.committer, "login", self.parent_committer) + self.changed_files = self.list_changed_commit_files() self.pull_request_branch = self.pull_request.base.ref self.approvers_and_reviewers = self.get_approvers_and_reviewers() self.all_approvers = self.get_all_approvers() @@ -2107,8 +2108,7 @@ def get_all_reviewers(self) -> list[str]: def owners_data_for_changed_files(self) -> dict[str, list[str]]: data: dict[str, list[str]] = {"approvers": [], "reviewers": []} - changed_files = self.list_changed_commit_files() - changed_folders = [Path(cf).parent for cf in changed_files] + changed_folders = [Path(cf).parent for cf in self.changed_files] for changed_folder_path in changed_folders: for owners_dir, owners_data in self.approvers_and_reviewers.items(): From a3ae3664a8ba27768b9f610b6c3297c7e1ae1a48 Mon Sep 17 00:00:00 2001 From: Meni Yakove Date: Thu, 21 Nov 2024 18:16:54 +0200 Subject: [PATCH 15/31] Add more logs --- webhook_server_container/libs/github_api.py | 23 ++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/webhook_server_container/libs/github_api.py b/webhook_server_container/libs/github_api.py index 38b9ceaa..d5bcaf86 100644 --- a/webhook_server_container/libs/github_api.py +++ b/webhook_server_container/libs/github_api.py @@ -892,6 +892,7 @@ def process_pull_request_webhook_data(self) -> None: pull_request_opened_futures.append(executor.submit(self.process_opened_or_synchronize_pull_request)) if self.jira_track_pr: pull_request_opened_futures.append(executor.submit(self.create_jira_when_open_pull_reques)) + pull_request_opened_futures.append(executor.submit(self.set_pull_request_automerge)) for _ in as_completed(pull_request_opened_futures): @@ -908,6 +909,9 @@ def process_pull_request_webhook_data(self) -> None: if self.jira_track_pr: pull_request_synchronize_futures.append(executor.submit(self.update_jira_when_pull_request_sync)) + for _ in as_completed(pull_request_synchronize_futures): + pass + if hook_action == "closed": self.close_issue_for_merged_or_closed_pr(hook_action=hook_action) self.delete_remote_tag_for_merged_or_closed_pr() @@ -1312,8 +1316,8 @@ def check_if_can_be_merged(self) -> None: failure_output = "" try: - self.all_required_status_checks = self.get_all_required_status_checks() self.logger.info(f"{self.log_prefix} Check if {CAN_BE_MERGED_STR}.") + self.all_required_status_checks = self.get_all_required_status_checks() self.set_merge_check_queued() last_commit_check_runs = list(self.last_commit.get_check_runs()) self.logger.debug(f"{self.log_prefix} Check if any required check runs in progress.") @@ -1334,6 +1338,7 @@ def check_if_can_be_merged(self) -> None: _labels = self.pull_request_labels_names() is_hold = HOLD_LABEL_STR in _labels is_wip = WIP_STR in _labels + if is_hold or is_wip: if is_hold: failure_output += "Hold label exists.\n" @@ -2108,20 +2113,28 @@ def get_all_reviewers(self) -> list[str]: def owners_data_for_changed_files(self) -> dict[str, list[str]]: data: dict[str, list[str]] = {"approvers": [], "reviewers": []} - changed_folders = [Path(cf).parent for cf in self.changed_files] - for changed_folder_path in changed_folders: - for owners_dir, owners_data in self.approvers_and_reviewers.items(): - _owners_dir = Path(owners_dir) + # Convert to set for O(1) lookups and deduplication + changed_folders = {Path(cf).parent for cf in self.changed_files} + + # Sort owners directories by depth for proper precedence + sorted_owners = sorted(self.approvers_and_reviewers.items(), key=lambda x: len(Path(x[0]).parts)) + for changed_folder_path in sorted(changed_folders): + for owners_dir, owners_data in sorted_owners: + _owners_dir = Path(owners_dir) if _owners_dir == changed_folder_path or _owners_dir in changed_folder_path.parents: _reviewers = owners_data.get("reviewers", []) self.logger.debug(f"{self.log_prefix} Found reviewers for {owners_dir}: {_reviewers}") data["reviewers"].extend(_reviewers) + _approvers = owners_data.get("approvers", []) self.logger.debug(f"{self.log_prefix} Found approvers for {owners_dir}: {_approvers}") data["approvers"].extend(_approvers) + # Deduplicate the lists while preserving order + data["reviewers"] = list(dict.fromkeys(data["reviewers"])) + data["approvers"] = list(dict.fromkeys(data["approvers"])) return data def _validate_owners_content(self, content: Any, path: str) -> bool: From bffb6ba1e0143b1b8fe41799e262109747f9ffdb Mon Sep 17 00:00:00 2001 From: Meni Yakove Date: Thu, 21 Nov 2024 19:36:35 +0200 Subject: [PATCH 16/31] Add more logs --- webhook_server_container/libs/github_api.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/webhook_server_container/libs/github_api.py b/webhook_server_container/libs/github_api.py index d5bcaf86..ce4e7695 100644 --- a/webhook_server_container/libs/github_api.py +++ b/webhook_server_container/libs/github_api.py @@ -314,11 +314,12 @@ def prepare_log_prefix(self, pull_request: Optional[PullRequest] = None) -> str: def process_pull_request_check_run_webhook_data(self) -> None: _check_run: Dict[str, Any] = self.hook_data["check_run"] + check_run_name: str = _check_run["name"] + if _check_run.get("action", "") != "completed": - self.logger.debug(f"{self.log_prefix} check run action is not completed, skipping") + self.logger.debug(f"{self.log_prefix} check run {check_run_name} action is not completed, skipping") return - check_run_name: str = _check_run["name"] check_run_status: str = _check_run["status"] check_run_conclusion: str = _check_run["conclusion"] check_run_head_sha: str = _check_run["head_sha"] @@ -895,8 +896,9 @@ def process_pull_request_webhook_data(self) -> None: pull_request_opened_futures.append(executor.submit(self.set_pull_request_automerge)) - for _ in as_completed(pull_request_opened_futures): - pass + for result in as_completed(pull_request_opened_futures): + if _exp := result.exception(): + self.logger.error(f"{self.log_prefix} {_exp}") if hook_action == "synchronize": pull_request_synchronize_futures: List[Future] = [] @@ -909,8 +911,9 @@ def process_pull_request_webhook_data(self) -> None: if self.jira_track_pr: pull_request_synchronize_futures.append(executor.submit(self.update_jira_when_pull_request_sync)) - for _ in as_completed(pull_request_synchronize_futures): - pass + for result in as_completed(pull_request_synchronize_futures): + if _exp := result.exception(): + self.logger.error(f"{self.log_prefix} {_exp}") if hook_action == "closed": self.close_issue_for_merged_or_closed_pr(hook_action=hook_action) From 68910d77a1e52257f3b19fde8f3233aac83b66bf Mon Sep 17 00:00:00 2001 From: Meni Yakove Date: Thu, 21 Nov 2024 19:50:06 +0200 Subject: [PATCH 17/31] Add more logs --- webhook_server_container/libs/github_api.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/webhook_server_container/libs/github_api.py b/webhook_server_container/libs/github_api.py index ce4e7695..d0544e32 100644 --- a/webhook_server_container/libs/github_api.py +++ b/webhook_server_container/libs/github_api.py @@ -305,19 +305,20 @@ def _get_random_color(_colors: List[str], _json: Dict[str, str]) -> str: def prepare_log_prefix(self, pull_request: Optional[PullRequest] = None) -> str: _repository_color = self._get_reposiroty_color_for_log_prefix() - _id = self.x_github_delivery.split("-", 1)[-1] return ( - f"{_repository_color}[{self.github_event}][{_id}][PR {pull_request.number}]:" + f"{_repository_color}[{self.github_event}][{self.x_github_delivery}][PR {pull_request.number}]:" if pull_request - else f"{_repository_color}[{self.github_event}][{_id}]:" + else f"{_repository_color}[{self.github_event}][{self.x_github_delivery}]:" ) def process_pull_request_check_run_webhook_data(self) -> None: _check_run: Dict[str, Any] = self.hook_data["check_run"] check_run_name: str = _check_run["name"] - if _check_run.get("action", "") != "completed": - self.logger.debug(f"{self.log_prefix} check run {check_run_name} action is not completed, skipping") + if _check_run_action := _check_run.get("action", "") != "completed": + self.logger.debug( + f"{self.log_prefix} check run {check_run_name} action is {_check_run_action} and not completed, skipping" + ) return check_run_status: str = _check_run["status"] From ef3d9d6d4c5fc8452ce9b34c9faf64d3b93dd65b Mon Sep 17 00:00:00 2001 From: Meni Yakove Date: Thu, 21 Nov 2024 19:54:14 +0200 Subject: [PATCH 18/31] Add more logs --- webhook_server_container/libs/github_api.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/webhook_server_container/libs/github_api.py b/webhook_server_container/libs/github_api.py index d0544e32..b7848ca7 100644 --- a/webhook_server_container/libs/github_api.py +++ b/webhook_server_container/libs/github_api.py @@ -315,9 +315,9 @@ def process_pull_request_check_run_webhook_data(self) -> None: _check_run: Dict[str, Any] = self.hook_data["check_run"] check_run_name: str = _check_run["name"] - if _check_run_action := _check_run.get("action", "") != "completed": + if _check_run.get("action", "") != "completed": self.logger.debug( - f"{self.log_prefix} check run {check_run_name} action is {_check_run_action} and not completed, skipping" + f"{self.log_prefix} check run {check_run_name} action is {_check_run.get('action', 'N/A')} and not completed, skipping" ) return From c78e3561ef5fa3534562adda3c6c7377a7db25b2 Mon Sep 17 00:00:00 2001 From: Meni Yakove Date: Thu, 21 Nov 2024 19:58:01 +0200 Subject: [PATCH 19/31] Fix check if check-run is completed --- webhook_server_container/libs/github_api.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/webhook_server_container/libs/github_api.py b/webhook_server_container/libs/github_api.py index b7848ca7..974b8f52 100644 --- a/webhook_server_container/libs/github_api.py +++ b/webhook_server_container/libs/github_api.py @@ -315,9 +315,9 @@ def process_pull_request_check_run_webhook_data(self) -> None: _check_run: Dict[str, Any] = self.hook_data["check_run"] check_run_name: str = _check_run["name"] - if _check_run.get("action", "") != "completed": + if self.hook_data.get("action", "") != "completed": self.logger.debug( - f"{self.log_prefix} check run {check_run_name} action is {_check_run.get('action', 'N/A')} and not completed, skipping" + f"{self.log_prefix} check run {check_run_name} action is {self.hook_data.get('action', 'N/A')} and not completed, skipping" ) return From 225f49e59c75f53ac9f2e3c386fca5ffb1f3b77e Mon Sep 17 00:00:00 2001 From: Meni Yakove Date: Thu, 21 Nov 2024 20:09:10 +0200 Subject: [PATCH 20/31] Ignore edited comments --- webhook_server_container/libs/github_api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webhook_server_container/libs/github_api.py b/webhook_server_container/libs/github_api.py index 974b8f52..aaf7ddf5 100644 --- a/webhook_server_container/libs/github_api.py +++ b/webhook_server_container/libs/github_api.py @@ -847,7 +847,7 @@ def delete_remote_tag_for_merged_or_closed_pr(self) -> None: self.logger.error(f"{self.log_prefix} Failed to delete tag: {repository_full_tag}. OUT:{out}. ERR:{err}") def process_comment_webhook_data(self) -> None: - if comment_action := self.hook_data["action"] in ("action", "deleted"): + if comment_action := self.hook_data["action"] in ("edited", "deleted"): self.logger.debug(f"{self.log_prefix} Not processing comment. action is {comment_action}") return From 53a0dfe13e787f63b0998a9a66aac02276a35f98 Mon Sep 17 00:00:00 2001 From: Meni Yakove Date: Fri, 22 Nov 2024 11:05:58 +0200 Subject: [PATCH 21/31] set approvers and reviewers func to hold list of lists --- webhook_server_container/libs/github_api.py | 36 ++++++++++++--------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/webhook_server_container/libs/github_api.py b/webhook_server_container/libs/github_api.py index aaf7ddf5..bbec21d2 100644 --- a/webhook_server_container/libs/github_api.py +++ b/webhook_server_container/libs/github_api.py @@ -1393,7 +1393,7 @@ def check_if_can_be_merged(self) -> None: for _label in _labels: if APPROVED_BY_LABEL_PREFIX.lower() in _label.lower(): approved_user = _label.split("-")[-1] - if approved_user in self.root_approvers and self.parent_committer != approved_user: + if approved_user in self.all_approvers and self.parent_committer != approved_user: pr_approved = True break @@ -2110,35 +2110,39 @@ def get_approvers_and_reviewers(self) -> dict[str, dict[str, list[str]]]: return _owners def get_all_approvers(self) -> list[str]: - return list(set(self.root_approvers + self.owners_data_for_changed_files()["approvers"])) + _approvers: list[str] = [] + for val in self.owners_data_for_changed_files()["approvers"]: + for _approver in val: + _approvers.extend(_approver) + + return list(set(self.root_approvers + _approvers)) def get_all_reviewers(self) -> list[str]: - return list(set(self.root_reviewers + self.owners_data_for_changed_files()["reviewers"])) + _reviewers: list[str] = [] + for val in self.owners_data_for_changed_files()["reviewers"]: + for _approver in val: + _reviewers.extend(_approver) - def owners_data_for_changed_files(self) -> dict[str, list[str]]: - data: dict[str, list[str]] = {"approvers": [], "reviewers": []} + return list(set(self.root_approvers + _reviewers)) - # Convert to set for O(1) lookups and deduplication - changed_folders = {Path(cf).parent for cf in self.changed_files} + def owners_data_for_changed_files(self) -> dict[str, list[list[str]]]: + data: dict[str, list[list[str]]] = {"approvers": [], "reviewers": []} - # Sort owners directories by depth for proper precedence - sorted_owners = sorted(self.approvers_and_reviewers.items(), key=lambda x: len(Path(x[0]).parts)) + changed_folders = {Path(cf).parent for cf in self.changed_files} - for changed_folder_path in sorted(changed_folders): - for owners_dir, owners_data in sorted_owners: + for changed_folder_path in changed_folders: + for owners_dir, owners_data in self.approvers_and_reviewers.items(): _owners_dir = Path(owners_dir) + if _owners_dir == changed_folder_path or _owners_dir in changed_folder_path.parents: _reviewers = owners_data.get("reviewers", []) self.logger.debug(f"{self.log_prefix} Found reviewers for {owners_dir}: {_reviewers}") - data["reviewers"].extend(_reviewers) + data["reviewers"].append(_reviewers) _approvers = owners_data.get("approvers", []) self.logger.debug(f"{self.log_prefix} Found approvers for {owners_dir}: {_approvers}") - data["approvers"].extend(_approvers) + data["approvers"].append(_approvers) - # Deduplicate the lists while preserving order - data["reviewers"] = list(dict.fromkeys(data["reviewers"])) - data["approvers"] = list(dict.fromkeys(data["approvers"])) return data def _validate_owners_content(self, content: Any, path: str) -> bool: From 4c12b6a55e2333d0cf2d63366e2784928b2e1921 Mon Sep 17 00:00:00 2001 From: Meni Yakove Date: Fri, 22 Nov 2024 11:26:42 +0200 Subject: [PATCH 22/31] can_be_merge: approvers --- webhook_server_container/libs/github_api.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/webhook_server_container/libs/github_api.py b/webhook_server_container/libs/github_api.py index bbec21d2..ba165a94 100644 --- a/webhook_server_container/libs/github_api.py +++ b/webhook_server_container/libs/github_api.py @@ -1390,12 +1390,22 @@ def check_if_can_be_merged(self) -> None: failure_output += f"Missing required labels: {', '.join(missing_required_labels)}\n" pr_approved = False + _pr_approvers: list[str] = [] + all_needed_approvers = self.get_approvers_and_reviewers()["approvers"] + for _label in _labels: if APPROVED_BY_LABEL_PREFIX.lower() in _label.lower(): approved_user = _label.split("-")[-1] - if approved_user in self.all_approvers and self.parent_committer != approved_user: - pr_approved = True - break + if self.parent_committer == approved_user: + continue + + for _need_approve in all_needed_approvers: + if approved_user in _need_approve: + _pr_approvers.append(approved_user) + break + + if len(_pr_approvers) == len(all_needed_approvers): + pr_approved = True if not pr_approved: missing_approvers = [approver for approver in self.all_approvers if approver != self.parent_committer] From cbdfd409068d9e1fae1ef95271212ef563cbacc4 Mon Sep 17 00:00:00 2001 From: Meni Yakove Date: Fri, 22 Nov 2024 11:33:08 +0200 Subject: [PATCH 23/31] debug --- webhook_server_container/libs/github_api.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/webhook_server_container/libs/github_api.py b/webhook_server_container/libs/github_api.py index ba165a94..09bc4381 100644 --- a/webhook_server_container/libs/github_api.py +++ b/webhook_server_container/libs/github_api.py @@ -2121,16 +2121,17 @@ def get_approvers_and_reviewers(self) -> dict[str, dict[str, list[str]]]: def get_all_approvers(self) -> list[str]: _approvers: list[str] = [] - for val in self.owners_data_for_changed_files()["approvers"]: - for _approver in val: + for list_of_approvers in self.owners_data_for_changed_files()["approvers"]: + for _approver in list_of_approvers: _approvers.extend(_approver) return list(set(self.root_approvers + _approvers)) def get_all_reviewers(self) -> list[str]: _reviewers: list[str] = [] - for val in self.owners_data_for_changed_files()["reviewers"]: - for _approver in val: + for list_of_reviewers in self.owners_data_for_changed_files()["reviewers"]: + self.logger.error(f"{self.log_prefix} list_of_reviewers: {list_of_reviewers}") + for _approver in list_of_reviewers: _reviewers.extend(_approver) return list(set(self.root_approvers + _reviewers)) From bdd4e39f9cc4b3eaac7ba1e020b40682e18d4cb5 Mon Sep 17 00:00:00 2001 From: Meni Yakove Date: Fri, 22 Nov 2024 11:34:48 +0200 Subject: [PATCH 24/31] Fix all approvers/viewers --- webhook_server_container/libs/github_api.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/webhook_server_container/libs/github_api.py b/webhook_server_container/libs/github_api.py index 09bc4381..4fd39d85 100644 --- a/webhook_server_container/libs/github_api.py +++ b/webhook_server_container/libs/github_api.py @@ -2123,16 +2123,15 @@ def get_all_approvers(self) -> list[str]: _approvers: list[str] = [] for list_of_approvers in self.owners_data_for_changed_files()["approvers"]: for _approver in list_of_approvers: - _approvers.extend(_approver) + _approvers.append(_approver) return list(set(self.root_approvers + _approvers)) def get_all_reviewers(self) -> list[str]: _reviewers: list[str] = [] for list_of_reviewers in self.owners_data_for_changed_files()["reviewers"]: - self.logger.error(f"{self.log_prefix} list_of_reviewers: {list_of_reviewers}") for _approver in list_of_reviewers: - _reviewers.extend(_approver) + _reviewers.append(_approver) return list(set(self.root_approvers + _reviewers)) From 31078f7f94d73c88c45472b8db08fb4d7f75ddc2 Mon Sep 17 00:00:00 2001 From: Meni Yakove Date: Fri, 22 Nov 2024 11:44:26 +0200 Subject: [PATCH 25/31] Fix can-be-merged --- webhook_server_container/libs/github_api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webhook_server_container/libs/github_api.py b/webhook_server_container/libs/github_api.py index 4fd39d85..7efaab5b 100644 --- a/webhook_server_container/libs/github_api.py +++ b/webhook_server_container/libs/github_api.py @@ -1391,7 +1391,7 @@ def check_if_can_be_merged(self) -> None: pr_approved = False _pr_approvers: list[str] = [] - all_needed_approvers = self.get_approvers_and_reviewers()["approvers"] + all_needed_approvers = self.owners_data_for_changed_files()["approvers"] for _label in _labels: if APPROVED_BY_LABEL_PREFIX.lower() in _label.lower(): From 5a08fb71c95eb4f14aa01540b24a07dc18feeaac Mon Sep 17 00:00:00 2001 From: Meni Yakove Date: Fri, 22 Nov 2024 22:36:54 +0200 Subject: [PATCH 26/31] Add tests for owners code --- webhook_server_container/libs/github_api.py | 8 +- .../tests/test_github_api.py | 83 ++++++++++++++++++- 2 files changed, 86 insertions(+), 5 deletions(-) diff --git a/webhook_server_container/libs/github_api.py b/webhook_server_container/libs/github_api.py index bbec21d2..efc5667f 100644 --- a/webhook_server_container/libs/github_api.py +++ b/webhook_server_container/libs/github_api.py @@ -2113,17 +2113,17 @@ def get_all_approvers(self) -> list[str]: _approvers: list[str] = [] for val in self.owners_data_for_changed_files()["approvers"]: for _approver in val: - _approvers.extend(_approver) + _approvers.append(_approver) return list(set(self.root_approvers + _approvers)) def get_all_reviewers(self) -> list[str]: _reviewers: list[str] = [] for val in self.owners_data_for_changed_files()["reviewers"]: - for _approver in val: - _reviewers.extend(_approver) + for _reviewer in val: + _reviewers.append(_reviewer) - return list(set(self.root_approvers + _reviewers)) + return list(set(self.root_reviewers + _reviewers)) def owners_data_for_changed_files(self) -> dict[str, list[list[str]]]: data: dict[str, list[list[str]]] = {"approvers": [], "reviewers": []} diff --git a/webhook_server_container/tests/test_github_api.py b/webhook_server_container/tests/test_github_api.py index 61288948..4eb403cf 100644 --- a/webhook_server_container/tests/test_github_api.py +++ b/webhook_server_container/tests/test_github_api.py @@ -3,14 +3,53 @@ from simple_logger.logger import logging from stringcolor.ops import os +import yaml from webhook_server_container.libs.github_api import ProcessGithubWehook from webhook_server_container.utils.constants import SIZE_LABEL_PREFIX +class Tree: + def __init__(self, path: str): + self.type = "blob" + self.path = path + + @property + def tree(self): + trees = [] + for _path in ["OWNERS", "test1/OWNERS", "code/file.py", "README.md"]: + trees.append(Tree(_path)) + return trees + + +class ContentFile: + def __init__(self, content: str): + self.content = content + + @property + def decoded_content(self): + return self.content + + class Repository: def __init__(self): self.name = "test-repo" + def get_git_tree(self, sha: str, recursive: bool): + return Tree("") + + def get_contents(self, path: str): + owners_data = yaml.dump({"approvers": ["approver1", "approver2"], "reviewers": ["reviewer1", "reviewer2"]}) + + test1_owners_data = yaml.dump({ + "approvers": ["approver3", "approver4"], + "reviewers": ["reviewer3", "reviewer4"], + }) + + if path == "OWNERS": + return ContentFile(owners_data) + elif path == "test1/OWNERS": + return ContentFile(test1_owners_data) + class PullRequest: def __init__(self, additions: int, deletions: int): @@ -28,9 +67,12 @@ def process_github_webhook(mocker): mocker.patch(f"{base_import_path}.get_api_with_highest_rate_limit", return_value=("API", "TOKEN")) mocker.patch(f"{base_import_path}.get_github_repo_api", return_value=Repository()) - return ProcessGithubWehook( + process_github_webhook = ProcessGithubWehook( {"repository": {"name": Repository().name}}, Headers({"X-GitHub-Event": "test-event"}), logging.getLogger() ) + process_github_webhook.pull_request_branch = "main" + process_github_webhook.changed_files = ["OWNERS", "test1/OWNERS", "code/file.py", "README.md"] + return process_github_webhook @pytest.mark.parametrize( @@ -50,3 +92,42 @@ def test_get_size_thresholds(process_github_webhook, additions, deletions, expec result = process_github_webhook.get_size() assert result == f"{SIZE_LABEL_PREFIX}{expected_label}" + + +def test_get_approvers_and_reviewers(process_github_webhook): + process_github_webhook.repository = Repository() + read_owners_result = process_github_webhook.get_approvers_and_reviewers() + process_github_webhook.approvers_and_reviewers = { + ".": {"approvers": ["approver1", "approver2"], "reviewers": ["reviewer1", "reviewer2"]}, + "test1": {"approvers": ["approver3", "approver4"], "reviewers": ["reviewer3", "reviewer4"]}, + } + assert read_owners_result == process_github_webhook.approvers_and_reviewers + + owners_data_chaged_files_result = process_github_webhook.owners_data_for_changed_files() + owners_data_chaged_files_expected = { + "approvers": [ + ["approver1", "approver2"], + ["approver1", "approver2"], + ["approver3", "approver4"], + ["approver1", "approver2"], + ], + "reviewers": [ + ["reviewer1", "reviewer2"], + ["reviewer1", "reviewer2"], + ["reviewer3", "reviewer4"], + ["reviewer1", "reviewer2"], + ], + } + owners_data_chaged_files_result["approvers"].sort() + owners_data_chaged_files_result["reviewers"].sort() + owners_data_chaged_files_expected["approvers"].sort() + owners_data_chaged_files_expected["reviewers"].sort() + assert owners_data_chaged_files_result == owners_data_chaged_files_expected + + all_approvers = process_github_webhook.get_all_approvers() + all_approvers.sort() + assert all_approvers == ["approver1", "approver2", "approver3", "approver4"] + + all_reviewers = process_github_webhook.get_all_reviewers() + all_reviewers.sort() + assert all_reviewers == ["reviewer1", "reviewer2", "reviewer3", "reviewer4"] From c2c61c2d4303bfb19fd663b0ce67c356abd703db Mon Sep 17 00:00:00 2001 From: Meni Yakove Date: Fri, 22 Nov 2024 23:51:39 +0200 Subject: [PATCH 27/31] Fix missing approves msg --- pyproject.toml | 11 +++ pytest.ini | 3 + tox.ini | 15 ---- tox.toml | 17 +++++ uv.lock | 81 ++++++++++++++++++++- webhook_server_container/libs/github_api.py | 6 +- 6 files changed, 116 insertions(+), 17 deletions(-) create mode 100644 pytest.ini delete mode 100644 tox.ini create mode 100644 tox.toml diff --git a/pyproject.toml b/pyproject.toml index 4303372f..6605be22 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,3 +1,13 @@ +[tool.coverage.run] +omit = ["tests/*"] + +[tool.coverage.report] +fail_under = 0 +skip_empty = true + +[tool.coverage.html] +directory = ".tests_coverage" + [tool.ruff] preview = true line-length = 120 @@ -41,6 +51,7 @@ dependencies = [ "jira>=3.8.0", "pygithub>=2.4.0", "pyhelper-utils>=0.0.42", + "pytest-cov>=6.0.0", "pytest-mock>=3.14.0", "pytest>=8.3.3", "python-simple-logger>=1.0.40", diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 00000000..fcf52b9a --- /dev/null +++ b/pytest.ini @@ -0,0 +1,3 @@ +[pytest] +addopts = + --cov-config=pyproject.toml --cov-report=html --cov-report=term --cov=github_webhook_server diff --git a/tox.ini b/tox.ini deleted file mode 100644 index 6515ce72..00000000 --- a/tox.ini +++ /dev/null @@ -1,15 +0,0 @@ -[tox] -envlist = unused-code, pytest -skipsdist = True - -[testenv:unused-code] -deps = - python-utility-scripts -commands = - pyutils-unusedcode --exclude-function-prefixes 'process_webhook' - -[testenv:pytest] -deps = - uv -commands = - uv run pytest webhook_server_container/tests diff --git a/tox.toml b/tox.toml new file mode 100644 index 00000000..7cabf92a --- /dev/null +++ b/tox.toml @@ -0,0 +1,17 @@ +skipsdist = true + +envlist = ["unused-code", "unittests"] + +[env.unused-code] +deps = ["python-utility-scripts"] +commands = [ + [ + "pyutils-unusedcode", + "--exclude-function-prefixes", + "'process_webhook'", + ], +] + +[env.unittests] +deps = ["uv"] +commands = [["uv", "run", "pytest", "-s", "webhook_server_container/tests"]] diff --git a/uv.lock b/uv.lock index 867158ee..c7f97b12 100644 --- a/uv.lock +++ b/uv.lock @@ -270,6 +270,70 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/06/00/a17a5657bf090b9dffdb310ac273c553a38f9252f60224da9fe62d9b60e9/Columnar-1.4.1-py3-none-any.whl", hash = "sha256:8efb692a7e6ca07dcc8f4ea889960421331a5dffa8e5af81f0a67ad8ea1fc798", size = 11845 }, ] +[[package]] +name = "coverage" +version = "7.6.7" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/bf/68/26895f8b068e384b1ec9ab122565b913b735e6b4c618b3d265a280607edc/coverage-7.6.7.tar.gz", hash = "sha256:d79d4826e41441c9a118ff045e4bccb9fdbdcb1d02413e7ea6eb5c87b5439d24", size = 799938 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/62/c9/84898713e61208ddbe71b991d8f311d9ca175629ce5f1a46018acc643572/coverage-7.6.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:108bb458827765d538abcbf8288599fee07d2743357bdd9b9dad456c287e121e", size = 206875 }, + { url = "https://files.pythonhosted.org/packages/f0/69/7dfd65f0e284617f72d974f6dfedc7bc16f86172e5bc6ebc8b63430263f3/coverage-7.6.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c973b2fe4dc445cb865ab369df7521df9c27bf40715c837a113edaa2aa9faf45", size = 207307 }, + { url = "https://files.pythonhosted.org/packages/d1/ce/6e356b2bc751bdaadd77c714336b98ec45ccaf0cfe085b6b25d34f7cceb8/coverage-7.6.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c6b24007c4bcd0b19fac25763a7cac5035c735ae017e9a349b927cfc88f31c1", size = 235744 }, + { url = "https://files.pythonhosted.org/packages/35/49/a7ab3d5a507d32344994cab856784e8d603c0b698070f7667c3ae41e8e50/coverage-7.6.7-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:acbb8af78f8f91b3b51f58f288c0994ba63c646bc1a8a22ad072e4e7e0a49f1c", size = 233645 }, + { url = "https://files.pythonhosted.org/packages/bd/41/de07328d2e79916fcc6cd53a5a1d18d163483519ab95f7f60fe15276811c/coverage-7.6.7-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad32a981bcdedb8d2ace03b05e4fd8dace8901eec64a532b00b15217d3677dd2", size = 234807 }, + { url = "https://files.pythonhosted.org/packages/e4/cc/2a669319b1295e0c52e8cfbbb163b32188b62f3b0bbe7014ef402b24b7cf/coverage-7.6.7-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:34d23e28ccb26236718a3a78ba72744212aa383141961dd6825f6595005c8b06", size = 233902 }, + { url = "https://files.pythonhosted.org/packages/68/71/a1bb90cb177358a2d364b3968a2069225f614d6824c3d959dee688ca0902/coverage-7.6.7-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e25bacb53a8c7325e34d45dddd2f2fbae0dbc230d0e2642e264a64e17322a777", size = 232363 }, + { url = "https://files.pythonhosted.org/packages/eb/dc/87551219d3437214523d1c7de0a717bead7a3369ed9bae05a7fd2854476f/coverage-7.6.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:af05bbba896c4472a29408455fe31b3797b4d8648ed0a2ccac03e074a77e2314", size = 233493 }, + { url = "https://files.pythonhosted.org/packages/ca/a4/d74ae3a3fb9e55fe5d9b811ce68a6bd8df3ae0a92c336acbc00075bc24fa/coverage-7.6.7-cp310-cp310-win32.whl", hash = "sha256:796c9b107d11d2d69e1849b2dfe41730134b526a49d3acb98ca02f4985eeff7a", size = 209593 }, + { url = "https://files.pythonhosted.org/packages/77/cb/7984c4d0404e8fcc4ada226b240965ef056e7a20e61a18c9038bf88e7624/coverage-7.6.7-cp310-cp310-win_amd64.whl", hash = "sha256:987a8e3da7da4eed10a20491cf790589a8e5e07656b6dc22d3814c4d88faf163", size = 210398 }, + { url = "https://files.pythonhosted.org/packages/c6/d7/1bf7bb0943237149ad01977190ac5c2e17add1f4fe7cabc06401682137f6/coverage-7.6.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7e61b0e77ff4dddebb35a0e8bb5a68bf0f8b872407d8d9f0c726b65dfabe2469", size = 206979 }, + { url = "https://files.pythonhosted.org/packages/83/eb/863b2cd654353b94c6ad834008df813424bf3e8f110e5f655fe5dc4c423b/coverage-7.6.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1a5407a75ca4abc20d6252efeb238377a71ce7bda849c26c7a9bece8680a5d99", size = 207431 }, + { url = "https://files.pythonhosted.org/packages/35/c9/d7a02a9654c41174fb99402c0fbd9583d0d2cb8714e7f948117fa7f919c4/coverage-7.6.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df002e59f2d29e889c37abd0b9ee0d0e6e38c24f5f55d71ff0e09e3412a340ec", size = 239368 }, + { url = "https://files.pythonhosted.org/packages/11/64/6c43a0ec43e5ddc5e09b0b589e3fd31def05fc463920d084e5af35fe527d/coverage-7.6.7-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:673184b3156cba06154825f25af33baa2671ddae6343f23175764e65a8c4c30b", size = 236769 }, + { url = "https://files.pythonhosted.org/packages/1c/dc/e77d98ae433c556c29328712a07fed0e6d159a63b2ec81039ce0a13a24a3/coverage-7.6.7-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e69ad502f1a2243f739f5bd60565d14a278be58be4c137d90799f2c263e7049a", size = 238634 }, + { url = "https://files.pythonhosted.org/packages/cc/84/50df3a8426d686057496171b4ccdb64528dacc4f42e94dceb7de3c598a69/coverage-7.6.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:60dcf7605c50ea72a14490d0756daffef77a5be15ed1b9fea468b1c7bda1bc3b", size = 237562 }, + { url = "https://files.pythonhosted.org/packages/2e/0f/9560196247574c1ccdab64cb923d69119fd5abd5b3db28d601ab2b452861/coverage-7.6.7-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:9c2eb378bebb2c8f65befcb5147877fc1c9fbc640fc0aad3add759b5df79d55d", size = 236197 }, + { url = "https://files.pythonhosted.org/packages/df/14/38b7c081e86e845df1867143ddb6e05bf8395f60ab3923c023a56d97cca1/coverage-7.6.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3c0317288f032221d35fa4cbc35d9f4923ff0dfd176c79c9b356e8ef8ef2dff4", size = 236970 }, + { url = "https://files.pythonhosted.org/packages/8b/f3/af34f814ca3814f798878ae368b638edb91298595470614f5265f3f416fa/coverage-7.6.7-cp311-cp311-win32.whl", hash = "sha256:951aade8297358f3618a6e0660dc74f6b52233c42089d28525749fc8267dccd2", size = 209557 }, + { url = "https://files.pythonhosted.org/packages/5a/9e/5d1080d83d752873bd9dedea5524c0f5fe68a3d5e1e58c590865bd724591/coverage-7.6.7-cp311-cp311-win_amd64.whl", hash = "sha256:5e444b8e88339a2a67ce07d41faabb1d60d1004820cee5a2c2b54e2d8e429a0f", size = 210402 }, + { url = "https://files.pythonhosted.org/packages/84/30/30e9df650b9038962c62d900b093a17414d5b43b4d07d47b8698d9e7ce26/coverage-7.6.7-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f07ff574986bc3edb80e2c36391678a271d555f91fd1d332a1e0f4b5ea4b6ea9", size = 207172 }, + { url = "https://files.pythonhosted.org/packages/88/8b/e28f86412317b9514692fd6f9d8ac6faa12494c3f470c3c63f202e10c756/coverage-7.6.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:49ed5ee4109258973630c1f9d099c7e72c5c36605029f3a91fe9982c6076c82b", size = 207406 }, + { url = "https://files.pythonhosted.org/packages/ac/46/da1bd9a3a893f74f5ce85f35e2755fcb00a80ed21e18d300c54f64938b1c/coverage-7.6.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3e8796434a8106b3ac025fd15417315d7a58ee3e600ad4dbcfddc3f4b14342c", size = 240424 }, + { url = "https://files.pythonhosted.org/packages/f6/12/af8e932496de1997bf4a36785d025ddac6427cbaf6954f26c2edaf21a58a/coverage-7.6.7-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a3b925300484a3294d1c70f6b2b810d6526f2929de954e5b6be2bf8caa1f12c1", size = 237456 }, + { url = "https://files.pythonhosted.org/packages/60/a2/23eb11eb60f825a84397cb94701d6f41d2e8e88ad7d0ba2b4339f38435fb/coverage-7.6.7-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3c42ec2c522e3ddd683dec5cdce8e62817afb648caedad9da725001fa530d354", size = 239527 }, + { url = "https://files.pythonhosted.org/packages/47/9e/63b318bc469308a32b0fbd6c80e2ea05dd7a2b7e840a46b3974843083a8c/coverage-7.6.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0266b62cbea568bd5e93a4da364d05de422110cbed5056d69339bd5af5685433", size = 239011 }, + { url = "https://files.pythonhosted.org/packages/99/47/1e84b067df3f021dfbc9cba09ec9acd4cb64938648a234e5bdf3006fd08b/coverage-7.6.7-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e5f2a0f161d126ccc7038f1f3029184dbdf8f018230af17ef6fd6a707a5b881f", size = 237316 }, + { url = "https://files.pythonhosted.org/packages/12/9d/96baaafc948d4a0ef2248a611d41051eea0917ef881d744879dd20be7c4a/coverage-7.6.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c132b5a22821f9b143f87446805e13580b67c670a548b96da945a8f6b4f2efbb", size = 238980 }, + { url = "https://files.pythonhosted.org/packages/87/d9/97af1886ca3f612d0cea2999d33e90d2f5b8fdf9bedc2d3bc75883efec4c/coverage-7.6.7-cp312-cp312-win32.whl", hash = "sha256:7c07de0d2a110f02af30883cd7dddbe704887617d5c27cf373362667445a4c76", size = 209801 }, + { url = "https://files.pythonhosted.org/packages/f8/4d/1e31c2018b1b3738154639f94188b1f54098fbf0f80c7ec104928576d0bb/coverage-7.6.7-cp312-cp312-win_amd64.whl", hash = "sha256:fd49c01e5057a451c30c9b892948976f5d38f2cbd04dc556a82743ba8e27ed8c", size = 210587 }, + { url = "https://files.pythonhosted.org/packages/21/87/c590d0c7eeb884995d9d06b429c5e88e9fcd65d3a6a686d9476cb50b72a9/coverage-7.6.7-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:46f21663e358beae6b368429ffadf14ed0a329996248a847a4322fb2e35d64d3", size = 207199 }, + { url = "https://files.pythonhosted.org/packages/40/ee/c88473c4f69c952f4425fabe045cb78d2027634ce50c9d7f7987d389b604/coverage-7.6.7-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:40cca284c7c310d622a1677f105e8507441d1bb7c226f41978ba7c86979609ab", size = 207454 }, + { url = "https://files.pythonhosted.org/packages/b8/07/afda6e10c50e3a8c21020c5c1d1b4f3d7eff1c190305cef2962adf8de018/coverage-7.6.7-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77256ad2345c29fe59ae861aa11cfc74579c88d4e8dbf121cbe46b8e32aec808", size = 239971 }, + { url = "https://files.pythonhosted.org/packages/85/43/bd1934b75e31f2a49665be6a6b7f8bfaff7266ba19721bdb90239f5e9ed7/coverage-7.6.7-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:87ea64b9fa52bf395272e54020537990a28078478167ade6c61da7ac04dc14bc", size = 237119 }, + { url = "https://files.pythonhosted.org/packages/2b/19/7a70458c1624724086195b40628e91bc5b9ca180cdfefcc778285c49c7b2/coverage-7.6.7-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2d608a7808793e3615e54e9267519351c3ae204a6d85764d8337bd95993581a8", size = 239109 }, + { url = "https://files.pythonhosted.org/packages/f3/2c/3dee671415ff13c05ca68243b2264fc95a5eea57697cffa7986b68b8f608/coverage-7.6.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdd94501d65adc5c24f8a1a0eda110452ba62b3f4aeaba01e021c1ed9cb8f34a", size = 238769 }, + { url = "https://files.pythonhosted.org/packages/37/ad/e0d1228638711aeacacc98d1197af6226b6d062d12c81a6bcc17d3234533/coverage-7.6.7-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:82c809a62e953867cf57e0548c2b8464207f5f3a6ff0e1e961683e79b89f2c55", size = 236854 }, + { url = "https://files.pythonhosted.org/packages/90/95/6467e9d9765a63c7f142703a7f212f6af114bd73a6c1cffeb7ad7f003a86/coverage-7.6.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:bb684694e99d0b791a43e9fc0fa58efc15ec357ac48d25b619f207c41f2fd384", size = 238701 }, + { url = "https://files.pythonhosted.org/packages/b2/7a/fc11a163f0fd6ce8539d0f1b565873fe6903b900214ff71b5d80d16154c3/coverage-7.6.7-cp313-cp313-win32.whl", hash = "sha256:963e4a08cbb0af6623e61492c0ec4c0ec5c5cf74db5f6564f98248d27ee57d30", size = 209865 }, + { url = "https://files.pythonhosted.org/packages/f2/91/58be3a56efff0c3481e48e2caa56d5d6f3c5c8d385bf4adbecdfd85484b0/coverage-7.6.7-cp313-cp313-win_amd64.whl", hash = "sha256:14045b8bfd5909196a90da145a37f9d335a5d988a83db34e80f41e965fb7cb42", size = 210597 }, + { url = "https://files.pythonhosted.org/packages/34/7e/fed983809c2eccb09c5ddccfdb08efb7f2dd1ae3454dabf1c92c5a2e9946/coverage-7.6.7-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:f2c7a045eef561e9544359a0bf5784b44e55cefc7261a20e730baa9220c83413", size = 207944 }, + { url = "https://files.pythonhosted.org/packages/c7/e0/2c1a157986a3927c3920e8e3938a3fdf33ea22b6f371dc3b679f13f619e2/coverage-7.6.7-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:5dd4e4a49d9c72a38d18d641135d2fb0bdf7b726ca60a103836b3d00a1182acd", size = 208215 }, + { url = "https://files.pythonhosted.org/packages/35/2f/77b086b228f6443ae5499467d1629c7428925b390cd171350c403bc00f14/coverage-7.6.7-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c95e0fa3d1547cb6f021ab72f5c23402da2358beec0a8e6d19a368bd7b0fb37", size = 250930 }, + { url = "https://files.pythonhosted.org/packages/60/d8/2ffea937d89ee328fc6e47c2515b890735bdf3f195d507d1c78b5fa96939/coverage-7.6.7-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f63e21ed474edd23f7501f89b53280014436e383a14b9bd77a648366c81dce7b", size = 246647 }, + { url = "https://files.pythonhosted.org/packages/b2/81/efbb3b00a7f7eb5f54a3b3b9f19b26d770a0b7d3870d651f07d2451c5504/coverage-7.6.7-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ead9b9605c54d15be228687552916c89c9683c215370c4a44f1f217d2adcc34d", size = 249006 }, + { url = "https://files.pythonhosted.org/packages/eb/91/ce36990cbefaf7909e96c888ed4d83f3471fc1be3273a5beda10896cde0f/coverage-7.6.7-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:0573f5cbf39114270842d01872952d301027d2d6e2d84013f30966313cadb529", size = 248500 }, + { url = "https://files.pythonhosted.org/packages/75/3f/b8c87dfdd96276870fb4abc7e2957cba7d20d8a435fcd816d807869ec833/coverage-7.6.7-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:e2c8e3384c12dfa19fa9a52f23eb091a8fad93b5b81a41b14c17c78e23dd1d8b", size = 246388 }, + { url = "https://files.pythonhosted.org/packages/a0/51/62273e1d5c25bb8fbef5fbbadc75b4a3e08c11b80516d0a97c25e5cced5b/coverage-7.6.7-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:70a56a2ec1869e6e9fa69ef6b76b1a8a7ef709972b9cc473f9ce9d26b5997ce3", size = 247669 }, + { url = "https://files.pythonhosted.org/packages/75/e5/d7772e56a7eace80e98ac39f2756d4b690fc0ce2384418174e02519a26a8/coverage-7.6.7-cp313-cp313t-win32.whl", hash = "sha256:dbba8210f5067398b2c4d96b4e64d8fb943644d5eb70be0d989067c8ca40c0f8", size = 210510 }, + { url = "https://files.pythonhosted.org/packages/2d/12/f2666e4e36b43221391ffcd971ab0c50e19439c521c2c87cd7e0b49ddba2/coverage-7.6.7-cp313-cp313t-win_amd64.whl", hash = "sha256:dfd14bcae0c94004baba5184d1c935ae0d1231b8409eb6c103a5fd75e8ecdc56", size = 211660 }, + { url = "https://files.pythonhosted.org/packages/e1/ec/dc663f7d34651aca74a531d10800595d9ec28a78b8306705721900b17a23/coverage-7.6.7-pp39.pp310-none-any.whl", hash = "sha256:0ddcb70b3a3a57581b450571b31cb774f23eb9519c2aaa6176d3a84c9fc57671", size = 199113 }, +] + +[package.optional-dependencies] +toml = [ + { name = "tomli", marker = "python_full_version <= '3.11'" }, +] + [[package]] name = "cryptography" version = "43.0.3" @@ -378,6 +442,7 @@ dependencies = [ { name = "pygithub" }, { name = "pyhelper-utils" }, { name = "pytest" }, + { name = "pytest-cov" }, { name = "pytest-mock" }, { name = "python-simple-logger" }, { name = "pyyaml" }, @@ -406,6 +471,7 @@ requires-dist = [ { name = "pygithub", specifier = ">=2.4.0" }, { name = "pyhelper-utils", specifier = ">=0.0.42" }, { name = "pytest", specifier = ">=8.3.3" }, + { name = "pytest-cov", specifier = ">=6.0.0" }, { name = "pytest-mock", specifier = ">=3.14.0" }, { name = "python-simple-logger", specifier = ">=1.0.40" }, { name = "pyyaml", specifier = ">=6.0.2" }, @@ -447,7 +513,7 @@ name = "importlib-metadata" version = "8.5.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "zipp" }, + { name = "zipp", marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/cd/12/33e59336dca5be0c398a7482335911a33aa0e20776128f038019f1a95f1b/importlib_metadata-8.5.0.tar.gz", hash = "sha256:71522656f0abace1d072b9e5481a48f07c138e00f079c38c8f883823f9c26bd7", size = 55304 } wheels = [ @@ -935,6 +1001,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/6b/77/7440a06a8ead44c7757a64362dd22df5760f9b12dc5f11b6188cd2fc27a0/pytest-8.3.3-py3-none-any.whl", hash = "sha256:a6853c7375b2663155079443d2e45de913a911a11d669df02a50814944db57b2", size = 342341 }, ] +[[package]] +name = "pytest-cov" +version = "6.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "coverage", extra = ["toml"] }, + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/be/45/9b538de8cef30e17c7b45ef42f538a94889ed6a16f2387a6c89e73220651/pytest-cov-6.0.0.tar.gz", hash = "sha256:fde0b595ca248bb8e2d76f020b465f3b107c9632e6a1d1705f17834c89dcadc0", size = 66945 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/36/3b/48e79f2cd6a61dbbd4807b4ed46cb564b4fd50a76166b1c4ea5c1d9e2371/pytest_cov-6.0.0-py3-none-any.whl", hash = "sha256:eee6f1b9e61008bd34975a4d5bab25801eb31898b032dd55addc93e96fcaaa35", size = 22949 }, +] + [[package]] name = "pytest-mock" version = "3.14.0" diff --git a/webhook_server_container/libs/github_api.py b/webhook_server_container/libs/github_api.py index 3b7db898..0cd7cb84 100644 --- a/webhook_server_container/libs/github_api.py +++ b/webhook_server_container/libs/github_api.py @@ -1408,7 +1408,11 @@ def check_if_can_be_merged(self) -> None: pr_approved = True if not pr_approved: - missing_approvers = [approver for approver in self.all_approvers if approver != self.parent_committer] + missing_approvers = [ + approver + for approver in self.all_approvers + if approver != self.parent_committer and approver not in _pr_approvers + ] failure_output += f"Missing lgtm/approved from approvers: {', '.join(missing_approvers)}\n" if not failure_output: From 1e724e188c85e153165aafabaa82f4cdf572f6f3 Mon Sep 17 00:00:00 2001 From: Meni Yakove Date: Sat, 23 Nov 2024 12:49:31 +0200 Subject: [PATCH 28/31] can_be_merged: extract to function, add more owners tests --- webhook_server_container/libs/github_api.py | 224 +++++++++++------- .../tests/test_github_api.py | 55 ++++- 2 files changed, 188 insertions(+), 91 deletions(-) diff --git a/webhook_server_container/libs/github_api.py b/webhook_server_container/libs/github_api.py index 0cd7cb84..9fefddc7 100644 --- a/webhook_server_container/libs/github_api.py +++ b/webhook_server_container/libs/github_api.py @@ -11,6 +11,7 @@ import time from concurrent.futures import Future, ThreadPoolExecutor, as_completed from typing import Any, Callable, Dict, Generator, List, Optional, Set, Tuple +from github.CheckRun import CheckRun from stringcolor import cs from github.Branch import Branch @@ -1321,99 +1322,37 @@ def check_if_can_be_merged(self) -> None: try: self.logger.info(f"{self.log_prefix} Check if {CAN_BE_MERGED_STR}.") - self.all_required_status_checks = self.get_all_required_status_checks() self.set_merge_check_queued() last_commit_check_runs = list(self.last_commit.get_check_runs()) - self.logger.debug(f"{self.log_prefix} Check if any required check runs in progress.") - check_runs_in_progress = [ - check_run.name - for check_run in last_commit_check_runs - if check_run.status == IN_PROGRESS_STR - and check_run.name != CAN_BE_MERGED_STR - and check_run.name in self.all_required_status_checks - ] - if check_runs_in_progress: - self.logger.debug( - f"{self.log_prefix} Some required check runs in progress {check_runs_in_progress}, " - f"skipping check if {CAN_BE_MERGED_STR}." - ) - failure_output += f"Some required check runs in progress {', '.join(check_runs_in_progress)}\n" - _labels = self.pull_request_labels_names() - is_hold = HOLD_LABEL_STR in _labels - is_wip = WIP_STR in _labels - - if is_hold or is_wip: - if is_hold: - failure_output += "Hold label exists.\n" - - if is_wip: - failure_output += "WIP label exists.\n" + self.logger.debug(f"{self.log_prefix} check if can be merged. PR labels are: {_labels}") if not self.pull_request.mergeable: failure_output += "PR is not mergeable: {self.pull_request.mergeable_state}\n" - failed_check_runs = [] - for check_run in last_commit_check_runs: - if ( - check_run.name == CAN_BE_MERGED_STR - or check_run.conclusion == SUCCESS_STR - or check_run.conclusion == QUEUED_STR - or check_run.name not in self.all_required_status_checks - ): - continue + required_check_in_progress_failure_output, check_runs_in_progress = self._required_check_in_progress( + last_commit_check_runs=last_commit_check_runs + ) + if required_check_in_progress_failure_output: + failure_output += required_check_in_progress_failure_output - failed_check_runs.append(check_run.name) + labels_failure_output = self._wip_or_hold_lables_exists(labels=_labels) + if labels_failure_output: + failure_output += labels_failure_output - if failed_check_runs: - exclude_in_progress = [ - failed_check_run - for failed_check_run in failed_check_runs - if failed_check_run not in check_runs_in_progress - ] - failure_output += f"Some check runs failed: {', '.join(exclude_in_progress)}\n" + required_check_failed_failure_output = self._required_check_failed( + last_commit_check_runs=last_commit_check_runs, check_runs_in_progress=check_runs_in_progress + ) + if required_check_failed_failure_output: + failure_output += required_check_failed_failure_output - self.logger.debug(f"{self.log_prefix} check if can be merged. PR labels are: {_labels}") + lables_failue_output = self._check_lables_for_can_be_merged(labels=_labels) + if lables_failue_output: + failure_output += lables_failue_output - for _label in _labels: - if CHANGED_REQUESTED_BY_LABEL_PREFIX.lower() in _label.lower(): - change_request_user = _label.split("-")[-1] - if change_request_user in self.all_approvers: - failure_output += "PR has changed requests from approvers\n" - - missing_required_labels = [] - for _req_label in self.can_be_merged_required_labels: - if _req_label not in _labels: - missing_required_labels.append(_req_label) - - if missing_required_labels: - failure_output += f"Missing required labels: {', '.join(missing_required_labels)}\n" - - pr_approved = False - _pr_approvers: list[str] = [] - all_needed_approvers = self.owners_data_for_changed_files()["approvers"] - - for _label in _labels: - if APPROVED_BY_LABEL_PREFIX.lower() in _label.lower(): - approved_user = _label.split("-")[-1] - if self.parent_committer == approved_user: - continue - - for _need_approve in all_needed_approvers: - if approved_user in _need_approve: - _pr_approvers.append(approved_user) - break - - if len(_pr_approvers) == len(all_needed_approvers): - pr_approved = True - - if not pr_approved: - missing_approvers = [ - approver - for approver in self.all_approvers - if approver != self.parent_committer and approver not in _pr_approvers - ] - failure_output += f"Missing lgtm/approved from approvers: {', '.join(missing_approvers)}\n" + pr_approvered_failure_output = self._check_if_pr_approved(labels=_labels) + if pr_approvered_failure_output: + failure_output += pr_approvered_failure_output if not failure_output: self._add_label(label=CAN_BE_MERGED_STR) @@ -2129,7 +2068,9 @@ def get_all_approvers(self) -> list[str]: for _approver in list_of_approvers: _approvers.append(_approver) - return list(set(self.root_approvers + _approvers)) + reviewers = list(set(self.root_approvers + _approvers)) + reviewers.sort() + return reviewers def get_all_reviewers(self) -> list[str]: _reviewers: list[str] = [] @@ -2137,7 +2078,9 @@ def get_all_reviewers(self) -> list[str]: for _approver in list_of_reviewers: _reviewers.append(_approver) - return list(set(self.root_reviewers + _reviewers)) + approvers = list(set(self.root_reviewers + _reviewers)) + approvers.sort() + return approvers def owners_data_for_changed_files(self) -> dict[str, list[list[str]]]: data: dict[str, list[list[str]]] = {"approvers": [], "reviewers": []} @@ -2157,6 +2100,8 @@ def owners_data_for_changed_files(self) -> dict[str, list[list[str]]]: self.logger.debug(f"{self.log_prefix} Found approvers for {owners_dir}: {_approvers}") data["approvers"].append(_approvers) + data["reviewers"].sort() + data["approvers"].sort() return data def _validate_owners_content(self, content: Any, path: str) -> bool: @@ -2178,3 +2123,114 @@ def _validate_owners_content(self, content: Any, path: str) -> bool: except ValueError as e: self.logger.error(f"{self.log_prefix} Invalid OWNERS file {path}: {e}") return False + + def _required_check_in_progress(self, last_commit_check_runs: list[CheckRun]) -> tuple[str, list[str]]: + self.all_required_status_checks = self.get_all_required_status_checks() + last_commit_check_runs = list(self.last_commit.get_check_runs()) + self.logger.debug(f"{self.log_prefix} Check if any required check runs in progress.") + check_runs_in_progress = [ + check_run.name + for check_run in last_commit_check_runs + if check_run.status == IN_PROGRESS_STR + and check_run.name != CAN_BE_MERGED_STR + and check_run.name in self.all_required_status_checks + ] + if check_runs_in_progress: + self.logger.debug( + f"{self.log_prefix} Some required check runs in progress {check_runs_in_progress}, " + f"skipping check if {CAN_BE_MERGED_STR}." + ) + return f"Some required check runs in progress {', '.join(check_runs_in_progress)}\n", check_runs_in_progress + return "", [] + + def _required_check_failed(self, last_commit_check_runs: list[CheckRun], check_runs_in_progress: list[str]) -> str: + failed_check_runs = [] + for check_run in last_commit_check_runs: + if ( + check_run.name == CAN_BE_MERGED_STR + or check_run.conclusion == SUCCESS_STR + or check_run.conclusion == QUEUED_STR + or check_run.name not in self.all_required_status_checks + ): + continue + + failed_check_runs.append(check_run.name) + + if failed_check_runs: + exclude_in_progress = [ + failed_check_run + for failed_check_run in failed_check_runs + if failed_check_run not in check_runs_in_progress + ] + return f"Some check runs failed: {', '.join(exclude_in_progress)}\n" + + return "" + + def _wip_or_hold_lables_exists(self, labels: list[str]) -> str: + failure_output = "" + is_hold = HOLD_LABEL_STR in labels + is_wip = WIP_STR in labels + + if is_hold or is_wip: + if is_hold: + failure_output += "Hold label exists.\n" + + if is_wip: + failure_output += "WIP label exists.\n" + + return failure_output + + def _check_lables_for_can_be_merged(self, labels: list[str]) -> str: + failure_output = "" + + for _label in labels: + if CHANGED_REQUESTED_BY_LABEL_PREFIX.lower() in _label.lower(): + change_request_user = _label.split("-")[-1] + if change_request_user in self.all_approvers: + failure_output += "PR has changed requests from approvers\n" + + missing_required_labels = [] + for _req_label in self.can_be_merged_required_labels: + if _req_label not in labels: + missing_required_labels.append(_req_label) + + if missing_required_labels: + failure_output += f"Missing required labels: {', '.join(missing_required_labels)}\n" + + return failure_output + + def _check_if_pr_approved(self, labels: list[str]) -> str: + failure_output = "" + pr_approved = False + _pr_approvers: list[str] = [] + _all_needed_approvers = self.owners_data_for_changed_files()["approvers"] + all_needed_approvers = [] + for approvers_list in _all_needed_approvers: + if approvers_list not in all_needed_approvers: + all_needed_approvers.append(approvers_list) + + # all_needed_approvers is [['approver1', 'approver2'], ['approver3', 'approver4']] + # To mark PR as approved we need at least one lgtm/approved from each nested list inside all_needed_approvers + for _label in labels: + if APPROVED_BY_LABEL_PREFIX.lower() in _label.lower(): + approved_user = _label.split("-")[-1] + if self.parent_committer == approved_user: + continue + + for _need_approve in all_needed_approvers: + if approved_user in _need_approve: + _pr_approvers.append(approved_user) + break + + if len(_pr_approvers) >= len(all_needed_approvers): + pr_approved = True + + if not pr_approved: + missing_approvers = [ + approver + for approver in self.all_approvers + if approver != self.parent_committer and approver not in _pr_approvers + ] + failure_output += f"Missing lgtm/approved from approvers: {', '.join(missing_approvers)}\n" + + return failure_output diff --git a/webhook_server_container/tests/test_github_api.py b/webhook_server_container/tests/test_github_api.py index 4eb403cf..d60825f8 100644 --- a/webhook_server_container/tests/test_github_api.py +++ b/webhook_server_container/tests/test_github_api.py @@ -5,7 +5,12 @@ from stringcolor.ops import os import yaml from webhook_server_container.libs.github_api import ProcessGithubWehook -from webhook_server_container.utils.constants import SIZE_LABEL_PREFIX +from webhook_server_container.utils.constants import APPROVED_BY_LABEL_PREFIX, SIZE_LABEL_PREFIX + + +class Label: + def __init__(self, name: str): + self.name = name class Tree: @@ -52,9 +57,18 @@ def get_contents(self, path: str): class PullRequest: - def __init__(self, additions: int, deletions: int): + def __init__(self, additions: int, deletions: int, labels: list[str] | None = None): self.additions = additions self.deletions = deletions + self.labels = labels or [] + + @property + def lables(self) -> list[Label]: + _lables = [] + for label in self.labels: + _lables.append(Label(label)) + + return _lables @pytest.fixture(scope="function") @@ -94,7 +108,7 @@ def test_get_size_thresholds(process_github_webhook, additions, deletions, expec assert result == f"{SIZE_LABEL_PREFIX}{expected_label}" -def test_get_approvers_and_reviewers(process_github_webhook): +def test_get_approvers_and_reviewers(mocker, process_github_webhook): process_github_webhook.repository = Repository() read_owners_result = process_github_webhook.get_approvers_and_reviewers() process_github_webhook.approvers_and_reviewers = { @@ -118,16 +132,43 @@ def test_get_approvers_and_reviewers(process_github_webhook): ["reviewer1", "reviewer2"], ], } - owners_data_chaged_files_result["approvers"].sort() - owners_data_chaged_files_result["reviewers"].sort() + # owners_data_chaged_files_result["approvers"].sort() + # owners_data_chaged_files_result["reviewers"].sort() owners_data_chaged_files_expected["approvers"].sort() owners_data_chaged_files_expected["reviewers"].sort() assert owners_data_chaged_files_result == owners_data_chaged_files_expected all_approvers = process_github_webhook.get_all_approvers() - all_approvers.sort() + # all_approvers.sort() assert all_approvers == ["approver1", "approver2", "approver3", "approver4"] + process_github_webhook.all_approvers = all_approvers all_reviewers = process_github_webhook.get_all_reviewers() - all_reviewers.sort() + # all_reviewers.sort() assert all_reviewers == ["reviewer1", "reviewer2", "reviewer3", "reviewer4"] + process_github_webhook.all_reviewers = all_reviewers + + pr_approved_all_result = process_github_webhook._check_if_pr_approved( + labels=[ + f"{APPROVED_BY_LABEL_PREFIX}approver1", + f"{APPROVED_BY_LABEL_PREFIX}approver2", + f"{APPROVED_BY_LABEL_PREFIX}approver3", + f"{APPROVED_BY_LABEL_PREFIX}approver4", + ] + ) + assert pr_approved_all_result == "" + + pr_approved_minimum_result = process_github_webhook._check_if_pr_approved( + labels=[ + f"{APPROVED_BY_LABEL_PREFIX}approver1", + f"{APPROVED_BY_LABEL_PREFIX}approver3", + ] + ) + assert pr_approved_minimum_result == "" + + pr_not_approved_result = process_github_webhook._check_if_pr_approved( + labels=[ + f"{APPROVED_BY_LABEL_PREFIX}approver1", + ] + ) + assert pr_not_approved_result == "Missing lgtm/approved from approvers: approver2, approver3, approver4\n" From 69c77e9e9a326fa5f7587c2e0780e3a47dd95654 Mon Sep 17 00:00:00 2001 From: Meni Yakove Date: Sat, 23 Nov 2024 13:03:26 +0200 Subject: [PATCH 29/31] Split tests --- .../tests/test_github_api.py | 36 ++++++++++++------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/webhook_server_container/tests/test_github_api.py b/webhook_server_container/tests/test_github_api.py index d60825f8..c7013d4d 100644 --- a/webhook_server_container/tests/test_github_api.py +++ b/webhook_server_container/tests/test_github_api.py @@ -89,6 +89,20 @@ def process_github_webhook(mocker): return process_github_webhook +@pytest.fixture(scope="function") +def approvers_and_reviewers(process_github_webhook): + process_github_webhook.approvers_and_reviewers = { + ".": {"approvers": ["approver1", "approver2"], "reviewers": ["reviewer1", "reviewer2"]}, + "test1": {"approvers": ["approver3", "approver4"], "reviewers": ["reviewer3", "reviewer4"]}, + } + + +@pytest.fixture(scope="function") +def all_approvers_reviewers(process_github_webhook): + process_github_webhook.all_approvers = ["approver1", "approver2", "approver3", "approver4"] + process_github_webhook.all_reviewers = ["reviewer1", "reviewer2", "reviewer3", "reviewer4"] + + @pytest.mark.parametrize( "additions, deletions, expected_label", [ @@ -108,15 +122,13 @@ def test_get_size_thresholds(process_github_webhook, additions, deletions, expec assert result == f"{SIZE_LABEL_PREFIX}{expected_label}" -def test_get_approvers_and_reviewers(mocker, process_github_webhook): +def test_get_approvers_and_reviewers(process_github_webhook, approvers_and_reviewers): process_github_webhook.repository = Repository() read_owners_result = process_github_webhook.get_approvers_and_reviewers() - process_github_webhook.approvers_and_reviewers = { - ".": {"approvers": ["approver1", "approver2"], "reviewers": ["reviewer1", "reviewer2"]}, - "test1": {"approvers": ["approver3", "approver4"], "reviewers": ["reviewer3", "reviewer4"]}, - } assert read_owners_result == process_github_webhook.approvers_and_reviewers + +def test_owners_data_for_changed_files(process_github_webhook, approvers_and_reviewers): owners_data_chaged_files_result = process_github_webhook.owners_data_for_changed_files() owners_data_chaged_files_expected = { "approvers": [ @@ -132,22 +144,20 @@ def test_get_approvers_and_reviewers(mocker, process_github_webhook): ["reviewer1", "reviewer2"], ], } - # owners_data_chaged_files_result["approvers"].sort() - # owners_data_chaged_files_result["reviewers"].sort() owners_data_chaged_files_expected["approvers"].sort() owners_data_chaged_files_expected["reviewers"].sort() assert owners_data_chaged_files_result == owners_data_chaged_files_expected + +def test_all_approvers_reviewers(process_github_webhook, approvers_and_reviewers, all_approvers_reviewers): all_approvers = process_github_webhook.get_all_approvers() - # all_approvers.sort() - assert all_approvers == ["approver1", "approver2", "approver3", "approver4"] - process_github_webhook.all_approvers = all_approvers + assert all_approvers == process_github_webhook.all_approvers all_reviewers = process_github_webhook.get_all_reviewers() - # all_reviewers.sort() - assert all_reviewers == ["reviewer1", "reviewer2", "reviewer3", "reviewer4"] - process_github_webhook.all_reviewers = all_reviewers + assert all_reviewers == process_github_webhook.all_reviewers + +def test_check_if_pr_approved(process_github_webhook, approvers_and_reviewers, all_approvers_reviewers): pr_approved_all_result = process_github_webhook._check_if_pr_approved( labels=[ f"{APPROVED_BY_LABEL_PREFIX}approver1", From ee98b3c9bcc51c4ad46814fdde0e4c3a8ceff3a0 Mon Sep 17 00:00:00 2001 From: Meni Yakove Date: Sat, 23 Nov 2024 14:18:15 +0200 Subject: [PATCH 30/31] Fix partial approved PR use case --- webhook_server_container/libs/github_api.py | 34 +++++++++---------- .../tests/test_github_api.py | 10 +++++- 2 files changed, 25 insertions(+), 19 deletions(-) diff --git a/webhook_server_container/libs/github_api.py b/webhook_server_container/libs/github_api.py index 9fefddc7..4ccb21d4 100644 --- a/webhook_server_container/libs/github_api.py +++ b/webhook_server_container/libs/github_api.py @@ -2200,37 +2200,35 @@ def _check_lables_for_can_be_merged(self, labels: list[str]) -> str: return failure_output def _check_if_pr_approved(self, labels: list[str]) -> str: - failure_output = "" - pr_approved = False _pr_approvers: list[str] = [] - _all_needed_approvers = self.owners_data_for_changed_files()["approvers"] all_needed_approvers = [] - for approvers_list in _all_needed_approvers: + for approvers_list in self.owners_data_for_changed_files()["approvers"]: if approvers_list not in all_needed_approvers: all_needed_approvers.append(approvers_list) # all_needed_approvers is [['approver1', 'approver2'], ['approver3', 'approver4']] # To mark PR as approved we need at least one lgtm/approved from each nested list inside all_needed_approvers + approved_by = [] for _label in labels: if APPROVED_BY_LABEL_PREFIX.lower() in _label.lower(): approved_user = _label.split("-")[-1] if self.parent_committer == approved_user: continue - for _need_approve in all_needed_approvers: - if approved_user in _need_approve: - _pr_approvers.append(approved_user) - break + approved_by.append(approved_user) - if len(_pr_approvers) >= len(all_needed_approvers): - pr_approved = True + missing_approvers = self.all_approvers.copy() - if not pr_approved: - missing_approvers = [ - approver - for approver in self.all_approvers - if approver != self.parent_committer and approver not in _pr_approvers - ] - failure_output += f"Missing lgtm/approved from approvers: {', '.join(missing_approvers)}\n" + for owners_data in self.approvers_and_reviewers.values(): + _approvers = owners_data.get("approvers", []) + for approver in _approvers: + if approver in approved_by: + _pr_approvers.append(approver) + # Once we found approver in approved_by list, we remove all approvers from missing_approvers list for this owners file + {missing_approvers.remove(_approver) for _approver in _approvers if _approver in missing_approvers} # type: ignore + break - return failure_output + if missing_approvers: + return f"Missing lgtm/approved from approvers: {', '.join(missing_approvers)}\n" + + return "" diff --git a/webhook_server_container/tests/test_github_api.py b/webhook_server_container/tests/test_github_api.py index c7013d4d..392f9c4d 100644 --- a/webhook_server_container/tests/test_github_api.py +++ b/webhook_server_container/tests/test_github_api.py @@ -181,4 +181,12 @@ def test_check_if_pr_approved(process_github_webhook, approvers_and_reviewers, a f"{APPROVED_BY_LABEL_PREFIX}approver1", ] ) - assert pr_not_approved_result == "Missing lgtm/approved from approvers: approver2, approver3, approver4\n" + assert pr_not_approved_result == "Missing lgtm/approved from approvers: approver3, approver4\n" + + pr_partial_approved_result = process_github_webhook._check_if_pr_approved( + labels=[ + f"{APPROVED_BY_LABEL_PREFIX}approver1", + f"{APPROVED_BY_LABEL_PREFIX}approver2", + ] + ) + assert pr_partial_approved_result == "Missing lgtm/approved from approvers: approver3, approver4\n" From 62859cb154319755adf2d70857a4aece903040e6 Mon Sep 17 00:00:00 2001 From: Meni Yakove Date: Mon, 25 Nov 2024 10:18:43 +0200 Subject: [PATCH 31/31] Remove test owners file --- webhook_server_container/OWNERS | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 webhook_server_container/OWNERS diff --git a/webhook_server_container/OWNERS b/webhook_server_container/OWNERS deleted file mode 100644 index 0456f047..00000000 --- a/webhook_server_container/OWNERS +++ /dev/null @@ -1,6 +0,0 @@ -approvers: - - myakove - - dbasunag -reviewers: - - rnetser - - dbasunag